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 fetch/#92 #95

Merged
merged 19 commits into from
May 7, 2024
Merged
Show file tree
Hide file tree
Changes from 15 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
37 changes: 11 additions & 26 deletions app/(sub-page)/result/page.tsx
Original file line number Diff line number Diff line change
@@ -1,44 +1,29 @@
import ResultCategoryCard from '@/app/ui/result/result-category-card/result-category-card';
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 { RESULT_CATEGORY } from '@/app/utils/key/result-category.key';
import ResultCategoryDetail from '@/app/ui/result/result-category-detail/result-category-detail';
import { Suspense } from 'react';
import UserInfoCardSkeleton from '@/app/ui/user/user-info-card/user-info-card.skeleton';
import ResultCategory from '@/app/ui/result/result-category/result-category';
import ResultCategorySkeleton from '@/app/ui/result/result-category/result-category.skeleton';

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

function ResultPage({ searchParams }: ResultPageProp) {
const { category } = searchParams;
const DUMMY_DATA = {
category: 'COMMON_CULTURE' as keyof typeof RESULT_CATEGORY,
totalCredit: 70,
takenCredit: 68,
completed: false,
};

return (
<div className="flex justify-center items-end">
<ContentContainer className="md:w-[700px] p-8">
<UserInfoCard />
<Suspense fallback={<UserInfoCardSkeleton />}>
<UserInfoCard />
</Suspense>
</ContentContainer>
<div
className={cn(
'absolute grid grid-cols-2 gap-2 top-[30rem] w-full',
'md:max-w-[700px] md:gap-10 md:top-[33rem]',
)}
>
{Array.from({ length: 8 }).map((_, index) => (
<ResultCategoryCard
key={index}
category={RESULT_CATEGORY[DUMMY_DATA.category]}
totalCredit={DUMMY_DATA.totalCredit}
takenCredit={DUMMY_DATA.takenCredit}
/>
))}
</div>
{category && <ResultCategoryDetail category={category} />}
<Suspense fallback={<ResultCategorySkeleton />}>
<ResultCategory />
</Suspense>
<ResultCategoryDetail category={category} />
</div>
);
}
Expand Down
2 changes: 2 additions & 0 deletions app/business/api-path.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ export const API_PATH = {
takenLectures: `${BASE_URL}/taken-lectures`,
resultCategoryDetailInfo: `${BASE_URL}/result-category-detail-info`,
user: `${BASE_URL}/users`,
resultUserInfo: `${BASE_URL}/resultUserInfo`,
credits: `${BASE_URL}/credits`,
auth: `${BASE_URL}/auth`,
lectures: `${BASE_URL}/lectures`,
};
39 changes: 38 additions & 1 deletion app/business/result/result.query.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { LectureInfo } from '@/app/type/lecture';
import { API_PATH } from '../api-path';
import { cookies } from 'next/headers';
import { httpErrorHandler } from '@/app/utils/http/http-error-handler';
import { RESULT_CATEGORY } from '@/app/utils/key/result-category.key';

export interface ResultCategoryDetailLectures {
yougyung marked this conversation as resolved.
Show resolved Hide resolved
categoryName: string;
Expand All @@ -19,6 +20,20 @@ export interface ResultCategoryDetailInfo {
completed: boolean;
}

export interface Major {
majorType: 'PRIMARY' | 'DUAL' | 'SUB';
major: string;
}

export interface ResultUserInfo {
Copy link
Collaborator

Choose a reason for hiding this comment

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

