From a64aa6bc77f70201147b39bd46c00574061d57b2 Mon Sep 17 00:00:00 2001 From: yougyung Date: Wed, 1 May 2024 11:56:01 +0900 Subject: [PATCH 01/25] feat: implement fetch result user info --- app/business/api-path.ts | 1 + app/business/result/result.query.ts | 15 +++++++++++++ app/business/user/user.query.ts | 18 ++++++++++++++++ app/mocks/data.mock.ts | 14 +++++++++++++ app/mocks/db.mock.ts | 8 +++++-- app/mocks/handlers/user-handler.mock.ts | 16 ++++++++++++++ app/ui/user/user-info-card/user-info-card.tsx | 21 ++++++++++++------- 7 files changed, 83 insertions(+), 10 deletions(-) diff --git a/app/business/api-path.ts b/app/business/api-path.ts index 6bfec0c4..6c9d1a36 100644 --- a/app/business/api-path.ts +++ b/app/business/api-path.ts @@ -7,5 +7,6 @@ export const API_PATH = { takenLectures: `${BASE_URL}/taken-lectures`, resultCategoryDetailInfo: `${BASE_URL}/result-category-detail-info`, user: `${BASE_URL}/users`, + resultUserInfo: `${BASE_URL}/resultUserInfo`, auth: `${BASE_URL}/auth`, }; diff --git a/app/business/result/result.query.ts b/app/business/result/result.query.ts index 5101fa06..932ed9db 100644 --- a/app/business/result/result.query.ts +++ b/app/business/result/result.query.ts @@ -1,3 +1,4 @@ +import { ResultUserInfo } from './result.query'; import { LectureInfo } from '@/app/type/lecture'; import { API_PATH } from '../api-path'; import { cookies } from 'next/headers'; @@ -19,6 +20,20 @@ export interface ResultCategoryDetailInfo { completed: boolean; } +export interface Major { + majorType: 'PRIMARY' | 'DUAL' | 'SUB'; + major: string; +} + +export interface ResultUserInfo { + studentNumber: string; + studentName: string; + completionDivision: Major[]; + totalCredit: number; + takenCredit: number; + graduated: boolean; +} + export interface ResultUserInfo { studentNumber: string; studentName: string; diff --git a/app/business/user/user.query.ts b/app/business/user/user.query.ts index 6704e6f7..c199f000 100644 --- a/app/business/user/user.query.ts +++ b/app/business/user/user.query.ts @@ -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 { try { @@ -26,3 +27,20 @@ export async function getUserInfo(): Promise { throw error; } } + +export async function getResultUserInfo(): Promise { + 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; + } +} diff --git a/app/mocks/data.mock.ts b/app/mocks/data.mock.ts index 250c9bb2..65dd5bfc 100644 --- a/app/mocks/data.mock.ts +++ b/app/mocks/data.mock.ts @@ -69,6 +69,20 @@ export const takenLectures = JSON.parse(`{ ] }`); +export const resultUserInfo = JSON.parse(`{ + "studentNumber": "60181666", + "studentName": "장진욱", + "completionDivision" : [ + { + "majorType" : "PRIMARY", + "major": "디지털콘텐츠디자인학과" + } + ], + "totalCredit": 132, + "takenCredit": 50, + "graduated" : true +}`); + export const resultCategoryDetailInfo = JSON.parse(`{ "totalCredit": 15, "takenCredit": 12, diff --git a/app/mocks/db.mock.ts b/app/mocks/db.mock.ts index 302d1615..10fc6df0 100644 --- a/app/mocks/db.mock.ts +++ b/app/mocks/db.mock.ts @@ -1,7 +1,7 @@ import { TakenLectures } from '../business/lecture/taken-lecture.query'; -import { ResultCategoryDetailInfo } from '../business/result/result.query'; +import { ResultCategoryDetailInfo, ResultUserInfo } from '../business/result/result.query'; import { SignUpRequestBody, SignInRequestBody, UserInfoResponse } from '../business/user/user.type'; -import { takenLectures, resultCategoryDetailInfo } from './data.mock'; +import { takenLectures, resultCategoryDetailInfo, resultUserInfo } from './data.mock'; interface MockUser { authId: string; @@ -16,6 +16,7 @@ interface MockUser { interface MockDatabaseState { takenLectures: TakenLectures; resultCategoryDetailInfo: ResultCategoryDetailInfo; + resultUserInfo: ResultUserInfo; users: MockUser[]; } @@ -28,10 +29,12 @@ type MockDatabaseAction = { createUser: (user: SignUpRequestBody) => boolean; signIn: (userData: SignInRequestBody) => boolean; getUserInfo: (authId: string) => UserInfoResponse; + getResultUserInfo: () => ResultUserInfo; }; export const mockDatabase: MockDatabaseAction = { getTakenLectures: () => mockDatabaseStore.takenLectures, + getResultUserInfo: () => mockDatabaseStore.resultUserInfo, deleteTakenLecture: (lectureId) => { if (mockDatabaseStore.takenLectures.takenLectures.find((lecture) => lecture.id === lectureId)) { mockDatabaseStore.takenLectures.takenLectures = mockDatabaseStore.takenLectures.takenLectures.filter( @@ -98,6 +101,7 @@ export const mockDatabase: MockDatabaseAction = { const initialState: MockDatabaseState = { takenLectures: takenLectures, resultCategoryDetailInfo: resultCategoryDetailInfo, + resultUserInfo: resultUserInfo, users: [ { authId: 'admin', diff --git a/app/mocks/handlers/user-handler.mock.ts b/app/mocks/handlers/user-handler.mock.ts index af26d9d7..54d09ade 100644 --- a/app/mocks/handlers/user-handler.mock.ts +++ b/app/mocks/handlers/user-handler.mock.ts @@ -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') { @@ -46,6 +47,21 @@ export const userHandlers = [ return HttpResponse.json(userInfo); }), + http.get(`${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(3000); + + if (!userInfo) { + return HttpResponse.json({ status: 401, message: 'Unauthorized' }, { status: 401 }); + } + + return HttpResponse.json(userInfo); + }), http.post(`${API_PATH.user}/sign-up`, async ({ request }) => { const userData = await request.json(); diff --git a/app/ui/user/user-info-card/user-info-card.tsx b/app/ui/user/user-info-card/user-info-card.tsx index 9dbaef28..92b21e74 100644 --- a/app/ui/user/user-info-card/user-info-card.tsx +++ b/app/ui/user/user-info-card/user-info-card.tsx @@ -1,10 +1,15 @@ +import { fetchResultCategoryDetailInfo } from '@/app/business/result/result.query'; import PieChart from '../../view/molecule/pie-chart/pie-chart'; +import { getResultUserInfo } from '@/app/business/user/user.query'; + +async function UserInfoCard() { + const data = await getResultUserInfo(); + const { studentNumber, studentName, completionDivision, totalCredit, takenCredit, graduated } = data; -function UserInfoCard() { return ( <>

- 졸업필요학점보다 57학점이 부족합니다. + 졸업필요학점보다 {totalCredit - takenCredit}학점이 부족합니다.

@@ -17,12 +22,12 @@ function UserInfoCard() {
  • 졸업가능여부
    • -
    • 모유경
    • -
    • 60201671
    • -
    • 응용소프트웨어전공
    • -
    • 134
    • -
    • 77
    • -
    • 불가능
    • +
    • {studentNumber}
    • +
    • {studentName}
    • +
    • {completionDivision[0].major}
    • +
    • {totalCredit}
    • +
    • {takenCredit}
    • +
    • {graduated ? '' : '불'}가능
    From c2bf74aa6b2e7099ce0b677dec80e58bf598ca74 Mon Sep 17 00:00:00 2001 From: yougyung Date: Wed, 1 May 2024 21:15:12 +0900 Subject: [PATCH 02/25] feat: implement fetch result category info --- app/(sub-page)/result/page.tsx | 14 +++-- app/business/api-path.ts | 1 + app/business/result/result.query.ts | 25 +++++++- app/mocks/data.mock.ts | 75 +++++++++++++++++++++++ app/mocks/db.mock.ts | 8 ++- app/mocks/handlers/result-handler.mock.ts | 5 ++ app/ui/view/molecule/navigation-bar.tsx | 2 +- 7 files changed, 121 insertions(+), 9 deletions(-) diff --git a/app/(sub-page)/result/page.tsx b/app/(sub-page)/result/page.tsx index 6e3946dd..fd08f74e 100644 --- a/app/(sub-page)/result/page.tsx +++ b/app/(sub-page)/result/page.tsx @@ -4,13 +4,17 @@ 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 { fetchCredits } from '@/app/business/result/result.query'; interface ResultPageProp { searchParams: { category: string }; } -function ResultPage({ searchParams }: ResultPageProp) { +async function ResultPage({ searchParams }: ResultPageProp) { + const categorys = await fetchCredits(); + const { category } = searchParams; + const DUMMY_DATA = { category: 'COMMON_CULTURE' as keyof typeof RESULT_CATEGORY, totalCredit: 70, @@ -29,12 +33,12 @@ function ResultPage({ searchParams }: ResultPageProp) { 'md:max-w-[700px] md:gap-10 md:top-[33rem]', )} > - {Array.from({ length: 8 }).map((_, index) => ( + {categorys.map((category, index) => ( ))}
    diff --git a/app/business/api-path.ts b/app/business/api-path.ts index 6c9d1a36..7f41fcfb 100644 --- a/app/business/api-path.ts +++ b/app/business/api-path.ts @@ -8,5 +8,6 @@ export const API_PATH = { resultCategoryDetailInfo: `${BASE_URL}/result-category-detail-info`, user: `${BASE_URL}/users`, resultUserInfo: `${BASE_URL}/resultUserInfo`, + credits: `${BASE_URL}/credits`, auth: `${BASE_URL}/auth`, }; diff --git a/app/business/result/result.query.ts b/app/business/result/result.query.ts index 932ed9db..ba07fa3b 100644 --- a/app/business/result/result.query.ts +++ b/app/business/result/result.query.ts @@ -1,8 +1,8 @@ -import { ResultUserInfo } from './result.query'; 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; @@ -43,6 +43,12 @@ 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 => { //FIX : category를 querystring으로 호출하는 건은 mock단계에서는 불필요할 것으로 예상, 실제 api 연결시 변경 예정 try { @@ -60,3 +66,20 @@ export const fetchResultCategoryDetailInfo = async (category: string): Promise => { + try { + const response = await fetch(API_PATH.credits, { + headers: { + Authorization: `Bearer ${cookies().get('accessToken')?.value}`, + }, + }); + const result = await response.json(); + + httpErrorHandler(response, result); + + return result; + } catch (error) { + throw error; + } +}; diff --git a/app/mocks/data.mock.ts b/app/mocks/data.mock.ts index 65dd5bfc..f9567a97 100644 --- a/app/mocks/data.mock.ts +++ b/app/mocks/data.mock.ts @@ -189,3 +189,78 @@ 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 + } +]`); diff --git a/app/mocks/db.mock.ts b/app/mocks/db.mock.ts index 10fc6df0..12f82528 100644 --- a/app/mocks/db.mock.ts +++ b/app/mocks/db.mock.ts @@ -1,7 +1,7 @@ import { TakenLectures } from '../business/lecture/taken-lecture.query'; -import { ResultCategoryDetailInfo, ResultUserInfo } 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, resultUserInfo } from './data.mock'; +import { takenLectures, resultCategoryDetailInfo, resultUserInfo, credits } from './data.mock'; interface MockUser { authId: string; @@ -17,6 +17,7 @@ interface MockDatabaseState { takenLectures: TakenLectures; resultCategoryDetailInfo: ResultCategoryDetailInfo; resultUserInfo: ResultUserInfo; + credits: Credit[]; users: MockUser[]; } @@ -28,6 +29,7 @@ type MockDatabaseAction = { getUser: (authId: string) => MockUser | undefined; createUser: (user: SignUpRequestBody) => boolean; signIn: (userData: SignInRequestBody) => boolean; + getCredits: () => Credit[]; getUserInfo: (authId: string) => UserInfoResponse; getResultUserInfo: () => ResultUserInfo; }; @@ -60,6 +62,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; @@ -101,6 +104,7 @@ export const mockDatabase: MockDatabaseAction = { const initialState: MockDatabaseState = { takenLectures: takenLectures, resultCategoryDetailInfo: resultCategoryDetailInfo, + credits: credits, resultUserInfo: resultUserInfo, users: [ { diff --git a/app/mocks/handlers/result-handler.mock.ts b/app/mocks/handlers/result-handler.mock.ts index b25ced53..b94e33b3 100644 --- a/app/mocks/handlers/result-handler.mock.ts +++ b/app/mocks/handlers/result-handler.mock.ts @@ -8,4 +8,9 @@ export const resultHandlers = [ await delay(4000); return HttpResponse.json(resultCategoryDetailInfo); }), + http.get(API_PATH.credits, async () => { + const resultCategoryInfo = mockDatabase.getCredits(); + await delay(4000); + return HttpResponse.json(resultCategoryInfo); + }), ]; diff --git a/app/ui/view/molecule/navigation-bar.tsx b/app/ui/view/molecule/navigation-bar.tsx index 4394b731..9a2e829e 100644 --- a/app/ui/view/molecule/navigation-bar.tsx +++ b/app/ui/view/molecule/navigation-bar.tsx @@ -4,7 +4,7 @@ import logo from '../../../../public/assets/logo.svg'; export default function NavigationBar() { return (
    - main-logo + {/* main-logo */}
    ); } From 27c0dbf5e185c1e2afb55e1dc37ba048ab9048d3 Mon Sep 17 00:00:00 2001 From: yougyung Date: Wed, 1 May 2024 21:29:11 +0900 Subject: [PATCH 03/25] feat: add getPercentage util --- .../result-category-card.tsx | 8 +++++--- app/utils/chart.util.ts | 17 ++--------------- 2 files changed, 7 insertions(+), 18 deletions(-) diff --git a/app/ui/result/result-category-card/result-category-card.tsx b/app/ui/result/result-category-card/result-category-card.tsx index 1862c034..053af282 100644 --- a/app/ui/result/result-category-card/result-category-card.tsx +++ b/app/ui/result/result-category-card/result-category-card.tsx @@ -10,6 +10,7 @@ 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 { useRouter } from 'next/navigation'; +import { getPercentage } from '@/app/utils/chart.util'; interface ResultCategoryCardProps { category: ResultCategoryKey; @@ -37,12 +38,13 @@ function ResultCategoryCard({ category, totalCredit, takenCredit }: ResultCatego const { toggle } = useDialog(DIALOG_KEY.RESULT_CATEGORY); const { replace } = useRouter(); - const percentage = Number(((takenCredit / totalCredit) * 100).toFixed(0)); + const percentage = getPercentage(takenCredit, totalCredit); - function handleClickButton() { + const handleClickButton = () => { toggle(); replace('/result?category=COMMON_CULTURE'); - } + }; + return (
    { - // Calculate what labels we need to display on the y-axis - // based on highest record and in 1000s - const yAxisLabels = []; - const highestRecord = Math.max(...revenue.map((month) => month.revenue)); - const topLabel = Math.ceil(highestRecord / 1000) * 1000; - - for (let i = topLabel; i >= 0; i -= 1000) { - yAxisLabels.push(`$${i / 1000}K`); - } - - return { yAxisLabels, topLabel }; +export const getPercentage = (numerator: number, denominator: number) => { + return Number(((numerator / denominator) * 100).toFixed(0)); }; From 42a0e34179ab816bbde100dcad876a252883a29b Mon Sep 17 00:00:00 2001 From: yougyung Date: Wed, 1 May 2024 21:29:53 +0900 Subject: [PATCH 04/25] fix: change naming fetch result user info --- app/(sub-page)/result/page.tsx | 7 ------- app/business/user/user.query.ts | 2 +- .../result-category-detail-content.tsx | 2 +- app/ui/user/user-info-card/user-info-card.tsx | 10 +++++----- 4 files changed, 7 insertions(+), 14 deletions(-) diff --git a/app/(sub-page)/result/page.tsx b/app/(sub-page)/result/page.tsx index fd08f74e..cfcf58b8 100644 --- a/app/(sub-page)/result/page.tsx +++ b/app/(sub-page)/result/page.tsx @@ -15,13 +15,6 @@ async 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 (
    diff --git a/app/business/user/user.query.ts b/app/business/user/user.query.ts index c199f000..c690aae5 100644 --- a/app/business/user/user.query.ts +++ b/app/business/user/user.query.ts @@ -28,7 +28,7 @@ export async function getUserInfo(): Promise { } } -export async function getResultUserInfo(): Promise { +export async function fetchResultUserInfo(): Promise { try { const response = await fetch(`${API_PATH.resultUserInfo}`, { headers: { diff --git a/app/ui/result/result-category-detail-content/result-category-detail-content.tsx b/app/ui/result/result-category-detail-content/result-category-detail-content.tsx index ec6c41f2..9fa1f7a2 100644 --- a/app/ui/result/result-category-detail-content/result-category-detail-content.tsx +++ b/app/ui/result/result-category-detail-content/result-category-detail-content.tsx @@ -1,6 +1,6 @@ 'use client'; -import { cn } from '@/app/utils/shadcn/utils'; +import { cn } from '@/app/utils/shadcn/utils'; import { useState } from 'react'; import { ResultCategoryDetailLectureToggle } from '../result-category-detail-lecture/result-category-detail-lecture-toggle'; import ResultCagegoryDetailLecture from '../result-category-detail-lecture/result-cagegory-detail-lecture'; diff --git a/app/ui/user/user-info-card/user-info-card.tsx b/app/ui/user/user-info-card/user-info-card.tsx index 92b21e74..14f160c0 100644 --- a/app/ui/user/user-info-card/user-info-card.tsx +++ b/app/ui/user/user-info-card/user-info-card.tsx @@ -1,9 +1,9 @@ -import { fetchResultCategoryDetailInfo } from '@/app/business/result/result.query'; +import { getPercentage } from '@/app/utils/chart.util'; import PieChart from '../../view/molecule/pie-chart/pie-chart'; -import { getResultUserInfo } from '@/app/business/user/user.query'; +import { fetchResultUserInfo } from '@/app/business/user/user.query'; async function UserInfoCard() { - const data = await getResultUserInfo(); + const data = await fetchResultUserInfo(); const { studentNumber, studentName, completionDivision, totalCredit, takenCredit, graduated } = data; return ( @@ -27,11 +27,11 @@ async function UserInfoCard() {
  • {completionDivision[0].major}
  • {totalCredit}
  • {takenCredit}
  • -
  • {graduated ? '' : '불'}가능
  • +
  • {graduated ? '가능' : '불가능'}
  • - +

    From 05062a9f7a7373899f39551942764e8eb983f29e Mon Sep 17 00:00:00 2001 From: yougyung Date: Wed, 1 May 2024 21:42:16 +0900 Subject: [PATCH 05/25] feat: add chaple option in category --- .../result/result-category-card/result-category-card.tsx | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/app/ui/result/result-category-card/result-category-card.tsx b/app/ui/result/result-category-card/result-category-card.tsx index 053af282..64d12e6c 100644 --- a/app/ui/result/result-category-card/result-category-card.tsx +++ b/app/ui/result/result-category-card/result-category-card.tsx @@ -45,6 +45,10 @@ function ResultCategoryCard({ category, totalCredit, takenCredit }: ResultCatego replace('/result?category=COMMON_CULTURE'); }; + const takeCategoryCredit = (category: ResultCategoryKey, credit: number): number => { + return category === RESULT_CATEGORY.CHAPEL ? credit * 2 : credit; + }; + return (

    기준학점 - {totalCredit} + {takeCategoryCredit(category, totalCredit)}
    이수학점 - {takenCredit} + {takeCategoryCredit(category, totalCredit)}
    From a2a65093a693ed7a7af2eb022a3eefab4ab1e4b2 Mon Sep 17 00:00:00 2001 From: yougyung Date: Wed, 1 May 2024 22:06:36 +0900 Subject: [PATCH 06/25] feat: add skeleton view in UserInfoCard --- app/(sub-page)/result/page.tsx | 6 +++- .../result-category-card.tsx | 2 +- .../user-info-card.skeleton.tsx | 33 +++++++++++++++++++ 3 files changed, 39 insertions(+), 2 deletions(-) create mode 100644 app/ui/user/user-info-card/user-info-card.skeleton.tsx diff --git a/app/(sub-page)/result/page.tsx b/app/(sub-page)/result/page.tsx index cfcf58b8..03c15ad4 100644 --- a/app/(sub-page)/result/page.tsx +++ b/app/(sub-page)/result/page.tsx @@ -5,6 +5,8 @@ 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 { fetchCredits } from '@/app/business/result/result.query'; +import { Suspense } from 'react'; +import UserInfoCardSkeleton from '@/app/ui/user/user-info-card/user-info-card.skeleton'; interface ResultPageProp { searchParams: { category: string }; @@ -18,7 +20,9 @@ async function ResultPage({ searchParams }: ResultPageProp) { return (
    - + }> + +
    이수학점 - {takeCategoryCredit(category, totalCredit)} + {takeCategoryCredit(category, takenCredit)}
    diff --git a/app/ui/user/user-info-card/user-info-card.skeleton.tsx b/app/ui/user/user-info-card/user-info-card.skeleton.tsx new file mode 100644 index 00000000..a72b2062 --- /dev/null +++ b/app/ui/user/user-info-card/user-info-card.skeleton.tsx @@ -0,0 +1,33 @@ +import Skeleton from '../../view/atom/skeleton'; + +function UserInfoCardSkeleton() { + return ( + <> + +
    +
    +
      + {Array.from({ length: 5 }).map((_, index) => ( +
    • + +
    • + ))} +
    +
      + {Array.from({ length: 5 }).map((_, index) => ( +
    • + +
    • + ))} +
    +
    +
    + +
    +
    + + + ); +} + +export default UserInfoCardSkeleton; From 93e1b473caa656fe0baf1556dfb89380601760c6 Mon Sep 17 00:00:00 2001 From: yougyung Date: Wed, 1 May 2024 22:29:38 +0900 Subject: [PATCH 07/25] refactor: change structure in result page --- app/(sub-page)/result/page.tsx | 22 ++++------------- app/business/user/user.query.ts | 2 +- app/mocks/handlers/result-handler.mock.ts | 2 +- app/mocks/handlers/user-handler.mock.ts | 4 ++-- .../result-category/result-category.tsx | 24 +++++++++++++++++++ 5 files changed, 33 insertions(+), 21 deletions(-) create mode 100644 app/ui/result/result-category/result-category.tsx diff --git a/app/(sub-page)/result/page.tsx b/app/(sub-page)/result/page.tsx index 03c15ad4..97f551b3 100644 --- a/app/(sub-page)/result/page.tsx +++ b/app/(sub-page)/result/page.tsx @@ -7,14 +7,13 @@ import ResultCategoryDetail from '@/app/ui/result/result-category-detail/result- import { fetchCredits } from '@/app/business/result/result.query'; 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'; interface ResultPageProp { searchParams: { category: string }; } async function ResultPage({ searchParams }: ResultPageProp) { - const categorys = await fetchCredits(); - const { category } = searchParams; return ( @@ -24,21 +23,10 @@ async function ResultPage({ searchParams }: ResultPageProp) { -
    - {categorys.map((category, index) => ( - - ))} -
    + }> + + + {category && }
    ); diff --git a/app/business/user/user.query.ts b/app/business/user/user.query.ts index c690aae5..88d666da 100644 --- a/app/business/user/user.query.ts +++ b/app/business/user/user.query.ts @@ -30,7 +30,7 @@ export async function getUserInfo(): Promise { export async function fetchResultUserInfo(): Promise { try { - const response = await fetch(`${API_PATH.resultUserInfo}`, { + const response = await fetch(API_PATH.resultUserInfo, { headers: { Authorization: `Bearer ${cookies().get('accessToken')?.value}`, }, diff --git a/app/mocks/handlers/result-handler.mock.ts b/app/mocks/handlers/result-handler.mock.ts index b94e33b3..2d7e9646 100644 --- a/app/mocks/handlers/result-handler.mock.ts +++ b/app/mocks/handlers/result-handler.mock.ts @@ -10,7 +10,7 @@ export const resultHandlers = [ }), http.get(API_PATH.credits, async () => { const resultCategoryInfo = mockDatabase.getCredits(); - await delay(4000); + await delay(5000); return HttpResponse.json(resultCategoryInfo); }), ]; diff --git a/app/mocks/handlers/user-handler.mock.ts b/app/mocks/handlers/user-handler.mock.ts index 54d09ade..e045474a 100644 --- a/app/mocks/handlers/user-handler.mock.ts +++ b/app/mocks/handlers/user-handler.mock.ts @@ -47,14 +47,14 @@ export const userHandlers = [ return HttpResponse.json(userInfo); }), - http.get(`${API_PATH.resultUserInfo}`, async ({ request }) => { + http.get(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(3000); + await delay(2000); if (!userInfo) { return HttpResponse.json({ status: 401, message: 'Unauthorized' }, { status: 401 }); diff --git a/app/ui/result/result-category/result-category.tsx b/app/ui/result/result-category/result-category.tsx new file mode 100644 index 00000000..6731ced5 --- /dev/null +++ b/app/ui/result/result-category/result-category.tsx @@ -0,0 +1,24 @@ +import { cn } from '@/app/utils/shadcn/utils'; +import ResultCategoryCard from '../result-category-card/result-category-card'; +import { fetchCredits } from '@/app/business/result/result.query'; +import { RESULT_CATEGORY } from '@/app/utils/key/result-category.key'; + +async function ResultCategory() { + const categorys = await fetchCredits(); + + return ( +
    + {categorys.map((category, index) => ( + + ))} +
    + ); +} +export default ResultCategory; From 81bfc3cf94550131639ccfed94116e560dd7baea Mon Sep 17 00:00:00 2001 From: yougyung Date: Thu, 2 May 2024 09:24:50 +0900 Subject: [PATCH 08/25] feat: add skeleton view in resultCategory --- app/(sub-page)/result/page.tsx | 3 +- .../result-category-card.skeleton.tsx | 40 +++++++++++++++++++ .../result-category.skeleton.tsx | 15 +++++++ 3 files changed, 57 insertions(+), 1 deletion(-) create mode 100644 app/ui/result/result-category-card/result-category-card.skeleton.tsx create mode 100644 app/ui/result/result-category/result-category.skeleton.tsx diff --git a/app/(sub-page)/result/page.tsx b/app/(sub-page)/result/page.tsx index 97f551b3..9fb2255e 100644 --- a/app/(sub-page)/result/page.tsx +++ b/app/(sub-page)/result/page.tsx @@ -8,6 +8,7 @@ import { fetchCredits } from '@/app/business/result/result.query'; 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 }; @@ -23,7 +24,7 @@ async function ResultPage({ searchParams }: ResultPageProp) { - }> + }> diff --git a/app/ui/result/result-category-card/result-category-card.skeleton.tsx b/app/ui/result/result-category-card/result-category-card.skeleton.tsx new file mode 100644 index 00000000..ade06bc0 --- /dev/null +++ b/app/ui/result/result-category-card/result-category-card.skeleton.tsx @@ -0,0 +1,40 @@ +'use client'; +import { cn } from '@/app/utils/shadcn/utils'; + +import Book from '@/public/assets/book.svg'; +import Image from 'next/image'; +import * as React from 'react'; +import Skeleton from '../../view/atom/skeleton'; + +function ResultCategoryCardSkeleton() { + return ( +
    +
    +
    + category-img + +
    +
    +
    + +
    +
    +
    +
    + + +
    +
    + + +
    +
    + +
    +
    + ); +} + +export default ResultCategoryCardSkeleton; diff --git a/app/ui/result/result-category/result-category.skeleton.tsx b/app/ui/result/result-category/result-category.skeleton.tsx new file mode 100644 index 00000000..2d2a02ba --- /dev/null +++ b/app/ui/result/result-category/result-category.skeleton.tsx @@ -0,0 +1,15 @@ +import { cn } from '@/app/utils/shadcn/utils'; +import ResultCategoryCardSkeleton from '../result-category-card/result-category-card.skeleton'; + +async function ResultCategorySkeleton() { + return ( +
    + {Array.from({ length: 3 }).map((_, index) => ( + + ))} +
    + ); +} +export default ResultCategorySkeleton; From 9c284e3f951484c5b9b385139b10b5b9b325ea54 Mon Sep 17 00:00:00 2001 From: yougyung Date: Thu, 2 May 2024 10:16:16 +0900 Subject: [PATCH 09/25] feat: add severalMajor option in resultUserCard --- app/mocks/data.mock.ts | 5 +++++ .../result-category-card.tsx | 16 +++++++++++----- app/ui/user/user-info-card/user-info-card.tsx | 16 +++++++++++++--- app/utils/key/result-category.key.ts | 6 ++++++ 4 files changed, 35 insertions(+), 8 deletions(-) diff --git a/app/mocks/data.mock.ts b/app/mocks/data.mock.ts index f9567a97..94df7222 100644 --- a/app/mocks/data.mock.ts +++ b/app/mocks/data.mock.ts @@ -76,7 +76,12 @@ export const resultUserInfo = JSON.parse(`{ { "majorType" : "PRIMARY", "major": "디지털콘텐츠디자인학과" + }, + { + "majorType" : "DUAL", + "major": "경영학과" } + ], "totalCredit": 132, "takenCredit": 50, diff --git a/app/ui/result/result-category-card/result-category-card.tsx b/app/ui/result/result-category-card/result-category-card.tsx index d4417d57..9902ed5d 100644 --- a/app/ui/result/result-category-card/result-category-card.tsx +++ b/app/ui/result/result-category-card/result-category-card.tsx @@ -5,7 +5,12 @@ import Book from '@/public/assets/book.svg'; import Image from 'next/image'; import useDialog from '@/app/hooks/useDialog'; import * as React from 'react'; -import { RESULT_CATEGORY, RESULT_CATEGORY_KO, ResultCategoryKey } from '@/app/utils/key/result-category.key'; +import { + MAJOR_NOTATION, + RESULT_CATEGORY, + RESULT_CATEGORY_KO, + ResultCategoryKey, +} from '@/app/utils/key/result-category.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'; @@ -19,16 +24,17 @@ interface ResultCategoryCardProps { completed?: boolean; } -const filterSeveralMajor = (category: ResultCategoryKey) => { +const displaySeveralMajor = (category: ResultCategoryKey) => { const { DUAL_MANDATORY_MAJOR, DUAL_ELECTIVE_MAJOR, DUAL_BASIC_ACADEMICAL_CULTURE, SUB_MAJOR } = RESULT_CATEGORY; + const { DUAL, SUB } = MAJOR_NOTATION; switch (category) { case DUAL_MANDATORY_MAJOR: case DUAL_ELECTIVE_MAJOR: case DUAL_BASIC_ACADEMICAL_CULTURE: - return