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

Result refactoring/#79 #84

Merged
merged 25 commits into from
Apr 29, 2024
Merged
Show file tree
Hide file tree
Changes from 14 commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
794f1e1
style: add style drawer overflow
yougyung Apr 18, 2024
0174175
feat: test storybook resultdetailcontent
yougyung Apr 19, 2024
dd33ac8
refactor: use httpErrorHandler resultCategoryDetailInfo
yougyung Apr 19, 2024
e17ea0c
refactor: move ResultCategoryDetailContainer in page
yougyung Apr 20, 2024
0e4dd11
feat: add suspense for loading ResultCategoryDetailContainer
yougyung Apr 24, 2024
32e0c76
feat: add queryparam to resultdetailcategory
yougyung Apr 24, 2024
a995bf6
refactor: refactor structure ResultCategoryDetailContainer
yougyung Apr 24, 2024
36bf087
chore: merge for commit
yougyung Apr 24, 2024
734d78f
feat: add skeleton component to ResultCategoryDetailContainer
yougyung Apr 24, 2024
6bd3ba6
refactor: refactor result-category-detail-content folder structure
yougyung Apr 24, 2024
48ebed3
rename: change file name
yougyung Apr 25, 2024
bce47c4
feat: implement synchronous modal, url query
yougyung Apr 25, 2024
d0a9048
feat: implement search url including querystring
yougyung Apr 25, 2024
dc23a2b
chore: fix incorrect comments
yougyung Apr 25, 2024
2d2df65
fix: add className attribute in Drawer
yougyung Apr 26, 2024
2938193
refactor: change to domain component ResultCategoryDetailContainer
yougyung Apr 26, 2024
fa02c44
fix: change to useRouter category query parameter
yougyung Apr 26, 2024
11021c2
fix: change structure ResultCategoryDetailContainer
yougyung Apr 26, 2024
c7ce414
fix: change variableName closeDialog to onClose
yougyung Apr 27, 2024
253fbd2
chore: add prefix comment
yougyung Apr 27, 2024
5c7c293
fix: change structure UserInfoNavigatorSkeleton
yougyung Apr 27, 2024
c783255
fix: change naming result-category-detail-container
yougyung Apr 27, 2024
31400f3
fix: change to domain component result-category-detail-dialog
yougyung Apr 27, 2024
5a78e22
fix: change folder structrue in result domain
yougyung Apr 27, 2024
2d519a5
fix: add cookie in ResultCategoryDetail request
yougyung Apr 27, 2024
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: 1 addition & 1 deletion app/(sub-page)/my/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import TakenLecture from '@/app/ui/lecture/taken-lecture';
import UserInfoNavigator, { UserInfoNavigatorSkeleton } from '@/app/ui/user/user-info-navigator/user-info-navigator';
import ContentContainer from '@/app/ui/view/atom/content-container';
import Drawer from '@/app/ui/view/molecule/drawer/drawer';
import { DIALOG_KEY } from '@/app/utils/key/dialog.key';
import { DIALOG_KEY } from '@/app/utils/key/dialog-key.util';
import { Suspense } from 'react';

