diff --git a/.pnp.cjs b/.pnp.cjs index 39d97a37..ceb3eb7e 100755 --- a/.pnp.cjs +++ b/.pnp.cjs @@ -8538,6 +8538,7 @@ const RAW_RUNTIME_STATE = ["lodash.debounce", "npm:4.0.8"],\ ["qrcode.react", "virtual:9845906954fdbefbb879db24fa8772d77a945dca59f459806df47a5b67245d4bc6502880b373cca7201062c81bea9f13f699f52de2004c037e79dbdbd5d97fb3#npm:3.1.0"],\ ["react", "npm:18.2.0"],\ + ["react-daum-postcode", "virtual:9845906954fdbefbb879db24fa8772d77a945dca59f459806df47a5b67245d4bc6502880b373cca7201062c81bea9f13f699f52de2004c037e79dbdbd5d97fb3#npm:3.1.3"],\ ["react-dom", "virtual:de80dc576383b2386358abc0e9fe49c00e3397fe355a0337462b73ab3115c2e557eb85784ee0fe776394cc11dd020b4e84dbbd75acf72ee6d54415d82d21f5c5#npm:18.2.0"],\ ["react-dropzone", "virtual:9845906954fdbefbb879db24fa8772d77a945dca59f459806df47a5b67245d4bc6502880b373cca7201062c81bea9f13f699f52de2004c037e79dbdbd5d97fb3#npm:14.2.3"],\ ["react-hook-form", "virtual:9845906954fdbefbb879db24fa8772d77a945dca59f459806df47a5b67245d4bc6502880b373cca7201062c81bea9f13f699f52de2004c037e79dbdbd5d97fb3#npm:7.50.0"],\ @@ -16073,6 +16074,28 @@ const RAW_RUNTIME_STATE = "linkType": "HARD"\ }]\ ]],\ + ["react-daum-postcode", [\ + ["npm:3.1.3", {\ + "packageLocation": "../../.yarn/berry/cache/react-daum-postcode-npm-3.1.3-95dbfacabd-10c0.zip/node_modules/react-daum-postcode/",\ + "packageDependencies": [\ + ["react-daum-postcode", "npm:3.1.3"]\ + ],\ + "linkType": "SOFT"\ + }],\ + ["virtual:9845906954fdbefbb879db24fa8772d77a945dca59f459806df47a5b67245d4bc6502880b373cca7201062c81bea9f13f699f52de2004c037e79dbdbd5d97fb3#npm:3.1.3", {\ + "packageLocation": "./.yarn/__virtual__/react-daum-postcode-virtual-a70527b50a/3/.yarn/berry/cache/react-daum-postcode-npm-3.1.3-95dbfacabd-10c0.zip/node_modules/react-daum-postcode/",\ + "packageDependencies": [\ + ["react-daum-postcode", "virtual:9845906954fdbefbb879db24fa8772d77a945dca59f459806df47a5b67245d4bc6502880b373cca7201062c81bea9f13f699f52de2004c037e79dbdbd5d97fb3#npm:3.1.3"],\ + ["@types/react", "npm:18.2.48"],\ + ["react", "npm:18.2.0"]\ + ],\ + "packagePeers": [\ + "@types/react",\ + "react"\ + ],\ + "linkType": "HARD"\ + }]\ + ]],\ ["react-docgen", [\ ["npm:7.0.3", {\ "packageLocation": "../../.yarn/berry/cache/react-docgen-npm-7.0.3-ea0f679a0f-10c0.zip/node_modules/react-docgen/",\ diff --git a/apps/admin/package.json b/apps/admin/package.json index 598b902c..8c5a9c43 100644 --- a/apps/admin/package.json +++ b/apps/admin/package.json @@ -27,6 +27,7 @@ "lodash.debounce": "^4.0.8", "qrcode.react": "^3.1.0", "react": "^18.2.0", + "react-daum-postcode": "^3.1.3", "react-dom": "^18.2.0", "react-dropzone": "^14.2.3", "react-hook-form": "^7.50.0", diff --git a/apps/admin/src/components/ShowInfoFormContent/ShowBasicInfoFormContent.tsx b/apps/admin/src/components/ShowInfoFormContent/ShowBasicInfoFormContent.tsx index bf129806..fcea10c6 100644 --- a/apps/admin/src/components/ShowInfoFormContent/ShowBasicInfoFormContent.tsx +++ b/apps/admin/src/components/ShowInfoFormContent/ShowBasicInfoFormContent.tsx @@ -1,20 +1,22 @@ import { ImageFile } from '@boolti/api'; import { CloseIcon, FileUpIcon } from '@boolti/icon'; -import { TextField, TimePicker } from '@boolti/ui'; +import { Button, TextField, TimePicker, useDialog } from '@boolti/ui'; import { add, format } from 'date-fns'; -import { useState } from 'react'; +import { useRef, useState } from 'react'; import { useDropzone } from 'react-dropzone'; import { Controller, UseFormReturn } from 'react-hook-form'; +import DaumPostcode from 'react-daum-postcode'; import Styled from './ShowInfoFormContent.styles'; import { ShowInfoFormInputs } from './types'; +import { useBodyScrollLock } from '~/hooks/useBodyScrollLock'; const MAX_IMAGE_COUNT = 3; type ShowBasicInfoFormInputs = Omit; interface ShowBasicInfoFormContentProps { - form: UseFormReturn; + form: UseFormReturn; imageFiles: ImageFile[]; disabled?: boolean; onDropImage: (acceptedFiles: File[]) => void; @@ -28,7 +30,10 @@ const ShowBasicInfoFormContent = ({ onDropImage, onDeleteImage, }: ShowBasicInfoFormContentProps) => { - const { watch, control } = form; + const { open, close, isOpen } = useDialog(); + const detailAdressInputRef = useRef(null); + + const { watch, control, setValue } = form; const { getRootProps, getInputProps } = useDropzone({ accept: { @@ -48,6 +53,29 @@ const ShowBasicInfoFormContent = ({ placeDetailAddress: false, }); + const openDaumPostCodeWithDialog: React.MouseEventHandler = (e) => { + e.preventDefault(); + open({ + title: '주소 찾기', + content: ( + { + setValue('placeStreetAddress', address.roadAddress); + detailAdressInputRef.current?.focus(); + }} + onClose={() => { + setHasBlurred((prev) => ({ ...prev, placeStreetAddress: true })); + close(); + }} + /> + ), + onClose: close, + }); + }; + + useBodyScrollLock(isOpen); + return ( 기본 정보 @@ -220,14 +248,14 @@ const ShowBasicInfoFormContent = ({ - 공연 장소 + 공연장명 ( + render={({ field: { value, onChange, onBlur } }) => ( - - - ( + + + + + 공연장 주소 + + ( + <> { - onBlur(); - setHasBlurred((prev) => ({ ...prev, placeStreetAddress: true })); - }} + disabled value={value ?? ''} errorMessage={ hasBlurred.placeStreetAddress && !value ? '필수 입력사항입니다.' : undefined } /> - )} - name="placeStreetAddress" - /> - - - ( - { - onBlur(); - setHasBlurred((prev) => ({ ...prev, placeDetailAddress: true })); - }} - value={value ?? ''} - errorMessage={ - hasBlurred.placeDetailAddress && !value ? '필수 입력사항입니다.' : undefined - } - /> - )} - name="placeDetailAddress" - /> - - + + + )} + name="placeStreetAddress" + /> + + + ( + { + onBlur(); + setHasBlurred((prev) => ({ ...prev, placeDetailAddress: true })); + }} + value={value ?? ''} + errorMessage={ + hasBlurred.placeDetailAddress && !value ? '필수 입력사항입니다.' : undefined + } + /> + )} + name="placeDetailAddress" + /> + diff --git a/apps/admin/src/components/ShowInfoFormContent/ShowInfoFormContent.styles.ts b/apps/admin/src/components/ShowInfoFormContent/ShowInfoFormContent.styles.ts index 85b561f8..79be6da0 100644 --- a/apps/admin/src/components/ShowInfoFormContent/ShowInfoFormContent.styles.ts +++ b/apps/admin/src/components/ShowInfoFormContent/ShowInfoFormContent.styles.ts @@ -223,7 +223,7 @@ const FileUploadAreaText = styled.span` const TextField = styled.div` margin-top: 8px; display: flex; - align-items: center; + align-items: start; gap: 8px; flex: ${({ flex }) => flex}; @@ -239,16 +239,6 @@ const TextFieldSuffix = styled.span` flex: 0; `; -const TextFieldRow = styled.div` - display: flex; - flex-direction: column; - - ${mq_lg} { - flex-direction: row; - gap: 8px; - } -`; - const TextAreaContainer = styled.div` position: relative; display: flex; @@ -541,7 +531,6 @@ export default { FileUploadAreaText, TextField, TextFieldSuffix, - TextFieldRow, TextAreaContainer, TextArea, TextAreaErrorMessage, diff --git a/apps/admin/src/components/TicketForm/SalesTicketForm.tsx b/apps/admin/src/components/TicketForm/SalesTicketForm.tsx index 491301c4..d4b8f1fc 100644 --- a/apps/admin/src/components/TicketForm/SalesTicketForm.tsx +++ b/apps/admin/src/components/TicketForm/SalesTicketForm.tsx @@ -28,11 +28,27 @@ const SalesTicketForm = ({ onSubmit }: SalesTicketFormProps) => { totalForSale: false, }); + const validatePrice = (price: string) => { + const parsedPrice = Number(price); + return parsedPrice >= 200 || parsedPrice === 0; + }; + + const handlePriceErrorMessage = (hasBlurred: boolean, price: string) => { + if (hasBlurred && (!price || !validatePrice(price))) { + return '0원 또는 200원 이상을 입력해 주세요.'; + } + return ''; + }; + return ( - + 만들고 싶은 티켓 정보를 입력해 주세요. - + + * 퀵계좌이체 지원을 위해 유료 티켓은 200원 이상 입력이 필요합니다.{'\n'}* 무료 티켓 생성을 + 원하시면 0원을 입력해 주세요. + + 티켓 이름 @@ -65,15 +81,23 @@ const SalesTicketForm = ({ onSubmit }: SalesTicketFormProps) => { placeholder="0" min={0} {...register('price', { required: true })} + onChange={(event) => { + register('price', { + required: true, + validate: validatePrice, + }).onChange(event); + }} onBlur={(event) => { register('price', { required: true }).onBlur(event); setHasBlurred((prev) => ({ ...prev, price: true })); }} - errorMessage={hasBlurred.price && !getValues('price') ? '필수 입력사항입니다.' : ''} + errorMessage={handlePriceErrorMessage(hasBlurred.price, getValues('price'))} /> + + 수량 @@ -94,6 +118,7 @@ const SalesTicketForm = ({ onSubmit }: SalesTicketFormProps) => { +