diff --git a/client/src/apis/request/bill.ts b/client/src/apis/request/bill.ts index 03bbd5317..63173a06f 100644 --- a/client/src/apis/request/bill.ts +++ b/client/src/apis/request/bill.ts @@ -10,17 +10,17 @@ import {WithBillId, WithEventId} from '@apis/withId.type'; export interface RequestPostBill { title: string; price: number; - members: number[]; + memberIds: number[]; } -export const requestPostBill = async ({eventId, title, price, members}: WithEventId) => { +export const requestPostBill = async ({eventId, title, price, memberIds}: WithEventId) => { await requestPostWithoutResponse({ baseUrl: BASE_URL.HD, endpoint: `${ADMIN_API_PREFIX}/${eventId}/bills`, body: { title, price, - members, + memberIds, }, }); }; diff --git a/client/src/apis/request/member.ts b/client/src/apis/request/member.ts index 4e5da1bb4..756412784 100644 --- a/client/src/apis/request/member.ts +++ b/client/src/apis/request/member.ts @@ -7,7 +7,7 @@ import {ADMIN_API_PREFIX, USER_API_PREFIX} from '@apis/endpointPrefix'; import {requestDelete, requestGet, requestPut, requestPostWithResponse} from '@apis/fetcher'; import {WithEventId} from '@apis/withId.type'; -interface PostMember { +export interface PostMember { name: string; } diff --git a/client/src/components/Design/components/NumberKeyboard/NumberKeyboard.tsx b/client/src/components/Design/components/NumberKeyboard/NumberKeyboard.tsx index 0da3862de..61e36da7d 100644 --- a/client/src/components/Design/components/NumberKeyboard/NumberKeyboard.tsx +++ b/client/src/components/Design/components/NumberKeyboard/NumberKeyboard.tsx @@ -11,16 +11,18 @@ export type KeyboardType = 'number' | 'string' | 'amount'; interface Props { type: KeyboardType; maxNumber: number; + initialValue?: string; onChange: (value: string) => void; } -export default function NumberKeyboard({type, maxNumber, onChange}: Props) { +export default function NumberKeyboard({type, maxNumber, initialValue, onChange}: Props) { const {theme} = useTheme(); const amountKeypads = ['1', '2', '3', '4', '5', '6', '7', '8', '9', '00', '0', '<-']; const numberKeypads = ['1', '2', '3', '4', '5', '6', '7', '8', '9', '', '0', '<-']; const {onClickKeypad, onClickDelete, onClickDeleteAll, onClickAddAmount} = useNumberKeyboard({ type, + initialValue, maxNumber, onChange, }); diff --git a/client/src/components/Design/components/NumberKeyboard/useNumberKeyboard.tsx b/client/src/components/Design/components/NumberKeyboard/useNumberKeyboard.tsx index f8d5fbaed..48301ea8e 100644 --- a/client/src/components/Design/components/NumberKeyboard/useNumberKeyboard.tsx +++ b/client/src/components/Design/components/NumberKeyboard/useNumberKeyboard.tsx @@ -5,11 +5,12 @@ import {KeyboardType} from './NumberKeyboard'; interface Props { type: KeyboardType; maxNumber?: number; + initialValue?: string; onChange: (value: string) => void; } -const useNumberKeyboard = ({type, maxNumber, onChange}: Props) => { - const [value, setValue] = useState(''); +const useNumberKeyboard = ({type, maxNumber, initialValue, onChange}: Props) => { + const [value, setValue] = useState(initialValue ?? ''); const onClickKeypad = (inputValue: string) => { const newValue = (value + inputValue).replace(/,/g, ''); diff --git a/client/src/constants/regExp.ts b/client/src/constants/regExp.ts index 4d910ac99..d235c1f11 100644 --- a/client/src/constants/regExp.ts +++ b/client/src/constants/regExp.ts @@ -1,8 +1,8 @@ const REGEXP = { eventPassword: /^[0-9]*$/, - memberName: /^[ㄱ-ㅎㅏ-ㅣ가-힣a-zA-Z\s]*$/, - purchaseTitle: /^[ㄱ-ㅎㅏ-ㅣ가-힣a-zA-Z0-9\s]*$/, eventUrl: /\/event\/([a-zA-Z0-9-]+)\//, + billTitle: /^([ㄱ-ㅎ가-힣a-zA-Z0-9ㆍᆢ]\s?)*$/, + memberName: /^([ㄱ-ㅎ가-힣a-zA-Zㆍᆢ]\s?)*$/, }; export default REGEXP; diff --git a/client/src/constants/routerUrls.ts b/client/src/constants/routerUrls.ts index 2955115d7..8cd486d9b 100644 --- a/client/src/constants/routerUrls.ts +++ b/client/src/constants/routerUrls.ts @@ -7,6 +7,6 @@ export const ROUTER_URLS = { eventLogin: '/event/:eventId/login', eventManage: '/event/:eventId/admin', home: '/event/:eventId/home', - addBill: 'event/:eventId/addBill', + addBill: '/event/:eventId/add-bill', eventEdit: 'event/:eventId/admin/edit', }; diff --git a/client/src/hooks/queries/bill/useRequestPostBill.ts b/client/src/hooks/queries/bill/useRequestPostBill.ts index 80e4b685c..a9c7bce62 100644 --- a/client/src/hooks/queries/bill/useRequestPostBill.ts +++ b/client/src/hooks/queries/bill/useRequestPostBill.ts @@ -11,7 +11,7 @@ const useRequestPostBill = () => { const queryClient = useQueryClient(); const {mutate, ...rest} = useMutation({ - mutationFn: ({title, price, members}: RequestPostBill) => requestPostBill({eventId, title, price, members}), + mutationFn: ({title, price, memberIds}: RequestPostBill) => requestPostBill({eventId, title, price, memberIds}), onSuccess: () => { queryClient.invalidateQueries({queryKey: [QUERY_KEYS.steps]}); queryClient.invalidateQueries({queryKey: [QUERY_KEYS.reports]}); diff --git a/client/src/hooks/queries/member/useRequestPostMembers.ts b/client/src/hooks/queries/member/useRequestPostMembers.ts index 72da9f363..bddb9a322 100644 --- a/client/src/hooks/queries/member/useRequestPostMembers.ts +++ b/client/src/hooks/queries/member/useRequestPostMembers.ts @@ -10,23 +10,17 @@ const useRequestPostMembers = () => { const eventId = getEventIdByUrl(); const queryClient = useQueryClient(); - const {mutate, ...rest} = useMutation({ + const {mutate, mutateAsync, data, ...rest} = useMutation({ mutationFn: ({members}: RequestPostMembers) => requestPostMembers({eventId, members}), - // TODO: (@todari) : 낙관적 업데이트 적고 있었어용 - // onMutate: async ({type, memberName}) => { - // await queryClient.cancelQueries({queryKey: [QUERY_KEYS.step]}); - // const previousStep = queryClient.getQueryData([QUERY_KEYS.step]); - // queryClient.setQueryData([QUERY_KEYS.step], (prev: (MemberStep | BillStep)[]) => prev && { - // }); - // }, - onSuccess: () => { + onSuccess: responseData => { queryClient.invalidateQueries({queryKey: [QUERY_KEYS.allMembers]}); queryClient.invalidateQueries({queryKey: [QUERY_KEYS.steps]}); queryClient.invalidateQueries({queryKey: [QUERY_KEYS.reports]}); + return responseData; }, }); - return {postMember: mutate, ...rest}; + return {postMembers: mutate, postMembersAsync: mutateAsync, responseMemberIds: data, ...rest}; }; export default useRequestPostMembers; diff --git a/client/src/hooks/useAddBillFunnel.ts b/client/src/hooks/useAddBillFunnel.ts new file mode 100644 index 000000000..f0b1e87e8 --- /dev/null +++ b/client/src/hooks/useAddBillFunnel.ts @@ -0,0 +1,33 @@ +import {useEffect, useState} from 'react'; + +import {BillInfo} from '@pages/AddBillFunnel/AddBillFunnel'; + +import useRequestGetCurrentMembers from './queries/member/useRequestGetCurrentMembers'; + +export type BillStep = 'title' | 'price' | 'members'; + +const useAddBillFunnel = () => { + const {currentMembers} = useRequestGetCurrentMembers(); + const [step, setStep] = useState('price'); + const [billInfo, setBillInfo] = useState({ + price: '', + title: '', + members: [], + }); + + useEffect(() => { + document.body.style.overflow = 'hidden'; + + return () => { + document.body.style.overflow = 'auto'; + }; + }, []); + + useEffect(() => { + currentMembers && setBillInfo(prev => ({...prev, members: currentMembers})); + }, [currentMembers]); + + return {step, setStep, billInfo, setBillInfo, currentMembers}; +}; + +export default useAddBillFunnel; diff --git a/client/src/hooks/useMembersStep.ts b/client/src/hooks/useMembersStep.ts new file mode 100644 index 000000000..9cd68f2a4 --- /dev/null +++ b/client/src/hooks/useMembersStep.ts @@ -0,0 +1,122 @@ +import {useEffect, useState} from 'react'; +import {useNavigate} from 'react-router-dom'; + +import {BillInfo} from '@pages/AddBillFunnel/AddBillFunnel'; +import {Member} from 'types/serviceType'; + +import getEventIdByUrl from '@utils/getEventIdByUrl'; + +import REGEXP from '@constants/regExp'; + +import useRequestPostMembers from './queries/member/useRequestPostMembers'; +import useRequestPostBill from './queries/bill/useRequestPostBill'; +import {BillStep} from './useAddBillFunnel'; + +interface Props { + billInfo: BillInfo; + setBillInfo: React.Dispatch>; + setStep: React.Dispatch>; + currentMembers: Member[]; +} + +const useMembersStep = ({billInfo, setBillInfo, currentMembers, setStep}: Props) => { + const [errorMessage, setErrorMessage] = useState(''); + const [nameInput, setNameInput] = useState(''); + + const {postMembersAsync, isPending: isPendingPostMembers} = useRequestPostMembers(); + + const {postBill, isSuccess: isSuccessPostBill, isPending: isPendingPostBill} = useRequestPostBill(); + const navigate = useNavigate(); + const eventId = getEventIdByUrl(); + + const onNameInputChange = (value: string) => { + if (REGEXP.memberName.test(value)) { + setNameInput(value); + } + }; + + const handleNameInputChange = (event: React.ChangeEvent) => { + if (event.target.value.length > 4) { + setErrorMessage('이름은 4자까지 입력 가능해요'); + onNameInputChange(nameInput.slice(0, 4)); + } else { + setErrorMessage(''); + onNameInputChange(event.target.value); + } + }; + + const canAddMembers = nameInput && !errorMessage; + + const canSubmitMembers = billInfo.members.length !== 0; + + const setBillInfoMemberWithId = (name: string) => { + const existingMember = currentMembers.find(currentMember => currentMember.name === name); + if (existingMember) { + setBillInfo(prev => ({...prev, members: [...prev.members, {id: existingMember.id, name: name}]})); + } else { + setBillInfo(prev => ({...prev, members: [...prev.members, {id: -1, name: name}]})); + } + }; + + const handleNameInputEnter = (event: React.KeyboardEvent) => { + if (event.nativeEvent.isComposing) { + return; + } + if (event.key === 'Enter' && canAddMembers) { + event.preventDefault(); + if (!billInfo.members.map(({name}) => name).includes(nameInput)) { + setBillInfoMemberWithId(nameInput); + } + setNameInput(''); + } + }; + + const handlePostBill = async () => { + if (billInfo.members.map(({id}) => id).includes(-1)) { + const newMembers = await postMembersAsync({ + members: billInfo.members + .filter(({id}) => id === -1) + .map(({name}) => ({ + name, + })), + }); + postBill({ + title: billInfo.title, + price: Number(billInfo.price.replace(',', '')), + memberIds: billInfo.members.map(member => + member.id === -1 ? newMembers.members.find(m => m.name === member.name)?.id || member.id : member.id, + ), + }); + } else { + postBill({ + title: billInfo.title, + price: Number(billInfo.price.replace(',', '')), + memberIds: billInfo.members.map(({id}) => id), + }); + } + }; + + useEffect(() => { + if (isSuccessPostBill) { + navigate(`/event/${eventId}/admin`); + } + }, [isSuccessPostBill]); + + const handlePrevStep = () => { + setStep('title'); + }; + + return { + errorMessage, + nameInput, + handleNameInputChange, + handleNameInputEnter, + isPendingPostBill, + isPendingPostMembers, + canSubmitMembers, + handlePostBill, + handlePrevStep, + }; +}; + +export default useMembersStep; diff --git a/client/src/hooks/usePriceStep.ts b/client/src/hooks/usePriceStep.ts new file mode 100644 index 000000000..181a44f3d --- /dev/null +++ b/client/src/hooks/usePriceStep.ts @@ -0,0 +1,27 @@ +import {useCallback} from 'react'; + +import {BillInfo} from '@pages/AddBillFunnel/AddBillFunnel'; + +import {BillStep} from './useAddBillFunnel'; + +interface Props { + setStep: React.Dispatch>; + setBillInfo: React.Dispatch>; +} + +const usePriceStep = ({setStep, setBillInfo}: Props) => { + const handleNumberKeyboardChange = useCallback( + (value: string) => { + setBillInfo(prev => ({...prev, price: value})); + }, + [setBillInfo], + ); + + const handleNextStep = () => { + setStep('title'); + }; + + return {handleNumberKeyboardChange, handleNextStep}; +}; + +export default usePriceStep; diff --git a/client/src/hooks/useTitleStep.ts b/client/src/hooks/useTitleStep.ts new file mode 100644 index 000000000..8ed5c787c --- /dev/null +++ b/client/src/hooks/useTitleStep.ts @@ -0,0 +1,64 @@ +import {useState} from 'react'; + +import {BillInfo} from '@pages/AddBillFunnel/AddBillFunnel'; + +import REGEXP from '@constants/regExp'; + +import {BillStep} from './useAddBillFunnel'; + +interface Props { + billInfo: BillInfo; + setBillInfo: React.Dispatch>; + setStep: React.Dispatch>; +} + +const useTitleStep = ({billInfo, setBillInfo, setStep}: Props) => { + const [errorMessage, setErrorMessage] = useState(''); + + const onTitleInputChange = (value: string) => { + if (REGEXP.billTitle.test(value)) { + setBillInfo(prev => ({...prev, title: value})); + } + }; + + const handleTitleInputChange = (event: React.ChangeEvent) => { + if (event.target.value.length > 12) { + setErrorMessage('지출내역은 12자까지 입력 가능해요'); + onTitleInputChange(billInfo.title.slice(0, 12)); + } else { + setErrorMessage(''); + onTitleInputChange(event.target.value); + } + }; + + const canSubmitTitleInput = billInfo.title && !errorMessage; + + const handleTitleInputEnter = (event: React.KeyboardEvent) => { + if (event.nativeEvent.isComposing) { + return; + } + if (event.key === 'Enter' && canSubmitTitleInput) { + event.preventDefault(); + setStep('members'); + } + }; + + const handleNextStep = () => { + setStep('members'); + }; + + const handlePrevStep = () => { + setStep('price'); + }; + + return { + errorMessage, + handleTitleInputChange, + handleTitleInputEnter, + canSubmitTitleInput, + handleNextStep, + handlePrevStep, + }; +}; + +export default useTitleStep; diff --git a/client/src/mocks/handlers/billHandler.ts b/client/src/mocks/handlers/billHandler.ts index 0130b52a2..67a8935b7 100644 --- a/client/src/mocks/handlers/billHandler.ts +++ b/client/src/mocks/handlers/billHandler.ts @@ -31,13 +31,23 @@ export const billHandler = [ const {title, price, members} = await request.json(); const newBill = {id: Date.now(), title, price, isFixed: false}; - billData.steps[0].bills.push(newBill); - billData.steps[0].members = members.map(id => ({id, name: `Member ${id}`})); + const lastStep = billData.steps[billData.steps.length - 1]; + const isSameMembers = JSON.stringify(lastStep.members.map(m => m.id).sort()) === JSON.stringify(members.sort()); + + if (isSameMembers) { + lastStep.bills.push(newBill); + } else { + billData.steps.push({ + bills: [newBill], + members: members.map(id => ({id, name: `Member ${id}`})), + }); + } (billDetailsData as unknown as BillDetailsData)[newBill.id.toString()] = { billDetails: members.map((id, index) => ({ id, - memberName: `Member ${id}`, + memberName: + billData.steps.flatMap(step => step.members).find(member => member.id === id)?.name || `Member ${id}`, price: (Math.floor(price / members.length) + (index < price % members.length ? 1 : 0)).toString(), })), }; diff --git a/client/src/pages/AddBillFunnel/AddBillFunnel.tsx b/client/src/pages/AddBillFunnel/AddBillFunnel.tsx new file mode 100644 index 000000000..a98b60919 --- /dev/null +++ b/client/src/pages/AddBillFunnel/AddBillFunnel.tsx @@ -0,0 +1,34 @@ +import {Member} from 'types/serviceType'; + +import useAddBillFunnel from '@hooks/useAddBillFunnel'; + +import {Back, MainLayout, TopNav} from '@components/Design'; + +import PriceStep from './steps/PriceStep'; +import {TitleStep} from './steps/TitleStep'; +import MembersStep from './steps/MembersStep'; + +export interface BillInfo { + price: string; + title: string; + members: Member[]; +} + +const AddBillFunnel = () => { + const {step, setStep, billInfo, setBillInfo, currentMembers} = useAddBillFunnel(); + + return ( + + + + + {step === 'price' && } + {step === 'title' && } + {step === 'members' && ( + + )} + + ); +}; + +export default AddBillFunnel; diff --git a/client/src/pages/AddBillFunnel/steps/MembersStep.tsx b/client/src/pages/AddBillFunnel/steps/MembersStep.tsx new file mode 100644 index 000000000..5d78083a9 --- /dev/null +++ b/client/src/pages/AddBillFunnel/steps/MembersStep.tsx @@ -0,0 +1,102 @@ +import {css} from '@emotion/react'; + +import Top from '@components/Design/components/Top/Top'; +import ChipButton from '@components/Design/components/ChipButton/ChipButton'; +import {Member} from 'types/serviceType'; + +import useMembersStep from '@hooks/useMembersStep'; +import {BillStep} from '@hooks/useAddBillFunnel'; + +import {FixedButton, Flex, LabelInput, Text} from '@components/Design'; + +import {BillInfo} from '../AddBillFunnel'; + +interface Props { + billInfo: BillInfo; + setBillInfo: React.Dispatch>; + setStep: React.Dispatch>; + currentMembers: Member[]; +} + +const MembersStep = ({billInfo, setBillInfo, currentMembers, setStep}: Props) => { + const { + errorMessage, + nameInput, + handleNameInputChange, + handleNameInputEnter, + isPendingPostBill, + isPendingPostMembers, + canSubmitMembers, + handlePostBill, + handlePrevStep, + } = useMembersStep({billInfo, setBillInfo, currentMembers, setStep}); + + return ( + <> +
+ + + + + +
+ + + 참여 인원 + + {`총 ${billInfo.members.length}명`} + +
+ {billInfo.members.map(member => ( + setBillInfo(prev => ({...prev, members: prev.members.filter(name => name !== member)}))} + /> + ))} +
+
+
+ + 추가완료 + + + ); +}; + +export default MembersStep; diff --git a/client/src/pages/AddBillFunnel/steps/PriceStep.tsx b/client/src/pages/AddBillFunnel/steps/PriceStep.tsx new file mode 100644 index 000000000..260fb5b4a --- /dev/null +++ b/client/src/pages/AddBillFunnel/steps/PriceStep.tsx @@ -0,0 +1,62 @@ +import {css} from '@emotion/react'; +import {useNavigate} from 'react-router-dom'; + +import AmountInput from '@components/AmountInput/AmountInput'; +import NumberKeyboard from '@components/Design/components/NumberKeyboard/NumberKeyboard'; +import Top from '@components/Design/components/Top/Top'; + +import usePriceStep from '@hooks/usePriceStep'; +import {BillStep} from '@hooks/useAddBillFunnel'; + +import {FixedButton} from '@components/Design'; + +import {BillInfo} from '../AddBillFunnel'; + +interface Props { + billInfo: BillInfo; + setBillInfo: React.Dispatch>; + setStep: React.Dispatch>; +} + +const PriceStep = ({billInfo, setBillInfo, setStep}: Props) => { + const navigate = useNavigate(); + const {handleNumberKeyboardChange, handleNextStep} = usePriceStep({setBillInfo, setStep}); + + return ( + <> +
+ + + + +
+
+ +
+ navigate(-1)}> + 다음으로 + + + ); +}; + +export default PriceStep; diff --git a/client/src/pages/AddBillFunnel/steps/TitleStep.tsx b/client/src/pages/AddBillFunnel/steps/TitleStep.tsx new file mode 100644 index 000000000..eb910991f --- /dev/null +++ b/client/src/pages/AddBillFunnel/steps/TitleStep.tsx @@ -0,0 +1,63 @@ +import {css} from '@emotion/react'; + +import Top from '@components/Design/components/Top/Top'; + +import useTitleStep from '@hooks/useTitleStep'; +import {BillStep} from '@hooks/useAddBillFunnel'; + +import {FixedButton, LabelInput} from '@components/Design'; + +import {BillInfo} from '../AddBillFunnel'; + +interface Props { + billInfo: BillInfo; + setBillInfo: React.Dispatch>; + setStep: React.Dispatch>; +} + +export const TitleStep = ({billInfo, setBillInfo, setStep}: Props) => { + const { + errorMessage, + handleTitleInputChange, + handleTitleInputEnter, + canSubmitTitleInput, + handlePrevStep, + handleNextStep, + } = useTitleStep({ + billInfo, + setBillInfo, + setStep, + }); + + return ( + <> +
+ + + + + +
+ + 다음으로 + + + ); +}; diff --git a/client/src/pages/BillPage/AddBillFunnel.tsx b/client/src/pages/BillPage/AddBillFunnel.tsx deleted file mode 100644 index d8ff14845..000000000 --- a/client/src/pages/BillPage/AddBillFunnel.tsx +++ /dev/null @@ -1,220 +0,0 @@ -import {css} from '@emotion/react'; -import {useEffect, useState} from 'react'; -import {useNavigate} from 'react-router-dom'; - -import NumberKeyboard from '@components/Design/components/NumberKeyboard/NumberKeyboard'; -import useRequestGetCurrentMembers from '@hooks/queries/member/useRequestGetCurrentMembers'; -import Top from '@components/Design/components/Top/Top'; -import ChipButton from '@components/Design/components/ChipButton/ChipButton'; -import AmountInput from '@components/AmountInput/AmountInput'; - -import {Back, FixedButton, Flex, LabelInput, MainLayout, Text, TopNav} from '@components/Design'; - -type BillStep = 'title' | 'price' | 'members'; - -interface BillInfo { - price: string; - title: string; - members: string[]; -} - -const AddBillFunnel = () => { - const {currentMembers} = useRequestGetCurrentMembers(); - const [step, setStep] = useState('price'); - const [billInfo, setBillInfo] = useState({ - price: '', - title: '', - members: [], - }); - const [errorMessage, setErrorMessage] = useState(''); - const [nameInput, setNameInput] = useState(''); - const navigate = useNavigate(); - - useEffect(() => { - currentMembers && setBillInfo(prev => ({...prev, members: currentMembers.map(member => member.name)})); - }, [currentMembers]); - - const handleNumberKeyboardChange = (value: string) => { - setBillInfo(prev => ({...prev, price: value || prev.price})); - }; - - const handleTitleInputChange = (event: React.ChangeEvent) => { - setBillInfo(prev => ({...prev, title: event.target.value})); - }; - - const handleTitleInputEnter = (event: React.KeyboardEvent) => { - if (event.nativeEvent.isComposing) { - return; - } - if (event.key === 'Enter') { - event.preventDefault(); - setStep('members'); - } - }; - - const handleNameInputChange = (event: React.ChangeEvent) => setNameInput(event.target.value); - - const handleNameInputEnter = (event: React.KeyboardEvent) => { - if (event.nativeEvent.isComposing) { - return; - } - if (event.key === 'Enter') { - console.log(nameInput); - event.preventDefault(); - if (!billInfo.members.includes(nameInput)) { - setBillInfo(prev => ({...prev, members: [...prev.members, nameInput]})); - } - setNameInput(''); - } - }; - - const setStepPrice = () => { - setStep('price'); - }; - - const setStepTitle = () => { - setStep('title'); - }; - - const setStepMembers = () => { - setStep('members'); - }; - - const priceStep = () => ( - <> -
- - - - -
-
- -
- navigate(-1)}> - 다음으로 - - - ); - - const titleStep = () => ( - <> -
- - - - - -
- - 다음으로 - - - ); - - const membersStep = () => ( - <> -
- - - - - -
- - - 참여 인원 - - {`총 ${billInfo.members.length}명`} - -
- {billInfo.members.map(member => ( - setBillInfo(prev => ({...prev, members: prev.members.filter(name => name !== member)}))} - /> - ))} -
-
-
- - 추가완료 - - - ); - - return ( - - - - - {step === 'price' && priceStep()} - {step === 'title' && titleStep()} - {step === 'members' && membersStep()} - - ); -}; - -export default AddBillFunnel; diff --git a/client/src/router.tsx b/client/src/router.tsx index 01dc4d53e..0a1372829 100644 --- a/client/src/router.tsx +++ b/client/src/router.tsx @@ -1,11 +1,11 @@ import {createBrowserRouter} from 'react-router-dom'; +import AddBillFunnel from '@pages/AddBillFunnel/AddBillFunnel'; 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 Account from '@pages/AccountPage/Account'; -import AddBillFunnel from '@pages/BillPage/AddBillFunnel'; import {CompleteCreateEventPage, SetEventNamePage, SetEventPasswordPage} from '@pages/CreateEventPage'; import {MainPage} from '@pages/MainPage'; diff --git a/client/src/utils/validate/validatePurchase.ts b/client/src/utils/validate/validatePurchase.ts deleted file mode 100644 index 9915ce8b7..000000000 --- a/client/src/utils/validate/validatePurchase.ts +++ /dev/null @@ -1,47 +0,0 @@ -import type {Bill} from 'types/serviceType'; - -import {ERROR_MESSAGE} from '@constants/errorMessage'; -import RULE from '@constants/rule'; -import REGEXP from '@constants/regExp'; - -import {ValidateResult} from './type'; - -const validatePurchase = (inputPair: Bill): ValidateResult => { - const {title, price} = inputPair; - let errorMessage: string | null = null; - - const errorInfo = { - price: false, - title: false, - }; - - const validatePrice = () => { - if (price > RULE.maxPrice) { - errorMessage = ERROR_MESSAGE.purchasePrice; - errorInfo.price = true; - return false; - } - - errorInfo.price = false; - return true; - }; - - const validateTitle = () => { - if (!REGEXP.purchaseTitle.test(title)) { - errorMessage = ERROR_MESSAGE.purchaseTitle; - errorInfo.title = true; - return false; - } - - errorInfo.title = false; - return true; - }; - - if (validatePrice() && validateTitle()) { - return {isValid: true, errorMessage: null}; - } - - return {isValid: false, errorMessage, errorInfo}; -}; - -export default validatePurchase;