Skip to content

Commit

Permalink
Merge pull request #95 from Myongji-Graduate/result-fetch/#92
Browse files Browse the repository at this point in the history
  • Loading branch information
yougyung authored May 7, 2024
2 parents 2353533 + b46edb8 commit 4c2c89a
Show file tree
Hide file tree
Showing 18 changed files with 393 additions and 134 deletions.
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`,
};
38 changes: 33 additions & 5 deletions 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 {
categoryName: string;
Expand All @@ -12,26 +13,53 @@ export interface ResultCategoryDetailLectures {
completed: boolean;
}

export interface ResultCategoryDetailInfo {
export interface ResultCategoryDetailResponse {
totalCredit: number;
takenCredit: number;
detailCategory: ResultCategoryDetailLectures[];
completed: boolean;
}

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

export interface ResultUserInfo {
studentNumber: string;
studentName: string;
studentCategory: 'NORMAL' | 'CHANGE_MAJOR' | 'DUAL_MAJOR' | 'SUB_MAJOR';
major: string;
completionDivision: Major[];
totalCredit: number;
takenCredit: number;
graduated: boolean;
}

export const fetchResultCategoryDetailInfo = async (category: string): Promise<ResultCategoryDetailInfo> => {
export interface CreditResponse {
category: keyof typeof RESULT_CATEGORY;
totalCredit: number;
takenCredit: number;
completed: boolean;
}
export const fetchResultCategoryDetailInfo = async (category: string): Promise<ResultCategoryDetailResponse> => {
//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<CreditResponse[]> => {
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;
}
}
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
16 changes: 12 additions & 4 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 { CreditResponse, ResultCategoryDetailResponse, 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 @@ -16,25 +16,30 @@ interface MockUser {

interface MockDatabaseState {
takenLectures: TakenLectures;
resultCategoryDetailInfo: ResultCategoryDetailInfo;
resultCategoryDetailInfo: ResultCategoryDetailResponse;
resultUserInfo: ResultUserInfo;
credits: CreditResponse[];
users: MockUser[];
searchLectures: SearchLectures;
}

type MockDatabaseAction = {
getTakenLectures: () => TakenLectures;
getSearchLectures: () => SearchLectures;
getResultCategoryDetailInfo: () => ResultCategoryDetailInfo;
getResultCategoryDetailInfo: () => ResultCategoryDetailResponse;
addTakenLecture: (lectureId: number) => boolean;
deleteTakenLecture: (lectureId: number) => boolean;
getUser: (authId: string) => MockUser | undefined;
createUser: (user: SignUpRequestBody) => boolean;
signIn: (userData: SignInRequestBody) => boolean;
getCredits: () => CreditResponse[];
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 @@ -117,6 +123,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 { CreditResponse, ResultCategoryDetailResponse } 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, ResultCategoryDetailResponse | 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, CreditResponse[] | 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);
}),
];
Loading

0 comments on commit 4c2c89a

Please sign in to comment.