From ea6190e408694c63fbf2b533ccbb9e4f6674c869 Mon Sep 17 00:00:00 2001 From: Shim MunSeong Date: Sat, 26 Oct 2024 19:08:52 +0900 Subject: [PATCH 1/3] =?UTF-8?q?feat:=20=EA=B3=B5=EC=97=B0=20=EA=B8=B0?= =?UTF-8?q?=EB=B3=B8=20=EC=A0=95=EB=B3=B4=20=EC=88=98=EC=A0=95=EC=82=AC?= =?UTF-8?q?=ED=95=AD=20=EB=B0=98=EC=98=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 공연을 등록할 때, 모바일 버전에서 출연진 정보 UI가 나타나지 않았던 문제 수정 --- .../ShowBasicInfoFormContent.tsx | 6 +- .../ShowCastInfoFormContent.tsx | 9 +-- .../ShowDetailInfoFormContent.tsx | 78 ++++++++++++------- .../ShowInfoFormContent.styles.ts | 24 +++++- .../ShowTicketInfoFormContent.tsx | 6 +- apps/admin/src/pages/ShowAddPage/index.tsx | 32 +++++++- apps/admin/src/pages/ShowInfoPage/index.tsx | 1 - 7 files changed, 107 insertions(+), 49 deletions(-) diff --git a/apps/admin/src/components/ShowInfoFormContent/ShowBasicInfoFormContent.tsx b/apps/admin/src/components/ShowInfoFormContent/ShowBasicInfoFormContent.tsx index fea5be56..fc46cb2e 100644 --- a/apps/admin/src/components/ShowInfoFormContent/ShowBasicInfoFormContent.tsx +++ b/apps/admin/src/components/ShowInfoFormContent/ShowBasicInfoFormContent.tsx @@ -87,8 +87,8 @@ const ShowBasicInfoFormContent = ({ 공연 포스터 - 원하시는 노출 순서대로 이미지를 업로드해주세요. (최소 1장, 최대{' '} - {MAX_IMAGE_COUNT}장 업로드 가능 / jpg, png 형식) + 원하시는 노출 순서대로 이미지를 업로드해주세요. 표준 종이규격(A, B)의 이미지를 권장합니다.
+ (최소 1장, 최대 {MAX_IMAGE_COUNT}장 업로드 가능 / jpg, png 형식)
{imageFiles.map((file, index) => ( @@ -190,7 +190,7 @@ const ShowBasicInfoFormContent = ({ - 공연 시작 시간 + 시작 시간 Promise; } -const ShowCastInfoFormContent = ({ hasPreviousCastInfo = false, onSave }: Props) => { +const ShowCastInfoFormContent = ({ onSave }: Props) => { const dialog = useDialog(); const onClick = () => { @@ -46,12 +45,6 @@ const ShowCastInfoFormContent = ({ hasPreviousCastInfo = false, onSave }: Props) 출연진 정보를 팀 단위로 등록해 주세요. - {!hasPreviousCastInfo && ( - <> -
- 정보는 공연 등록 이후에도 수정 및 추가할 수 있어요. - - )}
; - interface ShowDetailInfoFormContentProps { form: UseFormReturn; disabled?: boolean; } -const ShowDetailInfoFormContent = ({ form, disabled }: ShowDetailInfoFormContentProps) => { - const { control } = form; +const phoneNumberRegExp = /^\d{3}-\d{3,4}-\d{4}$/ - const [hasBlurred, setHasBlurred] = useState>({ - notice: false, - hostName: false, - hostPhoneNumber: false, - }); +const ShowDetailInfoFormContent = ({ form, disabled }: ShowDetailInfoFormContentProps) => { + const { control, formState: { errors }, setError, clearErrors } = form; return ( @@ -45,16 +38,22 @@ const ShowDetailInfoFormContent = ({ form, disabled }: ShowDetailInfoFormContent placeholder="(ex. 공연 참가팀, 팀소개, 공연곡 소개 등)" rows={10} disabled={disabled} - onChange={onChange} + onChange={(event) => { + onChange(event); + clearErrors('notice'); + }} onBlur={() => { onBlur(); - setHasBlurred((prev) => ({ ...prev, notice: true })); + + if (!value) { + setError('notice', { type: 'required', message: '필수 입력사항입니다.' }); + } }} value={value ?? ''} - hasError={hasBlurred.notice && !value} + hasError={!!errors.notice?.message} /> - {hasBlurred.notice && !value && ( - 필수 입력사항입니다. + {errors.notice?.message && ( + {errors.notice.message} )} )} @@ -62,37 +61,45 @@ const ShowDetailInfoFormContent = ({ form, disabled }: ShowDetailInfoFormContent />
- - - 대표자 이름 + + + 대표자명 { + if (!fieldValue) return '필수 입력사항입니다.' + + return true + } }} render={({ field: { onChange, onBlur, value } }) => ( { + onChange(event); + clearErrors('hostName') + }} onBlur={() => { onBlur(); - setHasBlurred((prev) => ({ ...prev, hostName: true })); + if (!value) { + setError('hostName', { type: 'required', message: '필수 입력사항입니다.' }); + } }} value={value ?? ''} - errorMessage={hasBlurred.hostName && !value ? '필수 입력사항입니다.' : undefined} + errorMessage={errors.hostName?.message} /> )} name="hostName" /> - - 대표자 연락처 @@ -100,6 +107,7 @@ const ShowDetailInfoFormContent = ({ form, disabled }: ShowDetailInfoFormContent control={control} rules={{ required: true, + pattern: phoneNumberRegExp, }} render={({ field: { onChange, onBlur, value } }) => ( { + onChange(event); + clearErrors('hostPhoneNumber') + }} onBlur={() => { onBlur(); - setHasBlurred((prev) => ({ ...prev, hostPhoneNumber: true })); + + if (!value) { + setError('hostPhoneNumber', { type: 'required', message: '필수 입력사항입니다.' }); + return + } + + if (!phoneNumberRegExp.test(value)) { + setError('hostPhoneNumber', { type: 'pattern', message: '유효한 전화번호 형식이 아닙니다.' }); + return + } }} value={value ?? ''} - errorMessage={ - hasBlurred.hostPhoneNumber && !value ? '필수 입력사항입니다.' : undefined - } + errorMessage={errors.hostPhoneNumber?.message} /> )} name="hostPhoneNumber" /> - + ); }; diff --git a/apps/admin/src/components/ShowInfoFormContent/ShowInfoFormContent.styles.ts b/apps/admin/src/components/ShowInfoFormContent/ShowInfoFormContent.styles.ts index da0a2fe6..49b4b619 100644 --- a/apps/admin/src/components/ShowInfoFormContent/ShowInfoFormContent.styles.ts +++ b/apps/admin/src/components/ShowInfoFormContent/ShowInfoFormContent.styles.ts @@ -75,6 +75,22 @@ const ShowInfoFormRow = styled.div` } `; +const ShowInfoFormResponsiveRowColumn = styled.div` + display: flex; + flex-direction: column; + gap: 28px; + margin-bottom: 28px; + + ${mq_lg} { + flex-direction: row; + gap: 12px; + } + + &:last-of-type { + margin-bottom: 0; + } +` + const ShowInfoFormContent = styled.div` flex: 1; `; @@ -108,7 +124,7 @@ const ShowInfoFormButtonContainer = styled.div` gap: 8px; `; -const ShowInfoFormButton = styled(Button)` +const ShowInfoFormButton = styled(Button) ` width: ${({ width }) => width}; `; @@ -275,7 +291,7 @@ const TextArea = styled.textarea` padding: 12px; border: 1px solid ${({ theme, hasError }) => - hasError ? `${theme.palette.status.error} !important` : theme.palette.grey.g20}; + hasError ? `${theme.palette.status.error} !important` : theme.palette.grey.g20}; border-radius: 4px; background-color: ${({ theme }) => theme.palette.grey.w}; color: ${({ theme }) => theme.palette.grey.g90}; @@ -527,7 +543,7 @@ const MobileTicketAction = styled.div` width: 24px; height: 24px; stroke: ${({ theme, disabled }) => - disabled ? theme.palette.grey.g40 : theme.palette.grey.g90}; + disabled ? theme.palette.grey.g40 : theme.palette.grey.g90}; } } } @@ -541,6 +557,7 @@ const MobileCastInfoRegisterButton = styled.button` display: inline-flex; justify-content: center; align-items: center; + gap: 4px; ${({ theme }) => theme.typo.sh1}; color: ${({ theme }) => theme.palette.grey.g90}; cursor: pointer; @@ -564,6 +581,7 @@ export default { ShowInfoFormTitle, ShowInfoFormSubtitle, ShowInfoFormRow, + ShowInfoFormResponsiveRowColumn, ShowInfoFormContent, ShowInfoFormLabel, ShowInfoFormDescription, diff --git a/apps/admin/src/components/ShowInfoFormContent/ShowTicketInfoFormContent.tsx b/apps/admin/src/components/ShowInfoFormContent/ShowTicketInfoFormContent.tsx index 1e54673c..4f252592 100644 --- a/apps/admin/src/components/ShowInfoFormContent/ShowTicketInfoFormContent.tsx +++ b/apps/admin/src/components/ShowInfoFormContent/ShowTicketInfoFormContent.tsx @@ -32,7 +32,9 @@ const ShowTicketInfoFormContent = ({ return ( - 티켓 판매 정보 + + 티켓 판매 정보 + @@ -90,7 +92,7 @@ const ShowTicketInfoFormContent = ({ placeholder={value} min={format( watch('startDate') || - (salesStartTime ? new Date(salesStartTime) : new Date()), + (salesStartTime ? new Date(salesStartTime) : new Date()), 'yyyy-MM-dd', )} max={format( diff --git a/apps/admin/src/pages/ShowAddPage/index.tsx b/apps/admin/src/pages/ShowAddPage/index.tsx index 3bf48dfc..aff858c3 100644 --- a/apps/admin/src/pages/ShowAddPage/index.tsx +++ b/apps/admin/src/pages/ShowAddPage/index.tsx @@ -138,7 +138,7 @@ const ShowAddPage = ({ step }: ShowAddPageProps) => { 등록하려는 공연의 정보를 입력해 주세요.
- 공연 정보는 공연일 하루 전까지 수정할 수 있어요. + 입력한 정보는 등록 후에도 수정할 수 있어요.
@@ -348,7 +348,7 @@ const ShowAddPage = ({ step }: ShowAddPageProps) => { 등록하려는 공연의 정보를 입력해 주세요.
- 공연 정보는 공연일 하루 전까지 수정할 수 있어요. + 입력한 정보는 등록 후에도 수정할 수 있어요.
@@ -374,6 +374,34 @@ const ShowAddPage = ({ step }: ShowAddPageProps) => { + + { + setShowCastInfo((prev) => [...prev, showCastInfoFormInput]); + return new Promise((reslve) => reslve()); + }} + /> + {showCastInfo.map((info, index) => ( + { + setShowCastInfo((prev) => + prev.map((prevCastInfo, currentIndex) => + index === currentIndex ? showCastInfoFormInput : prevCastInfo, + ), + ); + return new Promise((reslve) => reslve()); + }} + onDelete={() => { + setShowCastInfo((prev) => + prev.filter((_, currentIndex) => index !== currentIndex), + ); + return new Promise((reslve) => reslve()); + }} + /> + ))} +