Skip to content

Commit

Permalink
chore: commit for merge
Browse files Browse the repository at this point in the history
  • Loading branch information
yougyung committed Apr 16, 2024
2 parents 5cce4d9 + 636c35b commit 2d77a84
Show file tree
Hide file tree
Showing 21 changed files with 188 additions and 32 deletions.
32 changes: 32 additions & 0 deletions app/(sub-page)/error.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
'use client';
import { useEffect } from 'react';
import Link from 'next/link';

export default function Error({ error, reset }: { error: Error; reset: () => void }) {
useEffect(() => {
console.error(error);
}, [error]);

if (error.message === 'Unauthorized') {
return (
<div>
<h2>Unauthorized</h2>
<Link href="/sign-in">to Login</Link>
</div>
);
}

return (
<div>
<h2>Something went wrong!!!!!</h2>
<button
onClick={
// 세그먼트를 재 렌더링 하여 복구를 시도합니다.
() => reset()
}
>
Try again
</button>
</div>
);
}
19 changes: 13 additions & 6 deletions app/(sub-page)/my/page.tsx
Original file line number Diff line number Diff line change
@@ -1,14 +1,21 @@
import LectureSearch from '@/app/ui/lecture/lecture-search';
import TakenLecture from '@/app/ui/lecture/taken-lecture';
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';

export default function MyPage() {
return (
<ContentContainer className="flex">
<div className="hidden lg:w-[30%] lg:block">정보칸</div>
<div className="w-full lg:w-[70%] lg:px-[20px]">
<TakenLecture />
</div>
</ContentContainer>
<>
<ContentContainer className="flex">
<div className="hidden lg:w-[30%] lg:block">정보칸</div>
<div className="w-full lg:w-[70%] lg:px-[20px]">
<TakenLecture />
</div>
</ContentContainer>
<Drawer drawerKey={DIALOG_KEY.LECTURE_SEARCH}>
<LectureSearch />
</Drawer>
</>
);
}
15 changes: 15 additions & 0 deletions app/(sub-page)/protected/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import { API_PATH } from '@/app/business/api-path';
import { httpErrorHandler } from '@/app/utils/http/http-error-handler';

async function trigger() {
const response = await fetch(`${API_PATH.auth}/failure`, {
cache: 'no-store',
});
const result = await response.json();
httpErrorHandler(response, result);
}

export default async function ProtectedPage() {
const data = await trigger();
return <div>Auth protected</div>;
}
2 changes: 1 addition & 1 deletion app/business/user/user.command.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ import { cookies } from 'next/headers';
import { isValidation } from '@/app/utils/zod/validation.util';
import { redirect } from 'next/navigation';

