From 08c96a0b80b45d828190a4b2e6663e745c87da1a Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Thu, 8 Aug 2024 10:35:15 +0000 Subject: [PATCH 1/6] Create draft PR for #348 From c5f11ac4b1d802fa17a9de63a6ca46627e455010 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Thu, 8 Aug 2024 20:05:07 +0900 Subject: [PATCH 2/6] chore-fe: Snake To Camel (#334) Co-authored-by: Jeongwoo Park <121204715+lurgi@users.noreply.github.com> --- frontend/src/api/applicant.ts | 7 +++--- frontend/src/api/evaluation.ts | 11 ++++----- frontend/src/api/process.ts | 9 ++++--- frontend/src/mocks/applicantDetails.json | 4 ++-- frontend/src/mocks/evaluationMockData.json | 6 ++--- .../src/mocks/handlers/applicantHandlers.ts | 12 +++++----- .../src/mocks/handlers/evaluationHandlers.ts | 4 ++-- .../src/mocks/handlers/processHandlers.ts | 18 +++++++------- frontend/src/mocks/processMockData.json | 24 +++++++++---------- frontend/src/mocks/specificApplicant.json | 6 ++--- frontend/src/utils/snakeToCamel.ts | 21 ---------------- 11 files changed, 49 insertions(+), 73 deletions(-) delete mode 100644 frontend/src/utils/snakeToCamel.ts diff --git a/frontend/src/api/applicant.ts b/frontend/src/api/applicant.ts index 57eb9ea57..7b433cd5a 100644 --- a/frontend/src/api/applicant.ts +++ b/frontend/src/api/applicant.ts @@ -1,4 +1,3 @@ -import snakeToCamel from '@utils/snakeToCamel'; import { APPLICANTS } from './endPoint'; const moveApplicant = async ({ processId, applicants }: { processId: number; applicants: number[] }) => { @@ -8,7 +7,7 @@ const moveApplicant = async ({ processId, applicants }: { processId: number; app 'Content-Type': 'application/json', }, body: JSON.stringify({ - applicant_ids: applicants, + applicantIds: applicants, }), }); @@ -31,7 +30,7 @@ const getSpecificApplicant = async ({ applicantId }: { applicantId: number }) => } const data = await response.json(); - return snakeToCamel(data); + return data; }; const rejectApplicant = async ({ applicantId }: { applicantId: number }) => { @@ -56,7 +55,7 @@ const getDetailApplicant = async ({ applicantId }: { applicantId: number }) => { } const data = await response.json(); - return snakeToCamel(data); + return data; }; const applicantApis = { diff --git a/frontend/src/api/evaluation.ts b/frontend/src/api/evaluation.ts index 9f802ea7b..26210f4e8 100644 --- a/frontend/src/api/evaluation.ts +++ b/frontend/src/api/evaluation.ts @@ -1,12 +1,11 @@ -import snakeToCamel from '@utils/snakeToCamel'; import { convertParamsToQueryString } from './utils'; import { EVALUATIONS } from './endPoint'; const evaluationApis = { get: async ({ processId, applicantId }: { processId: number; applicantId: number }) => { const queryParams = { - process_id: processId.toString(), - applicant_id: applicantId.toString(), + processId: processId.toString(), + applicantId: applicantId.toString(), }; const response = await fetch(`${EVALUATIONS}?${convertParamsToQueryString(queryParams)}`, { @@ -20,7 +19,7 @@ const evaluationApis = { } const data = await response.json(); - return snakeToCamel(data); + return data; }, create: async ({ @@ -35,8 +34,8 @@ const evaluationApis = { content: string; }) => { const queryParams = { - process_id: processId.toString(), - applicant_id: applicantId.toString(), + processId: processId.toString(), + applicantId: applicantId.toString(), }; const response = await fetch(`${EVALUATIONS}?${convertParamsToQueryString(queryParams)}`, { diff --git a/frontend/src/api/process.ts b/frontend/src/api/process.ts index 04c39f322..83862ac9f 100644 --- a/frontend/src/api/process.ts +++ b/frontend/src/api/process.ts @@ -1,4 +1,3 @@ -import snakeToCamel from '@utils/snakeToCamel'; import { PROCESSES } from './endPoint'; import { createParams } from './utils'; import ApiError from './ApiError'; @@ -20,7 +19,7 @@ const processApis = { } const data = await response.json(); - return snakeToCamel(data); + return data; }, create: async (params: { dashboardId: number; orderIndex: number; name: string; description?: string }) => { @@ -30,8 +29,8 @@ const processApis = { 'Content-Type': 'application/json', }, body: JSON.stringify({ - order_index: params.orderIndex, - process_name: params.name, + orderIndex: params.orderIndex, + processName: params.name, description: params?.description, }), }); @@ -50,7 +49,7 @@ const processApis = { 'Content-Type': 'application/json', }, body: JSON.stringify({ - process_name: params.name, + processName: params.name, description: params?.description, }), }); diff --git a/frontend/src/mocks/applicantDetails.json b/frontend/src/mocks/applicantDetails.json index 4c0da95bd..1046b0e72 100644 --- a/frontend/src/mocks/applicantDetails.json +++ b/frontend/src/mocks/applicantDetails.json @@ -1,12 +1,12 @@ { "details": [ { - "order_index": 0, + "orderIndex": 0, "question": "성별", "answer": "남" }, { - "order_index": 1, + "orderIndex": 1, "question": "좋아하는 숫자가 무엇인가요?", "answer": "1" } diff --git a/frontend/src/mocks/evaluationMockData.json b/frontend/src/mocks/evaluationMockData.json index cbce11529..0a9c3766d 100644 --- a/frontend/src/mocks/evaluationMockData.json +++ b/frontend/src/mocks/evaluationMockData.json @@ -1,17 +1,17 @@ { "evaluations": [ { - "evaluation_id": 1, + "evaluationId": 1, "score": 2, "content": "인재상과 맞지 않습니다." }, { - "evaluation_id": 2, + "evaluationId": 2, "score": 3, "content": "맞춤법 틀림" }, { - "evaluation_id": 3, + "evaluationId": 3, "score": 5, "content": "전 마음에 들어요" } diff --git a/frontend/src/mocks/handlers/applicantHandlers.ts b/frontend/src/mocks/handlers/applicantHandlers.ts index 2b955af55..b233fcc78 100644 --- a/frontend/src/mocks/handlers/applicantHandlers.ts +++ b/frontend/src/mocks/handlers/applicantHandlers.ts @@ -10,18 +10,18 @@ const applicantHandlers = [ const { processId } = params; const body = await request.json(); - if (typeof body !== 'object' || !body?.applicant_ids || body.applicant_ids.length === 0) { - throw new Error('body로 주어진 값이 {applicant_ids: number[]} 형식이 아닙니다.'); + if (typeof body !== 'object' || !body?.applicantIds || body.applicantIds.length === 0) { + throw new Error('body로 주어진 값이 {applicantIds: number[]} 형식이 아닙니다.'); } - body.applicant_ids.forEach((id: string) => { + body.applicantIds.forEach((id: string) => { if (!(typeof id === 'number')) { - throw new Error('body로 주어진 값이 {applicant_ids: number[]} 형식이 아닙니다.'); + throw new Error('body로 주어진 값이 {applicantIds: number[]} 형식이 아닙니다.'); } }); - const { applicant_ids } = body; - console.log(`${applicant_ids}지원자(들)을/를 ${processId}에 해당하는 프로세스로 이동합니다.`); + const { applicantIds } = body; + console.log(`${applicantIds}지원자(들)을/를 ${processId}에 해당하는 프로세스로 이동합니다.`); return HttpResponse.json({ status: 200 }); }), diff --git a/frontend/src/mocks/handlers/evaluationHandlers.ts b/frontend/src/mocks/handlers/evaluationHandlers.ts index 36914ee7e..a7968618e 100644 --- a/frontend/src/mocks/handlers/evaluationHandlers.ts +++ b/frontend/src/mocks/handlers/evaluationHandlers.ts @@ -9,8 +9,8 @@ const evaluationHandlers = [ http.post(EVALUATIONS, async ({ request }) => { const url = new URL(request.url); - const processId = url.searchParams.get('process_id'); - const applicantId = url.searchParams.get('applicant_id'); + const processId = url.searchParams.get('processId'); + const applicantId = url.searchParams.get('applicantId'); const body = (await request.json()) as { score: string; content: string; diff --git a/frontend/src/mocks/handlers/processHandlers.ts b/frontend/src/mocks/handlers/processHandlers.ts index b327f2aa8..3030d232e 100644 --- a/frontend/src/mocks/handlers/processHandlers.ts +++ b/frontend/src/mocks/handlers/processHandlers.ts @@ -8,14 +8,14 @@ const processHandlers = [ http.post(`${PROCESSES}`, async ({ request }) => { const url = new URL(request.url); - const dashboardId = url.searchParams.get('dashboard_id'); + const dashboardId = url.searchParams.get('dashboardId'); const body = (await request.json()) as { - order_index: number; - process_name: string; + orderIndex: number; + processName: string; description?: string; }; - if (!body.order_index || !body.process_name || !dashboardId) { + if (!body.orderIndex || !body.processName || !dashboardId) { return new Response(null, { status: 404, statusText: 'Process Not Found', @@ -28,8 +28,8 @@ const processHandlers = [ }); }), - http.delete(`${PROCESSES}/:process_id`, async ({ params }) => { - if (!params.process_id) { + http.delete(`${PROCESSES}/:processId`, async ({ params }) => { + if (!params.processId) { return new Response(null, { status: 400, statusText: 'Process Not Found', @@ -42,13 +42,13 @@ const processHandlers = [ }); }), - http.patch(`${PROCESSES}/:process_id`, async ({ request, params }) => { + http.patch(`${PROCESSES}/:processId`, async ({ request, params }) => { const body = (await request.json()) as { - process_name: string; + processName: string; description?: string; }; - if (!body.process_name || !params.process_id) { + if (!body.processName || !params.processId) { return new Response(null, { status: 400, statusText: 'Process Not Found', diff --git a/frontend/src/mocks/processMockData.json b/frontend/src/mocks/processMockData.json index ee04ef9e0..0e14bcdf7 100644 --- a/frontend/src/mocks/processMockData.json +++ b/frontend/src/mocks/processMockData.json @@ -1,25 +1,25 @@ { "processes": [ { - "process_id": 1, - "order_index": 0, + "processId": 1, + "orderIndex": 0, "applicants": [ - { "applicant_id": 1, "applicant_name": "러기", "created_at": "2024-07-16T05:46:08.328593" }, - { "applicant_id": 2, "applicant_name": "도비", "created_at": "2024-07-16T05:46:08.341936" }, - { "applicant_id": 6, "applicant_name": "러시", "created_at": "2024-07-16T05:46:08.356121" }, - { "applicant_id": 7, "applicant_name": "냥인", "created_at": "2024-07-16T05:46:08.359131" }, - { "applicant_id": 8, "applicant_name": "렛서", "created_at": "2024-07-16T05:46:08.361759" } + { "applicantId": 1, "applicantName": "러기", "createdAt": "2024-07-16T05:46:08.328593" }, + { "applicantId": 2, "applicantName": "도비", "createdAt": "2024-07-16T05:46:08.341936" }, + { "applicantId": 6, "applicantName": "러시", "createdAt": "2024-07-16T05:46:08.356121" }, + { "applicantId": 7, "applicantName": "냥인", "createdAt": "2024-07-16T05:46:08.359131" }, + { "applicantId": 8, "applicantName": "렛서", "createdAt": "2024-07-16T05:46:08.361759" } ], "name": "지원서", "description": "지원서를 확인한다." }, { - "process_id": 2, - "order_index": 1, + "processId": 2, + "orderIndex": 1, "applicants": [ - { "applicant_id": 3, "applicant_name": "아르", "created_at": "2024-07-16T05:46:08.345472" }, - { "applicant_id": 4, "applicant_name": "초코칩", "created_at": "2024-07-16T05:46:08.350114" }, - { "applicant_id": 5, "applicant_name": "명오", "created_at": "2024-07-16T05:46:08.352613" } + { "applicantId": 3, "applicantName": "아르", "createdAt": "2024-07-16T05:46:08.345472" }, + { "applicantId": 4, "applicantName": "초코칩", "createdAt": "2024-07-16T05:46:08.350114" }, + { "applicantId": 5, "applicantName": "명오", "createdAt": "2024-07-16T05:46:08.352613" } ], "name": "최종 합격", "description": "최종 합격자" diff --git a/frontend/src/mocks/specificApplicant.json b/frontend/src/mocks/specificApplicant.json index a78639270..d6929d7ec 100644 --- a/frontend/src/mocks/specificApplicant.json +++ b/frontend/src/mocks/specificApplicant.json @@ -1,8 +1,8 @@ { - "applicant_id": 1, - "created_at": "2024-07-19T11:32:16.156406", + "applicantId": 1, + "createdAt": "2024-07-19T11:32:16.156406", "name": "러기", "email": "lurg@mail.com", "phone": "01011111111", - "process_name": "최종 합격" + "processName": "최종 합격" } diff --git a/frontend/src/utils/snakeToCamel.ts b/frontend/src/utils/snakeToCamel.ts deleted file mode 100644 index 3616ed19c..000000000 --- a/frontend/src/utils/snakeToCamel.ts +++ /dev/null @@ -1,21 +0,0 @@ -/* eslint-disable @typescript-eslint/no-explicit-any */ -/* eslint-disable no-else-return */ -const toCamelCase = (str: string): string => str.replace(/_([a-z])/g, (_, letter) => letter.toUpperCase()); - -/** - * 사용 예시 const camelCaseObject = keysToCamelCase(snakeCaseObject); - * @param obj - * @returns - */ -export default function snakeToCamel(obj: any): any { - if (Array.isArray(obj)) { - return obj.map((item) => snakeToCamel(item)); - } else if (obj !== null && obj.constructor === Object) { - const newObj: any = {}; - Object.keys(obj).forEach((key) => { - newObj[toCamelCase(key)] = snakeToCamel(obj[key]); - }); - return newObj; - } - return obj; -} From ed7807c12ec1a34e6e0e5dfe75fdd404d44c1e0f Mon Sep 17 00:00:00 2001 From: Jeongwoo Park <121204715+lurgi@users.noreply.github.com> Date: Thu, 8 Aug 2024 20:40:36 +0900 Subject: [PATCH 3/6] =?UTF-8?q?chore:=20=EC=83=81=EC=88=98=20param?= =?UTF-8?q?=EA=B0=92=EC=9C=BC=EB=A1=9C=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- frontend/src/api/process.ts | 4 ++-- .../components/appModal/ApplicantBaseInfo/index.tsx | 5 ++++- .../src/components/dashboard/ProcessColumn/index.tsx | 5 ++++- frontend/src/hooks/useProcess/index.ts | 12 ++++++++---- frontend/src/hooks/useProcess/useProcess.test.tsx | 2 +- frontend/src/pages/Dashboard/index.tsx | 7 +++++-- 6 files changed, 24 insertions(+), 11 deletions(-) diff --git a/frontend/src/api/process.ts b/frontend/src/api/process.ts index 83862ac9f..b064da0cc 100644 --- a/frontend/src/api/process.ts +++ b/frontend/src/api/process.ts @@ -3,8 +3,8 @@ import { createParams } from './utils'; import ApiError from './ApiError'; const processApis = { - get: async ({ id }: { id: number }) => { - const response = await fetch(`${PROCESSES}?${createParams({ dashboard_id: String(id) })}`, { + get: async ({ id }: { id: string }) => { + const response = await fetch(`${PROCESSES}?${createParams({ dashboard_id: id })}`, { headers: { Accept: 'application/json', }, diff --git a/frontend/src/components/appModal/ApplicantBaseInfo/index.tsx b/frontend/src/components/appModal/ApplicantBaseInfo/index.tsx index 90d4d160a..c0d830382 100644 --- a/frontend/src/components/appModal/ApplicantBaseInfo/index.tsx +++ b/frontend/src/components/appModal/ApplicantBaseInfo/index.tsx @@ -1,3 +1,5 @@ +import { useParams } from 'react-router-dom'; + import Dropdown from '@components/common/Dropdown'; import Button from '@components/common/Button'; import useProcess from '@hooks/useProcess'; @@ -14,7 +16,8 @@ interface ApplicantBaseInfoProps { export default function ApplicantBaseInfo({ applicantId }: ApplicantBaseInfoProps) { const { data: applicantBaseDetail } = specificApplicant.useGetBaseInfo({ applicantId }); const { mutate: rejectMutate } = specificApplicant.useRejectApplicant(); - const { processList } = useProcess(); + const { dashboardId, postId } = useParams() as { dashboardId: string; postId: string }; + const { processList } = useProcess({ dashboardId, postId }); const { moveApplicantProcess } = useApplicant({ applicantId }); const { close } = useModal(); diff --git a/frontend/src/components/dashboard/ProcessColumn/index.tsx b/frontend/src/components/dashboard/ProcessColumn/index.tsx index 5d64d4806..321ec1170 100644 --- a/frontend/src/components/dashboard/ProcessColumn/index.tsx +++ b/frontend/src/components/dashboard/ProcessColumn/index.tsx @@ -1,3 +1,5 @@ +import { useParams } from 'react-router-dom'; + import { useSpecificApplicantId } from '@contexts/SpecificApplicnatIdContext'; import { Process } from '@customTypes/process'; import useProcess from '@hooks/useProcess'; @@ -12,7 +14,8 @@ interface ProcessColumnProps { } export default function ProcessColumn({ process }: ProcessColumnProps) { - const { processList } = useProcess(); + const { dashboardId, postId } = useParams() as { dashboardId: string; postId: string }; + const { processList } = useProcess({ dashboardId, postId }); const { moveApplicantProcess } = useApplicant({}); const { setApplicantId } = useSpecificApplicantId(); const { open } = useModal(); diff --git a/frontend/src/hooks/useProcess/index.ts b/frontend/src/hooks/useProcess/index.ts index d522d2aad..ea66903e4 100644 --- a/frontend/src/hooks/useProcess/index.ts +++ b/frontend/src/hooks/useProcess/index.ts @@ -3,7 +3,6 @@ import { useQuery } from '@tanstack/react-query'; import type { Process } from '@customTypes/process'; import processApis from '@api/process'; -import { DASHBOARD_ID } from '@constants/constants'; import QUERY_KEYS from '@hooks/queryKeys'; interface SimpleProcess { @@ -11,6 +10,11 @@ interface SimpleProcess { processId: number; } +interface UseProcessProps { + dashboardId: string; + postId: string; +} + interface UseProcessReturn { processes: Process[]; processList: SimpleProcess[]; @@ -18,10 +22,10 @@ interface UseProcessReturn { isLoading: boolean; } -export default function useProcess(): UseProcessReturn { +export default function useProcess({ dashboardId, postId }: UseProcessProps): UseProcessReturn { const { data, error, isLoading } = useQuery<{ processes: Process[] }>({ - queryKey: [QUERY_KEYS.DASHBOARD, DASHBOARD_ID], - queryFn: () => processApis.get({ id: DASHBOARD_ID }), + queryKey: [QUERY_KEYS.DASHBOARD, dashboardId, postId], + queryFn: () => processApis.get({ id: postId }), }); const processes = data?.processes || []; diff --git a/frontend/src/hooks/useProcess/useProcess.test.tsx b/frontend/src/hooks/useProcess/useProcess.test.tsx index 899580988..4ea4502c8 100644 --- a/frontend/src/hooks/useProcess/useProcess.test.tsx +++ b/frontend/src/hooks/useProcess/useProcess.test.tsx @@ -11,7 +11,7 @@ const wrapper = ({ children }: PropsWithChildren) => ( describe('useProcess', () => { it('should return processes and processNameList when data is loaded', async () => { - const { result } = renderHook(() => useProcess(), { wrapper }); + const { result } = renderHook(() => useProcess({ dashboardId: '1', postId: '1' }), { wrapper }); await waitFor(() => expect(result.current.isLoading).toBe(false)); diff --git a/frontend/src/pages/Dashboard/index.tsx b/frontend/src/pages/Dashboard/index.tsx index 89bc16be6..3e2f24638 100644 --- a/frontend/src/pages/Dashboard/index.tsx +++ b/frontend/src/pages/Dashboard/index.tsx @@ -1,6 +1,8 @@ +import { useParams } from 'react-router-dom'; + +import ProcessManageBoard from '@components/processManagement/ProcessManageBoard'; import Tab from '@components/common/Tab'; import KanbanBoard from '@components/dashboard/KanbanBoard'; -import ProcessManageBoard from '@components/processManagement/ProcessManageBoard'; import useTab from '@components/common/Tab/useTab'; import useProcess from '@hooks/useProcess'; @@ -13,7 +15,8 @@ import S from './style'; export type DashboardTabItems = '지원자 관리' | '모집 과정 관리'; export default function Dashboard() { - const { processes } = useProcess(); + const { dashboardId, postId } = useParams() as { dashboardId: string; postId: string }; + const { processes } = useProcess({ dashboardId, postId }); const { currentMenu, moveTab } = useTab({ defaultValue: '지원자 관리' }); From a3a6a118883eeae4371f54f44208b16774e690f3 Mon Sep 17 00:00:00 2001 From: Jeongwoo Park <121204715+lurgi@users.noreply.github.com> Date: Thu, 8 Aug 2024 20:41:25 +0900 Subject: [PATCH 4/6] =?UTF-8?q?feat:=20dashboard=20list=20GET=20API=20?= =?UTF-8?q?=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- frontend/src/api/dashboard.ts | 24 ++++++++++ frontend/src/hooks/useGetDashboards/index.tsx | 17 +++++++ frontend/src/mocks/dashboardList.json | 44 +++++++++++++++++++ .../src/mocks/handlers/dashboardHandlers.ts | 5 ++- frontend/src/types/dashboard.ts | 22 ++++++++++ 5 files changed, 111 insertions(+), 1 deletion(-) create mode 100644 frontend/src/hooks/useGetDashboards/index.tsx create mode 100644 frontend/src/mocks/dashboardList.json diff --git a/frontend/src/api/dashboard.ts b/frontend/src/api/dashboard.ts index fe896c9ba..d18cda7b6 100644 --- a/frontend/src/api/dashboard.ts +++ b/frontend/src/api/dashboard.ts @@ -4,6 +4,30 @@ import { convertParamsToQueryString } from './utils'; import ApiError from './ApiError'; const dashboardApis = { + get: async ({ dashboardId }: { dashboardId: string }) => { + const queryParams = { + dashboardId, + }; + + const response = await fetch(`${DASHBOARDS}?${convertParamsToQueryString(queryParams)}`, { + headers: { + Accept: 'application/json', + }, + }); + + if (!response.ok) { + throw new ApiError({ + method: 'GET', + statusCode: response.status, + message: '공고 리스트의 정보를 불러오지 못했습니다.', + }); + } + + const data = await response.json(); + + return data; + }, + create: async ({ clubId, dashboardFormInfo }: { clubId: number; dashboardFormInfo: DashboardFormInfo }) => { const queryParams = { clubId: String(clubId), diff --git a/frontend/src/hooks/useGetDashboards/index.tsx b/frontend/src/hooks/useGetDashboards/index.tsx new file mode 100644 index 000000000..1e72dae45 --- /dev/null +++ b/frontend/src/hooks/useGetDashboards/index.tsx @@ -0,0 +1,17 @@ +import dashboardApis from '@api/dashboard'; +import type { Club } from '@customTypes/dashboard'; +import QUERY_KEYS from '@hooks/queryKeys'; +import { useQuery } from '@tanstack/react-query'; + +interface UseGetDashboardsProps { + dashboardId: string; +} + +export default function useGetDashboards({ dashboardId }: UseGetDashboardsProps) { + const { data, error, isLoading } = useQuery({ + queryKey: [QUERY_KEYS.DASHBOARD, dashboardId], + queryFn: () => dashboardApis.get({ dashboardId }), + }); + + return { data, error, isLoading }; +} diff --git a/frontend/src/mocks/dashboardList.json b/frontend/src/mocks/dashboardList.json new file mode 100644 index 000000000..da07a01f6 --- /dev/null +++ b/frontend/src/mocks/dashboardList.json @@ -0,0 +1,44 @@ +{ + "clubName": "크루루", + "dashboards": [ + { + "dashboardId": "대시보드 id1", + "title": "프론트엔드 7기 모집", + "stats": { + "accept": 3, + "fail": 3, + "inProgress": 9, + "total": 15 + }, + "postUrl": "https://www.cruru.kr/123543920/recruit", + "startDate": "1900-01-21T00:00:00", + "endDate": "2024-07-24T18:00:00" + }, + { + "dashboardId": "대시보드 id2", + "title": "백엔드 7기 모집", + "stats": { + "accept": 3, + "fail": 3, + "inProgress": 9, + "total": 15 + }, + "postUrl": "https://www.cruru.kr/98765101/recruit", + "startDate": "1900-01-21T00:00:00", + "endDate": "2024-07-24T20:30:00" + }, + { + "dashboardId": "대시보드 id", + "title": "안드로이드 7기 모집", + "stats": { + "accept": 3, + "fail": 3, + "inProgress": 9, + "total": 15 + }, + "postUrl": "https://www.cruru.kr/7777890/recruit", + "startDate": "1900-01-21T00:00:00", + "endDate": "2024-07-24T21:00:00" + } + ] +} diff --git a/frontend/src/mocks/handlers/dashboardHandlers.ts b/frontend/src/mocks/handlers/dashboardHandlers.ts index 045ef74a4..5032e3194 100644 --- a/frontend/src/mocks/handlers/dashboardHandlers.ts +++ b/frontend/src/mocks/handlers/dashboardHandlers.ts @@ -1,7 +1,8 @@ -import { http } from 'msw'; +import { http, HttpResponse } from 'msw'; import { DASHBOARDS } from '@api/endPoint'; import { DashboardFormInfo } from '@customTypes/dashboard'; +import DASHBOARD_LIST from '../dashboardList.json'; const dashboardHandlers = [ http.post(DASHBOARDS, async ({ request }) => { @@ -21,6 +22,8 @@ const dashboardHandlers = [ statusText: 'Created', }); }), + + http.get(DASHBOARDS, () => HttpResponse.json(DASHBOARD_LIST)), ]; export default dashboardHandlers; diff --git a/frontend/src/types/dashboard.ts b/frontend/src/types/dashboard.ts index b24a70729..c256a9cec 100644 --- a/frontend/src/types/dashboard.ts +++ b/frontend/src/types/dashboard.ts @@ -23,3 +23,25 @@ export interface QuestionOption { choice: string; orderIndex: number; } + +interface Stats { + accept: number; + fail: number; + inProgress: number; + total: number; +} + +interface Dashboard { + dashboard_id?: string; + dashboardId?: string; + title: string; + stats: Stats; + postUrl: string; + startDate: string; + endDate: string; +} + +export interface Club { + clubName: string; + dashboards: Dashboard[]; +} From 135c57fb90fbbbdda9794761cec8116bd829bce5 Mon Sep 17 00:00:00 2001 From: Jeongwoo Park <121204715+lurgi@users.noreply.github.com> Date: Thu, 8 Aug 2024 21:52:59 +0900 Subject: [PATCH 5/6] =?UTF-8?q?chore:=20api=20=EB=8D=B0=EC=9D=B4=ED=84=B0?= =?UTF-8?q?=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- frontend/src/api/dashboard.ts | 2 +- frontend/src/mocks/dashboardList.json | 6 +++--- frontend/src/mocks/handlers/dashboardHandlers.ts | 14 +++++++++++++- frontend/src/types/dashboard.ts | 3 +-- 4 files changed, 18 insertions(+), 7 deletions(-) diff --git a/frontend/src/api/dashboard.ts b/frontend/src/api/dashboard.ts index d18cda7b6..a348b3ddc 100644 --- a/frontend/src/api/dashboard.ts +++ b/frontend/src/api/dashboard.ts @@ -6,7 +6,7 @@ import ApiError from './ApiError'; const dashboardApis = { get: async ({ dashboardId }: { dashboardId: string }) => { const queryParams = { - dashboardId, + clubId: dashboardId, }; const response = await fetch(`${DASHBOARDS}?${convertParamsToQueryString(queryParams)}`, { diff --git a/frontend/src/mocks/dashboardList.json b/frontend/src/mocks/dashboardList.json index da07a01f6..325c3c939 100644 --- a/frontend/src/mocks/dashboardList.json +++ b/frontend/src/mocks/dashboardList.json @@ -2,7 +2,7 @@ "clubName": "크루루", "dashboards": [ { - "dashboardId": "대시보드 id1", + "dashboardId": "1", "title": "프론트엔드 7기 모집", "stats": { "accept": 3, @@ -15,7 +15,7 @@ "endDate": "2024-07-24T18:00:00" }, { - "dashboardId": "대시보드 id2", + "dashboardId": "2", "title": "백엔드 7기 모집", "stats": { "accept": 3, @@ -28,7 +28,7 @@ "endDate": "2024-07-24T20:30:00" }, { - "dashboardId": "대시보드 id", + "dashboardId": "3", "title": "안드로이드 7기 모집", "stats": { "accept": 3, diff --git a/frontend/src/mocks/handlers/dashboardHandlers.ts b/frontend/src/mocks/handlers/dashboardHandlers.ts index 5032e3194..6224383f3 100644 --- a/frontend/src/mocks/handlers/dashboardHandlers.ts +++ b/frontend/src/mocks/handlers/dashboardHandlers.ts @@ -23,7 +23,19 @@ const dashboardHandlers = [ }); }), - http.get(DASHBOARDS, () => HttpResponse.json(DASHBOARD_LIST)), + http.get(DASHBOARDS, ({ request }) => { + const url = new URL(request.url); + const clubId = url.searchParams.get('clubId'); + + if (!clubId) { + return new Response(null, { + status: 400, + statusText: 'The request Param is missing required information.', + }); + } + + return HttpResponse.json(DASHBOARD_LIST); + }), ]; export default dashboardHandlers; diff --git a/frontend/src/types/dashboard.ts b/frontend/src/types/dashboard.ts index c256a9cec..e3cf5070f 100644 --- a/frontend/src/types/dashboard.ts +++ b/frontend/src/types/dashboard.ts @@ -32,8 +32,7 @@ interface Stats { } interface Dashboard { - dashboard_id?: string; - dashboardId?: string; + dashboardId: string; title: string; stats: Stats; postUrl: string; From 8abe32731f12fd9ec0b747b0b46d43703621498a Mon Sep 17 00:00:00 2001 From: Jeongwoo Park <121204715+lurgi@users.noreply.github.com> Date: Thu, 8 Aug 2024 21:53:28 +0900 Subject: [PATCH 6/6] =?UTF-8?q?feat:=20=EA=B3=B5=EA=B3=A0=20=EB=A6=AC?= =?UTF-8?q?=EC=8A=A4=ED=8A=B8=20=ED=99=94=EB=A9=B4=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../DashBoardList/DashboardList.stories.tsx | 36 ++++++++++++ frontend/src/pages/DashBoardList/index.tsx | 40 +++++++++++-- frontend/src/pages/DashBoardList/style.ts | 58 +++++++++++++++++++ frontend/src/pages/DashboardLayout/index.tsx | 17 +++++- 4 files changed, 142 insertions(+), 9 deletions(-) create mode 100644 frontend/src/pages/DashBoardList/DashboardList.stories.tsx create mode 100644 frontend/src/pages/DashBoardList/style.ts diff --git a/frontend/src/pages/DashBoardList/DashboardList.stories.tsx b/frontend/src/pages/DashBoardList/DashboardList.stories.tsx new file mode 100644 index 000000000..c838d49fb --- /dev/null +++ b/frontend/src/pages/DashBoardList/DashboardList.stories.tsx @@ -0,0 +1,36 @@ +import { reactRouterParameters } from 'storybook-addon-remix-react-router'; +import type { Meta, StoryObj } from '@storybook/react'; +import DashboardList from '.'; + +const meta: Meta = { + title: 'Components/DashboardList', + component: DashboardList, + parameters: { + layout: 'centered', + docs: { + description: { + component: 'DashboardList 컴포넌트는 여러 대시보드를 리스트 형태로 보여줍니다.', + }, + }, + reactRouter: reactRouterParameters({ + location: { + pathParams: { dashboardId: '1' }, + }, + routing: { path: '/dashboardId/:dashboardId' }, + }), + }, + tags: ['autodocs'], +}; + +export default meta; +type Story = StoryObj; + +export const Default: Story = { + decorators: [ + (Child) => ( +
+ +
+ ), + ], +}; diff --git a/frontend/src/pages/DashBoardList/index.tsx b/frontend/src/pages/DashBoardList/index.tsx index 668b25943..b13282193 100644 --- a/frontend/src/pages/DashBoardList/index.tsx +++ b/frontend/src/pages/DashBoardList/index.tsx @@ -1,10 +1,38 @@ +import RecruitmentCard from '@components/recruitment/RecruitmentCard'; +import { useNavigate, useParams } from 'react-router-dom'; +import useGetDashboards from '@hooks/useGetDashboards'; +import S from './style'; + export default function DashboardList() { + const { dashboardId } = useParams() as { dashboardId: string }; + const { data } = useGetDashboards({ dashboardId }); + const navigate = useNavigate(); + + const handleCardClick = (postId: number) => { + navigate(`/dashboard/${dashboardId}/${postId}`); + }; + return ( -
- List - {/* - TODO: 여기에 PostList를 넣어주세요!!!!!! - */} -
+ + {data?.clubName} + + {data?.dashboards.map((dashboard) => ( + postId로 변경 + dashboardId={Number(dashboard.dashboardId)} + title={dashboard.title} + postStats={dashboard.stats} + startDate={dashboard.startDate} + endDate={dashboard.endDate} + onClick={handleCardClick} + /> + ))} + navigate(`/dashboard/${dashboardId}/create`)}> +
+
+ 새 공고 추가 +
+
+
); } diff --git a/frontend/src/pages/DashBoardList/style.ts b/frontend/src/pages/DashBoardList/style.ts new file mode 100644 index 000000000..1b01a7d72 --- /dev/null +++ b/frontend/src/pages/DashBoardList/style.ts @@ -0,0 +1,58 @@ +import styled from '@emotion/styled'; + +const Container = styled.div` + display: flex; + flex-direction: column; + padding: 3.8rem 2.4rem; + gap: 2.4rem; +`; + +const Title = styled.h1` + ${({ theme }) => theme.typography.heading[700]} + padding-bottom: 2.4rem; + border-bottom: 1px solid ${({ theme }) => theme.baseColors.grayscale[300]}; +`; + +const CardGrid = styled.div` + display: grid; + grid-template-columns: repeat(auto-fill, 37.8rem); + gap: 2.4rem; +`; + +const AddCard = styled.div` + display: flex; + height: 20rem; + + flex-direction: column; + align-items: center; + justify-content: center; + + border: 0.2rem dashed ${({ theme }) => theme.baseColors.grayscale[600]}; + border-radius: 0.5rem; + padding: 2rem; + + cursor: pointer; + text-align: center; + + &:hover { + background-color: ${({ theme }) => theme.baseColors.grayscale[200]}; + } + + div { + font-size: 6rem; + color: ${({ theme }) => theme.colors.brand.primary}; + } + span { + ${({ theme }) => theme.typography.common.large} + color: ${({ theme }) => theme.baseColors.grayscale[800]}; + } +`; + +const S = { + Container, + Title, + CardGrid, + AddCard, +}; + +export default S; diff --git a/frontend/src/pages/DashboardLayout/index.tsx b/frontend/src/pages/DashboardLayout/index.tsx index 149689654..06fe91029 100644 --- a/frontend/src/pages/DashboardLayout/index.tsx +++ b/frontend/src/pages/DashboardLayout/index.tsx @@ -1,14 +1,25 @@ +import useGetDashboards from '@hooks/useGetDashboards'; import DashboardSidebar from '@components/dashboard/DashboardSidebar'; -import { Outlet } from 'react-router-dom'; +import { Outlet, useParams } from 'react-router-dom'; import S from './style'; export default function DashboardLayout() { - const options = [{ text: '프론트엔드 7기 모집', isSelected: true, postId: 1 }]; + const { dashboardId, postId } = useParams() as { dashboardId: string; postId: string }; + const { data, isLoading } = useGetDashboards({ dashboardId }); + + if (isLoading) return
Loading...
; + if (!data) return
something wrong
; + + const titleList = data.dashboards.map(({ title, dashboardId: postId2 }) => ({ + text: title, + isSelected: !!postId && postId === postId2, + postId: Number(postId2), + })); return ( - +