diff --git a/.yarn/cache/@types-daum-postcode-npm-2.0.3-f480b4f904-72283bd2c3.zip b/.yarn/cache/@types-daum-postcode-npm-2.0.3-f480b4f904-72283bd2c3.zip new file mode 100644 index 00000000..66715a55 Binary files /dev/null and b/.yarn/cache/@types-daum-postcode-npm-2.0.3-f480b4f904-72283bd2c3.zip differ diff --git a/package.json b/package.json index 280f4e70..18914cd6 100644 --- a/package.json +++ b/package.json @@ -46,6 +46,7 @@ "@testing-library/react": "^12.1.2", "@testing-library/react-hooks": "^7.0.2", "@testing-library/user-event": "^13.5.0", + "@types/daum-postcode": "^2.0.3", "@types/editorjs__header": "^2.6.0", "@types/jest": "^27.4.0", "@types/lodash-es": "^4.17.6", diff --git a/public/index.html b/public/index.html index 25433363..5b9493d7 100644 --- a/public/index.html +++ b/public/index.html @@ -40,6 +40,7 @@ /> +
diff --git a/src/components/Schedule/ScheduleTemplate/ScheduleTemplate.component.tsx b/src/components/Schedule/ScheduleTemplate/ScheduleTemplate.component.tsx index 45b7104c..e61862c2 100644 --- a/src/components/Schedule/ScheduleTemplate/ScheduleTemplate.component.tsx +++ b/src/components/Schedule/ScheduleTemplate/ScheduleTemplate.component.tsx @@ -1,7 +1,7 @@ import React, { useMemo } from 'react'; import { useFieldArray, useFormContext } from 'react-hook-form'; import { useRecoilValue } from 'recoil'; -import { DatePickerField, InputField, SelectField } from '@/components'; +import { Button, DatePickerField, InputField, RadioButtonField, SelectField } from '@/components'; import { InputSize } from '@/components/common/Input/Input.component'; import * as Styled from './ScheduleTemplate.styled'; import { $generations } from '@/store'; @@ -9,7 +9,7 @@ import { SelectOption } from '@/components/common/Select/Select.component'; import { SessionTemplate } from '../SessionTemplate'; import Plus from '@/assets/svg/plus-20.svg'; import { EventCreateRequest } from '@/types'; -import { ScheduleFormValues } from '@/utils'; +import { LocationType, ScheduleFormValues } from '@/utils'; const DEFAULT_SESSION: EventCreateRequest = { startedAt: '', @@ -19,9 +19,12 @@ const DEFAULT_SESSION: EventCreateRequest = { }; const ScheduleTemplate = () => { - const { register, control, formState, getValues } = useFormContext(); + const { register, control, formState, getValues, watch, setValue } = + useFormContext(); const generations = useRecoilValue($generations); + const locationType = watch('locationType'); + const { fields, append, remove } = useFieldArray({ name: 'sessions', control, @@ -37,6 +40,31 @@ const ScheduleTemplate = () => { const defaultOption = generationOptions.find( (option) => option.value === getValues('generationNumber')?.toString(), ); + const handleClickAddressSearch = () => { + new window.daum.Postcode({ + async oncomplete(data: { address: string }) { + const res = await fetch( + `https://dapi.kakao.com/v2/local/search/address?query=${data.address}`, + { + method: 'GET', + headers: { + 'Content-Type': 'application/json', + Authorization: 'KakaoAK cc4af66dc10aa1a20830f3cc62c40a87', + }, + }, + ); + const json = await res.json(); + const roadAddress = json.documents[0].road_address; + setValue('locationInfo', { + address: roadAddress.address_name, + latitude: roadAddress.y, + longitude: roadAddress.x, + placeName: roadAddress.building_name ?? roadAddress.address_name, + }); + setValue('placeName', roadAddress.building_name); + }, + }).open(); + }; return ( <> @@ -66,6 +94,38 @@ const ScheduleTemplate = () => { defaultDate={getValues('date')} {...register('date', { required: true })} /> +
+ + 장소 + + + + + + + {locationType === LocationType.OFFLINE && ( + + + + + )} +
세션 정보 diff --git a/src/components/Schedule/ScheduleTemplate/ScheduleTemplate.styled.ts b/src/components/Schedule/ScheduleTemplate/ScheduleTemplate.styled.ts index 85092199..4dd16bc6 100644 --- a/src/components/Schedule/ScheduleTemplate/ScheduleTemplate.styled.ts +++ b/src/components/Schedule/ScheduleTemplate/ScheduleTemplate.styled.ts @@ -6,7 +6,6 @@ export const ScheduleContent = styled.div` display: flex; flex-direction: column; gap: 2rem; - height: 36.9rem; padding: 2.4rem; background-color: ${theme.colors.white}; border: 0.1rem solid ${theme.colors.gray30}; @@ -43,3 +42,32 @@ export const AddButton = styled.button` margin-top: 2.4rem; background-color: transparent; `; + +export const InputLabel = styled.label` + ${({ theme }) => css` + ${theme.fonts.medium15} + display: flex; + margin-bottom: 0.6rem; + color: ${theme.colors.gray70}; + `} +`; + +export const RequiredDot = styled.span` + width: 0.6rem; + min-width: 0.6rem; + height: 0.6rem; + margin: 0.8rem 0 0 0.6rem; + background-color: #eb6963; + border-radius: 50%; +`; + +export const RadioButtonGroup = styled.div` + display: flex; + gap: 2rem; + margin-bottom: 1rem; +`; + +export const InputWithButton = styled.div` + display: flex; + gap: 1rem; +`; diff --git a/src/components/ScheduleDetail/ScheduleInfoList/ScheduleInfoList.component.tsx b/src/components/ScheduleDetail/ScheduleInfoList/ScheduleInfoList.component.tsx index b49e2aad..37dd9dd4 100644 --- a/src/components/ScheduleDetail/ScheduleInfoList/ScheduleInfoList.component.tsx +++ b/src/components/ScheduleDetail/ScheduleInfoList/ScheduleInfoList.component.tsx @@ -11,6 +11,10 @@ interface ScheduleInfoListProps { startedAt: string; publishedAt?: string; status: ValueOf; + location: { + address: string | null; + placeName: string; + }; } const ScheduleInfoList = ({ @@ -20,6 +24,7 @@ const ScheduleInfoList = ({ createdAt, publishedAt, status, + location, }: ScheduleInfoListProps) => { const scheduleInfoListItem = useMemo(() => { return [ @@ -39,6 +44,13 @@ const ScheduleInfoList = ({ label: '등록 일시', value: formatDate(createdAt, 'YYYY년 M월 D일 A hh시 mm분'), }, + { + label: '장소', + value: + location.address === null + ? location.placeName + : `${location.placeName}, ${location.address}`, + }, { label: '배포 일시', value: formatDate(publishedAt, 'YYYY년 M월 D일 A hh시 mm분'), @@ -48,7 +60,7 @@ const ScheduleInfoList = ({ value: getScheduleStatusText(status), }, ]; - }, [createdAt, generationNumber, name, publishedAt, startedAt, status]); + }, [createdAt, generationNumber, name, publishedAt, startedAt, status, location]); return ( diff --git a/src/components/common/RadioButton/RadioButton.styled.ts b/src/components/common/RadioButton/RadioButton.styled.ts index 1ea0ad75..ee44d722 100644 --- a/src/components/common/RadioButton/RadioButton.styled.ts +++ b/src/components/common/RadioButton/RadioButton.styled.ts @@ -67,6 +67,6 @@ export const RadioButtonMark = styled.span` export const RadioButtonText = styled.span` ${({ theme }) => css` ${theme.fonts.medium14} - padding-left: 3.8rem; + padding-left: 3rem; `} `; diff --git a/src/pages/ScheduleDetail/ScheduleDetail.page.tsx b/src/pages/ScheduleDetail/ScheduleDetail.page.tsx index e131dd3c..647cb07f 100644 --- a/src/pages/ScheduleDetail/ScheduleDetail.page.tsx +++ b/src/pages/ScheduleDetail/ScheduleDetail.page.tsx @@ -29,6 +29,7 @@ const ScheduleDetail = () => { publishedAt, status, eventList: sessionList, + location, } = useRecoilValue($scheduleDetail({ scheduleId: scheduleId ?? '' })); const isPublished = status === ScheduleStatus.PUBLIC; @@ -147,6 +148,7 @@ const ScheduleDetail = () => { createdAt={createdAt} publishedAt={publishedAt} status={status} + location={location} /> diff --git a/src/types/dto/schedule.ts b/src/types/dto/schedule.ts index 4fd79c8e..4bb70b4e 100644 --- a/src/types/dto/schedule.ts +++ b/src/types/dto/schedule.ts @@ -31,6 +31,10 @@ export interface ScheduleCreateRequest { name: string; startedAt: string; eventsCreateRequests: EventCreateRequest[]; + address?: string; + latitude?: number; + longitude?: number; + placeName?: string; } export interface ScheduleUpdateRequest { @@ -39,6 +43,10 @@ export interface ScheduleUpdateRequest { name: string; startedAt: string; eventsCreateRequests: EventCreateRequest[]; + address?: string; + latitude?: number; + longitude?: number; + placeName?: string; } export interface ScheduleResponse { @@ -51,6 +59,12 @@ export interface ScheduleResponse { publishedAt?: string; eventList: Session[]; status: ValueOf; + location: { + address: string | null; + latitude: number | null; + longitude: number | null; + placeName: string; + }; } export interface QRCodeRequest { diff --git a/src/utils/schedule.ts b/src/utils/schedule.ts index 399c45d0..caaa7a89 100644 --- a/src/utils/schedule.ts +++ b/src/utils/schedule.ts @@ -9,11 +9,24 @@ import { } from '@/types'; import { formatDate, toUtcFormat } from '.'; +export const LocationType = { + OFFLINE: 'offline', + ONLINE: 'online', +} as const; + export interface ScheduleFormValues { name: string; generationNumber: number; date: Dayjs; sessions: EventCreateRequest[]; + locationType: ValueOf; + placeName?: string; + locationInfo?: { + address: string; + latitude: string; + longitude: string; + placeName: string; + }; } export const getScheduleStatusText = (status: ValueOf) => { @@ -30,7 +43,7 @@ export const getScheduleStatusText = (status: ValueOf) => export const parseScheduleResponseToFormValues = ( response: ScheduleResponse, ): ScheduleFormValues => { - const { name, generationNumber, startedAt, eventList } = response; + const { name, generationNumber, startedAt, eventList, location } = response; const date: Dayjs = dayjs(startedAt, 'YYYY-MM-DD').startOf('day'); @@ -45,18 +58,22 @@ export const parseScheduleResponseToFormValues = ( })), })); + const isOffline = !!location.address; + return { name, generationNumber, date, sessions, + locationType: isOffline ? LocationType.OFFLINE : LocationType.ONLINE, + placeName: isOffline ? location.placeName : undefined, }; }; export const parseFormValuesToScheduleRequest = ( formValues: ScheduleFormValues, ): ScheduleCreateRequest | ScheduleUpdateRequest => { - const { generationNumber, date, sessions, name } = formValues; + const { generationNumber, date, sessions, name, locationType, locationInfo } = formValues; const formattedDate = date.format('YYYY-MM-DD'); @@ -81,5 +98,12 @@ export const parseFormValuesToScheduleRequest = ( eventsCreateRequests, }; + if (locationType === LocationType.OFFLINE && locationInfo) { + scheduleRequest.address = locationInfo.address; + scheduleRequest.latitude = Number(locationInfo.latitude); + scheduleRequest.longitude = Number(locationInfo.longitude); + scheduleRequest.placeName = locationInfo.placeName; + } + return scheduleRequest; }; diff --git a/yarn.lock b/yarn.lock index 289cee3c..9cdd74b6 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3921,6 +3921,13 @@ __metadata: languageName: node linkType: hard +"@types/daum-postcode@npm:^2.0.3": + version: 2.0.3 + resolution: "@types/daum-postcode@npm:2.0.3" + checksum: 72283bd2c3bb17073eb1324089e2bc5f5ff6df89c8731e1b9123620328cca0f7b77ac6480a06b5e35c0436c652782af376a896af5044416e2dfb809d040ee728 + languageName: node + linkType: hard + "@types/editorjs__header@npm:^2.6.0": version: 2.6.0 resolution: "@types/editorjs__header@npm:2.6.0" @@ -12951,6 +12958,7 @@ __metadata: "@testing-library/react": ^12.1.2 "@testing-library/react-hooks": ^7.0.2 "@testing-library/user-event": ^13.5.0 + "@types/daum-postcode": ^2.0.3 "@types/editorjs__header": ^2.6.0 "@types/jest": ^27.4.0 "@types/lodash-es": ^4.17.6