export async function validateToken(): Promise<ValidateTokenResponse | boolean> {
export async function validateToken(): Promise<ValidateTokenResponse | false> {
const accessToken = cookies().get('accessToken')?.value;
const refreshToken = cookies().get('refreshToken')?.value;
try {
Expand Down
27 changes: 27 additions & 0 deletions app/global-error.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
'use client';
import { useEffect } from 'react';

// https://nextjs.org/docs/app/building-your-application/routing/error-handling#handling-errors-in-root-layouts
// global-error는 프로덕션에서만 활성화
export default function GlobalError({ error, reset }: { error: Error & { digest?: string }; reset: () => void }) {
useEffect(() => {
// Log the error to an error reporting service
console.error(error);
}, [error]);

return (
<html>
<body>
<h2>Something went wrong!!!!!</h2>
<button
onClick={
// 세그먼트를 재 렌더링 하여 복구를 시도합니다.
() => reset()
}
>
Try again
</button>
</body>
</html>
);
}
13 changes: 11 additions & 2 deletions app/globals.css
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,15 @@ a {
font-family: 'Pretendard';
}

a:-webkit-any-link {
text-decoration: none;
::-webkit-scrollbar {
width: 10px;
}

::-webkit-scrollbar-track {
border-radius: 5px;
}

::-webkit-scrollbar-thumb {
background: #c4c4c4;
border-radius: 5px;
}
2 changes: 1 addition & 1 deletion app/mocks/handlers/taken-lecture-handler.mock.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ export const takenLectureHandlers = [
http.delete<never, { lectureId: number }>(API_PATH.takenLectures, async ({ request }) => {
const body = await request.json();
const isDeleted = mockDatabase.deleteTakenLecture(body.lectureId);
await delay(3000);
await delay(1000);
if (isDeleted) {
return HttpResponse.json({ message: '삭제되었습니다' }, { status: 200 });
}
Expand Down
4 changes: 4 additions & 0 deletions app/mocks/handlers/user-handler.mock.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,10 @@ function mockDecryptToken(token: string) {
}

export const userHandlers = [
http.get<never, never, never>(`${API_PATH.auth}/failure`, async ({ request }) => {
await delay(500);
return HttpResponse.json({ status: 401, message: 'Unauthorized' }, { status: 401 });
}),
http.post<never, never, ValidateTokenResponse>(`${API_PATH.auth}/token`, async ({ request }) => {
return HttpResponse.json({
accessToken: 'fake-access-token',
Expand Down
1 change: 1 addition & 0 deletions app/store/dialog.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { DIALOG_KEY } from '../utils/key/dialog.key';
const initialState = {
[DIALOG_KEY.RESULT_CATEGORY]: false,
[DIALOG_KEY.DIALOG_TEST]: true,
[DIALOG_KEY.LECTURE_SEARCH]: false,
};

const dialogAtom = atom(initialState);
Expand Down
8 changes: 5 additions & 3 deletions app/ui/lecture/lecture-search/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,11 @@ import LectureSearchResultContainer from './lecture-search-result-container';

export default function LectureSearch() {
return (
<div className="flex flex-col gap-4" data-testid="lecture-search-component">
<LectureSearchBar />
<LectureSearchResultContainer />
<div className="bg-white w-full h-[500px] sm:h-[400px] z-[10] flex justify-center">
<div className="w-[800px] mx-auto my-7 flex flex-col gap-10 sm:gap-6">
<LectureSearchBar />
<LectureSearchResultContainer />
</div>
</div>
);
}
7 changes: 3 additions & 4 deletions app/ui/lecture/lecture-search/lecture-search-bar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,16 +3,15 @@ import TextInput from '../../view/atom/text-input/text-input';
import { MagnifyingGlassIcon } from '@radix-ui/react-icons';

export default function LectureSearchBar() {
// 검색 기능을 해당 컴포넌트에서 구현 예정
return (
<div className="flex justify-between">
<div className="w-[15%]">
<div className="flex justify-between mt-4 sm:mt-0">
<div className="sm:w-[15%] w-[30%]">
<Select defaultValue="lectureName" placeholder="과목명">
<Select.Item value="lectureName" placeholder="과목명" />
<Select.Item value="lectureCode" placeholder="과목코드" />
</Select>
</div>
<div className="w-[40%] flex justify-between">
<div className="w-[60%] sm:w-[40%] flex justify-between">
<TextInput placeholder="검색어를 입력해주세요" icon={MagnifyingGlassIcon} />
</div>
</div>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,10 @@ export default function LectureSearchResultContainer() {
data={[
{ id: 3, lectureCode: 'HCB03490', name: '경영정보사례연구', credit: 3 },
{ id: 4, lectureCode: 'HCB03490', name: '게임을통한경영의이해', credit: 3 },
{ id: 5, lectureCode: 'HCB03490', name: '게임을통한경영의이해', credit: 3 },
{ id: 6, lectureCode: 'HCB03490', name: '게임을통한경영의이해', credit: 3 },
{ id: 7, lectureCode: 'HCB03490', name: '게임을통한경영의이해', credit: 3 },
{ id: 8, lectureCode: 'HCB03490', name: '게임을통한경영의이해', credit: 3 },
]}
render={render}
isScrollList={true}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ export default function DeleteTakenLectureButton({ lectureId, onDelete }: Delete
return (
<AlertDialog open={open} onOpenChange={setOpen}>
<AlertDialogTrigger asChild>
<div className="opacity-100 group-hover:opacity-100">
<div className="opacity-0 group-hover:opacity-100">
<Button label="삭제" variant="text" size="default" data-testid="taken-lecture-delete-button" />
</div>
</AlertDialogTrigger>
Expand Down
10 changes: 4 additions & 6 deletions app/ui/lecture/taken-lecture/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,10 @@ export default async function TakenLecture() {
const data = await fetchTakenLectures();
return (
<div className="flex flex-col gap-2">
<Provider>
<TakenLectureAtomHydrator initialValue={data.takenLectures}>
<TakenLectureLabel />
<TakenLectureList />
</TakenLectureAtomHydrator>
</Provider>
<TakenLectureAtomHydrator initialValue={data.takenLectures}>
<TakenLectureLabel />
<TakenLectureList />
</TakenLectureAtomHydrator>
</div>
);
}
14 changes: 13 additions & 1 deletion app/ui/lecture/taken-lecture/taken-lecture-label.tsx
Original file line number Diff line number Diff line change
@@ -1,14 +1,26 @@
/* eslint-disable react-hooks/exhaustive-deps */
'use client';
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';

export default function TakenLectureLabel() {
const { toggle } = useDialog(DIALOG_KEY.LECTURE_SEARCH);

return (
<LabelContainer
label="내 기이수 과목"
rightElement={
<div className="flex gap-2">
<Button label="과목 추가" variant="secondary" size="xs" data-testid="lecture-add-button" />
<Button
label="과목 추가"
variant="secondary"
size="xs"
data-testid="lecture-add-button"
onClick={() => toggle}
/>
<Link href="/file-upload">
<Button label="성적표 재업로드" variant="secondary" size="xs" />
</Link>
Expand Down
45 changes: 45 additions & 0 deletions app/ui/lecture/taken-lecture/taken-lecture.stories.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import type { Meta, StoryObj } from '@storybook/react';
import { expect, userEvent, within } from '@storybook/test';
import { mockDatabase, resetMockDB } from '@/app/mocks/db.mock';
import TakenLectureList from './taken-lecture-list';
import TakenLectureAtomHydrator from '@/app/store/taken-lecture-atom-hydrator';
import { screen } from '@storybook/testing-library';
import { delay } from 'msw';

const meta = {
title: 'ui/taken-lecture/TakenLecture',
component: TakenLectureList,
decorators: [
(Story) => {
resetMockDB();
const data = mockDatabase.getTakenLectures();
return (
<TakenLectureAtomHydrator initialValue={data.takenLectures}>
<Story />
</TakenLectureAtomHydrator>
);
},
],
} as Meta<typeof TakenLectureList>;

export default meta;
type Story = StoryObj<typeof meta>;

export const DeleteSenario: Story = {
play: async ({ step }) => {
await step('사용자가 삭제를 클릭하면 alert창이 보여진다', async () => {
const deleteButton = await screen.findAllByTestId('taken-lecture-delete-button');
await userEvent.click(deleteButton[0]);

expect(screen.getByText('과목을 삭제하시겠습니까?')).toBeInTheDocument();
});
await step('확인 버튼을 클릭하면 과목이 삭제된다', async () => {
const confirmButton = await screen.findAllByTestId('confirm-button');
await userEvent.click(confirmButton[0]);

await delay(3000);

expect(screen.queryByText('인공지능')).not.toBeInTheDocument();
});
},
};
2 changes: 1 addition & 1 deletion app/ui/view/atom/button/button.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ export const ButtonVariants = cva(`flex justify-center items-center`, {
primary: 'bg-primary rounded-[100px] text-white border-0 hover:bg-primary-hover',
secondary: 'bg-white rounded-[100px] border-solid border-[1px] border-gray-6 hover:bg-white-hover',
text: 'font-medium text-slate-400 text-sm hover:text-slate-600',
list: 'py-2 px-3.5 bg-neutral-400 rounded-[7px] text-white leading-5 font-medium text-[18px] hover:bg-neutral-500',
list: 'py-1 px-3 bg-blue-500 rounded-[7px] text-white leading-5 font-medium text-base hover:bg-blue-500',
},
size: {
default: '',
Expand Down
6 changes: 3 additions & 3 deletions app/ui/view/molecule/drawer/drawer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,13 +16,13 @@ const Drawer = ({ children, drawerKey }: DrawerProps) => {
return (
<DrawerPrimitive.Root open={isOpen} onClose={close}>
<DrawerPrimitive.Portal>
<DrawerPrimitive.Overlay className="fixed inset-0 z-50 bg-black/80" />
<DrawerPrimitive.Overlay className="fixed inset-0 z-50 bg-black/60" />
<DrawerPrimitive.Content
className={cn(
'fixed inset-x-0 bottom-0 z-50 mt-24 flex h-auto flex-col rounded-t-[10px] border border-slate-200 bg-white dark:border-slate-800 dark:bg-slate-950',
'fixed inset-x-0 bottom-0 z-50 mt-24 flex h-auto flex-col rounded-t-[10px] border border-slate-200 bg-white',
)}
>
<div className="mx-auto mt-4 h-2 w-[100px] rounded-full bg-slate-100 dark:bg-slate-800" />
<div className="mx-auto mt-4 h-2 w-[100px] rounded-full bg-slate-300" />
{children}
</DrawerPrimitive.Content>
</DrawerPrimitive.Portal>
Expand Down
4 changes: 2 additions & 2 deletions app/ui/view/molecule/list/swipeable-custom-list.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import React, { ReactNode } from 'react';
import { ListRow } from './list-root';
import { SwipeableList, Type as ListType } from 'react-swipeable-list';
// import { SwipeableList, Type as ListType } from 'react-swipeable-list';

interface SwipeableListProps<T extends ListRow> {
data: T[];
Expand All @@ -10,7 +10,7 @@ interface SwipeableListProps<T extends ListRow> {
export default function SwipeableCustomList<T extends ListRow>({ data, render }: SwipeableListProps<T>) {
return (
<div className="rounded-xl border-[1px] border-gray-300 w-full ">
<SwipeableList type={ListType.IOS}>{data.map((item, index) => render(item, index))}</SwipeableList>
{/* <SwipeableList type={ListType.IOS}>{data.map((item, index) => render(item, index))}</SwipeableList> */}
</div>
);
}
2 changes: 1 addition & 1 deletion app/ui/view/molecule/table/table.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@ export const SwipeableLectureTable: StoryObj = {
credit: 3,
},
];
const actionButton = () => <DeleteTakenLectureButton lectureId={3} handleDelete={() => {}} />;
const actionButton = () => <DeleteTakenLectureButton lectureId={3} onDelete={() => {}} />;
return (
<main>
<Table
Expand Down
1 change: 1 addition & 0 deletions app/utils/key/dialog.key.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
export const DIALOG_KEY = {
RESULT_CATEGORY: 'RESULT_CATEGORY',
DIALOG_TEST: 'DIALOG_TEST',
LECTURE_SEARCH: 'LECTURE_SEARCH',
} as const;

export type DialogKey = (typeof DIALOG_KEY)[keyof typeof DIALOG_KEY];

0 comments on commit 2d77a84

Please sign in to comment.