From ddeb93e27e4cbc64672e0ba82a70c4080ba78ceb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B9=80=EC=A7=84=ED=98=B8?= Date: Mon, 19 Aug 2024 17:47:09 +0900 Subject: [PATCH 01/27] =?UTF-8?q?feat:=20=EA=B3=A0=EC=A0=95=20=EA=B0=80?= =?UTF-8?q?=EA=B2=A9=EC=9D=84=20=EC=84=A4=EC=A0=95=ED=96=88=EB=8A=94?= =?UTF-8?q?=EC=A7=80=EC=97=90=20=EB=8C=80=ED=95=9C=20isFixed=20=ED=83=80?= =?UTF-8?q?=EC=9E=85=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- client/src/mocks/invalidMemberStepList.json | 27 ++++++++++++++------- client/src/mocks/memberActionStepList.json | 6 +++-- client/src/mocks/stepList.json | 27 ++++++++++++++------- client/src/types/serviceType.ts | 1 + 4 files changed, 41 insertions(+), 20 deletions(-) diff --git a/client/src/mocks/invalidMemberStepList.json b/client/src/mocks/invalidMemberStepList.json index 5c4d7fee1..33ca77e4c 100644 --- a/client/src/mocks/invalidMemberStepList.json +++ b/client/src/mocks/invalidMemberStepList.json @@ -8,13 +8,15 @@ "actionId": 999, "name": "망쵸", "price": null, - "sequence": 1 + "sequence": 1, + "isFixed": false }, { "actionId": 2, "name": "백호", "price": null, - "sequence": 2 + "sequence": 2, + "isFixed": false } ] }, @@ -27,13 +29,15 @@ "actionId": 3, "name": "감자탕", "price": 10000, - "sequence": 3 + "sequence": 3, + "isFixed": false }, { "actionId": 4, "name": "인생네컷", "price": 10000, - "sequence": 4 + "sequence": 4, + "isFixed": false } ] }, @@ -46,13 +50,15 @@ "actionId": 5, "name": "소하", "price": null, - "sequence": 5 + "sequence": 5, + "isFixed": false }, { "actionId": 6, "name": "웨디", "price": null, - "sequence": 6 + "sequence": 6, + "isFixed": false } ] }, @@ -65,7 +71,8 @@ "actionId": 9, "name": "노래방", "price": 20000, - "sequence": 10 + "sequence": 10, + "isFixed": false } ] }, @@ -78,13 +85,15 @@ "actionId": 7, "name": "망쵸", "price": null, - "sequence": 7 + "sequence": 7, + "isFixed": false }, { "actionId": 8, "name": "백호", "price": null, - "sequence": 8 + "sequence": 8, + "isFixed": false } ] } diff --git a/client/src/mocks/memberActionStepList.json b/client/src/mocks/memberActionStepList.json index 578dfdd11..734db4a75 100644 --- a/client/src/mocks/memberActionStepList.json +++ b/client/src/mocks/memberActionStepList.json @@ -8,13 +8,15 @@ "actionId": 1, "name": "망쵸", "price": null, - "sequence": 1 + "sequence": 1, + "isFixed": false }, { "actionId": 2, "name": "백호", "price": null, - "sequence": 2 + "sequence": 2, + "isFixed": false } ] } diff --git a/client/src/mocks/stepList.json b/client/src/mocks/stepList.json index e4221ffd3..355692d58 100644 --- a/client/src/mocks/stepList.json +++ b/client/src/mocks/stepList.json @@ -8,13 +8,15 @@ "actionId": 1, "name": "망쵸", "price": null, - "sequence": 1 + "sequence": 1, + "isFixed": false }, { "actionId": 2, "name": "백호", "price": null, - "sequence": 2 + "sequence": 2, + "isFixed": false } ] }, @@ -27,13 +29,15 @@ "actionId": 3, "name": "감자탕", "price": 10000, - "sequence": 3 + "sequence": 3, + "isFixed": false }, { "actionId": 4, "name": "인생네컷", "price": 10000, - "sequence": 4 + "sequence": 4, + "isFixed": false } ] }, @@ -46,13 +50,15 @@ "actionId": 5, "name": "소하", "price": null, - "sequence": 5 + "sequence": 5, + "isFixed": false }, { "actionId": 6, "name": "웨디", "price": null, - "sequence": 6 + "sequence": 6, + "isFixed": false } ] }, @@ -65,7 +71,8 @@ "actionId": 9, "name": "노래방", "price": 20000, - "sequence": 10 + "sequence": 10, + "isFixed": false } ] }, @@ -78,13 +85,15 @@ "actionId": 7, "name": "망쵸", "price": null, - "sequence": 7 + "sequence": 7, + "isFixed": false }, { "actionId": 8, "name": "백호", "price": null, - "sequence": 8 + "sequence": 8, + "isFixed": false } ] } diff --git a/client/src/types/serviceType.ts b/client/src/types/serviceType.ts index bddb36329..6446f46d3 100644 --- a/client/src/types/serviceType.ts +++ b/client/src/types/serviceType.ts @@ -38,6 +38,7 @@ export type Action = { name: string; price: number | null; sequence: number; + isFixed: boolean; }; export type BillAction = Omit & { From e08b5059fe9cde4160a97d0ce1008a7518009a4b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B9=80=EC=A7=84=ED=98=B8?= Date: Mon, 19 Aug 2024 22:44:37 +0900 Subject: [PATCH 02/27] =?UTF-8?q?feat:=20=EC=A7=80=EC=B6=9C=20=EC=83=81?= =?UTF-8?q?=EC=84=B8=20=EC=A1=B0=ED=9A=8C,=20=EC=88=98=EC=A0=95=20api=20?= =?UTF-8?q?=ED=95=A8=EC=88=98=20=EC=9E=91=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- client/src/apis/request/bill.ts | 35 ++++++++++++++++++++++++++------- 1 file changed, 28 insertions(+), 7 deletions(-) diff --git a/client/src/apis/request/bill.ts b/client/src/apis/request/bill.ts index dbf2fdd45..5be7fdcc6 100644 --- a/client/src/apis/request/bill.ts +++ b/client/src/apis/request/bill.ts @@ -1,8 +1,8 @@ -import type {Bill} from 'types/serviceType'; +import type {Bill, MemberReport} from 'types/serviceType'; import {BASE_URL} from '@apis/baseUrl'; import {TEMP_PREFIX} from '@apis/tempPrefix'; -import {requestDelete, requestPostWithoutResponse, requestPut} from '@apis/fetcher'; +import {requestDelete, requestGet, requestPostWithoutResponse, requestPut} from '@apis/fetcher'; import {WithEventId} from '@apis/withEventId.type'; type RequestPostBillList = { @@ -19,20 +19,18 @@ export const requestPostBillList = async ({eventId, billList}: WithEventId) => { +export const requestDeleteBillAction = async ({eventId, actionId}: WithEventId) => { await requestDelete({ baseUrl: BASE_URL.HD, endpoint: `${TEMP_PREFIX}/${eventId}/bill-actions/${actionId}`, }); }; -type RequestPutBillAction = Bill & { - actionId: number; -}; +type RequestPutBillAction = Bill & RequestBillAction; export const requestPutBillAction = async ({eventId, actionId, title, price}: WithEventId) => { await requestPut({ @@ -44,3 +42,26 @@ export const requestPutBillAction = async ({eventId, actionId, title, price}: Wi }, }); }; + +type MemberReportList = {members: MemberReport[]}; + +export const requestGetMemberReportListInAction = async ({eventId, actionId}: WithEventId) => { + return requestGet({ + baseUrl: BASE_URL.HD, + endpoint: `${TEMP_PREFIX}/${eventId}/bill-actions/${actionId}/fixed`, + }); +}; + +type RequestPutMemberReportList = RequestBillAction & MemberReportList; + +export const requestPutMemberReportListInAction = async ({ + eventId, + actionId, + members, +}: WithEventId) => { + return requestPut({ + baseUrl: BASE_URL.HD, + endpoint: `${TEMP_PREFIX}/${eventId}/bill-actions/${actionId}/fixed`, + body: members, + }); +}; From ee585478fec6e118109e45c7d8f31ae976360f1c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B9=80=EC=A7=84=ED=98=B8?= Date: Mon, 19 Aug 2024 22:45:02 +0900 Subject: [PATCH 03/27] =?UTF-8?q?feat:=20Get,=20Put=20query,=20mutation=20?= =?UTF-8?q?=EC=9E=91=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- client/src/constants/queryKeys.ts | 1 + .../useRequestGetMemberReportListInAction.ts | 24 +++++++++++++++ .../useRequestPutMemberReportListInAction.ts | 29 +++++++++++++++++++ 3 files changed, 54 insertions(+) create mode 100644 client/src/hooks/queries/useRequestGetMemberReportListInAction.ts create mode 100644 client/src/hooks/queries/useRequestPutMemberReportListInAction.ts diff --git a/client/src/constants/queryKeys.ts b/client/src/constants/queryKeys.ts index 23ae4b90e..17f3f4586 100644 --- a/client/src/constants/queryKeys.ts +++ b/client/src/constants/queryKeys.ts @@ -4,6 +4,7 @@ const QUERY_KEYS = { allMemberList: 'allMemberList', currentInMember: 'currentInMember', memberReport: 'memberReport', + memberReportInAction: 'memberReportInAction', }; export default QUERY_KEYS; diff --git a/client/src/hooks/queries/useRequestGetMemberReportListInAction.ts b/client/src/hooks/queries/useRequestGetMemberReportListInAction.ts new file mode 100644 index 000000000..85b458228 --- /dev/null +++ b/client/src/hooks/queries/useRequestGetMemberReportListInAction.ts @@ -0,0 +1,24 @@ +import {useQuery} from '@tanstack/react-query'; + +import {requestGetMemberReportListInAction} from '@apis/request/bill'; + +import getEventIdByUrl from '@utils/getEventIdByUrl'; + +import QUERY_KEYS from '@constants/queryKeys'; + +const useRequestGetMemberReportListInAction = (actionId: number) => { + const eventId = getEventIdByUrl(); + + const {data, ...queryResult} = useQuery({ + queryKey: [QUERY_KEYS.memberReportInAction, actionId], + queryFn: () => requestGetMemberReportListInAction({eventId, actionId}), + select: data => data.members, + }); + + return { + memberReportListInActionFromServer: data ?? [], + queryResult, + }; +}; + +export default useRequestGetMemberReportListInAction; diff --git a/client/src/hooks/queries/useRequestPutMemberReportListInAction.ts b/client/src/hooks/queries/useRequestPutMemberReportListInAction.ts new file mode 100644 index 000000000..6639f905d --- /dev/null +++ b/client/src/hooks/queries/useRequestPutMemberReportListInAction.ts @@ -0,0 +1,29 @@ +import {useMutation, useQueryClient} from '@tanstack/react-query'; + +import {requestPutMemberReportListInAction} from '@apis/request/bill'; +import {MemberReport} from 'types/serviceType'; + +import getEventIdByUrl from '@utils/getEventIdByUrl'; + +import QUERY_KEYS from '@constants/queryKeys'; + +const useRequestPutMemberReportListInAction = (actionId: number) => { + const eventId = getEventIdByUrl(); + const queryClient = useQueryClient(); + + const {mutate, ...mutationProps} = useMutation({ + mutationFn: (members: MemberReport[]) => requestPutMemberReportListInAction({eventId, actionId, members}), + onSuccess: () => { + queryClient.invalidateQueries({queryKey: [QUERY_KEYS.stepList]}); + queryClient.invalidateQueries({queryKey: [QUERY_KEYS.memberReport]}); + queryClient.invalidateQueries({queryKey: [QUERY_KEYS.memberReportInAction]}); + }, + }); + + return { + putMemberReportListInAction: mutate, + mutationProps, + }; +}; + +export default useRequestPutMemberReportListInAction; From d4907403d23ba9af79914d1edd2c07f8a66724e8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B9=80=EC=A7=84=ED=98=B8?= Date: Mon, 19 Aug 2024 22:45:46 +0900 Subject: [PATCH 04/27] =?UTF-8?q?feat:=20=EA=B3=A0=EC=A0=95=EA=B0=92?= =?UTF-8?q?=EC=9C=BC=EB=A1=9C=20=EC=88=98=EC=A0=95=ED=95=A0=20=EB=95=8C,?= =?UTF-8?q?=20=EB=82=98=EB=A8=B8=EC=A7=80=20=EC=9D=B8=EC=9B=90=EC=9D=98=20?= =?UTF-8?q?=EA=B0=80=EA=B2=A9=EC=9D=B4=20=EA=B3=84=EC=82=B0=EB=90=98?= =?UTF-8?q?=EB=8A=94=20=EA=B8=B0=EB=8A=A5=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../useMemberReportListInAction.ts | 83 +++++++++++++++++++ 1 file changed, 83 insertions(+) create mode 100644 client/src/hooks/useMemberReportListInAction/useMemberReportListInAction.ts diff --git a/client/src/hooks/useMemberReportListInAction/useMemberReportListInAction.ts b/client/src/hooks/useMemberReportListInAction/useMemberReportListInAction.ts new file mode 100644 index 000000000..b50e616d3 --- /dev/null +++ b/client/src/hooks/useMemberReportListInAction/useMemberReportListInAction.ts @@ -0,0 +1,83 @@ +import {useEffect, useState} from 'react'; + +import useRequestGetMemberReportListInAction from '@hooks/queries/useRequestGetMemberReportListInAction'; +import {MemberReport} from 'types/serviceType'; +import useRequestPutMemberReportListInAction from '@hooks/queries/useRequestPutMemberReportListInAction'; + +const useMemberReportListInAction = (actionId: number, totalPrice: number) => { + const {memberReportListInActionFromServer, queryResult} = useRequestGetMemberReportListInAction(actionId); + const {putMemberReportListInAction} = useRequestPutMemberReportListInAction(actionId); + + const [memberReportListInAction, setMemberReportListInAction] = useState( + memberReportListInActionFromServer, + ); + + // 초기에 랜더링할 때 정상적으로 서버의 값을 클라이언트 상태에 set 한다. + useEffect(() => { + if (queryResult.isSuccess) { + setMemberReportListInAction(memberReportListInActionFromServer); + } + }, [memberReportListInActionFromServer, queryResult.isSuccess]); + + // 가격이 조정된 멤버 리스트 + // 초기값은 어떻게 알지? 누가 조정된 가격인지 어떻게 파악하지? + const [adjustedMemberList, setAdjustedMemberList] = useState([]); + + const addAdjustedMember = (memberReport: MemberReport) => { + // 새 조정값을 반영하고 + const newMemberReportListInAction = [...memberReportListInAction]; + const targetIndex = newMemberReportListInAction.findIndex(member => member.name === memberReport.name); + newMemberReportListInAction[targetIndex].price = memberReport.price; + setMemberReportListInAction(newMemberReportListInAction); + + // 조정된 리스트에 추가한다. + setAdjustedMemberList(prev => [...prev, memberReport]); + }; + + const calculateDividedPrice = (totalAdjustedPrice: number) => { + const remainMemberCount = memberReportListInAction.length - adjustedMemberList.length; + + // 남은 인원이 0일 때는 초기화 + if (remainMemberCount === 0) return totalPrice / memberReportListInAction.length; + + return (totalPrice - totalAdjustedPrice) / remainMemberCount; + }; + + const calculateAnotherMemberPrice = () => { + // 총 조정치 금액 + const totalAdjustedPrice = adjustedMemberList.reduce((acc, cur) => acc + cur.price, 0); + + // 조정된 금액 명단 + const adjustedMemberNameList = adjustedMemberList.map(({name}) => name); + const dividedPrice = calculateDividedPrice(totalAdjustedPrice); + + const newMemberReportListInAction = [...memberReportListInAction]; + + if (totalAdjustedPrice !== 0) { + newMemberReportListInAction.forEach((memberReport, index) => { + if (!adjustedMemberNameList.includes(memberReport.name)) { + newMemberReportListInAction[index].price = dividedPrice; + } + }); + } + + setMemberReportListInAction(newMemberReportListInAction); + }; + + const onSubmit = () => { + putMemberReportListInAction(memberReportListInAction); + }; + + useEffect(() => { + calculateAnotherMemberPrice(); + }, [adjustedMemberList]); + + return { + memberReportListInAction, + addAdjustedMember, + onSubmit, + queryResult, + }; +}; + +export default useMemberReportListInAction; From 0e3d927b4c0337398c8f788cc1d88b78887a5e28 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B9=80=EC=A7=84=ED=98=B8?= Date: Mon, 19 Aug 2024 22:46:10 +0900 Subject: [PATCH 05/27] =?UTF-8?q?fix:=20isFixed=20field=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80=EB=A1=9C=20=EB=B0=98=EC=98=81=EB=90=98=EC=A7=80=20?= =?UTF-8?q?=EB=AA=BB=ED=95=9C=20=ED=85=8C=EC=8A=A4=ED=8A=B8=20=EC=88=98?= =?UTF-8?q?=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- client/src/mocks/handlers/stepListHandler.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/client/src/mocks/handlers/stepListHandler.ts b/client/src/mocks/handlers/stepListHandler.ts index e771d71c0..d79b7b0b4 100644 --- a/client/src/mocks/handlers/stepListHandler.ts +++ b/client/src/mocks/handlers/stepListHandler.ts @@ -73,6 +73,7 @@ export const stepListHandler = [ name, price: 0, sequence: 999, + isFixed: false, })), }, ]; @@ -99,6 +100,7 @@ export const stepListHandler = [ name: title, price, sequence: 999, + isFixed: false, })), }, ]; From cdeec00c729dde8840417a6d4b9d910b1da7d0af Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B9=80=EC=A7=84=ED=98=B8?= Date: Mon, 19 Aug 2024 22:46:37 +0900 Subject: [PATCH 06/27] =?UTF-8?q?test:=20useMemberReportListInAction=20?= =?UTF-8?q?=ED=9B=85=20=ED=85=9F=ED=8A=B8=20=EC=9E=91=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 아직 엣지케이스는 작성하지 못 함;; --- .../useMemberReportListInAction.test.tsx | 99 +++++++++++++++++++ client/src/mocks/handlers.ts | 10 +- .../handlers/memberReportInActionHandlers.ts | 22 +++++ .../src/mocks/memberReportListInAction.json | 6 ++ 4 files changed, 136 insertions(+), 1 deletion(-) create mode 100644 client/src/hooks/useMemberReportListInAction/useMemberReportListInAction.test.tsx create mode 100644 client/src/mocks/handlers/memberReportInActionHandlers.ts create mode 100644 client/src/mocks/memberReportListInAction.json diff --git a/client/src/hooks/useMemberReportListInAction/useMemberReportListInAction.test.tsx b/client/src/hooks/useMemberReportListInAction/useMemberReportListInAction.test.tsx new file mode 100644 index 000000000..7cb1e41ce --- /dev/null +++ b/client/src/hooks/useMemberReportListInAction/useMemberReportListInAction.test.tsx @@ -0,0 +1,99 @@ +import type {MemberReport} from 'types/serviceType'; + +import {renderHook, waitFor, act} from '@testing-library/react'; +import {MemoryRouter} from 'react-router-dom'; +import {QueryClient, QueryClientProvider} from '@tanstack/react-query'; + +import memberReportListInActionJson from '../../mocks/memberReportListInAction.json'; +import {ErrorProvider} from '../useError/ErrorProvider'; + +import useMemberReportListInAction from './useMemberReportListInAction'; + +describe('useMemberReportListInActionTest', () => { + const queryClient = new QueryClient({ + defaultOptions: { + queries: { + retry: 0, + }, + }, + }); + + const initializeProvider = (actionId: number, totalPrice: number) => + renderHook(() => useMemberReportListInAction(actionId, totalPrice), { + wrapper: ({children}) => ( + + + {children} + + + ), + }); + + const actionId = 123; + const totalPrice = 100000; + + it('초기값을 정상적으로 불러온다.', async () => { + const {result} = initializeProvider(actionId, totalPrice); + + await waitFor(() => expect(result.current.queryResult.isSuccess).toBe(true)); + + expect(result.current.memberReportListInAction).toStrictEqual(memberReportListInActionJson); + }); + + it('망쵸의 가격을 100원으로 바꾸면 망쵸의 가격은 100원으로 설정된다.', async () => { + const {result} = initializeProvider(actionId, totalPrice); + const adjustedMember: MemberReport = {name: '망쵸', price: 100}; + await waitFor(() => expect(result.current.queryResult.isSuccess).toBe(true)); + + act(() => { + result.current.addAdjustedMember(adjustedMember); + }); + + const targetMember = result.current.memberReportListInAction.find(member => member.name === '망쵸'); + + expect(targetMember?.price).toBe(100); + }); + + it('망쵸의 가격을 100원으로 바꾸면 망쵸의 가격은 100원으로 설정되고 나머지 인원의 가격이 33,300원으로 설정된다.', async () => { + const {result} = initializeProvider(actionId, totalPrice); + const adjustedMember: MemberReport = {name: '망쵸', price: 100}; + await waitFor(() => expect(result.current.queryResult.isSuccess).toBe(true)); + + act(() => { + result.current.addAdjustedMember(adjustedMember); + }); + + const targetMember = result.current.memberReportListInAction.find(member => member.name === '망쵸'); + expect(targetMember?.price).toBe(100); + + const anotherMemberList = result.current.memberReportListInAction.filter(member => member.name !== '망쵸'); + + anotherMemberList.forEach(member => { + expect(member.price).toBe(33300); + }); + }); + + it('망쵸의 가격을 100원 쿠키의 가격을 100원으로 바꾸면 나머지 인원의 가격이 49,900원으로 설정된다.', async () => { + const {result} = initializeProvider(actionId, totalPrice); + const adjustedMemberMangcho: MemberReport = {name: '망쵸', price: 100}; + const adjustedMemberCookie: MemberReport = {name: '망쵸', price: 100}; + + await waitFor(() => expect(result.current.queryResult.isSuccess).toBe(true)); + + act(() => { + result.current.addAdjustedMember(adjustedMemberMangcho); + }); + + act(() => { + result.current.addAdjustedMember(adjustedMemberCookie); + }); + + const anotherMemberList = result.current.memberReportListInAction.filter( + member => !(member.name === '망쵸' || member.name === '쿠키'), + ); + + anotherMemberList.forEach(member => { + expect(member.price).toBe(49900); + }); + }); +}); diff --git a/client/src/mocks/handlers.ts b/client/src/mocks/handlers.ts index f20429263..4189b2d66 100644 --- a/client/src/mocks/handlers.ts +++ b/client/src/mocks/handlers.ts @@ -3,5 +3,13 @@ import {eventHandler} from './handlers/eventHandlers'; import {reportHandlers} from './handlers/reportHandlers'; import {stepListHandler} from './handlers/stepListHandler'; import {testHandler} from './handlers/testHandlers'; +import {memberReportInActionHandler} from './handlers/memberReportInActionHandlers'; -export const handlers = [...authHandler, ...eventHandler, ...testHandler, ...stepListHandler, ...reportHandlers]; +export const handlers = [ + ...authHandler, + ...eventHandler, + ...testHandler, + ...stepListHandler, + ...reportHandlers, + ...memberReportInActionHandler, +]; diff --git a/client/src/mocks/handlers/memberReportInActionHandlers.ts b/client/src/mocks/handlers/memberReportInActionHandlers.ts new file mode 100644 index 000000000..ee2b36bbe --- /dev/null +++ b/client/src/mocks/handlers/memberReportInActionHandlers.ts @@ -0,0 +1,22 @@ +import {http, HttpResponse} from 'msw'; + +import {MemberReport} from 'types/serviceType'; + +import {TEMP_PREFIX} from '@apis/tempPrefix'; + +import memberReportInActionJson from '../memberReportListInAction.json'; + +let memberReportInActionMockData = memberReportInActionJson as MemberReport[]; + +type MemberReportListBody = {members: MemberReport[]}; + +export const memberReportInActionHandler = [ + http.get( + `${TEMP_PREFIX}/:eventId/bill-actions/:actionId/fixed`, + () => { + return HttpResponse.json({ + members: memberReportInActionMockData, + }); + }, + ), +]; diff --git a/client/src/mocks/memberReportListInAction.json b/client/src/mocks/memberReportListInAction.json new file mode 100644 index 000000000..68649355b --- /dev/null +++ b/client/src/mocks/memberReportListInAction.json @@ -0,0 +1,6 @@ +[ + {"name": "망쵸", "price": 25000}, + {"name": "이상", "price": 25000}, + {"name": "소하", "price": 25000}, + {"name": "쿠키", "price": 25000} +] From efc5b5794423cb121bad4ce83c21a5c6045e403c Mon Sep 17 00:00:00 2001 From: jinho_kim98 Date: Tue, 20 Aug 2024 00:40:08 +0900 Subject: [PATCH 07/27] =?UTF-8?q?fix:=20put=20body=20=ED=98=95=EC=8B=9D=20?= =?UTF-8?q?=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- client/src/apis/request/bill.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/client/src/apis/request/bill.ts b/client/src/apis/request/bill.ts index 5be7fdcc6..5bf96093d 100644 --- a/client/src/apis/request/bill.ts +++ b/client/src/apis/request/bill.ts @@ -62,6 +62,8 @@ export const requestPutMemberReportListInAction = async ({ return requestPut({ baseUrl: BASE_URL.HD, endpoint: `${TEMP_PREFIX}/${eventId}/bill-actions/${actionId}/fixed`, - body: members, + body: { + members, + }, }); }; From 10347159f7d5409330867705321d54fa042d42a1 Mon Sep 17 00:00:00 2001 From: jinho_kim98 Date: Tue, 20 Aug 2024 00:40:27 +0900 Subject: [PATCH 08/27] =?UTF-8?q?test:=20submit=20=ED=95=A8=EC=88=98=20?= =?UTF-8?q?=ED=85=8C=EC=8A=A4=ED=8A=B8=20=EC=BD=94=EB=93=9C=20=EC=9E=91?= =?UTF-8?q?=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../useMemberReportListInAction.test.tsx | 106 +++++++++++------- .../handlers/memberReportInActionHandlers.ts | 13 +++ 2 files changed, 77 insertions(+), 42 deletions(-) diff --git a/client/src/hooks/useMemberReportListInAction/useMemberReportListInAction.test.tsx b/client/src/hooks/useMemberReportListInAction/useMemberReportListInAction.test.tsx index 7cb1e41ce..399960541 100644 --- a/client/src/hooks/useMemberReportListInAction/useMemberReportListInAction.test.tsx +++ b/client/src/hooks/useMemberReportListInAction/useMemberReportListInAction.test.tsx @@ -32,68 +32,90 @@ describe('useMemberReportListInActionTest', () => { const actionId = 123; const totalPrice = 100000; - it('초기값을 정상적으로 불러온다.', async () => { - const {result} = initializeProvider(actionId, totalPrice); + describe('Flow: 유저가 정상적으로 값을 불러왔을 때의 test', () => { + it('초기값을 정상적으로 불러온다.', async () => { + const {result} = initializeProvider(actionId, totalPrice); - await waitFor(() => expect(result.current.queryResult.isSuccess).toBe(true)); + await waitFor(() => expect(result.current.queryResult.isSuccess).toBe(true)); - expect(result.current.memberReportListInAction).toStrictEqual(memberReportListInActionJson); - }); + expect(result.current.memberReportListInAction).toStrictEqual(memberReportListInActionJson); + }); + + it('망쵸의 가격을 100원으로 바꾸면 망쵸의 가격은 100원으로 설정된다.', async () => { + const {result} = initializeProvider(actionId, totalPrice); + const adjustedMember: MemberReport = {name: '망쵸', price: 100}; + await waitFor(() => expect(result.current.queryResult.isSuccess).toBe(true)); - it('망쵸의 가격을 100원으로 바꾸면 망쵸의 가격은 100원으로 설정된다.', async () => { - const {result} = initializeProvider(actionId, totalPrice); - const adjustedMember: MemberReport = {name: '망쵸', price: 100}; - await waitFor(() => expect(result.current.queryResult.isSuccess).toBe(true)); + act(() => { + result.current.addAdjustedMember(adjustedMember); + }); - act(() => { - result.current.addAdjustedMember(adjustedMember); + const targetMember = result.current.memberReportListInAction.find(member => member.name === '망쵸'); + + expect(targetMember?.price).toBe(100); }); - const targetMember = result.current.memberReportListInAction.find(member => member.name === '망쵸'); + it('망쵸의 가격을 100원으로 바꾸면 망쵸의 가격은 100원으로 설정되고 나머지 인원의 가격이 33,300원으로 설정된다.', async () => { + const {result} = initializeProvider(actionId, totalPrice); + const adjustedMember: MemberReport = {name: '망쵸', price: 100}; + await waitFor(() => expect(result.current.queryResult.isSuccess).toBe(true)); - expect(targetMember?.price).toBe(100); - }); + act(() => { + result.current.addAdjustedMember(adjustedMember); + }); + + const targetMember = result.current.memberReportListInAction.find(member => member.name === '망쵸'); + expect(targetMember?.price).toBe(100); - it('망쵸의 가격을 100원으로 바꾸면 망쵸의 가격은 100원으로 설정되고 나머지 인원의 가격이 33,300원으로 설정된다.', async () => { - const {result} = initializeProvider(actionId, totalPrice); - const adjustedMember: MemberReport = {name: '망쵸', price: 100}; - await waitFor(() => expect(result.current.queryResult.isSuccess).toBe(true)); + const anotherMemberList = result.current.memberReportListInAction.filter(member => member.name !== '망쵸'); - act(() => { - result.current.addAdjustedMember(adjustedMember); + anotherMemberList.forEach(member => { + expect(member.price).toBe(33300); + }); }); - const targetMember = result.current.memberReportListInAction.find(member => member.name === '망쵸'); - expect(targetMember?.price).toBe(100); + it('망쵸의 가격을 100원 쿠키의 가격을 100원으로 바꾸면 나머지 인원의 가격이 49,900원으로 설정된다.', async () => { + const {result} = initializeProvider(actionId, totalPrice); + const adjustedMemberMangcho: MemberReport = {name: '망쵸', price: 100}; + const adjustedMemberCookie: MemberReport = {name: '쿠키', price: 100}; - const anotherMemberList = result.current.memberReportListInAction.filter(member => member.name !== '망쵸'); + await waitFor(() => expect(result.current.queryResult.isSuccess).toBe(true)); - anotherMemberList.forEach(member => { - expect(member.price).toBe(33300); - }); - }); + act(() => { + result.current.addAdjustedMember(adjustedMemberMangcho); + }); - it('망쵸의 가격을 100원 쿠키의 가격을 100원으로 바꾸면 나머지 인원의 가격이 49,900원으로 설정된다.', async () => { - const {result} = initializeProvider(actionId, totalPrice); - const adjustedMemberMangcho: MemberReport = {name: '망쵸', price: 100}; - const adjustedMemberCookie: MemberReport = {name: '망쵸', price: 100}; + act(() => { + result.current.addAdjustedMember(adjustedMemberCookie); + }); - await waitFor(() => expect(result.current.queryResult.isSuccess).toBe(true)); + const anotherMemberList = result.current.memberReportListInAction.filter( + member => !(member.name === '망쵸' || member.name === '쿠키'), + ); - act(() => { - result.current.addAdjustedMember(adjustedMemberMangcho); + anotherMemberList.forEach(member => { + expect(member.price).toBe(49900); + }); }); - act(() => { - result.current.addAdjustedMember(adjustedMemberCookie); - }); + it('망쵸의 가격을 100원으로 바꾸고 저장하면 망쵸 100원이 반영된다.', async () => { + const {result} = initializeProvider(actionId, totalPrice); + const adjustedMemberMangcho: MemberReport = {name: '망쵸', price: 100}; + + await waitFor(() => expect(result.current.queryResult.isSuccess).toBe(true)); + + act(() => { + result.current.addAdjustedMember(adjustedMemberMangcho); + }); + + await waitFor(() => { + result.current.onSubmit(); + }); - const anotherMemberList = result.current.memberReportListInAction.filter( - member => !(member.name === '망쵸' || member.name === '쿠키'), - ); + await waitFor(() => expect(result.current.queryResult.isSuccess).toBe(true)); - anotherMemberList.forEach(member => { - expect(member.price).toBe(49900); + const targetMember = result.current.memberReportListInAction.find(member => member.name === '망쵸'); + expect(targetMember?.price).toBe(100); }); }); }); diff --git a/client/src/mocks/handlers/memberReportInActionHandlers.ts b/client/src/mocks/handlers/memberReportInActionHandlers.ts index ee2b36bbe..7c9b5b1b1 100644 --- a/client/src/mocks/handlers/memberReportInActionHandlers.ts +++ b/client/src/mocks/handlers/memberReportInActionHandlers.ts @@ -19,4 +19,17 @@ export const memberReportInActionHandler = [ }); }, ), + + http.put( + `${TEMP_PREFIX}/:eventId/bill-actions/:actionId/fixed`, + async ({request}) => { + const {members} = await request.json(); + + memberReportInActionMockData = members; + + return HttpResponse.json({ + status: 200, + }); + }, + ), ]; From c3e61c80b103df5eeded1722c0d391014834c6ad Mon Sep 17 00:00:00 2001 From: jinho_kim98 Date: Tue, 20 Aug 2024 00:57:41 +0900 Subject: [PATCH 09/27] =?UTF-8?q?feat:=20=EB=AA=A8=EB=93=A0=20=EC=9D=B8?= =?UTF-8?q?=EC=9B=90=EC=9D=98=20=EA=B0=80=EA=B2=A9=EC=9D=84=20=EA=B3=A0?= =?UTF-8?q?=EC=A0=95=EC=8B=9C=ED=82=A4=EB=A0=A4=20=ED=96=88=EC=9D=84=20?= =?UTF-8?q?=EB=95=8C=20=EB=B0=98=EC=98=81=ED=95=98=EC=A7=80=20=EC=95=8A?= =?UTF-8?q?=EB=8A=94=20=EA=B8=B0=EB=8A=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../useMemberReportListInAction.ts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/client/src/hooks/useMemberReportListInAction/useMemberReportListInAction.ts b/client/src/hooks/useMemberReportListInAction/useMemberReportListInAction.ts index b50e616d3..07c75a8d7 100644 --- a/client/src/hooks/useMemberReportListInAction/useMemberReportListInAction.ts +++ b/client/src/hooks/useMemberReportListInAction/useMemberReportListInAction.ts @@ -24,6 +24,10 @@ const useMemberReportListInAction = (actionId: number, totalPrice: number) => { const [adjustedMemberList, setAdjustedMemberList] = useState([]); const addAdjustedMember = (memberReport: MemberReport) => { + if (adjustedMemberList.length + 1 >= memberReportListInAction.length) { + return; + } + // 새 조정값을 반영하고 const newMemberReportListInAction = [...memberReportListInAction]; const targetIndex = newMemberReportListInAction.findIndex(member => member.name === memberReport.name); From 5a24a04c0f71d23cbdf6121e4d39d43dbf8538c2 Mon Sep 17 00:00:00 2001 From: jinho_kim98 Date: Tue, 20 Aug 2024 00:58:02 +0900 Subject: [PATCH 10/27] =?UTF-8?q?test:=20=EB=AA=A8=EB=93=A0=20=EC=9D=B8?= =?UTF-8?q?=EC=9B=90=EC=9D=84=20=EB=8B=B4=EC=9D=84=20=EB=95=8C=20=ED=85=8C?= =?UTF-8?q?=EC=8A=A4=ED=8A=B8=20=EC=9E=91=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../useMemberReportListInAction.test.tsx | 63 +++++++++++++++++++ 1 file changed, 63 insertions(+) diff --git a/client/src/hooks/useMemberReportListInAction/useMemberReportListInAction.test.tsx b/client/src/hooks/useMemberReportListInAction/useMemberReportListInAction.test.tsx index 399960541..363b367a3 100644 --- a/client/src/hooks/useMemberReportListInAction/useMemberReportListInAction.test.tsx +++ b/client/src/hooks/useMemberReportListInAction/useMemberReportListInAction.test.tsx @@ -98,6 +98,69 @@ describe('useMemberReportListInActionTest', () => { }); }); + it('망쵸의 가격을 100원 쿠키의 가격을 100원으로 바꾸면 나머지 인원의 가격이 49,900원으로 설정된다.', async () => { + const {result} = initializeProvider(actionId, totalPrice); + const adjustedMemberMangcho: MemberReport = {name: '망쵸', price: 100}; + const adjustedMemberCookie: MemberReport = {name: '쿠키', price: 100}; + + await waitFor(() => expect(result.current.queryResult.isSuccess).toBe(true)); + + act(() => { + result.current.addAdjustedMember(adjustedMemberMangcho); + }); + + act(() => { + result.current.addAdjustedMember(adjustedMemberCookie); + }); + + const anotherMemberList = result.current.memberReportListInAction.filter( + member => !(member.name === '망쵸' || member.name === '쿠키'), + ); + + anotherMemberList.forEach(member => { + expect(member.price).toBe(49900); + }); + }); + }); + + describe('예외 & 엣지케이스', () => { + it('참여인원의 가격을 모두 바꾸려고 하면, 마지막 사람의 조정치는 반영되지 않는다.', async () => { + const {result} = initializeProvider(actionId, totalPrice); + const adjustedMemberMangcho: MemberReport = {name: '망쵸', price: 100}; + const adjustedMemberCookie: MemberReport = {name: '쿠키', price: 100}; + const adjustedMemberSoha: MemberReport = {name: '소하', price: 100}; + + // 마지막 사람 + const adjustedMemberLeeSang: MemberReport = {name: '이상', price: 100}; + + await waitFor(() => expect(result.current.queryResult.isSuccess).toBe(true)); + + act(() => { + result.current.addAdjustedMember(adjustedMemberMangcho); + }); + + act(() => { + result.current.addAdjustedMember(adjustedMemberCookie); + }); + + act(() => { + result.current.addAdjustedMember(adjustedMemberSoha); + }); + + act(() => { + result.current.addAdjustedMember(adjustedMemberLeeSang); + }); + + console.log(result.current.memberReportListInAction); + + const targetMember = result.current.memberReportListInAction.find(member => member.name === '이상'); + + expect(targetMember?.price).not.toBe(100); + }); + }); + + // last + describe('onSubmit 실행 시 반영 테스트', () => { it('망쵸의 가격을 100원으로 바꾸고 저장하면 망쵸 100원이 반영된다.', async () => { const {result} = initializeProvider(actionId, totalPrice); const adjustedMemberMangcho: MemberReport = {name: '망쵸', price: 100}; From 9ec199d1c5a2b1d64e23dba54a9a3eef174561c9 Mon Sep 17 00:00:00 2001 From: jinho_kim98 Date: Tue, 20 Aug 2024 03:19:00 +0900 Subject: [PATCH 11/27] =?UTF-8?q?fix:=20invalidate=20queries=20querykey=20?= =?UTF-8?q?actionId=20=EB=AA=85=EC=8B=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/hooks/queries/useRequestPutMemberReportListInAction.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/src/hooks/queries/useRequestPutMemberReportListInAction.ts b/client/src/hooks/queries/useRequestPutMemberReportListInAction.ts index 6639f905d..76048d4c2 100644 --- a/client/src/hooks/queries/useRequestPutMemberReportListInAction.ts +++ b/client/src/hooks/queries/useRequestPutMemberReportListInAction.ts @@ -16,7 +16,7 @@ const useRequestPutMemberReportListInAction = (actionId: number) => { onSuccess: () => { queryClient.invalidateQueries({queryKey: [QUERY_KEYS.stepList]}); queryClient.invalidateQueries({queryKey: [QUERY_KEYS.memberReport]}); - queryClient.invalidateQueries({queryKey: [QUERY_KEYS.memberReportInAction]}); + queryClient.invalidateQueries({queryKey: [QUERY_KEYS.memberReportInAction, actionId]}); }, }); From 1df1dfe88b9d80a35e9c11294d77fc4d6da065ec Mon Sep 17 00:00:00 2001 From: jinho_kim98 Date: Tue, 20 Aug 2024 03:38:39 +0900 Subject: [PATCH 12/27] =?UTF-8?q?fix:=20=EC=A4=91=EB=B3=B5=20=EC=9D=B8?= =?UTF-8?q?=EC=9B=90=EC=9D=B4=20=EB=93=A4=EC=96=B4=EC=99=94=EC=9D=84=20?= =?UTF-8?q?=EB=95=8C=20=EC=9E=98=EB=AA=BB=20=EA=B3=84=EC=82=B0=EB=90=98?= =?UTF-8?q?=EB=8D=98=20=EB=AC=B8=EC=A0=9C=20=ED=95=B4=EA=B2=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../useMemberReportListInAction.ts | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/client/src/hooks/useMemberReportListInAction/useMemberReportListInAction.ts b/client/src/hooks/useMemberReportListInAction/useMemberReportListInAction.ts index 07c75a8d7..a4f379263 100644 --- a/client/src/hooks/useMemberReportListInAction/useMemberReportListInAction.ts +++ b/client/src/hooks/useMemberReportListInAction/useMemberReportListInAction.ts @@ -21,10 +21,13 @@ const useMemberReportListInAction = (actionId: number, totalPrice: number) => { // 가격이 조정된 멤버 리스트 // 초기값은 어떻게 알지? 누가 조정된 가격인지 어떻게 파악하지? - const [adjustedMemberList, setAdjustedMemberList] = useState([]); + const [adjustedMemberList, setAdjustedMemberList] = useState>(new Set()); + + // 조정된 금액 명단 + const adjustedMemberNameList = [...adjustedMemberList].map(({name}) => name); const addAdjustedMember = (memberReport: MemberReport) => { - if (adjustedMemberList.length + 1 >= memberReportListInAction.length) { + if (adjustedMemberList.size + 1 >= memberReportListInAction.length) { return; } @@ -35,11 +38,11 @@ const useMemberReportListInAction = (actionId: number, totalPrice: number) => { setMemberReportListInAction(newMemberReportListInAction); // 조정된 리스트에 추가한다. - setAdjustedMemberList(prev => [...prev, memberReport]); + setAdjustedMemberList(prev => new Set(prev.add(memberReport))); }; const calculateDividedPrice = (totalAdjustedPrice: number) => { - const remainMemberCount = memberReportListInAction.length - adjustedMemberList.length; + const remainMemberCount = memberReportListInAction.length - adjustedMemberNameList.length; // 남은 인원이 0일 때는 초기화 if (remainMemberCount === 0) return totalPrice / memberReportListInAction.length; @@ -49,10 +52,8 @@ const useMemberReportListInAction = (actionId: number, totalPrice: number) => { const calculateAnotherMemberPrice = () => { // 총 조정치 금액 - const totalAdjustedPrice = adjustedMemberList.reduce((acc, cur) => acc + cur.price, 0); + const totalAdjustedPrice = [...adjustedMemberList].reduce((acc, cur) => acc + cur.price, 0); - // 조정된 금액 명단 - const adjustedMemberNameList = adjustedMemberList.map(({name}) => name); const dividedPrice = calculateDividedPrice(totalAdjustedPrice); const newMemberReportListInAction = [...memberReportListInAction]; From ae6cf7d6ab137e528c2fce8f530486df966a30b2 Mon Sep 17 00:00:00 2001 From: jinho_kim98 Date: Tue, 20 Aug 2024 03:38:59 +0900 Subject: [PATCH 13/27] =?UTF-8?q?test:=20=EC=A4=91=EB=B3=B5=20=EC=9D=B8?= =?UTF-8?q?=EC=9B=90=EC=9D=BC=20=EB=95=8C=20=ED=85=8C=EC=8A=A4=ED=8A=B8=20?= =?UTF-8?q?=EC=BD=94=EB=93=9C=20=EC=9E=91=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../useMemberReportListInAction.test.tsx | 21 +++++++++++++++++-- 1 file changed, 19 insertions(+), 2 deletions(-) diff --git a/client/src/hooks/useMemberReportListInAction/useMemberReportListInAction.test.tsx b/client/src/hooks/useMemberReportListInAction/useMemberReportListInAction.test.tsx index 363b367a3..f8b1450cf 100644 --- a/client/src/hooks/useMemberReportListInAction/useMemberReportListInAction.test.tsx +++ b/client/src/hooks/useMemberReportListInAction/useMemberReportListInAction.test.tsx @@ -124,6 +124,25 @@ describe('useMemberReportListInActionTest', () => { }); describe('예외 & 엣지케이스', () => { + it('동일한 인원을 바꾸려고 할 때, 반영되지 않는다.', async () => { + const {result} = initializeProvider(actionId, totalPrice); + const adjustedMemberMangcho: MemberReport = {name: '망쵸', price: 100}; + + await waitFor(() => expect(result.current.queryResult.isSuccess).toBe(true)); + + act(() => { + result.current.addAdjustedMember(adjustedMemberMangcho); + }); + + act(() => { + result.current.addAdjustedMember(adjustedMemberMangcho); + }); + + const anotherMemberList = result.current.memberReportListInAction.filter(member => member.name !== '망쵸'); + + expect(anotherMemberList[0].price).toBe(33300); + }); + it('참여인원의 가격을 모두 바꾸려고 하면, 마지막 사람의 조정치는 반영되지 않는다.', async () => { const {result} = initializeProvider(actionId, totalPrice); const adjustedMemberMangcho: MemberReport = {name: '망쵸', price: 100}; @@ -151,8 +170,6 @@ describe('useMemberReportListInActionTest', () => { result.current.addAdjustedMember(adjustedMemberLeeSang); }); - console.log(result.current.memberReportListInAction); - const targetMember = result.current.memberReportListInAction.find(member => member.name === '이상'); expect(targetMember?.price).not.toBe(100); From e6239f0ab79d4fbee6032a0bba6343abb3861248 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B9=80=EC=A7=84=ED=98=B8?= Date: Tue, 20 Aug 2024 09:45:11 +0900 Subject: [PATCH 14/27] =?UTF-8?q?refactor:=20=EC=A7=80=EC=B6=9C=EC=83=81?= =?UTF-8?q?=EC=84=B8=20=ED=9B=85=20=EB=A6=AC=ED=8C=A9=ED=84=B0=EB=A7=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../useMemberReportListInAction.ts | 35 +++++++------------ 1 file changed, 13 insertions(+), 22 deletions(-) diff --git a/client/src/hooks/useMemberReportListInAction/useMemberReportListInAction.ts b/client/src/hooks/useMemberReportListInAction/useMemberReportListInAction.ts index a4f379263..86bffaaf7 100644 --- a/client/src/hooks/useMemberReportListInAction/useMemberReportListInAction.ts +++ b/client/src/hooks/useMemberReportListInAction/useMemberReportListInAction.ts @@ -21,10 +21,10 @@ const useMemberReportListInAction = (actionId: number, totalPrice: number) => { // 가격이 조정된 멤버 리스트 // 초기값은 어떻게 알지? 누가 조정된 가격인지 어떻게 파악하지? - const [adjustedMemberList, setAdjustedMemberList] = useState>(new Set()); + const [adjustedMemberList, setAdjustedMemberList] = useState>(new Set()); - // 조정된 금액 명단 - const adjustedMemberNameList = [...adjustedMemberList].map(({name}) => name); + // 조정값이 아닌 멤버의 수 + const remainMemberCount = memberReportListInAction.length - adjustedMemberList.size; const addAdjustedMember = (memberReport: MemberReport) => { if (adjustedMemberList.size + 1 >= memberReportListInAction.length) { @@ -32,18 +32,15 @@ const useMemberReportListInAction = (actionId: number, totalPrice: number) => { } // 새 조정값을 반영하고 - const newMemberReportListInAction = [...memberReportListInAction]; - const targetIndex = newMemberReportListInAction.findIndex(member => member.name === memberReport.name); - newMemberReportListInAction[targetIndex].price = memberReport.price; - setMemberReportListInAction(newMemberReportListInAction); + setMemberReportListInAction(prevList => + prevList.map(member => (member.name === memberReport.name ? {...member, price: memberReport.price} : member)), + ); // 조정된 리스트에 추가한다. - setAdjustedMemberList(prev => new Set(prev.add(memberReport))); + setAdjustedMemberList(prev => new Set(prev.add(memberReport.name))); }; const calculateDividedPrice = (totalAdjustedPrice: number) => { - const remainMemberCount = memberReportListInAction.length - adjustedMemberNameList.length; - // 남은 인원이 0일 때는 초기화 if (remainMemberCount === 0) return totalPrice / memberReportListInAction.length; @@ -52,21 +49,15 @@ const useMemberReportListInAction = (actionId: number, totalPrice: number) => { const calculateAnotherMemberPrice = () => { // 총 조정치 금액 - const totalAdjustedPrice = [...adjustedMemberList].reduce((acc, cur) => acc + cur.price, 0); + const totalAdjustedPrice = memberReportListInAction + .filter(memberReport => adjustedMemberList.has(memberReport.name)) + .reduce((acc, cur) => acc + cur.price, 0); const dividedPrice = calculateDividedPrice(totalAdjustedPrice); - const newMemberReportListInAction = [...memberReportListInAction]; - - if (totalAdjustedPrice !== 0) { - newMemberReportListInAction.forEach((memberReport, index) => { - if (!adjustedMemberNameList.includes(memberReport.name)) { - newMemberReportListInAction[index].price = dividedPrice; - } - }); - } - - setMemberReportListInAction(newMemberReportListInAction); + setMemberReportListInAction(prevList => + prevList.map(member => (adjustedMemberList.has(member.name) ? member : {...member, price: dividedPrice})), + ); }; const onSubmit = () => { From 042b58bb5bd9aeccc86068f2996989ced731dc5c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B9=80=EC=A7=84=ED=98=B8?= Date: Tue, 20 Aug 2024 09:45:38 +0900 Subject: [PATCH 15/27] =?UTF-8?q?test:=20=EA=B0=99=EC=9D=80=20=EB=A9=A4?= =?UTF-8?q?=EB=B2=84=20=EB=8B=A4=EB=A5=B8=20=EA=B0=80=EA=B2=A9=EC=9C=BC?= =?UTF-8?q?=EB=A1=9C=20=EB=B3=80=ED=99=98=20=EC=8B=9C=20=EB=8B=A4=EC=8B=9C?= =?UTF-8?q?=20=EA=B3=84=EC=82=B0=EB=90=98=EB=8A=94=20=ED=85=8C=EC=8A=A4?= =?UTF-8?q?=ED=8A=B8=20=EC=9E=91=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../useMemberReportListInAction.test.tsx | 22 +++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/client/src/hooks/useMemberReportListInAction/useMemberReportListInAction.test.tsx b/client/src/hooks/useMemberReportListInAction/useMemberReportListInAction.test.tsx index f8b1450cf..3c13c3d77 100644 --- a/client/src/hooks/useMemberReportListInAction/useMemberReportListInAction.test.tsx +++ b/client/src/hooks/useMemberReportListInAction/useMemberReportListInAction.test.tsx @@ -121,6 +121,28 @@ describe('useMemberReportListInActionTest', () => { expect(member.price).toBe(49900); }); }); + + it('망쵸의 가격을 100원으로 바꾸고 다시 망쵸의 가격을 10,000원으로 바꾸면 나머지 인원의 가격이 30,000원으로 설정된다.', async () => { + const {result} = initializeProvider(actionId, totalPrice); + const adjustedMemberMangcho: MemberReport = {name: '망쵸', price: 100}; + const adjustedMemberMangchoAfter: MemberReport = {name: '망쵸', price: 10000}; + + await waitFor(() => expect(result.current.queryResult.isSuccess).toBe(true)); + + act(() => { + result.current.addAdjustedMember(adjustedMemberMangcho); + }); + + act(() => { + result.current.addAdjustedMember(adjustedMemberMangchoAfter); + }); + + const anotherMemberList = result.current.memberReportListInAction.filter(member => member.name !== '망쵸'); + + anotherMemberList.forEach(member => { + expect(member.price).toBe(30000); + }); + }); }); describe('예외 & 엣지케이스', () => { From 388bfa8ea7cedf6ae602795c83ab13cc366e7e4c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B9=80=EC=A7=84=ED=98=B8?= Date: Tue, 20 Aug 2024 09:48:00 +0900 Subject: [PATCH 16/27] =?UTF-8?q?fix:=20isFixed=20=EC=B6=94=EA=B0=80?= =?UTF-8?q?=EB=A1=9C=20=EB=B0=9C=EC=83=9D=ED=95=9C=20=ED=85=8C=EC=8A=A4?= =?UTF-8?q?=ED=8A=B8=20=EC=98=A4=EB=A5=98=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../hooks/useDeleteMemberAction/useDeleteMemberAction.test.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/client/src/hooks/useDeleteMemberAction/useDeleteMemberAction.test.tsx b/client/src/hooks/useDeleteMemberAction/useDeleteMemberAction.test.tsx index 00436cafa..20b49b096 100644 --- a/client/src/hooks/useDeleteMemberAction/useDeleteMemberAction.test.tsx +++ b/client/src/hooks/useDeleteMemberAction/useDeleteMemberAction.test.tsx @@ -68,6 +68,7 @@ describe('useDeleteMemberAction', () => { name: '망쵸', price: null, sequence: 1, + isFixed: false, }; result.current.deleteMemberActionList.addDeleteMemberAction(memberAction); From ecda8ee3a113b88d7b00a3a4875ba3e4140c2521 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B9=80=EC=A7=84=ED=98=B8?= Date: Tue, 20 Aug 2024 10:58:32 +0900 Subject: [PATCH 17/27] =?UTF-8?q?feat:=20=EA=B0=81=20=EB=A9=A4=EB=B2=84=20?= =?UTF-8?q?=EB=B3=84=20isFixed=20=EC=B6=94=EA=B0=80=20=EB=B0=98=EC=98=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- client/src/apis/request/bill.ts | 4 ++-- .../hooks/queries/useRequestPutMemberReportListInAction.ts | 4 ++-- client/src/types/serviceType.ts | 4 ++++ 3 files changed, 8 insertions(+), 4 deletions(-) diff --git a/client/src/apis/request/bill.ts b/client/src/apis/request/bill.ts index 5bf96093d..418429d33 100644 --- a/client/src/apis/request/bill.ts +++ b/client/src/apis/request/bill.ts @@ -1,4 +1,4 @@ -import type {Bill, MemberReport} from 'types/serviceType'; +import type {Bill, MemberReportInAction} from 'types/serviceType'; import {BASE_URL} from '@apis/baseUrl'; import {TEMP_PREFIX} from '@apis/tempPrefix'; @@ -43,7 +43,7 @@ export const requestPutBillAction = async ({eventId, actionId, title, price}: Wi }); }; -type MemberReportList = {members: MemberReport[]}; +type MemberReportList = {members: MemberReportInAction[]}; export const requestGetMemberReportListInAction = async ({eventId, actionId}: WithEventId) => { return requestGet({ diff --git a/client/src/hooks/queries/useRequestPutMemberReportListInAction.ts b/client/src/hooks/queries/useRequestPutMemberReportListInAction.ts index 76048d4c2..2765e278e 100644 --- a/client/src/hooks/queries/useRequestPutMemberReportListInAction.ts +++ b/client/src/hooks/queries/useRequestPutMemberReportListInAction.ts @@ -1,7 +1,7 @@ import {useMutation, useQueryClient} from '@tanstack/react-query'; import {requestPutMemberReportListInAction} from '@apis/request/bill'; -import {MemberReport} from 'types/serviceType'; +import {MemberReportInAction} from 'types/serviceType'; import getEventIdByUrl from '@utils/getEventIdByUrl'; @@ -12,7 +12,7 @@ const useRequestPutMemberReportListInAction = (actionId: number) => { const queryClient = useQueryClient(); const {mutate, ...mutationProps} = useMutation({ - mutationFn: (members: MemberReport[]) => requestPutMemberReportListInAction({eventId, actionId, members}), + mutationFn: (members: MemberReportInAction[]) => requestPutMemberReportListInAction({eventId, actionId, members}), onSuccess: () => { queryClient.invalidateQueries({queryKey: [QUERY_KEYS.stepList]}); queryClient.invalidateQueries({queryKey: [QUERY_KEYS.memberReport]}); diff --git a/client/src/types/serviceType.ts b/client/src/types/serviceType.ts index 6446f46d3..1dd69dcfb 100644 --- a/client/src/types/serviceType.ts +++ b/client/src/types/serviceType.ts @@ -7,6 +7,10 @@ export type MemberReport = { price: number; }; +export type MemberReportInAction = MemberReport & { + isFixed: boolean; +}; + export type Bill = { title: string; price: number; From 5a0ff8ff21e4eceb2ffd2249192472cddbed611e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B9=80=EC=A7=84=ED=98=B8?= Date: Tue, 20 Aug 2024 11:32:10 +0900 Subject: [PATCH 18/27] =?UTF-8?q?refactor:=20=EC=84=9C=EB=B2=84=EC=9D=98?= =?UTF-8?q?=20isFixed=20=EA=B4=80=EB=A6=AC=EB=A1=9C=20=EB=B6=88=ED=95=84?= =?UTF-8?q?=EC=9A=94=ED=95=9C=20=EC=83=81=ED=83=9C=20=EC=A0=9C=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../useMemberReportListInAction.ts | 72 +++++++++++-------- 1 file changed, 43 insertions(+), 29 deletions(-) diff --git a/client/src/hooks/useMemberReportListInAction/useMemberReportListInAction.ts b/client/src/hooks/useMemberReportListInAction/useMemberReportListInAction.ts index 86bffaaf7..97ad364a2 100644 --- a/client/src/hooks/useMemberReportListInAction/useMemberReportListInAction.ts +++ b/client/src/hooks/useMemberReportListInAction/useMemberReportListInAction.ts @@ -1,73 +1,87 @@ +import type {MemberReportInAction} from 'types/serviceType'; + import {useEffect, useState} from 'react'; import useRequestGetMemberReportListInAction from '@hooks/queries/useRequestGetMemberReportListInAction'; -import {MemberReport} from 'types/serviceType'; import useRequestPutMemberReportListInAction from '@hooks/queries/useRequestPutMemberReportListInAction'; const useMemberReportListInAction = (actionId: number, totalPrice: number) => { const {memberReportListInActionFromServer, queryResult} = useRequestGetMemberReportListInAction(actionId); const {putMemberReportListInAction} = useRequestPutMemberReportListInAction(actionId); - const [memberReportListInAction, setMemberReportListInAction] = useState( + const [memberReportListInAction, setMemberReportListInAction] = useState( memberReportListInActionFromServer, ); - // 초기에 랜더링할 때 정상적으로 서버의 값을 클라이언트 상태에 set 한다. useEffect(() => { if (queryResult.isSuccess) { setMemberReportListInAction(memberReportListInActionFromServer); } }, [memberReportListInActionFromServer, queryResult.isSuccess]); - // 가격이 조정된 멤버 리스트 - // 초기값은 어떻게 알지? 누가 조정된 가격인지 어떻게 파악하지? - const [adjustedMemberList, setAdjustedMemberList] = useState>(new Set()); - - // 조정값이 아닌 멤버의 수 - const remainMemberCount = memberReportListInAction.length - adjustedMemberList.size; + // 조정값 멤버의 수를 구하는 함수 + const getAdjustedMemberCount = (memberReportListInAction: MemberReportInAction[]) => { + return memberReportListInAction.filter(member => member.isFixed === true).length; + }; - const addAdjustedMember = (memberReport: MemberReport) => { - if (adjustedMemberList.size + 1 >= memberReportListInAction.length) { + const addAdjustedMember = (memberReport: MemberReportInAction) => { + if (getAdjustedMemberCount(memberReportListInAction) + 1 >= memberReportListInAction.length) { return; } - // 새 조정값을 반영하고 - setMemberReportListInAction(prevList => - prevList.map(member => (member.name === memberReport.name ? {...member, price: memberReport.price} : member)), + const newMemberReportListInAction = memberReportListInAction.map(member => + member.name === memberReport.name ? {...member, price: memberReport.price, isFixed: true} : member, ); - // 조정된 리스트에 추가한다. - setAdjustedMemberList(prev => new Set(prev.add(memberReport.name))); + calculateAnotherMemberPrice(newMemberReportListInAction); }; - const calculateDividedPrice = (totalAdjustedPrice: number) => { - // 남은 인원이 0일 때는 초기화 - if (remainMemberCount === 0) return totalPrice / memberReportListInAction.length; + const calculateDividedPrice = (remainMemberCount: number, totalAdjustedPrice: number) => { + // 조정되지 않은 멤버가 없거나 다 조정된 멤버라면 + if (remainMemberCount === 0 || remainMemberCount === memberReportListInAction.length) { + return { + divided: Math.floor(totalPrice / memberReportListInAction.length), + remainder: (totalPrice - totalAdjustedPrice) % remainMemberCount, + }; + } - return (totalPrice - totalAdjustedPrice) / remainMemberCount; + return { + divided: Math.floor((totalPrice - totalAdjustedPrice) / remainMemberCount), + remainder: (totalPrice - totalAdjustedPrice) % remainMemberCount, + }; }; - const calculateAnotherMemberPrice = () => { + const calculateAnotherMemberPrice = (memberReportListInAction: MemberReportInAction[]) => { // 총 조정치 금액 const totalAdjustedPrice = memberReportListInAction - .filter(memberReport => adjustedMemberList.has(memberReport.name)) + .filter(memberReport => memberReport.isFixed === true) .reduce((acc, cur) => acc + cur.price, 0); - const dividedPrice = calculateDividedPrice(totalAdjustedPrice); + const remainMemberCount = memberReportListInAction.length - getAdjustedMemberCount(memberReportListInAction); + const {divided, remainder} = calculateDividedPrice(remainMemberCount, totalAdjustedPrice); - setMemberReportListInAction(prevList => - prevList.map(member => (adjustedMemberList.has(member.name) ? member : {...member, price: dividedPrice})), + const updatedList = memberReportListInAction.map(member => + member.isFixed === true ? member : {...member, price: divided}, ); + + // 나머지를 조정되지 않은 멤버 중 마지막 멤버에게 추가 + if (remainder !== 0) { + const nonAdjustedMembers = updatedList.filter(member => member.isFixed === false); + const lastNonAdjustedMemberIndex = updatedList.findIndex( + member => member.name === nonAdjustedMembers[nonAdjustedMembers.length - 1].name, + ); + + if (lastNonAdjustedMemberIndex !== -1) { + updatedList[lastNonAdjustedMemberIndex].price += remainder; + } + } + setMemberReportListInAction(updatedList); }; const onSubmit = () => { putMemberReportListInAction(memberReportListInAction); }; - useEffect(() => { - calculateAnotherMemberPrice(); - }, [adjustedMemberList]); - return { memberReportListInAction, addAdjustedMember, From 753e8ec05679aec0dd05e909601cec806a30c65f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B9=80=EC=A7=84=ED=98=B8?= Date: Tue, 20 Aug 2024 11:32:31 +0900 Subject: [PATCH 19/27] =?UTF-8?q?test:=20isFixed=20=ED=95=84=EB=93=9C=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80=20=ED=85=8C=EC=8A=A4=ED=8A=B8=20=EB=B0=98?= =?UTF-8?q?=EC=98=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../useMemberReportListInAction.test.tsx | 62 ++++++++++++++----- .../src/mocks/memberReportListInAction.json | 8 +-- 2 files changed, 49 insertions(+), 21 deletions(-) diff --git a/client/src/hooks/useMemberReportListInAction/useMemberReportListInAction.test.tsx b/client/src/hooks/useMemberReportListInAction/useMemberReportListInAction.test.tsx index 3c13c3d77..1281481fd 100644 --- a/client/src/hooks/useMemberReportListInAction/useMemberReportListInAction.test.tsx +++ b/client/src/hooks/useMemberReportListInAction/useMemberReportListInAction.test.tsx @@ -1,4 +1,4 @@ -import type {MemberReport} from 'types/serviceType'; +import type {MemberReport, MemberReportInAction} from 'types/serviceType'; import {renderHook, waitFor, act} from '@testing-library/react'; import {MemoryRouter} from 'react-router-dom'; @@ -43,7 +43,7 @@ describe('useMemberReportListInActionTest', () => { it('망쵸의 가격을 100원으로 바꾸면 망쵸의 가격은 100원으로 설정된다.', async () => { const {result} = initializeProvider(actionId, totalPrice); - const adjustedMember: MemberReport = {name: '망쵸', price: 100}; + const adjustedMember: MemberReportInAction = {name: '망쵸', price: 100, isFixed: false}; await waitFor(() => expect(result.current.queryResult.isSuccess).toBe(true)); act(() => { @@ -57,7 +57,7 @@ describe('useMemberReportListInActionTest', () => { it('망쵸의 가격을 100원으로 바꾸면 망쵸의 가격은 100원으로 설정되고 나머지 인원의 가격이 33,300원으로 설정된다.', async () => { const {result} = initializeProvider(actionId, totalPrice); - const adjustedMember: MemberReport = {name: '망쵸', price: 100}; + const adjustedMember: MemberReportInAction = {name: '망쵸', price: 100, isFixed: false}; await waitFor(() => expect(result.current.queryResult.isSuccess).toBe(true)); act(() => { @@ -76,8 +76,8 @@ describe('useMemberReportListInActionTest', () => { it('망쵸의 가격을 100원 쿠키의 가격을 100원으로 바꾸면 나머지 인원의 가격이 49,900원으로 설정된다.', async () => { const {result} = initializeProvider(actionId, totalPrice); - const adjustedMemberMangcho: MemberReport = {name: '망쵸', price: 100}; - const adjustedMemberCookie: MemberReport = {name: '쿠키', price: 100}; + const adjustedMemberMangcho: MemberReportInAction = {name: '망쵸', price: 100, isFixed: false}; + const adjustedMemberCookie: MemberReportInAction = {name: '쿠키', price: 100, isFixed: true}; await waitFor(() => expect(result.current.queryResult.isSuccess).toBe(true)); @@ -100,8 +100,8 @@ describe('useMemberReportListInActionTest', () => { it('망쵸의 가격을 100원 쿠키의 가격을 100원으로 바꾸면 나머지 인원의 가격이 49,900원으로 설정된다.', async () => { const {result} = initializeProvider(actionId, totalPrice); - const adjustedMemberMangcho: MemberReport = {name: '망쵸', price: 100}; - const adjustedMemberCookie: MemberReport = {name: '쿠키', price: 100}; + const adjustedMemberMangcho: MemberReportInAction = {name: '망쵸', price: 100, isFixed: false}; + const adjustedMemberCookie: MemberReportInAction = {name: '쿠키', price: 100, isFixed: false}; await waitFor(() => expect(result.current.queryResult.isSuccess).toBe(true)); @@ -124,8 +124,8 @@ describe('useMemberReportListInActionTest', () => { it('망쵸의 가격을 100원으로 바꾸고 다시 망쵸의 가격을 10,000원으로 바꾸면 나머지 인원의 가격이 30,000원으로 설정된다.', async () => { const {result} = initializeProvider(actionId, totalPrice); - const adjustedMemberMangcho: MemberReport = {name: '망쵸', price: 100}; - const adjustedMemberMangchoAfter: MemberReport = {name: '망쵸', price: 10000}; + const adjustedMemberMangcho: MemberReportInAction = {name: '망쵸', price: 100, isFixed: false}; + const adjustedMemberMangchoAfter: MemberReportInAction = {name: '망쵸', price: 10000, isFixed: true}; await waitFor(() => expect(result.current.queryResult.isSuccess).toBe(true)); @@ -146,9 +146,9 @@ describe('useMemberReportListInActionTest', () => { }); describe('예외 & 엣지케이스', () => { - it('동일한 인원을 바꾸려고 할 때, 반영되지 않는다.', async () => { + it('동일한 인원의 가격을 동일하게 바꾸면 변함없다.', async () => { const {result} = initializeProvider(actionId, totalPrice); - const adjustedMemberMangcho: MemberReport = {name: '망쵸', price: 100}; + const adjustedMemberMangcho: MemberReportInAction = {name: '망쵸', price: 100, isFixed: false}; await waitFor(() => expect(result.current.queryResult.isSuccess).toBe(true)); @@ -157,7 +157,7 @@ describe('useMemberReportListInActionTest', () => { }); act(() => { - result.current.addAdjustedMember(adjustedMemberMangcho); + result.current.addAdjustedMember({...adjustedMemberMangcho, isFixed: true}); }); const anotherMemberList = result.current.memberReportListInAction.filter(member => member.name !== '망쵸'); @@ -167,12 +167,12 @@ describe('useMemberReportListInActionTest', () => { it('참여인원의 가격을 모두 바꾸려고 하면, 마지막 사람의 조정치는 반영되지 않는다.', async () => { const {result} = initializeProvider(actionId, totalPrice); - const adjustedMemberMangcho: MemberReport = {name: '망쵸', price: 100}; - const adjustedMemberCookie: MemberReport = {name: '쿠키', price: 100}; - const adjustedMemberSoha: MemberReport = {name: '소하', price: 100}; + const adjustedMemberMangcho: MemberReportInAction = {name: '망쵸', price: 100, isFixed: false}; + const adjustedMemberCookie: MemberReportInAction = {name: '쿠키', price: 100, isFixed: false}; + const adjustedMemberSoha: MemberReportInAction = {name: '소하', price: 100, isFixed: false}; // 마지막 사람 - const adjustedMemberLeeSang: MemberReport = {name: '이상', price: 100}; + const adjustedMemberLeeSang: MemberReportInAction = {name: '이상', price: 100, isFixed: false}; await waitFor(() => expect(result.current.queryResult.isSuccess).toBe(true)); @@ -196,13 +196,41 @@ describe('useMemberReportListInActionTest', () => { expect(targetMember?.price).not.toBe(100); }); + + it('망쵸에게 300원을 주면 나머지 사람들은 33233원이고 마지막 사람은 33234원이 된다.', async () => { + const {result} = initializeProvider(actionId, totalPrice); + const adjustedMemberMangcho: MemberReportInAction = {name: '망쵸', price: 300, isFixed: false}; + + await waitFor(() => expect(result.current.queryResult.isSuccess).toBe(true)); + + act(() => { + result.current.addAdjustedMember(adjustedMemberMangcho); + }); + + console.log(result.current.memberReportListInAction); + + const targetMember = result.current.memberReportListInAction.find(member => member.name === '망쵸'); + expect(targetMember?.price).toBe(300); + + const anotherMemberList = result.current.memberReportListInAction.filter( + member => member.name === '이상' || member.name === '소하', + ); + + anotherMemberList.forEach(member => { + expect(member.price).toBe(33233); + }); + + const lastMember = result.current.memberReportListInAction.find(member => member.name === '쿠키'); + + expect(lastMember?.price).toBe(33234); + }); }); // last describe('onSubmit 실행 시 반영 테스트', () => { it('망쵸의 가격을 100원으로 바꾸고 저장하면 망쵸 100원이 반영된다.', async () => { const {result} = initializeProvider(actionId, totalPrice); - const adjustedMemberMangcho: MemberReport = {name: '망쵸', price: 100}; + const adjustedMemberMangcho: MemberReportInAction = {name: '망쵸', price: 100, isFixed: false}; await waitFor(() => expect(result.current.queryResult.isSuccess).toBe(true)); diff --git a/client/src/mocks/memberReportListInAction.json b/client/src/mocks/memberReportListInAction.json index 68649355b..2e24670cb 100644 --- a/client/src/mocks/memberReportListInAction.json +++ b/client/src/mocks/memberReportListInAction.json @@ -1,6 +1,6 @@ [ - {"name": "망쵸", "price": 25000}, - {"name": "이상", "price": 25000}, - {"name": "소하", "price": 25000}, - {"name": "쿠키", "price": 25000} + {"name": "망쵸", "price": 25000, "isFixed": false}, + {"name": "이상", "price": 25000, "isFixed": false}, + {"name": "소하", "price": 25000, "isFixed": false}, + {"name": "쿠키", "price": 25000, "isFixed": false} ] From c574f2d67daa0595e032b43df0083c604f5a885b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B9=80=EC=A7=84=ED=98=B8?= Date: Tue, 20 Aug 2024 14:26:17 +0900 Subject: [PATCH 20/27] =?UTF-8?q?feat:=20=EA=B3=84=EC=82=B0=EA=B0=92?= =?UTF-8?q?=EC=9C=BC=EB=A1=9C=20=EA=B0=92=EC=9D=84=20=EB=B3=80=EA=B2=BD?= =?UTF-8?q?=ED=96=88=EC=9D=84=20=EB=95=8C=20isFixed=EB=A5=BC=20=ED=95=B4?= =?UTF-8?q?=EC=A0=9C=ED=95=98=EB=8A=94=20=EA=B8=B0=EB=8A=A5=EA=B5=AC?= =?UTF-8?q?=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../useMemberReportListInAction.ts | 25 ++++++++++++------- 1 file changed, 16 insertions(+), 9 deletions(-) diff --git a/client/src/hooks/useMemberReportListInAction/useMemberReportListInAction.ts b/client/src/hooks/useMemberReportListInAction/useMemberReportListInAction.ts index 97ad364a2..9549c7e91 100644 --- a/client/src/hooks/useMemberReportListInAction/useMemberReportListInAction.ts +++ b/client/src/hooks/useMemberReportListInAction/useMemberReportListInAction.ts @@ -37,20 +37,24 @@ const useMemberReportListInAction = (actionId: number, totalPrice: number) => { }; const calculateDividedPrice = (remainMemberCount: number, totalAdjustedPrice: number) => { - // 조정되지 않은 멤버가 없거나 다 조정된 멤버라면 - if (remainMemberCount === 0 || remainMemberCount === memberReportListInAction.length) { - return { - divided: Math.floor(totalPrice / memberReportListInAction.length), - remainder: (totalPrice - totalAdjustedPrice) % remainMemberCount, - }; - } - return { divided: Math.floor((totalPrice - totalAdjustedPrice) / remainMemberCount), remainder: (totalPrice - totalAdjustedPrice) % remainMemberCount, }; }; + // 계산값으로 값을 변경했을 때 isFixed를 푸는 함수 + // 100 true 33300 true 33300 false 33300 false + const setIsFixedFalseAtResetToDividedPrice = (memberReportListInAction: MemberReportInAction[], divided: number) => { + return memberReportListInAction.map(memberReport => { + if (memberReport.isFixed === true && memberReport.price === divided) { + return {...memberReport, isFixed: false}; + } + + return memberReport; + }); + }; + const calculateAnotherMemberPrice = (memberReportListInAction: MemberReportInAction[]) => { // 총 조정치 금액 const totalAdjustedPrice = memberReportListInAction @@ -75,7 +79,10 @@ const useMemberReportListInAction = (actionId: number, totalPrice: number) => { updatedList[lastNonAdjustedMemberIndex].price += remainder; } } - setMemberReportListInAction(updatedList); + + // 조정됐지만 계산값으로 가격이 변한 경우 fixed 상태를 풀어야한다. + const result = setIsFixedFalseAtResetToDividedPrice(updatedList, divided); + setMemberReportListInAction(result); }; const onSubmit = () => { From d8b3957c80f057bbf74ca0f32cc2292c800a9759 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B9=80=EC=A7=84=ED=98=B8?= Date: Tue, 20 Aug 2024 14:26:34 +0900 Subject: [PATCH 21/27] =?UTF-8?q?test:=20isFixed=20=ED=95=B4=EC=A0=9C=20?= =?UTF-8?q?=EA=B4=80=EB=A0=A8=20=ED=85=8C=EC=8A=A4=ED=8A=B8=20=EC=9E=91?= =?UTF-8?q?=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../useMemberReportListInAction.test.tsx | 51 +++++++++++++++++++ 1 file changed, 51 insertions(+) diff --git a/client/src/hooks/useMemberReportListInAction/useMemberReportListInAction.test.tsx b/client/src/hooks/useMemberReportListInAction/useMemberReportListInAction.test.tsx index 1281481fd..e40e1694f 100644 --- a/client/src/hooks/useMemberReportListInAction/useMemberReportListInAction.test.tsx +++ b/client/src/hooks/useMemberReportListInAction/useMemberReportListInAction.test.tsx @@ -224,6 +224,57 @@ describe('useMemberReportListInActionTest', () => { expect(lastMember?.price).toBe(33234); }); + + it('망쵸, 쿠키의 가격을 100원으로 바꾼 후 다시 쿠키의 가격을 33000원으로 바꾸면 쿠키의 isFixed는 false가 된다.', async () => { + const {result} = initializeProvider(actionId, totalPrice); + const adjustedMemberMangcho: MemberReportInAction = {name: '망쵸', price: 100, isFixed: false}; + const adjustedMemberCookie: MemberReportInAction = {name: '쿠키', price: 100, isFixed: false}; + const adjustedMemberCookieReset: MemberReportInAction = {name: '쿠키', price: 33300, isFixed: true}; + + await waitFor(() => expect(result.current.queryResult.isSuccess).toBe(true)); + + act(() => { + result.current.addAdjustedMember(adjustedMemberMangcho); + }); + + act(() => { + result.current.addAdjustedMember(adjustedMemberCookie); + }); + + const targetMember = result.current.memberReportListInAction.find(member => member.name === '쿠키'); + + expect(targetMember?.isFixed).toBe(true); + + act(() => { + result.current.addAdjustedMember(adjustedMemberCookieReset); + }); + + const targetMemberReset = result.current.memberReportListInAction.find(member => member.name === '쿠키'); + + expect(targetMemberReset?.isFixed).toBe(false); + }); + + it('망쵸의 가격을 100원으로 바꾼 후 다시 망쵸의 가격을 25000원으로 바꾸면 망쵸의 isFixed는 false가 된다.', async () => { + const {result} = initializeProvider(actionId, totalPrice); + const adjustedMemberMangcho: MemberReportInAction = {name: '망쵸', price: 100, isFixed: false}; + const adjustedMemberMangchoAfter: MemberReportInAction = {name: '망쵸', price: 25000, isFixed: true}; + + await waitFor(() => expect(result.current.queryResult.isSuccess).toBe(true)); + + act(() => { + result.current.addAdjustedMember(adjustedMemberMangcho); + }); + + const targetMember = result.current.memberReportListInAction.find(member => member.name === '망쵸'); + expect(targetMember?.isFixed).toBe(true); + + act(() => { + result.current.addAdjustedMember(adjustedMemberMangchoAfter); + }); + + const targetMemberReset = result.current.memberReportListInAction.find(member => member.name === '망쵸'); + expect(targetMemberReset?.isFixed).toBe(false); + }); }); // last From e2d0abaf3ccfed016994c0d08432e10916821c4b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B9=80=EC=A7=84=ED=98=B8?= Date: Tue, 20 Aug 2024 21:58:03 +0900 Subject: [PATCH 22/27] =?UTF-8?q?feat:=20member=20report=20validation=20?= =?UTF-8?q?=EB=A1=9C=EC=A7=81=20=EC=9E=91=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../useMemberReportListInAction.ts | 30 ++++++++++++++++ .../validate/validateMemberReportInAction.ts | 36 +++++++++++++++++++ 2 files changed, 66 insertions(+) create mode 100644 client/src/utils/validate/validateMemberReportInAction.ts diff --git a/client/src/hooks/useMemberReportListInAction/useMemberReportListInAction.ts b/client/src/hooks/useMemberReportListInAction/useMemberReportListInAction.ts index 9549c7e91..d8c46aef7 100644 --- a/client/src/hooks/useMemberReportListInAction/useMemberReportListInAction.ts +++ b/client/src/hooks/useMemberReportListInAction/useMemberReportListInAction.ts @@ -4,6 +4,9 @@ import {useEffect, useState} from 'react'; import useRequestGetMemberReportListInAction from '@hooks/queries/useRequestGetMemberReportListInAction'; import useRequestPutMemberReportListInAction from '@hooks/queries/useRequestPutMemberReportListInAction'; +import validateMemberReportInAction from '@utils/validate/validateMemberReportInAction'; + +import useInput from '@hooks/useInput'; const useMemberReportListInAction = (actionId: number, totalPrice: number) => { const {memberReportListInActionFromServer, queryResult} = useRequestGetMemberReportListInAction(actionId); @@ -13,6 +16,31 @@ const useMemberReportListInAction = (actionId: number, totalPrice: number) => { memberReportListInActionFromServer, ); + const {inputList, canSubmit, handleChange} = useInput({ + validateFunc: validateMemberReportInAction, + initialInputList: memberReportListInActionFromServer.map((member, index) => ({value: String(member.price), index})), + }); + + const onChange = (event: React.ChangeEvent, index: number) => { + const {value} = event.target; + handleChange(index, value); + }; + + useEffect(() => { + inputList.forEach(input => { + const {index, value} = input; + const targetMemberReport = memberReportListInActionFromServer[index]; + + // 기존 값과 입력된 값이 다를 때만 addAdjustedMember 호출 + if (targetMemberReport && Number(value) !== targetMemberReport.price) { + addAdjustedMember({ + ...targetMemberReport, + price: Number(value), + }); + } + }); + }, [inputList]); + useEffect(() => { if (queryResult.isSuccess) { setMemberReportListInAction(memberReportListInActionFromServer); @@ -93,6 +121,8 @@ const useMemberReportListInAction = (actionId: number, totalPrice: number) => { memberReportListInAction, addAdjustedMember, onSubmit, + onChange, + canSubmit, queryResult, }; }; diff --git a/client/src/utils/validate/validateMemberReportInAction.ts b/client/src/utils/validate/validateMemberReportInAction.ts new file mode 100644 index 000000000..04cae5e20 --- /dev/null +++ b/client/src/utils/validate/validateMemberReportInAction.ts @@ -0,0 +1,36 @@ +import REGEXP from '@constants/regExp'; +import {ERROR_MESSAGE} from '@constants/errorMessage'; +import RULE from '@constants/rule'; + +import {ValidateResult} from './type'; + +const validateMemberReportInAction = (price: string): ValidateResult => { + let errorMessage = null; + const numberTypePrice = Number(price); + + const validateOnlyNaturalNumber = () => { + if (!(Number.isInteger(numberTypePrice) && numberTypePrice >= 0)) return false; + return true; + }; + + const validatePrice = () => { + if (numberTypePrice > RULE.maxPrice) return false; + return true; + }; + + const validateEmpty = () => { + if (!price.trim().length) { + errorMessage = ERROR_MESSAGE.preventEmpty; + return false; + } + return true; + }; + + if (validateOnlyNaturalNumber() && validatePrice() && validateEmpty()) { + return {isValid: true, errorMessage: null}; + } + + return {isValid: false, errorMessage: errorMessage || ERROR_MESSAGE.invalidInput}; +}; + +export default validateMemberReportInAction; From 0d5ec62a07cae5ce4321f3ec92a1c3c9a3061d80 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B9=80=EC=A7=84=ED=98=B8?= Date: Tue, 20 Aug 2024 21:58:30 +0900 Subject: [PATCH 23/27] =?UTF-8?q?chore:=20=ED=85=8C=EC=8A=A4=ED=8A=B8?= =?UTF-8?q?=EB=A5=BC=20=EC=9C=84=ED=95=9C=20=EC=BD=94=EB=93=9C=20=EC=9E=91?= =?UTF-8?q?=EC=84=B1=20=EC=B6=94=ED=9B=84=20=EC=82=AD=EC=A0=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- client/src/Test.tsx | 21 +++++++++++++++++++++ client/src/router.tsx | 5 +++++ 2 files changed, 26 insertions(+) create mode 100644 client/src/Test.tsx diff --git a/client/src/Test.tsx b/client/src/Test.tsx new file mode 100644 index 000000000..7837b454e --- /dev/null +++ b/client/src/Test.tsx @@ -0,0 +1,21 @@ +import {EditableItem, Flex, Text} from 'haengdong-design'; + +const Test = () => { + return ( + {}} onFocus={() => {}}> + + + + + + + ); +}; + +export default Test; diff --git a/client/src/router.tsx b/client/src/router.tsx index a6af2184d..eb6201e20 100644 --- a/client/src/router.tsx +++ b/client/src/router.tsx @@ -4,6 +4,7 @@ import {AdminPage} from '@pages/EventPage/AdminPage'; import {HomePage} from '@pages/EventPage/HomePage'; import ErrorPage from '@pages/ErrorPage/ErrorPage'; import EventLoginPage from '@pages/EventPage/AdminPage/EventLoginPage'; +import Test from 'Test'; import {CompleteCreateEventPage, SetEventNamePage, SetEventPasswordPage} from '@pages/CreateEventPage'; import {MainPage} from '@pages/MainPage'; @@ -47,6 +48,10 @@ const router = createBrowserRouter([ {path: ROUTER_URLS.home, element: }, ], }, + { + path: 'cookie-test', + element: , + }, { path: '*', element: , From 947b16acd735eca3dcb3c67b38af00cbb0623cd9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B9=80=EC=A7=84=ED=98=B8?= Date: Wed, 21 Aug 2024 10:42:58 +0900 Subject: [PATCH 24/27] =?UTF-8?q?chore:=20=ED=85=8C=EC=8A=A4=ED=8A=B8?= =?UTF-8?q?=EC=9A=A9=20=EB=9D=BC=EC=9A=B0=ED=8C=85=20=EC=82=AD=EC=A0=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- client/src/router.tsx | 5 ----- 1 file changed, 5 deletions(-) diff --git a/client/src/router.tsx b/client/src/router.tsx index eb6201e20..a6af2184d 100644 --- a/client/src/router.tsx +++ b/client/src/router.tsx @@ -4,7 +4,6 @@ import {AdminPage} from '@pages/EventPage/AdminPage'; import {HomePage} from '@pages/EventPage/HomePage'; import ErrorPage from '@pages/ErrorPage/ErrorPage'; import EventLoginPage from '@pages/EventPage/AdminPage/EventLoginPage'; -import Test from 'Test'; import {CompleteCreateEventPage, SetEventNamePage, SetEventPasswordPage} from '@pages/CreateEventPage'; import {MainPage} from '@pages/MainPage'; @@ -48,10 +47,6 @@ const router = createBrowserRouter([ {path: ROUTER_URLS.home, element: }, ], }, - { - path: 'cookie-test', - element: , - }, { path: '*', element: , From 203922d862831052b9528a2fd477fb913f4381d2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B9=80=EC=A7=84=ED=98=B8?= Date: Wed, 21 Aug 2024 10:43:39 +0900 Subject: [PATCH 25/27] =?UTF-8?q?feat:=20=EC=B4=9D=20=EA=B8=88=EC=95=A1?= =?UTF-8?q?=EB=B3=B4=EB=8B=A4=20=ED=81=B0=EC=A7=80=20=EA=B2=80=EC=A6=9D?= =?UTF-8?q?=ED=95=98=EB=8A=94=20=EA=B8=B0=EB=8A=A5=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../utils/validate/validateMemberReportInAction.ts | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/client/src/utils/validate/validateMemberReportInAction.ts b/client/src/utils/validate/validateMemberReportInAction.ts index 04cae5e20..8e02c3dde 100644 --- a/client/src/utils/validate/validateMemberReportInAction.ts +++ b/client/src/utils/validate/validateMemberReportInAction.ts @@ -1,10 +1,9 @@ -import REGEXP from '@constants/regExp'; import {ERROR_MESSAGE} from '@constants/errorMessage'; import RULE from '@constants/rule'; import {ValidateResult} from './type'; -const validateMemberReportInAction = (price: string): ValidateResult => { +const validateMemberReportInAction = (price: string, totalPrice: number): ValidateResult => { let errorMessage = null; const numberTypePrice = Number(price); @@ -26,7 +25,13 @@ const validateMemberReportInAction = (price: string): ValidateResult => { return true; }; - if (validateOnlyNaturalNumber() && validatePrice() && validateEmpty()) { + const validateUnderTotalPrice = () => { + if (numberTypePrice > totalPrice) return false; + + return true; + }; + + if (validateOnlyNaturalNumber() && validatePrice() && validateEmpty() && validateUnderTotalPrice()) { return {isValid: true, errorMessage: null}; } From 46f88e7136d9f83d6950a5591e399a2e1e5f250c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B9=80=EC=A7=84=ED=98=B8?= Date: Wed, 21 Aug 2024 10:44:22 +0900 Subject: [PATCH 26/27] =?UTF-8?q?feat:=20=EC=B0=A8=EB=93=B1=20=EC=A0=81?= =?UTF-8?q?=EC=9A=A9=20input=EC=9C=BC=EB=A1=9C=20handle=ED=95=98=EB=8A=94?= =?UTF-8?q?=20=EA=B8=B0=EB=8A=A5=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../useMemberReportInput.tsx | 57 +++++++++++++++++++ .../useMemberReportListInAction.ts | 30 ---------- 2 files changed, 57 insertions(+), 30 deletions(-) create mode 100644 client/src/hooks/useMemberReportListInAction/useMemberReportInput.tsx diff --git a/client/src/hooks/useMemberReportListInAction/useMemberReportInput.tsx b/client/src/hooks/useMemberReportListInAction/useMemberReportInput.tsx new file mode 100644 index 000000000..638cff2e5 --- /dev/null +++ b/client/src/hooks/useMemberReportListInAction/useMemberReportInput.tsx @@ -0,0 +1,57 @@ +import type {MemberReportInAction} from 'types/serviceType'; + +import {useEffect, useState} from 'react'; + +import validateMemberReportInAction from '@utils/validate/validateMemberReportInAction'; + +type MemberReportInput = MemberReportInAction & { + index: number; +}; + +type UseMemberReportProps = { + data: MemberReportInAction[]; + addAdjustedMember: (memberReport: MemberReportInAction) => void; + totalPrice: number; +}; + +const useMemberReportInput = ({data, addAdjustedMember, totalPrice}: UseMemberReportProps) => { + const [inputList, setInputList] = useState(data.map((item, index) => ({...item, index}))); + const [canSubmit, setCanSubmit] = useState(false); + + const onChange = (event: React.ChangeEvent, index: number) => { + const {value} = event.target; + + validateAndAddAdjustedMember(value, index); + }; + + const validateAndAddAdjustedMember = (price: string, index: number) => { + const {isValid, errorMessage} = validateMemberReportInAction(price, totalPrice); + setCanSubmit(errorMessage === null); + + if (isValid) { + const newInputList = [...inputList]; + newInputList[index].price = Number(price); + setInputList(newInputList); + + const memberReportData: MemberReportInAction = { + name: newInputList[index].name, + price: newInputList[index].price, + isFixed: newInputList[index].isFixed, + }; + addAdjustedMember(memberReportData); + } + }; + + // addAdjustedMember로 인해 data가 변했을 때 input list의 값을 맞춰주기 위함 + useEffect(() => { + setInputList(data.map((item, index) => ({...item, index}))); + }, [data]); + + return { + inputList, + onChange, + canSubmit, + }; +}; + +export default useMemberReportInput; diff --git a/client/src/hooks/useMemberReportListInAction/useMemberReportListInAction.ts b/client/src/hooks/useMemberReportListInAction/useMemberReportListInAction.ts index d8c46aef7..9549c7e91 100644 --- a/client/src/hooks/useMemberReportListInAction/useMemberReportListInAction.ts +++ b/client/src/hooks/useMemberReportListInAction/useMemberReportListInAction.ts @@ -4,9 +4,6 @@ import {useEffect, useState} from 'react'; import useRequestGetMemberReportListInAction from '@hooks/queries/useRequestGetMemberReportListInAction'; import useRequestPutMemberReportListInAction from '@hooks/queries/useRequestPutMemberReportListInAction'; -import validateMemberReportInAction from '@utils/validate/validateMemberReportInAction'; - -import useInput from '@hooks/useInput'; const useMemberReportListInAction = (actionId: number, totalPrice: number) => { const {memberReportListInActionFromServer, queryResult} = useRequestGetMemberReportListInAction(actionId); @@ -16,31 +13,6 @@ const useMemberReportListInAction = (actionId: number, totalPrice: number) => { memberReportListInActionFromServer, ); - const {inputList, canSubmit, handleChange} = useInput({ - validateFunc: validateMemberReportInAction, - initialInputList: memberReportListInActionFromServer.map((member, index) => ({value: String(member.price), index})), - }); - - const onChange = (event: React.ChangeEvent, index: number) => { - const {value} = event.target; - handleChange(index, value); - }; - - useEffect(() => { - inputList.forEach(input => { - const {index, value} = input; - const targetMemberReport = memberReportListInActionFromServer[index]; - - // 기존 값과 입력된 값이 다를 때만 addAdjustedMember 호출 - if (targetMemberReport && Number(value) !== targetMemberReport.price) { - addAdjustedMember({ - ...targetMemberReport, - price: Number(value), - }); - } - }); - }, [inputList]); - useEffect(() => { if (queryResult.isSuccess) { setMemberReportListInAction(memberReportListInActionFromServer); @@ -121,8 +93,6 @@ const useMemberReportListInAction = (actionId: number, totalPrice: number) => { memberReportListInAction, addAdjustedMember, onSubmit, - onChange, - canSubmit, queryResult, }; }; From ea9b6ce3b146303e23ea5c4c70529ce5239e6307 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B9=80=EC=A7=84=ED=98=B8?= Date: Wed, 21 Aug 2024 10:45:34 +0900 Subject: [PATCH 27/27] =?UTF-8?q?remove:=20=EC=82=AC=EC=9A=A9=ED=95=98?= =?UTF-8?q?=EC=A7=80=20=EC=95=8A=EB=8A=94=20=ED=8C=8C=EC=9D=BC=20=EC=A0=9C?= =?UTF-8?q?=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- client/src/Test.tsx | 21 --------------------- 1 file changed, 21 deletions(-) delete mode 100644 client/src/Test.tsx diff --git a/client/src/Test.tsx b/client/src/Test.tsx deleted file mode 100644 index 7837b454e..000000000 --- a/client/src/Test.tsx +++ /dev/null @@ -1,21 +0,0 @@ -import {EditableItem, Flex, Text} from 'haengdong-design'; - -const Test = () => { - return ( - {}} onFocus={() => {}}> - - - - - - - ); -}; - -export default Test;