Skip to content
Merged
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
58 changes: 38 additions & 20 deletions src/app/Router.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,48 +7,58 @@ import checkAuth from '@/entities/auth/lib/checkAuth';
import groupTokenUrlLoader from '@/entities/auth/lib/groupTokenUrlLoader';
import createExpensePageGuardLoader from '@/pages/CreateExpensePage/lib/createExpensePageGuardLoader';

const ExpenseDetail = lazy(() =>
const LazyExpenseDetail = lazy(() =>
import('@/pages/expenseDetail/').then(({ ExpenseDetailPage }) => ({
default: ExpenseDetailPage,
}))
);
const CharacterShare = lazy(() =>
const LazyCharacterShare = lazy(() =>
import('@/pages/characterShare').then(({ CharacterSharePage }) => ({
default: CharacterSharePage,
}))
);
const CreateExpense = lazy(() =>
const LazyCreateExpense = lazy(() =>
import('@/pages/CreateExpensePage').then(({ CreateExpensePage }) => ({
default: CreateExpensePage,
}))
);
const GroupSetup = lazy(() =>
const LazyGroupSetup = lazy(() =>
import('@/pages/groupSetup').then(({ GroupSetupPage }) => ({
default: GroupSetupPage,
}))
);
const Home = lazy(() =>
const LazyHome = lazy(() =>
import('@/pages/home').then(({ HomePage }) => ({ default: HomePage }))
);
const Login = lazy(() =>
const LazyLogin = lazy(() =>
import('@/pages/login').then(({ LoginPage }) => ({ default: LoginPage }))
);
const Onboarding = lazy(() =>
const LazyOnboarding = lazy(() =>
import('@/pages/onboarding').then(({ OnboardingPage }) => ({
default: OnboardingPage,
}))
);
const MyLinks = lazy(() =>
const LazyMyLinks = lazy(() =>
import('@/pages/myLinks').then(({ MyLinksPage }) => ({
default: MyLinksPage,
}))
);
const SelectGroup = lazy(() =>
const LazyMyPage = lazy(() =>
import('@/pages/my').then(({ MyPage }) => ({
default: MyPage,
}))
);
const LazyMyEditPage = lazy(() =>
import('@/pages/my-edit').then(({ MyEditPage }) => ({
default: MyEditPage,
}))
);
const LazySelectGroup = lazy(() =>
import('@/pages/selectGroup').then(({ SelectGroupPage }) => ({
default: SelectGroupPage,
}))
);
const NotFound = lazy(() =>
const LazyNotFound = lazy(() =>
import('@/pages/notFound').then(({ NotFoundPage }) => ({
default: NotFoundPage,
}))
Expand All @@ -69,7 +79,7 @@ function AppRouter() {
children: [
{
path: ROUTE.login,
element: <Login />,
element: <LazyLogin />,
},
{
id: 'protected',
Expand All @@ -78,45 +88,53 @@ function AppRouter() {
children: [
{
path: ROUTE.onboarding,
element: <Onboarding />,
element: <LazyOnboarding />,
},
{
path: ROUTE.home,
element: <Home />,
element: <LazyHome />,
},
{
path: ROUTE.myLinks,
element: <MyLinks />,
element: <LazyMyLinks />,
},
{
path: ROUTE.my,
element: <LazyMyPage />,
},
{
path: ROUTE.myEdit,
element: <LazyMyEditPage />,
},
{
path: ROUTE.selectGroup,
element: <SelectGroup />,
element: <LazySelectGroup />,
},
{
path: ROUTE.groupSetup,
element: <GroupSetup />,
element: <LazyGroupSetup />,
},
{
path: ROUTE.createExpense,
element: <CreateExpense />,
element: <LazyCreateExpense />,
loader: createExpensePageGuardLoader,
},
],
},
// TODO : 로그인 기능으로 변경될 예정
{
path: ROUTE.expenseDetail,
element: <ExpenseDetail />,
element: <LazyExpenseDetail />,
loader: groupTokenUrlLoader,
},
{
path: ROUTE.characterShare,
element: <CharacterShare />,
element: <LazyCharacterShare />,
loader: groupTokenUrlLoader,
},
{
path: '*',
element: <NotFound />,
element: <LazyNotFound />,
},
],
},
Expand Down
9 changes: 9 additions & 0 deletions src/entities/auth/api/auth.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import axiosInstance from '@/shared/api/axios';
import { User } from '../model/user.type';

// CHECK - 게스트 토큰 정책 제거 가능성 있음
export interface GuestTokenData {
Expand All @@ -21,3 +22,11 @@ export const getAuth = async () => {
});
return response.data;
};

export const getUserInfo = async () => {
const response = await axiosInstance.get<User>('/user/info', {
useMock: true,
});

return response.data;
};
9 changes: 9 additions & 0 deletions src/entities/auth/api/useGetUserInfo.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import { useSuspenseQuery } from '@tanstack/react-query';
import { getUserInfo } from './auth';

export const useGetUserInfo = () => {
return useSuspenseQuery({
queryKey: ['userInfo'],
queryFn: getUserInfo,
});
};
6 changes: 6 additions & 0 deletions src/entities/auth/model/user.type.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
export interface User {
id: number;
email: string;
name: string;
profileImageUrl?: string;
}
11 changes: 11 additions & 0 deletions src/features/user-profile/ui/MyProfile/index.styles.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import styled from 'styled-components';

export const ProfileContainer = styled.div`
display: flex;
align-items: center;
padding: ${({ theme }) => theme.unit[20]};
gap: ${({ theme }) => theme.unit[16]};
min-height: 5.925rem;
background-color: ${({ theme }) =>
theme.color.semantic.background.normal.alternative};
`;
44 changes: 44 additions & 0 deletions src/features/user-profile/ui/MyProfile/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
import { useNavigate } from 'react-router';
import { useTheme } from 'styled-components';
import { useGetUserInfo } from '@/entities/auth/api/useGetUserInfo';
import { ROUTE } from '@/shared/config/route';
import MemberProfileImage from '@/shared/ui/MemberProfileImage';
import Flex from '@/shared/ui/Flex';
import Text from '@/shared/ui/Text';
import Button from '@/shared/ui/Button';
import * as S from './index.styles';

function MyProfile() {
const { data: profile } = useGetUserInfo();
const navigate = useNavigate();
const theme = useTheme();

return (
<S.ProfileContainer>
<MemberProfileImage size="sm" src={profile?.profileImageUrl} />
<Flex direction="column" flex={1} gap={4}>
<Text variant="body1Sb">{profile.name}</Text>
{/* TODO: 디자인 시스템 정비 후 다시 디자인 확인이 필요합니다 (Opacity를 계속 쓰는지?) */}
<Text
variant="body2R"
color="semantic.text.default"
style={{ opacity: 0.5 }}
>
{profile.email}
</Text>
</Flex>
{/* TODO: 현 피그마 디자인은 Chip이 Button으로 쓰이고 있는 상황이라 우선 button 컴포넌트 기준으로 구현했습니다. 디자인시스템 정리 후 다시 확인이 필요합니다! */}
<Button
size="sm"
onClick={() => navigate(ROUTE.myEdit)}
style={{
backgroundColor: theme.color.semantic.background.normal.inverse,
}}
>
정보수정
</Button>
Comment on lines +30 to +39
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

현재 Chip이 버튼처럼 쓰이고 있는 부분이 많아서(홈 화면에서도 버튼처럼 쓰이고 있습니다), 홈페이지 디자인 반영 PR에서 Chip에 onClick props를 추가했습니다!
해당 수정에 대한 정연님의 의견이 궁금합니다 👀

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Chip에 onClick props를 추가할 수도 있지만 클릭 가능한 칩을 별도의 컴포넌트로 분리하는 것도 방법이 될 수 있을 것 같아요!

아직 수진님이 디자인 시스템을 정리중이시라 확정된 컴포넌트는 아니지만 지금까지 정리된 것을 봤을 땐 클릭 가능한 칩 형태의 컴포넌트는(홈 화면에 쓰이는 것!) tab 컴포넌트로 따로 분류되어 있더라구요. 이 부분에서 컴포넌트 정리하실 때 일반 칩이랑 클릭 가능한 칩을 분리하려는 의도가 아닐까 하는 생각을 했거든요.

제가 구현할 때 여길 TODO 로 남겨뒀던 이유는 클릭 가능한 컴포넌트는 button으로 정의하는 것이 역할이나 접근성 측면에서 더 좋지 않을까? 하는 생각에서였는데요.

만약에 홈 화면 tab 말고 다른 곳에서도 칩이 버튼처럼 사용되고 있는 부분이 있다면 좀 더 생각해봐야겠지만,,,
저는 클릭할 수 있는 칩이라면 따로 버튼 칩으로 만드는 것도 좋겠다는 의견입니다! (근데 이 부분은 컴포넌트 정리된거 보고 생각해봐도 좋을 것 같아요...!)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

아하 이 부분은 수진님과 논의가 더 필요하겠네요.
그럼 우선 props가 추가된 상태로 두고, 해당 props는 추후 디자인시스템 정리 후에 역할이 명확해지면 구조를 재검토하겠습니다!

</S.ProfileContainer>
);
}

export default MyProfile;
1 change: 1 addition & 0 deletions src/features/user-profile/ui/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { default as MyProfile } from './MyProfile';
13 changes: 13 additions & 0 deletions src/mocks/handlers/auth.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { User } from '@/entities/auth/model/user.type';
import { http, HttpResponse, passthrough } from 'msw';

const authHandlers = [
Expand All @@ -22,6 +23,18 @@ const authHandlers = [
user: { id: 'mock-test-user-id' },
});
}),

http.get('/api/v1/user/info', ({ request }) => {
const isMocked = request.headers.get('X-Mock-Request');
if (!isMocked || isMocked !== 'true') return passthrough();

const mockUserInfo: User = {
id: 1,
name: '김모또',
email: 'moddo@kakao.com',
};
return HttpResponse.json(mockUserInfo);
}),
];

export default authHandlers;
6 changes: 3 additions & 3 deletions src/pages/characterShare/CharacterSharePage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ function CharacterSharePage() {
const { groupToken } = useLoaderData();
const { data, isLoading, isError } = useGetCharacter(groupToken);
const navigate = useNavigate();
const { unit } = useTheme();
const { unit, color } = useTheme();
const imageRef = useRef<HTMLDivElement>(null);

const handleDownload = () => {
Expand Down Expand Up @@ -60,7 +60,7 @@ function CharacterSharePage() {
leftButtonOnClick={() => {
navigate(-1);
}}
bgColor="#F1F3F5"
bgColor={color.semantic.background.normal.alternative}
/>
<S.CharacterContainer>
<S.TitleContainer>
Expand All @@ -87,7 +87,7 @@ function CharacterSharePage() {
leftButtonOnClick={() => {
navigate(-1);
}}
bgColor="#F1F3F5"
bgColor="semantic.background.normal.alternative"
/>
<S.CharacterContainer>
<S.TitleContainer>
Expand Down
4 changes: 3 additions & 1 deletion src/pages/confirmStep/ConfirmStepPage.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { useLoaderData } from 'react-router';
import { useTheme } from 'styled-components';
import { ArrowLeft } from '@/shared/assets/svgs/icon';
import Header from '@/shared/ui/Header';
import DescriptionField from '@/shared/ui/DescriptionField';
Expand All @@ -21,6 +22,7 @@ interface ConfirmStepProps {
function ConfirmStepPage({ onNext, onBack, onEdit, onAdd }: ConfirmStepProps) {
const { groupToken } = useLoaderData();
const { data, isLoading } = useGetAllExpense(groupToken);
const { color } = useTheme();

if (isLoading) {
return <div>Loading...</div>;
Expand All @@ -38,7 +40,7 @@ function ConfirmStepPage({ onNext, onBack, onEdit, onAdd }: ConfirmStepProps) {
leftButtonOnClick={onBack}
rightButtonContent={<Text variant="body1Sb">지출 추가</Text>}
rightButtonOnClick={onAdd}
bgColor="#F1F3F5"
bgColor={color.semantic.background.normal.alternative}
/>
<DescriptionField
bgColor="semantic.background.normal.alternative"
Expand Down
5 changes: 2 additions & 3 deletions src/pages/expenseDetail/ExpenseDetailPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,12 +20,11 @@ import { StatusType } from './ui/ExpenseTimeHeader/index.type';
import * as S from './ExpenseDetailPage.styles';

function ExpenseDetailPage() {
const { unit } = useTheme();
const { unit, color } = useTheme();
const [activeTab, setActiveTab] = useState('member');
const { groupToken, groupData } = useLoaderData();
const [status, setStatus] = useState<StatusType>('pending');
const [openBottomSheet, setOpenBottomSheet] = useState<boolean>(false);
const theme = useTheme();
const { data: memberExpenseDetails } = useGetMemberExpenseDetails(groupToken);
const [isChecked, setIsChecked] = useState<boolean>(false);
const navigate = useNavigate();
Expand Down Expand Up @@ -63,7 +62,7 @@ function ExpenseDetailPage() {
관리
</Text>
}
bgColor={theme.color.semantic.background.normal.alternative}
bgColor={color.semantic.background.normal.alternative}
/>
<S.Content>
<ExpenseTimeHeader
Expand Down
5 changes: 5 additions & 0 deletions src/pages/my-edit/MyEditPage.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
function MyEditPage() {
return <div>정보 수정 페이지</div>;
}

export default MyEditPage;
1 change: 1 addition & 0 deletions src/pages/my-edit/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { default as MyEditPage } from './MyEditPage';
11 changes: 11 additions & 0 deletions src/pages/my/MyPage.styles.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import styled from 'styled-components';

export const ProfileContainer = styled.div`
display: flex;
align-items: center;
padding: ${({ theme }) => theme.unit[20]};
gap: ${({ theme }) => theme.unit[16]};
min-height: 5.925rem;
background-color: ${({ theme }) =>
theme.color.semantic.background.normal.alternative};
`;
31 changes: 31 additions & 0 deletions src/pages/my/MyPage.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import { Suspense } from 'react';
import { useNavigate } from 'react-router';
import { useTheme } from 'styled-components';
import { Menu } from '@/shared/assets/svgs/icon';
import Header from '@/shared/ui/Header';
import { ROUTE } from '@/shared/config/route';
import { MyProfile } from '@/features/user-profile/ui';
import * as S from './MyPage.styles';

function MyPage() {
const navigate = useNavigate();
const { color } = useTheme();

return (
<>
{/* TODO: 디자인 시스템 정리 + 헤더 컴포넌트 정비 후에 다시 스타일링이 필요합니다. */}
<Header
type="TitleCenter"
title="마이페이지"
rightButtonContent={<Menu width={24} />}
rightButtonOnClick={() => navigate(ROUTE.myEdit)}
bgColor={color.semantic.background.normal.alternative}
/>
<Suspense fallback={<S.ProfileContainer>로딩 중...</S.ProfileContainer>}>
<MyProfile />
</Suspense>
</>
);
}

export default MyPage;
1 change: 1 addition & 0 deletions src/pages/my/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { default as MyPage } from './MyPage';
Loading