export default function MyPage() {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,28 +1,22 @@
import Modal from '../../../ui/view/molecule/modal/modal';
import { useMediaQuery } from 'usehooks-ts';
import { DIALOG_KEY } from '@/app/utils/key/dialog.key';
import Drawer from '../../../ui/view/molecule/drawer/drawer';
import ResultCategoryDetailContent from '@/app/ui/result/result-category/result-category-detail-content';
import Responsive from '@/app/ui/responsive';
import ResultCategoryDetailContent from '@/app/ui/result/result-category/result-category-detail-content/result-category-detail-content';
import { fetchResultCategoryDetailInfo } from '@/app/business/result/result.query';
import ResultCategoryDetailContentSkeleton from '@/app/ui/result/result-category/result-category-detail-content/result-category-detail-content.skeleton';
import ResultCategoryDetailDialog from './result-category-detail-dialog';

async function ResultCategoryDetailContainer() {
const data = await fetchResultCategoryDetailInfo();
export default async function ResultCategoryDetailContainer({ category }: { category: string }) {
const data = await fetchResultCategoryDetailInfo(category);

return (
<>
<Responsive maxWidth={767}>
<Drawer drawerKey={DIALOG_KEY.RESULT_CATEGORY}>
<ResultCategoryDetailContent info={data} />
</Drawer>
</Responsive>
<Responsive minWidth={768}>
<Modal modalKey={DIALOG_KEY.RESULT_CATEGORY}>
<ResultCategoryDetailContent info={data} />
</Modal>
</Responsive>
</>
<ResultCategoryDetailDialog querystring={category}>
<ResultCategoryDetailContent info={data} />
</ResultCategoryDetailDialog>
);
}

export default ResultCategoryDetailContainer;
export function ResultCategoryDetailSkeleton() {
return (
<ResultCategoryDetailDialog>
<ResultCategoryDetailContentSkeleton />
</ResultCategoryDetailDialog>
);
}
44 changes: 44 additions & 0 deletions app/(sub-page)/result/components/result-category-detail-dialog.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
'use client';
import Modal from '../../../ui/view/molecule/modal/modal';
import { DIALOG_KEY } from '@/app/utils/key/dialog-key.util';
import Drawer from '../../../ui/view/molecule/drawer/drawer';
import Responsive from '@/app/ui/responsive';
import React, { useEffect } from 'react';
import { useAtomValue } from 'jotai';
import { isDialogOpenAtom } from '@/app/store/dialog';
import useDialog from '@/app/hooks/useDialog';

interface ResultCategoryDetailDialogProp {
children: React.ReactNode;
querystring?: string;
}

export default function ResultCategoryDetailDialog({ children, querystring }: ResultCategoryDetailDialogProp) {
const isOpenDialog = useAtomValue(isDialogOpenAtom);

const { isOpen, open } = useDialog(DIALOG_KEY.RESULT_CATEGORY);
yougyung marked this conversation as resolved.
Show resolved Hide resolved

useEffect(() => {
if (querystring && !isOpen) open();
}, []);

const handleCloseDialog = () => {
if (isOpenDialog) window.history.go(-1);
window.history.replaceState({}, '', '/result');
yougyung marked this conversation as resolved.
Show resolved Hide resolved
};

return (
<>
<Responsive maxWidth={767}>
<Drawer drawerKey={DIALOG_KEY.RESULT_CATEGORY} closeDialog={handleCloseDialog}>
{children}
</Drawer>
</Responsive>
<Responsive minWidth={768}>
<Modal modalKey={DIALOG_KEY.RESULT_CATEGORY} closeDialog={handleCloseDialog}>
{children}
</Modal>
</Responsive>
</>
);
}
16 changes: 0 additions & 16 deletions app/(sub-page)/result/layout.tsx

This file was deleted.

16 changes: 15 additions & 1 deletion app/(sub-page)/result/page.tsx
yougyung marked this conversation as resolved.
Show resolved Hide resolved
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,18 @@ import ResultCategoryCard from '@/app/ui/result/result-category/result-category-
import UserInfoCard from '@/app/ui/user/user-info-card/user-info-card';
import ContentContainer from '@/app/ui/view/atom/content-container';
import { cn } from '@/app/utils/shadcn/utils';
import ResultCategoryDetailContainer, {
ResultCategoryDetailSkeleton,
} from './components/result-category-detail-container';
import { Suspense } from 'react';

interface ResultPageProp {
searchParams: { category: string };
}

function ResultPage({ searchParams }: ResultPageProp) {
const { category } = searchParams;

function ResultPage() {
return (
<div className="flex justify-center items-end">
<ContentContainer className="md:w-[700px] p-8">
Expand All @@ -19,7 +29,11 @@ function ResultPage() {
<ResultCategoryCard key={index} />
))}
</div>
<Suspense fallback={<ResultCategoryDetailSkeleton />}>
<ResultCategoryDetailContainer category={category} />
</Suspense>
</div>
);
}

export default ResultPage;
16 changes: 12 additions & 4 deletions app/business/result/result.query.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { LectureInfo } from '@/app/type/lecture';
import { API_PATH } from '../api-path';
import { httpErrorHandler } from '@/app/utils/http/http-error-handler';

export interface ResultCategoryDetailLectures {
categoryName: string;
Expand All @@ -26,8 +27,15 @@ export interface ResultUserInfo {
takenCredit: number;
}

export const fetchResultCategoryDetailInfo = async (): Promise<ResultCategoryDetailInfo> => {
const response = await fetch(API_PATH.resultCategoryDetailInfo);
const data = await response.json();
return data;
export const fetchResultCategoryDetailInfo = async (category: string): Promise<ResultCategoryDetailInfo> => {
//category๋ฅผ querystring์œผ๋กœ ํ˜ธ์ถœํ•˜๋Š” ๊ฑด์€ mock๋‹จ๊ณ„์—์„œ๋Š” ๋ถˆํ•„์š”ํ•  ๊ฒƒ์œผ๋กœ ์˜ˆ์ƒ, ์‹ค์ œ api ์—ฐ๊ฒฐ์‹œ ๋ณ€๊ฒฝ ์˜ˆ์ •
yougyung marked this conversation as resolved.
Show resolved Hide resolved
try {
const response = await fetch(API_PATH.resultCategoryDetailInfo);
const result = await response.json();

httpErrorHandler(response, result);
return result;
} catch (error) {
throw error;
}
};
13 changes: 10 additions & 3 deletions app/hooks/useDialog.tsx
Original file line number Diff line number Diff line change
@@ -1,20 +1,27 @@
import { DialogKey } from '../utils/key/dialog.key';
import { DialogKey } from '../utils/key/dialog-key.util';
import { updateDialogAtom } from '../store/dialog';
import { useAtom } from 'jotai';

export default function useDialog(key: DialogKey) {
export default function useDialog(key: DialogKey, closeDialog?: () => void) {
yougyung marked this conversation as resolved.
Show resolved Hide resolved
const [isOpenDialogList, setOpenDialogList] = useAtom(updateDialogAtom);

const isOpen = isOpenDialogList[key];

const open = () => {
setOpenDialogList([key, true]);
};

const close = () => {
setOpenDialogList([key, false]);
closeDialog?.();
};

const toggle = () => {
const prevState = isOpenDialogList[key];
setOpenDialogList([key, !prevState]);

if (prevState) closeDialog?.();
};

return { isOpen, close, toggle };
return { isOpen, open, close, toggle };
}
2 changes: 1 addition & 1 deletion app/mocks/handlers/result-handler.mock.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import { mockDatabase } from '../db.mock';
export const resultHandlers = [
http.get(API_PATH.resultCategoryDetailInfo, async () => {
const resultCategoryDetailInfo = mockDatabase.getResultCategoryDetailInfo();
await delay(100);
await delay(4000);
return HttpResponse.json(resultCategoryDetailInfo);
}),
];
4 changes: 3 additions & 1 deletion app/store/dialog.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { atom } from 'jotai';
import { DIALOG_KEY } from '../utils/key/dialog.key';
import { DIALOG_KEY } from '../utils/key/dialog-key.util';

const initialState = {
[DIALOG_KEY.RESULT_CATEGORY]: false,
Expand All @@ -9,6 +9,8 @@ const initialState = {

const dialogAtom = atom(initialState);

export const isDialogOpenAtom = atom(false);

export const updateDialogAtom = atom(
(get) => get(dialogAtom),
(get, set, [key, value]) => {
Expand Down
2 changes: 1 addition & 1 deletion app/ui/lecture/lecture-search/lecture-search.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import { screen } from '@storybook/testing-library';
import TakenLectureLabel from '../taken-lecture/taken-lecture-label';
import TakenLectureList from '../taken-lecture/taken-lecture-list';
import LectureSearch from '.';
import { DIALOG_KEY } from '@/app/utils/key/dialog.key';
import { DIALOG_KEY } from '@/app/utils/key/dialog-key.util';
import Drawer from '../../view/molecule/drawer/drawer';
import { delay } from 'msw';

Expand Down
2 changes: 1 addition & 1 deletion app/ui/lecture/taken-lecture/taken-lecture-label.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import Link from 'next/link';
import Button from '../../view/atom/button/button';
import LabelContainer from '../../view/atom/label-container/label-container';
import useDialog from '@/app/hooks/useDialog';
import { DIALOG_KEY } from '@/app/utils/key/dialog.key';
import { DIALOG_KEY } from '@/app/utils/key/dialog-key.util';

export default function TakenLectureLabel() {
const { toggle } = useDialog(DIALOG_KEY.LECTURE_SEARCH);
Expand Down
21 changes: 19 additions & 2 deletions app/ui/result/result-category/result-category-card.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,21 @@ import Book from '@/public/assets/book.svg';
import Image from 'next/image';
import useDialog from '@/app/hooks/useDialog';
import * as React from 'react';
import { DIALOG_KEY } from '@/app/utils/key/dialog.key';
import { DIALOG_KEY } from '@/app/utils/key/dialog-key.util';
import PieChart from '../../view/molecule/pie-chart/pie-chart';
import Button from '../../view/atom/button/button';
import Link from 'next/link';
import { useSetAtom } from 'jotai';
import { isDialogOpenAtom } from '@/app/store/dialog';

function ResultCategoryCard() {
const { toggle } = useDialog(DIALOG_KEY.RESULT_CATEGORY);
const setIsOpenDialog = useSetAtom(isDialogOpenAtom);

function handleClickButton() {
toggle();
setIsOpenDialog(true);
}
return (
<div
className={cn('flex flex-col gap-6 zIndex-1 rounded-xl shadow-lg bg-white p-[0.4rem]', 'md:w-80 md:p-[1.8rem]')}
Expand All @@ -34,7 +42,16 @@ function ResultCategoryCard() {
<span className="font-bold text-point-blue">18</span>
</div>
</div>
<Button size="sm" label="๊ณผ๋ชฉ ํ™•์ธ" onClick={toggle} />
<Link
href={{
pathname: '/result',
query: {
category: 'COMMON_CULTURE',
},
}}
>
<Button size="sm" label="๊ณผ๋ชฉ ํ™•์ธ" onClick={handleClickButton} />
</Link>
</div>
</div>
);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import LabelContainerSkeleton from '@/app/ui/view/atom/label-container/label-container.skeleton';
import Skeleton from '@/app/ui/view/atom/skeleton';

export default function ResultCategoryDetailContentSkeleton() {
return (
<div className="md:w-[80vw] max-w-[1200px] p-2">
<div className="flex justify-between mb-14">
<div className="flex flex-col gap-4">
<Skeleton className="h-10 w-40" />
<Skeleton className="h-4 w-80" />
</div>
<Skeleton className="h-10 w-28" />
</div>
{Array.from({ length: 3 }).map((_, index) => (
<div className="my-4 flex flex-col gap-4" key={index}>
<LabelContainerSkeleton rightElement={<Skeleton className="h-10 w-28" />} />
<Skeleton className="h-40 w-30" />
</div>
))}
</div>
);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import type { Meta, StoryObj } from '@storybook/react';
import { expect, userEvent } from '@storybook/test';
import { mockDatabase } from '@/app/mocks/db.mock';
import ResultCategoryDetailContent from './result-category-detail-content';
import { screen } from '@storybook/testing-library';
import { delay } from 'msw';

const meta = {
title: 'ui/result-category/ResultCategoryDetailContent',
component: ResultCategoryDetailContent,
args: { info: mockDatabase.getResultCategoryDetailInfo() },
decorators: [(Story) => <Story />],
} as Meta<typeof ResultCategoryDetailContent>;

export default meta;

type Story = StoryObj<typeof meta>;

export const Default: Story = {
play: async ({ step }) => {
await step('๋ฏธ์ด์ˆ˜ ๊ณผ๋ชฉ์„ ๋…ธ์ถœํ•ฉ๋‹ˆ๋‹ค.', async () => {
await delay(2000);

expect(screen.getByText('์˜์–ด2')).toBeInTheDocument();
});

await step('ํ† ๊ธ€์„ ํด๋ฆญํ•˜๋ฉด ๊ธฐ์ด์ˆ˜ ๊ณผ๋ชฉ์„ ๋…ธ์ถœํ•ฉ๋‹ˆ๋‹ค.', async () => {
const lectureToggle = await screen.findAllByTestId('lecture-toggle');
await userEvent.click(lectureToggle[0]);

await delay(3000);

expect(screen.queryByText('์„ฑ์„œ์™€ ์ธ๊ฐ„ ์ดํ•ด')).not.toBeInTheDocument();
});
},
};
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@
import { cn } from '@/app/utils/shadcn/utils';

import { useState } from 'react';
import { ResultCategoryDetailLectureToggle } from './result-category-detail-lecture-toggle';
import ResultCagegoryDetailLecture from './result-cagegory-detail-lecture';
import { ResultCategoryDetailLectureToggle } from '../result-category-detail-lecture-toggle';
import ResultCagegoryDetailLecture from '../result-cagegory-detail-lecture';
import { ResultCategoryDetailInfo } from '@/app/business/result/result.query';

interface ResultCategoryDetailContentProps {
Expand All @@ -16,7 +16,7 @@ function ResultCategoryDetailContent({ info }: ResultCategoryDetailContentProps)
const [isTakenLecture, setIsTakenLectrue] = useState(false);

return (
<div className="md:w-[80vw] max-w-[1200px] p-2">
<div className="md:w-[80vw] max-w-[1200px] p-2 overflow-scroll">
<div className="flex justify-between">
<div>
<h1 className={cn('text-2xl font-bold', 'md:text-4xl')}>์ „๊ณตํ•„์ˆ˜</h1>
Expand All @@ -25,6 +25,7 @@ function ResultCategoryDetailContent({ info }: ResultCategoryDetailContentProps)
<div className="w-20 h-10">
<ResultCategoryDetailLectureToggle
checked={isTakenLecture}
data-testid="lecture-toggle"
onCheckedChange={setIsTakenLectrue}
className="absolute zIndex-2"
/>
Expand Down
17 changes: 17 additions & 0 deletions app/ui/view/atom/label-container/label-container.skeleton.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import { ReactNode } from 'react';
import Skeleton from '../skeleton';

interface LabelContainerProps {
rightElement: ReactNode;
}

export default function LabelContainerSkeleton({ rightElement }: LabelContainerProps) {
return (
<div className="flex justify-between items-center w-full">
<div className="rounded-[100px] bg-light-blue-6 p-2.5 text-white lg:text-sm xl:text-base 2xl:text-lg font-semibold">
<Skeleton className="h-6 w-20" />
</div>
{rightElement}
</div>
);
}
Loading