  • ์ด ๋ถ€๋ถ„ ๊ฐ€ํ˜„์ด PR๊ณผ ์ค‘๋ณต๋˜๋Š” ๊ฒƒ ๊ฐ™์Šต๋‹ˆ๋‹ค. ๋‘ ๋ถ„์ด ํ†ตํ•ฉ ๊ณ„ํš์ด ์žˆ์„๊นŒ์š”?

Copy link
Member Author

Choose a reason for hiding this comment

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

๊ธฐ์กด์— Mock data๋กœ ํ™œ์šฉ๋˜๊ณ  ์žˆ๋Š” user์™€ ์‹ค์ œ ์‘๋‹ต๋ฐ›๋Š” data์˜ ์ฐจ์ด๋กœ ์ธํ•ด ๋ถ„๋ฆฌ ๋ฐ ํ†ตํ•ฉํ•˜๋Š” ์ฝ”๋“œ๋กœ ๋ฆฌํŒฉํ† ๋ง์„ ์ง„ํ–‰ํ•  ์˜ˆ์ •์ž…๋‹ˆ๋‹ค.
๊ธฐ์กด์—๋Š” ๋™์ผ PR์— ์ž‘์—…์„ ์ง„ํ–‰ํ•  ๊ณ„ํš์ด์—ˆ์œผ๋‚˜ ๋ณด๋‹ค ์ž์„ธํ•œ ๊ฐœ๋ฐœ ๊ณผ์ •์— ๋Œ€ํ•œ ๊ณต์œ ๋ฅผ ์œ„ํ•ด ๋ณ„๋„์˜ PR๋กœ ๋ถ„๋ฆฌํ•ด์„œ ์˜ฌ๋ฆฌ๋„๋กํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค!

studentNumber: string;
studentName: string;
completionDivision: Major[];
totalCredit: number;
takenCredit: number;
graduated: boolean;
}

export interface ResultUserInfo {
yougyung marked this conversation as resolved.
Show resolved Hide resolved
studentNumber: string;
studentName: string;
Expand All @@ -28,10 +43,32 @@ export interface ResultUserInfo {
takenCredit: number;
}

export interface Credit {
category: keyof typeof RESULT_CATEGORY;
totalCredit: number;
takenCredit: number;
completed: boolean;
}
export const fetchResultCategoryDetailInfo = async (category: string): Promise<ResultCategoryDetailInfo> => {
//FIX : category๋ฅผ querystring์œผ๋กœ ํ˜ธ์ถœํ•˜๋Š” ๊ฑด์€ mock๋‹จ๊ณ„์—์„œ๋Š” ๋ถˆํ•„์š”ํ•  ๊ฒƒ์œผ๋กœ ์˜ˆ์ƒ, ์‹ค์ œ api ์—ฐ๊ฒฐ์‹œ ๋ณ€๊ฒฝ ์˜ˆ์ •
try {
const response = await fetch(`${API_PATH.resultCategoryDetailInfo}`, {
const response = await fetch(API_PATH.resultCategoryDetailInfo, {
headers: {
Authorization: `Bearer ${cookies().get('accessToken')?.value}`,
},
});
const result = await response.json();
httpErrorHandler(response, result);

return result;
} catch (error) {
throw error;
}
};

export const fetchCredits = async (): Promise<Credit[]> => {
try {
const response = await fetch(API_PATH.credits, {
headers: {
Authorization: `Bearer ${cookies().get('accessToken')?.value}`,
},
Expand Down
18 changes: 18 additions & 0 deletions app/business/user/user.query.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { UserInfoResponse } from './user.type';
import { cookies } from 'next/headers';
import { isValidation } from '@/app/utils/zod/validation.util';
import { UserInfoResponseSchema } from './user.validation';
import { ResultUserInfo } from '../result/result.query';

export async function getUserInfo(): Promise<UserInfoResponse> {
try {
Expand All @@ -26,3 +27,20 @@ export async function getUserInfo(): Promise<UserInfoResponse> {
throw error;
}
}

export async function fetchResultUserInfo(): Promise<ResultUserInfo> {
try {
const response = await fetch(API_PATH.resultUserInfo, {
headers: {
Authorization: `Bearer ${cookies().get('accessToken')?.value}`,
},
});

const result = await response.json();

httpErrorHandler(response, result);
return result;
} catch (error) {
throw error;
}
}
Comment on lines +31 to +46
Copy link
Member

Choose a reason for hiding this comment

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

ํšŒ์›๊ฐ€์ž… ๋กœ์ง์œผ๋กœ ์ธํ•ด fetch Result userInfo๋ฅผ ๋งŒ๋“œ์…จ๋‹ค๊ณ  ํ•˜์…จ๋Š”๋ฐ, ํ˜น์‹œ ์ด๋กœ ์ธํ•ด ์–ด๋–ค ๋ฌธ์ œ๊ฐ€ ์žˆ์—ˆ๋Š”์ง€ ์•Œ๋ ค์ฃผ์‹ค ์ˆ˜ ์žˆ๋‚˜์š”!?
์ €๋Š” getUserInfo๋ฅผ ์žฌ์‚ฌ์šฉํ–ˆ์–ด์„œ์š”..!

Copy link
Member Author

Choose a reason for hiding this comment

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

ํ˜„์žฌ mock data์™€ ์‹ค์ œ backend์—์„œ ์ „์†กํ•ด์ฃผ๋Š” ๏ฟฝtype์ด ์ผ์น˜ํ•˜์ง€ ์•Š์•„(๋‹ค์ „๊ณต ๋“ฑ์˜ ์‚ฌ์œ ๋กœ ๋ณ€๊ฒฝ๋˜์—ˆ์Šต๋‹ˆ๋‹ค) UserInfo type์„ ๋ณ€๊ฒฝํ•ด์ฃผ๋Š” ๊ณผ์ •์ด ํ•„์š”ํ–ˆ์Šต๋‹ˆ๋‹ค. englishlevel studentID ๋“ฑ์˜ ์†์„ฑ์€ ํšŒ์›๊ฐ€์ž… ๊ณผ์ •์—์„œ ์‚ฌ์šฉ๋˜๋Š” ์†์„ฑ์ด๋ผ๋Š” ์ƒ๊ฐ์ด ๋“ค์–ด์„œ, type์„ ์ œ๊ฐ€ ์ž„์˜๋กœ ๋ณ€๊ฒฝํ•˜์ง€ ์•Š๊ณ  ์ƒˆ๋กญ๊ฒŒ ์ƒ์„ฑํ–ˆ์Šต๋‹ˆ๋‹ค

93 changes: 93 additions & 0 deletions app/mocks/data.mock.ts
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,25 @@ export const takenLectures = JSON.parse(`{
]
}`);

export const resultUserInfo = JSON.parse(`{
"studentNumber": "60181666",
"studentName": "์žฅ์ง„์šฑ",
"completionDivision" : [
{
"majorType" : "PRIMARY",
"major": "๋””์ง€ํ„ธ์ฝ˜ํ…์ธ ๋””์ž์ธํ•™๊ณผ"
},
{
"majorType" : "DUAL",
"major": "๊ฒฝ์˜ํ•™๊ณผ"
}

],
"totalCredit": 132,
"takenCredit": 50,
"graduated" : true
}`);

export const resultCategoryDetailInfo = JSON.parse(`{
"totalCredit": 15,
"takenCredit": 12,
Expand Down Expand Up @@ -176,6 +195,80 @@ export const resultCategoryDetailInfo = JSON.parse(`{
"completed": false
}`);

export const credits = JSON.parse(`[
{
"category": "COMMON_CULTURE",
"totalCredit" : 70,
"takenCredit" : 40,
"completed": false
},
{
"category": "CORE_CULTURE",
"totalCredit" : 70,
"takenCredit" : 40,
"completed": false
},
{
"category": "PRIMARY_MANDATORY_MAJOR",
"totalCredit" : 70,
"takenCredit" : 40,
"completed": false
},
{
"category": "PRIMARY_ELECTIVE_MAJOR",
"totalCredit" : 70,
"takenCredit" : 40,
"completed": false
},
{
"category": "DUAL_MANDATORY_MAJOR",
"totalCredit" : 70,
"takenCredit" : 40,
"completed": false
},
{
"category": "DUAL_ELECTIVE_MAJOR",
"totalCredit" : 70,
"takenCredit" : 40,
"completed": false
},
{
"category": "SUB_MAJOR",
"totalCredit" : 70,
"takenCredit" : 40,
"completed": false
},
{
"category": "PRIMARY_BASIC_ACADEMICAL_CULTURE",
"totalCredit" : 70,
"takenCredit" : 40,
"completed": false
},
{
"category": "DUAL_BASIC_ACADEMICAL_CULTURE",
"totalCredit" : 70,
"takenCredit" : 40,
"completed": false
},
{
"category": "NORMAL_CULTURE",
"totalCredit" : 70,
"takenCredit" : 40,
"completed": false
},
{
"category": "FREE_ELECTIVE",
"totalCredit" : 70,
"takenCredit" : 40,
"completed": false
},
{
"category": "CHAPEL",
"totalCredit" : 4,
"takenCredit" : 1,
"completed": false
}
]`);
export const searchLectures = JSON.parse(`{
"lectures": [
{
Expand Down
12 changes: 10 additions & 2 deletions app/mocks/db.mock.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import { SearchLectures } from '../business/lecture/search-lecture.query';
import { TakenLectures } from '../business/lecture/taken-lecture.query';
import { ResultCategoryDetailInfo } from '../business/result/result.query';
import { Credit, ResultCategoryDetailInfo, ResultUserInfo } from '../business/result/result.query';
import { SignUpRequestBody, SignInRequestBody, UserInfoResponse } from '../business/user/user.type';
import { takenLectures, resultCategoryDetailInfo, searchLectures } from './data.mock';
import { takenLectures, resultCategoryDetailInfo, resultUserInfo, credits, searchLectures } from './data.mock';

interface MockUser {
authId: string;
Expand All @@ -17,6 +17,8 @@ interface MockUser {
interface MockDatabaseState {
takenLectures: TakenLectures;
resultCategoryDetailInfo: ResultCategoryDetailInfo;
resultUserInfo: ResultUserInfo;
credits: Credit[];
users: MockUser[];
searchLectures: SearchLectures;
}
Expand All @@ -30,11 +32,14 @@ type MockDatabaseAction = {
getUser: (authId: string) => MockUser | undefined;
createUser: (user: SignUpRequestBody) => boolean;
signIn: (userData: SignInRequestBody) => boolean;
getCredits: () => Credit[];
getUserInfo: (authId: string) => UserInfoResponse;
getResultUserInfo: () => ResultUserInfo;
};

export const mockDatabase: MockDatabaseAction = {
getTakenLectures: () => mockDatabaseStore.takenLectures,
getResultUserInfo: () => mockDatabaseStore.resultUserInfo,
getSearchLectures: () => mockDatabaseStore.searchLectures,
deleteTakenLecture: (lectureId) => {
if (mockDatabaseStore.takenLectures.takenLectures.find((lecture) => lecture.id === lectureId)) {
Expand All @@ -61,6 +66,7 @@ export const mockDatabase: MockDatabaseAction = {
},
getResultCategoryDetailInfo: () => mockDatabaseStore.resultCategoryDetailInfo,
getUser: (authId: string) => mockDatabaseStore.users.find((user) => user.authId === authId),
getCredits: () => mockDatabaseStore.credits,
createUser: (user: SignUpRequestBody) => {
if (mockDatabaseStore.users.find((u) => u.authId === user.authId || u.studentNumber === user.studentNumber)) {
return false;
Expand Down Expand Up @@ -102,6 +108,8 @@ export const mockDatabase: MockDatabaseAction = {
const initialState: MockDatabaseState = {
takenLectures: takenLectures,
resultCategoryDetailInfo: resultCategoryDetailInfo,
credits: credits,
resultUserInfo: resultUserInfo,
users: [
{
authId: 'admin',
Expand Down
37 changes: 33 additions & 4 deletions app/mocks/handlers/result-handler.mock.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,40 @@
import { HttpResponse, http, delay } from 'msw';
import { API_PATH } from '../../business/api-path';
import { mockDatabase } from '../db.mock';
import { ErrorResponseData } from '@/app/utils/http/http-error-handler';
import { Credit, ResultCategoryDetailInfo } from '@/app/business/result/result.query';

export const resultHandlers = [
http.get(API_PATH.resultCategoryDetailInfo, async () => {
const resultCategoryDetailInfo = mockDatabase.getResultCategoryDetailInfo();
await delay(4000);
return HttpResponse.json(resultCategoryDetailInfo);
http.get<never, never, ResultCategoryDetailInfo | ErrorResponseData>(
`${API_PATH.resultCategoryDetailInfo}`,
async ({ request }) => {
const accessToken = request.headers.get('Authorization')?.replace('Bearer ', '');
if (accessToken === 'undefined' || !accessToken) {
return HttpResponse.json({ status: 401, message: 'Unauthorized' }, { status: 401 });
}

const data = mockDatabase.getResultCategoryDetailInfo();
await delay(3000);

if (!data) {
return HttpResponse.json({ status: 401, message: 'Unauthorized' }, { status: 401 });
}

return HttpResponse.json(data);
},
),
http.get<never, never, Credit[] | ErrorResponseData>(`${API_PATH.credits}`, async ({ request }) => {
const accessToken = request.headers.get('Authorization')?.replace('Bearer ', '');
if (accessToken === 'undefined' || !accessToken) {
return HttpResponse.json({ status: 401, message: 'Unauthorized' }, { status: 401 });
}

const data = mockDatabase.getCredits();
await delay(5000);
if (!data) {
return HttpResponse.json({ status: 401, message: 'Unauthorized' }, { status: 401 });
}

return HttpResponse.json(data);
}),
];
16 changes: 16 additions & 0 deletions app/mocks/handlers/user-handler.mock.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import {
UserInfoResponse,
} from '@/app/business/user/user.type';
import { ErrorResponseData } from '@/app/utils/http/http-error-handler';
import { ResultUserInfo } from '@/app/business/result/result.query';

function mockDecryptToken(token: string) {
if (token === 'fake-access-token') {
Expand Down Expand Up @@ -46,6 +47,21 @@ export const userHandlers = [

return HttpResponse.json(userInfo);
}),
http.get<never, never, ResultUserInfo | ErrorResponseData>(API_PATH.resultUserInfo, async ({ request }) => {
const accessToken = request.headers.get('Authorization')?.replace('Bearer ', '');
if (accessToken === 'undefined' || !accessToken) {
return HttpResponse.json({ status: 401, message: 'Unauthorized' }, { status: 401 });
}

const userInfo = mockDatabase.getResultUserInfo();
await delay(2000);

if (!userInfo) {
return HttpResponse.json({ status: 401, message: 'Unauthorized' }, { status: 401 });
}

return HttpResponse.json(userInfo);
}),
http.post<never, SignUpRequestBody, never>(`${API_PATH.user}/sign-up`, async ({ request }) => {
const userData = await request.json();

Expand Down
Loading
Loading