Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: 설정 페이지 구성 #24

Open
wants to merge 5 commits into
base: feature/auth
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions apps/client/app/login/[slug]/page.tsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
'use client';

import { SettingKeywordPage, SettingLocationPage, SettingMatchPage, SettingNamePage } from '@components/login';

type Props = {
Expand Down
38 changes: 38 additions & 0 deletions apps/client/app/settings/keyword/page.css.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import { style } from '@vanilla-extract/css';

import { styleToken } from '@oseek/ui/core';

const { color } = styleToken;

export const rightAction = style({
display: 'flex',
alignItems: 'center',
justifyContent: 'flex-end',
});

export const Container = style({
backgroundColor: color.white,
flex: 1,
display: 'flex',
flexDirection: 'column',
alignItems: 'center',
});

export const ImageContainer = style({
width: '100%',
maxWidth: 80,
height: 'auto',
});

export const Image = style({
width: '100%',
height: 'auto',
});

export const TextFieldContainer = style({
display: 'flex',
flexDirection: 'column',
width: '100%',
gap: 16,
marginTop: 24,
});
33 changes: 33 additions & 0 deletions apps/client/app/settings/keyword/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
'use client';

import { useRouter } from 'next/navigation';
import { ArrowLeft, COLOR_PROPERTIES, IconButton } from '@oseek/ui';
import createLoginSettingSlice from '@store/slices/createLoginSettingSlice';
import { memberFoodKeywordApi } from '@oseek/apis';
import { MainLayout } from '../../../src/components';
import { SettingKeyword } from '../../../src/components/settings';

const KeywordPage = () => {
const router = useRouter();
const selectedKeywordCodes = createLoginSettingSlice((state) => state.selectedKeywordCodes);

const handleSubmit = async () => {
const foodKeywords = selectedKeywordCodes.map((foodKeyword) => ({ foodKeyword }));
await memberFoodKeywordApi.saveMemberFoodKeywordAxios(foodKeywords);
};

const handlePreviousPage = () => router.back();

return (
<MainLayout
title="취향설정"
activePath="/settings"
leftAction={<IconButton icon={<ArrowLeft color={COLOR_PROPERTIES.black} />} onClick={handlePreviousPage} />}
rightAction={<div />}
>
<SettingKeyword onSubmit={handleSubmit} submitText="저장" />
</MainLayout>
);
};

export default KeywordPage;
38 changes: 38 additions & 0 deletions apps/client/app/settings/notice/page.css.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import { style } from '@vanilla-extract/css';

import { styleToken } from '@oseek/ui/core';

const { color } = styleToken;

export const rightAction = style({
display: 'flex',
alignItems: 'center',
justifyContent: 'flex-end',
});

export const Container = style({
backgroundColor: color.white,
flex: 1,
display: 'flex',
flexDirection: 'column',
alignItems: 'center',
});

export const ImageContainer = style({
width: '100%',
maxWidth: 80,
height: 'auto',
});

export const Image = style({
width: '100%',
height: 'auto',
});

export const TextFieldContainer = style({
display: 'flex',
flexDirection: 'column',
width: '100%',
gap: 16,
marginTop: 24,
});
71 changes: 71 additions & 0 deletions apps/client/app/settings/notice/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
'use client';

import { useState, useEffect, useCallback } from 'react';
import { useRouter } from 'next/navigation';
import { ArrowLeft, COLOR_PROPERTIES, IconButton } from '@oseek/ui';
import { NoticeResDto } from '@oseek/apis';
import { MainLayout, Notice } from '../../../src/components';

const responseNotice = [
{
noticeId: 1,
title: '오늘의 식당 개인정보 처리방침 변경 안내',
registerDate: '23/04/25',
content: `안녕하세요 오늘의 식당입니다.\n\n저희 서비스를 이용해주시는 모든 분들께 감사드리며,\n2023년 04월 25일 이후로 오늘의 식당 서비스 이용약관이 개정됨을 알려드립니다.`,
},
{
noticeId: 2,
title: '봄과 가을 사이에서 찾아온 오늘의 식당 1.2.2 업데이트 안내',
registerDate: '23/04/25',
content: `안녕하세요 오늘의 식당입니다.\n\n저희 서비스를 이용해주시는 모든 분들께 감사드리며,\n2023년 04월 25일 이후로 오늘의 식당 서비스 이용약관이 개정됨을 알려드립니다.`,
},
{
noticeId: 3,
title: '공지사항 제목',
registerDate: '23/04/25',
content: `안녕하세요 오늘의 식당입니다.\n\n저희 서비스를 이용해주시는 모든 분들께 감사드리며,\n2023년 04월 25일 이후로 오늘의 식당 서비스 이용약관이 개정됨을 알려드립니다.`,
},
];

type NoticeType = Required<NoticeResDto> & { open: boolean };

const NoticePage = () => {
const router = useRouter();
// const { data: responseNotice } = useGetNotice();
const [notices, setNotices] = useState<NoticeType[]>([]);

const handlePreviousPage = useCallback(() => {
router.back();
}, [router]);

const handleOpenNotice = useCallback((id: number) => {
setNotices((prevNotices) =>
prevNotices.map((notice) => {
if (notice.noticeId === id) {
return { ...notice, open: !notice.open };
}
return notice;
}),
);
}, []);

useEffect(() => {
const noticeState = responseNotice?.map((notice) => ({ ...notice, open: false })) || [];
setNotices(noticeState);
}, []);

return (
<MainLayout
title="공지사항"
activePath="/settings"
leftAction={<IconButton icon={<ArrowLeft color={COLOR_PROPERTIES.black} />} onClick={handlePreviousPage} />}
rightAction={<div />}
>
{notices.map((notice) => (
<Notice key={notice.noticeId} id={notice.noticeId} title={notice.title} date={notice.registerDate} content={notice.content} open={notice.open} onClick={handleOpenNotice} />
))}
</MainLayout>
);
};

export default NoticePage;
18 changes: 18 additions & 0 deletions apps/client/app/settings/page.css.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import { style } from '@vanilla-extract/css';

import { styleToken } from '@oseek/ui/core';

const { color } = styleToken;

export const rightAction = style({
display: 'flex',
alignItems: 'center',
justifyContent: 'flex-end',
});

export const Container = style({
backgroundColor: color.white,
flex: 1,
display: 'flex',
flexDirection: 'column',
});
59 changes: 59 additions & 0 deletions apps/client/app/settings/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
'use client';

import { useRouter } from 'next/navigation';
import { COLOR_PROPERTIES, Notification } from '@oseek/ui';
import { PAGE_URL } from '@constant/pageUrl';
import { Container, Divider, MainLayout, useAuth } from '../../src/components';
import { Profile, MenuItem, RequiredLogin } from '../../src/components/settings';
import * as S from './page.css';

const { black } = COLOR_PROPERTIES;

const menuItems = [
{ path: PAGE_URL.SETTINGS_KEYWORD, title: '취향설정', isLoggined: true },
{ path: PAGE_URL.SETTINGS, title: '저장목록', isLoggined: true },
{ path: PAGE_URL.SETTINGS_NOTICE, title: '공지사항' },
{ path: PAGE_URL.SETTINGS, title: '환경설정' },
{ path: PAGE_URL.SETTINGS, title: '약관 및 정책' },
{ title: '앱 버전', subTitle: '1.0' },
];

const SettingsPage = () => {
const router = useRouter();
const { user, logout, isLoggedIn } = useAuth();

const handleMoveToProfile = () => {
router.push(PAGE_URL.SETTINGS_PROFILE);
};

return (
<MainLayout
activePath="/settings"
leftAction={<div />}
rightAction={
<div className={S.rightAction}>
<Notification color={black} />
</div>
}
>
<Container padding="0" className={S.Container}>
{isLoggedIn ? <Profile thumbnail="/images/thumbnail.png" nickname={user?.nickname} description="소개글을 입력해주세요" onClick={handleMoveToProfile} /> : <RequiredLogin />}
<Divider style={{ margin: 0, width: '100%' }} />
<Container padding="0">
{menuItems.map((item, index) => (item.isLoggined ? isLoggedIn && <MenuItem key={index} {...item} /> : <MenuItem key={index} {...item} />))}
</Container>

{isLoggedIn && (
<>
<Divider style={{ margin: 0, width: '100%' }} />
<Container padding="0">
<MenuItem title="로그아웃" onClick={logout} />
</Container>
</>
)}
</Container>
</MainLayout>
);
};

export default SettingsPage;
38 changes: 38 additions & 0 deletions apps/client/app/settings/profile/page.css.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import { style } from '@vanilla-extract/css';

import { styleToken } from '@oseek/ui/core';

const { color } = styleToken;

export const rightAction = style({
display: 'flex',
alignItems: 'center',
justifyContent: 'flex-end',
});

export const Container = style({
backgroundColor: color.white,
flex: 1,
display: 'flex',
flexDirection: 'column',
alignItems: 'center',
});

export const ImageContainer = style({
width: '100%',
maxWidth: 80,
height: 'auto',
});

export const Image = style({
width: '100%',
height: 'auto',
});

export const TextFieldContainer = style({
display: 'flex',
flexDirection: 'column',
width: '100%',
gap: 16,
marginTop: 24,
});
54 changes: 54 additions & 0 deletions apps/client/app/settings/profile/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
'use client';

import { ChangeEvent, Dispatch, SetStateAction, useEffect, useState } from 'react';
import { useRouter } from 'next/navigation';
import { ArrowLeft, COLOR_PROPERTIES, IconButton, TextField } from '@oseek/ui';
import { BottomSheet, Container, MainLayout, useAuth } from '../../../src/components';
import { memberApi } from '@oseek/apis';
import * as S from './page.css';

const ProfilePage = () => {
const router = useRouter();
const { user, setUser } = useAuth();
const [nickname, setNickname] = useState('');
const [description, setDescription] = useState('');

const handlePreviousPage = () => router.back();

const handleTextFieldChange = (setter: Dispatch<SetStateAction<string>>) => (e: ChangeEvent<HTMLInputElement>) => setter(e.target.value);

const handleSubmit = async () => {
try {
await memberApi.modifyMemberInfoAxios({ nickname, location: user?.location });
setUser({ ...user, nickname });
} catch (e) {
console.error(e);
}
};

useEffect(() => {
setNickname(user?.nickname || '');
}, [user]);

return (
<MainLayout
title="프로필 수정"
activePath="/settings"
leftAction={<IconButton icon={<ArrowLeft color={COLOR_PROPERTIES.black} />} onClick={handlePreviousPage} />}
rightAction={<div />}
>
<Container padding="20px 24px" className={S.Container}>
<div className={S.ImageContainer}>
<img className={S.Image} src="/images/thumbnail.png" alt="profile" />
</div>
<div className={S.TextFieldContainer}>
<TextField id="nick" label="닉네임" maxLength={12} value={nickname} onChange={handleTextFieldChange(setNickname)} placeholder="김오식 (최대 N자)" focus />
<TextField id="description" label="소개글" value={description} onChange={handleTextFieldChange(setDescription)} placeholder="먹는게 제일 좋아" />
</div>
</Container>
<BottomSheet text="저장" disabled={!nickname} onClick={handleSubmit} />
</MainLayout>
);
};

export default ProfilePage;
Binary file added apps/client/public/images/thumbnail.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added apps/client/public/images/user.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 1 addition & 0 deletions apps/client/src/components/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,5 +11,6 @@ export * from './login/index';
export * from './main/index';
export * from './provider/index';
export * from './section/index';
export * from './settings/index';
export * from './shop/index';
export * from './spacing/index';
2 changes: 1 addition & 1 deletion apps/client/src/components/layout/MainLayout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ type Props = { activePath?: string; title?: string; leftAction?: ReactElement; r

export const MainLayout = ({
children,
activePath = '/',
activePath = '/settings',
title = '',
leftAction = <IconButton icon={<ArrowLeft color="#000" />} />,
rightAction = <IconButton icon={<Search color="#000" style={{ marginLeft: 'auto' }} />} />,
Expand Down
2 changes: 2 additions & 0 deletions apps/client/src/components/login/LoginHeading.tsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
'use client';

import { ReactElement } from 'react';
import { Typography } from '@oseek/ui';

Expand Down
Loading