diff --git a/index.html b/index.html index 71e96c37..5378c7ca 100644 --- a/index.html +++ b/index.html @@ -3,6 +3,8 @@ + + + + diff --git a/package.json b/package.json index c64276fa..f6bd5901 100644 --- a/package.json +++ b/package.json @@ -41,6 +41,7 @@ "axios": "^1.7.2", "dayjs": "^1.11.11", "jotai": "^2.8.4", + "lodash": "^4.17.21", "lottie-react": "^2.4.0", "postcss": "^8.4.38", "postcss-scss": "^4.0.9", @@ -48,13 +49,16 @@ "puppeteer-core": "^22.15.0", "react": "^18.3.1", "react-beautiful-dnd": "^13.1.1", + "react-calendar": "^5.1.0", "react-components": "^0.5.1", "react-csv": "^2.2.2", "react-dom": "^18.3.1", "react-helmet-async": "^2.0.5", "react-image-crop": "^11.0.6", + "react-kakao-maps-sdk": "^1.1.27", "react-lottie-player": "^2.0.0", "react-router-dom": "^6.24.0", + "react-time-picker": "^7.0.0", "shelljs": "^0.8.5", "styled-components": "^6.1.11", "styled-reset": "^4.5.2", @@ -77,6 +81,7 @@ "@storybook/react": "^8.1.11", "@storybook/react-vite": "^8.1.11", "@storybook/test": "^8.1.11", + "@types/lodash": "^4", "@types/prettier-linter-helpers": "^1", "@types/qs": "^6", "@types/react": "^18.3.3", diff --git a/public/svgs/beat_map_marker.svg b/public/svgs/beat_map_marker.svg new file mode 100644 index 00000000..43503bf2 --- /dev/null +++ b/public/svgs/beat_map_marker.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/public/svgs/icon_search.svg b/public/svgs/icon_search.svg new file mode 100644 index 00000000..d708cde4 --- /dev/null +++ b/public/svgs/icon_search.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/public/svgs/kakao_map_arrow.svg b/public/svgs/kakao_map_arrow.svg new file mode 100644 index 00000000..cde2a24b --- /dev/null +++ b/public/svgs/kakao_map_arrow.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/src/apis/domains/admins/api.ts b/src/apis/domains/admins/api.ts index 9e6a1c24..aed2823a 100644 --- a/src/apis/domains/admins/api.ts +++ b/src/apis/domains/admins/api.ts @@ -1,7 +1,7 @@ import { put } from "@apis/index"; import { components } from "@typings/api/schema"; -export type SuccessResponse = components["schemas"]["SuccessResponse"]; +export type SuccessResponse = components["schemas"]["SuccessResponseVoid"]; // 캐러셀 수정 API (PUT) export const updateCarousel = async (formData): Promise => { diff --git a/src/apis/domains/home/api.ts b/src/apis/domains/home/api.ts index 7d9be603..1b191fbf 100644 --- a/src/apis/domains/home/api.ts +++ b/src/apis/domains/home/api.ts @@ -3,7 +3,7 @@ import { components } from "@typings/api/schema"; import { ApiResponseType } from "@typings/commonType"; import { AxiosResponse } from "axios"; -type HomeResponse = components["schemas"]["HomeResponse"]; +type HomeResponse = components["schemas"]["HomeFindAllResponse"]; // 1. API 요청 함수 작성 및 타입 추가 export const getAllScheduleList = async (): Promise => { diff --git a/src/assets/svgs/BeatMapMarker.tsx b/src/assets/svgs/BeatMapMarker.tsx new file mode 100644 index 00000000..e4460fd1 --- /dev/null +++ b/src/assets/svgs/BeatMapMarker.tsx @@ -0,0 +1,12 @@ +import * as React from "react"; +import type { SVGProps } from "react"; +const SvgBeatMapMarker = (props: SVGProps) => ( + + + + +); +export default SvgBeatMapMarker; diff --git a/src/assets/svgs/IconSearch.tsx b/src/assets/svgs/IconSearch.tsx new file mode 100644 index 00000000..9ee48803 --- /dev/null +++ b/src/assets/svgs/IconSearch.tsx @@ -0,0 +1,13 @@ +import * as React from "react"; +import type { SVGProps } from "react"; +const SvgIconSearch = (props: SVGProps) => ( + + + +); +export default SvgIconSearch; diff --git a/src/assets/svgs/KakaoMapArrow.tsx b/src/assets/svgs/KakaoMapArrow.tsx new file mode 100644 index 00000000..ed3ea9a4 --- /dev/null +++ b/src/assets/svgs/KakaoMapArrow.tsx @@ -0,0 +1,12 @@ +import * as React from "react"; +import type { SVGProps } from "react"; +const SvgKakaoMapArrow = (props: SVGProps) => ( + + + + +); +export default SvgKakaoMapArrow; diff --git a/src/assets/svgs/index.ts b/src/assets/svgs/index.ts new file mode 100644 index 00000000..d986239c --- /dev/null +++ b/src/assets/svgs/index.ts @@ -0,0 +1,68 @@ +export { default as BannerBasic } from "./BannerBasic"; +export { default as BeatMapMarker } from "./BeatMapMarker"; +export { default as BtnFloating } from "./BtnFloating"; +export { default as BtnModalDelete } from "./BtnModalDelete"; +export { default as ButtonDelete24 } from "./ButtonDelete24"; +export { default as CarouselPartInactive } from "./CarouselPartInactive"; +export { default as Empty } from "./Empty"; +export { default as IcomCopy } from "./IcomCopy"; +export { default as IconArrowLeft } from "./IconArrowLeft"; +export { default as IconArrowRight } from "./IconArrowRight"; +export { default as IconArrowDown } from "./IconArrowDown"; +export { default as IconArrowUp } from "./IconArrowUp"; +export { default as IconBnk } from "./IconBnk"; +export { default as IconCalendar } from "./IconCalendar"; +export { default as IconCamera } from "./IconCamera"; +export { default as IconCheckboxDisabledOn } from "./IconCheckboxDisabledOn"; +export { default as IconCheckboxSelectedOn } from "./IconCheckboxSelectedOn"; +export { default as IconCheckboxUnselectedOn } from "./IconCheckboxUnselectedOn"; +export { default as IconChecked } from "./IconChecked"; +export { default as IconCheck } from "./IconCheck"; +export { default as IconChevronBack } from "./IconChevronBack"; +export { default as IconEmpty } from "./IconEmpty"; +export { default as IconEyeOff } from "./IconEyeOff"; +export { default as IconEyeOn } from "./IconEyeOn"; +export { default as IconFooterLogo } from "./IconFooterLogo"; +export { default as IconHanna } from "./IconHanna"; +export { default as IconIbk } from "./IconIbk"; +export { default as IconIm } from "./IconIm"; +export { default as IconImg } from "./IconImg"; +export { default as IconKabank } from "./IconKabank"; +export { default as IconKb } from "./IconKb"; +export { default as IconLargeBand } from "./IconLargeBand"; +export { default as IconLargeDance } from "./IconLargeDance"; +export { default as IconLargeEtc } from "./IconLargeEtc"; +export { default as IconLargeMusical } from "./IconLargeMusical"; +export { default as IconLogo } from "./IconLogo"; +export { default as IconMinus } from "./IconMinus"; +export { default as IconNonghyup } from "./IconNonghyup"; +export { default as IconPhotoDelete } from "./IconPhotoDelete"; +export { default as IconPlus } from "./IconPlus"; +export { default as IconProfile } from "./IconProfile"; +export { default as IconRoleAdd } from "./IconRoleAdd"; +export { default as IconSaemauel } from "./IconSaemauel"; +export { default as IconSc } from "./IconSc"; +export { default as IconSearch } from "./IconSearch"; +export { default as IconShinhan } from "./IconShinhan"; +export { default as IconShinhyup } from "./IconShinhyup"; +export { default as IconSmallBand } from "./IconSmallBand"; +export { default as IconSmallDance } from "./IconSmallDance"; +export { default as IconSmallEtc } from "./IconSmallEtc"; +export { default as IconSmallMusical } from "./IconSmallMusical"; +export { default as IconSoohyup } from "./IconSoohyup"; +export { default as IconTextfiedlDelete } from "./IconTextfiedlDelete"; +export { default as IconTime } from "./IconTime"; +export { default as IconToggleOff } from "./IconToggleOff"; +export { default as IconToggleOn } from "./IconToggleOn"; +export { default as IconToss } from "./IconToss"; +export { default as IconWoochaegook } from "./IconWoochaegook"; +export { default as IconWoori } from "./IconWoori"; +export { default as IconXButton } from "./IconXButton"; +export { default as IcDelete } from "./IcDelete"; +export { default as IcHamburgar } from "./IcHamburgar"; +export { default as IcOutlinePlace } from "./IcOutlinePlace"; +export { default as KakaoMapArrow } from "./KakaoMapArrow"; +export { default as NotFoundAsset } from "./NotFoundAsset"; +export { default as Subtract } from "./Subtract"; +export { default as Switch } from "./Switch"; +export { default as Union } from "./Union"; diff --git a/src/assets/svgs/index.tsx b/src/assets/svgs/index.tsx index c29ceb05..b68fe76d 100644 --- a/src/assets/svgs/index.tsx +++ b/src/assets/svgs/index.tsx @@ -1,5 +1,6 @@ export { default as BannerBasic } from "./BannerBasic"; export { default as BtnFilter } from "./BtnFilter"; +export { default as BeatMapMarker } from "./BeatMapMarker"; export { default as BtnFloating } from "./BtnFloating"; export { default as BtnModalDelete } from "./BtnModalDelete"; export { default as ButtonDelete24 } from "./ButtonDelete24"; @@ -44,6 +45,7 @@ export { default as IconProfile } from "./IconProfile"; export { default as IconRoleAdd } from "./IconRoleAdd"; export { default as IconSaemauel } from "./IconSaemauel"; export { default as IconSc } from "./IconSc"; +export { default as IconSearch } from "./IconSearch"; export { default as IconShinhan } from "./IconShinhan"; export { default as IconShinhyup } from "./IconShinhyup"; export { default as IconSmallBand } from "./IconSmallBand"; @@ -61,6 +63,7 @@ export { default as IconWoori } from "./IconWoori"; export { default as IconXButton } from "./IconXButton"; export { default as IcOutlinePlace } from "./IcOutlinePlace"; export { default as IcRefresh } from "./IcRefresh"; +export { default as KakaoMapArrow } from "./KakaoMapArrow"; export { default as NotFoundAsset } from "./NotFoundAsset"; export { default as SelectionControlCheckboxSelectedOff } from "./SelectionControlCheckboxSelectedOff"; export { default as Subtract } from "./Subtract"; diff --git a/src/components/commons/datePicker/DatePicker.tsx b/src/components/commons/datePicker/DatePicker.tsx new file mode 100644 index 00000000..36d58c25 --- /dev/null +++ b/src/components/commons/datePicker/DatePicker.tsx @@ -0,0 +1,30 @@ +import Calendar from "react-calendar"; +import "react-calendar/dist/Calendar.css"; +import "./calendar.css"; +interface DatePickerProps { + date: Date | undefined; + onChangeDate: (date: Date) => void; +} + +const DatePicker = ({ date, onChangeDate }: DatePickerProps) => { + const isPastDate = (date) => { + const today = new Date(); + today.setHours(0, 0, 0, 0); + + return date < today; + }; + + return ( + date.getDate().toString()} + tileClassName={({ date }) => (isPastDate(date) ? "past-date" : "")} + /> + ); +}; + +export default DatePicker; diff --git a/src/components/commons/datePicker/calendar.css b/src/components/commons/datePicker/calendar.css new file mode 100644 index 00000000..30a08508 --- /dev/null +++ b/src/components/commons/datePicker/calendar.css @@ -0,0 +1,120 @@ +.custom-calendar { + background-color: #2a2a2a; + border: none; +} + +/* navigation 관련 */ +.custom-calendar .react-calendar__navigation { + height: 24px; + margin-bottom: 14px; + + .react-calendar__navigation__label__labelText.react-calendar__navigation__label__labelText--from { + color: #fff; + font-weight: 500; + font-size: 14px; + font-family: Pretendard, sans-serif; + line-height: 20px; + } + + /* label 클릭 할 수 없도록 */ + .react-calendar__navigation__label { + pointer-events: none; + } +} + +.custom-calendar .react-calendar__navigation__prev-button, +.custom-calendar .react-calendar__navigation__next-button { + padding-bottom: 2px; + + color: #fff; + + transform: scale(1.1); +} + +.custom-calendar .react-calendar__navigation__prev2-button, +.custom-calendar .react-calendar__navigation__next2-button { + visibility: hidden; +} + +.custom-calendar .react-calendar__navigation__prev-button:hover, +.custom-calendar .react-calendar__navigation__next-button:hover, +.custom-calendar .react-calendar__navigation__prev-button:focus, +.custom-calendar .react-calendar__navigation__next-button:focus { + color: #fff; + + background-color: #2a2a2a !important; +} + +/* month-view 관련 */ +.custom-calendar .react-calendar__month-view__weekdays { + display: grid !important; + grid-template-columns: repeat(7, 32px); + justify-content: center; + margin-bottom: 8px; + + color: #939393; + column-gap: 10.5px; + + /* text */ + abbr { + font-weight: 500; + font-size: 12px; + text-decoration: none; + } +} + +.custom-calendar .react-calendar__month-view__days { + display: grid !important; + grid-template-columns: repeat(7, 32px); + gap: 4px 10.5px; +} + +.custom-calendar .react-calendar__tile { + display: flex; + align-items: center; + justify-content: center; + height: 32px; + + color: #fff; + font-size: 14px; + + background: #444; + background-color: transparent; + border-radius: 4px; + + :hover { + background-color: transparent; + } +} + +.custom-calendar .react-calendar__tile--now:hover { + color: #fff; + + background-color: #3e3e3e; +} + +.custom-calendar .react-calendar__tile--active { + abbr { + color: black !important; + + background: #fff !important; + } + background: #fff !important; + + :hover { + background-color: transparent !important; + } +} + +.custom-calendar .react-calendar__month-view__days__day--neighboringMonth { + color: #626262; +} + +/* 지난 날짜 색상 */ +.past-date { + color: #939393 !important; + + cursor: not-allowed; + + pointer-events: none; +} diff --git a/src/components/commons/mapInput/MapInput.styled.ts b/src/components/commons/mapInput/MapInput.styled.ts new file mode 100644 index 00000000..94b18949 --- /dev/null +++ b/src/components/commons/mapInput/MapInput.styled.ts @@ -0,0 +1,196 @@ +import { IconTextfiedlDelete } from "@assets/svgs"; +import { Generators } from "@styles/generator"; +import styled from "styled-components"; + +export const Deemed = styled.div` + position: fixed; + top: 0; + left: 0; + z-index: 5; /* 모달보다 낮고 다른 요소 위 */ + width: 100vw; + height: 100vh; + + background-color: rgb(0 0 0 / 30%); +`; + +export const TextFieldLayout = styled.section<{ $narrow: boolean | undefined }>` + position: relative; + z-index: 6; + width: ${({ $narrow }) => ($narrow ? "13.6rem" : "32.7rem")}; +`; + +export const TextFieldWrapper = styled.article` + ${Generators.flexGenerator("row", "center", "center")} +`; + +export const TextFieldInput = styled.input<{ + $narrow: boolean | undefined; + $isDisabled?: boolean; + $isWarn?: boolean; +}>` + width: 100%; + height: ${({ $narrow }) => ($narrow ? "4.2rem" : "4.8rem")}; + padding: 0 1.6rem; + + color: ${({ theme, $isDisabled }) => ($isDisabled ? theme.colors.gray_600 : theme.colors.gray_0)}; + + background: ${({ theme }) => theme.colors.gray_800}; + border: 1px solid ${({ theme, $isWarn }) => ($isWarn ? theme.colors.red : "transparent")}; + border-radius: 0.6rem; + + ${({ theme }) => theme.fonts["body2-normal-medi"]}; + &::placeholder { + color: ${({ theme }) => theme.colors.gray_600}; + } + + &:focus { + border: 1px solid ${({ theme }) => theme.colors.gray_0}; + } +`; + +export const TextClear = styled(IconTextfiedlDelete)` + position: absolute; + top: 1.2rem; + right: 1.2rem; + width: 2.4rem; + + cursor: pointer; +`; + +export const TextUnit = styled.p` + position: absolute; + right: 1.6rem; + width: 2.4rem; + height: 2.4rem; + + color: ${({ theme }) => theme.colors.gray_0}; + ${({ theme }) => theme.fonts["body2-normal-medi"]}; +`; + +export const ToggleVisibilityIcon = styled.section` + position: absolute; + right: 1.6rem; + + width: 2.4rem; +`; + +export const TextCap = styled.p` + ${Generators.flexGenerator("row", "center", "end")} + + width: 100%; + margin: 0; + margin-top: 0.6rem; + + color: ${({ theme }) => theme.colors.gray_500}; + text-align: right; + ${({ theme }) => theme.fonts["body2-normal-medi"]}; +`; + +export const SearchDropDownWrapper = styled.div` + position: absolute; + top: 6.2rem; + z-index: 10; + + display: flex; + flex-direction: column; + gap: 1rem; + align-items: center; + width: 32.7rem; + max-height: 27.8rem; + padding: 1.4rem 2rem; + overflow: hidden scroll; + + background-color: ${({ theme }) => theme.colors.gray_800}; + border: 1px solid black; + border-radius: 6px; + + &::-webkit-scrollbar { + width: 5px; /* 스크롤바 너비 */ + } + + &::-webkit-scrollbar-track { + background: ${({ theme }) => theme.colors.gray_700}; /* 트랙 배경 */ + border-radius: 4px; + } + + &::-webkit-scrollbar-thumb { + background: ${({ theme }) => theme.colors.gray_500}; /* 손잡이 색상 */ + border-radius: 6px; + } + + &::-webkit-scrollbar-thumb:hover { + background: ${({ theme }) => theme.colors.gray_600}; /* 호버 시 손잡이 색상 */ + } +`; + +export const DropDownItem = styled.div` + display: flex; + flex-direction: column; + gap: 6px; + align-items: flex-start; + width: 29.9rem; + padding: 0.6rem; + + cursor: pointer; + border-radius: 0.4rem; + + &:hover { + background-color: ${({ theme }) => theme.colors.gray_700}; + } +`; + +export const RoadName = styled.p` + align-self: stretch; + + color: ${({ theme }) => theme.colors.pink_200}; + ${({ theme }) => theme.fonts["body1-normal-semi"]}; + white-space: nowrap; +`; + +export const PostName = styled.p` + align-self: stretch; + + color: ${({ theme }) => theme.colors.gray_300}; + ${({ theme }) => theme.fonts["body2-normal-medi"]}; +`; + +export const Divider = styled.div` + flex-shrink: 0; + width: 28.7rem; + height: 0.1rem; + + background-color: #3e3e3e; +`; + +export const DescriptionBox = styled.div` + display: flex; + flex-direction: column; + flex-shrink: 0; + gap: 0.8rem; + align-items: center; + justify-content: center; + width: 32.7rem; + height: 17.2rem; + margin-top: 1.6rem; + + background: ${({ theme }) => theme.colors.gray_800}; + border-radius: 6px; +`; + +export const NoSearchP = styled.p` + color: ${({ theme }) => theme.colors.gray_400}; + ${({ theme }) => theme.fonts["caption1-medi"]}; +`; + +export const NoSearchB = styled.p` + color: ${({ theme }) => theme.colors.gray_300}; + ${({ theme }) => theme.fonts["body2-normal-medi"]}; + text-align: center; +`; + +export const WarningText = styled.p` + margin-top: 0.8rem; + + color: ${({ theme }) => theme.colors.red}; + ${({ theme }) => theme.fonts["caption1-medi"]}; +`; diff --git a/src/components/commons/mapInput/MapInput.tsx b/src/components/commons/mapInput/MapInput.tsx new file mode 100644 index 00000000..258f0524 --- /dev/null +++ b/src/components/commons/mapInput/MapInput.tsx @@ -0,0 +1,217 @@ +import React, { + ChangeEvent, + InputHTMLAttributes, + useCallback, + useEffect, + useRef, + useState, +} from "react"; +import { IconEyeOff, IconEyeOn, IconSearch } from "@assets/svgs"; +import * as S from "./MapInput.styled"; +import { splitGraphemes } from "@utils/useInputFilter"; +import _ from "lodash"; + +export interface TextFieldProps extends InputHTMLAttributes { + onChange: (e: React.ChangeEvent) => void; + setLatitudeLongitude: (latitude: string, longitude: string) => void; + maxLength?: number; + placeholder: string; + narrow?: boolean; + filter?: (value: string) => string; + cap?: false | true; + isDisabled?: boolean; +} + +interface MapSearchResult { + address_name: string; + category_group_code: string; + category_group_name: string; + category_name: string; + distance: string; + id: string; + phone: string; + place_name: string; + place_url: string; + road_address_name: string; + x: string; + y: string; +} + +const MapInput = ({ + type = "input", + name, + value = "", + onChange, + setLatitudeLongitude, + maxLength, + placeholder, + narrow, + filter, + cap, + isDisabled, + inputMode, + ...rest +}: TextFieldProps) => { + const { kakao } = window; + const inputRef = useRef(null); + const [inputValue, setInputValue] = useState(value as string); // 현재 입력값 + const [places, setPlaces] = useState([]); + const [isDropDownOpen, setIsDropDownOpen] = useState(false); + const [isWarn, setIsWarn] = useState(false); + const prevValueRef = useRef(value as string); // 이전 입력값 + const rafRef = useRef(null); // requestAnimationFrame ID + + const ps = new kakao.maps.services.Places(); + + const debouncedSearch = useCallback( + _.debounce((query) => { + if (query) { + ps.keywordSearch(query, (data, status, _pagination) => { + if (status === kakao.maps.services.Status.OK) { + const temp = []; + for (var i = 0; i < data.length; i++) { + temp.push(data[i]); + } + setPlaces(temp); + } else if (status === kakao.maps.services.Status.ZERO_RESULT) { + setPlaces(data); + } + }); + } else { + setPlaces([]); + } + }, 200), + [] + ); + + const handleClickInput = () => { + setIsDropDownOpen(true); + setIsWarn(false); + }; + + const handleClickDeemed = () => { + setIsDropDownOpen(false); + if (prevValueRef.current === inputValue) { + setIsWarn(true); + setLatitudeLongitude("", ""); + } + }; + + useEffect(() => { + setInputValue(value as string); + prevValueRef.current = value as string; + }, [value]); + + const handleOnInput = useCallback( + (e: ChangeEvent) => { + const newValue = e.target.value; + setInputValue(newValue); + + if (rafRef.current) { + cancelAnimationFrame(rafRef.current); // 이전 requestAnimationFrame이 있으면 취소 + } + + rafRef.current = requestAnimationFrame(() => { + // 새로운 requestAnimationFrame 요청 + let filteredValue = newValue; + if (filter) { + filteredValue = filter(newValue); // 전체 값을 필터링 + } + + if (maxLength && splitGraphemes(filteredValue as string).length > maxLength) { + filteredValue = splitGraphemes(filteredValue as string) + .slice(0, maxLength) + .join(""); + } + + const newEvent = { + ...e, + target: { + ...e.target, + name: name, + value: filteredValue, + }, + } as ChangeEvent; + + onChange(newEvent); + prevValueRef.current = filteredValue; // 이전 입력 값을 현재 값으로 업데이트 + setInputValue(filteredValue); // 현재 입력값을 필터링된 값으로 업데이트 + }); + + debouncedSearch(newValue); + }, + [onChange, filter, maxLength, name] + ); + + return ( + <> + {isDropDownOpen && } + + + + + + + + {isWarn && 검색 시 나오는 주소를 선택해주세요.} + {maxLength && cap && ( + {`${splitGraphemes(value as string).length}/${maxLength}`} + )} + {isDropDownOpen && + (places.length > 0 ? ( + + {places.map((place, idx) => ( + + { + setInputValue(place.road_address_name || place.address_name); + setIsDropDownOpen(false); + setIsWarn(false); + // 추가: onChange 호출로 부모 상태 갱신 + const newEvent = { + target: { + name, + value: place.road_address_name || place.address_name, + }, + } as React.ChangeEvent; + onChange(newEvent); + setLatitudeLongitude(place.y, place.x); //lat이 y값 + }} + > + {place.road_address_name} + {place.address_name} + + {idx !== places.length - 1 && } + + ))} + + ) : ( + + 검색결과가 없어요. 아래와 같이 검색해보세요. + + {"• 도로명 + 건물번호 (판교역로 166)"} +
+ {"• 동/읍/면/리 + 번지 (백현동 532)"} +
+ {"• 건물명, 아파트명 (분당 주공)"} +
+
+ ))} +
+ + ); +}; + +export default MapInput; diff --git a/src/components/commons/renewTimePicker/RenewTimePicker.styled.ts b/src/components/commons/renewTimePicker/RenewTimePicker.styled.ts new file mode 100644 index 00000000..ce85eda8 --- /dev/null +++ b/src/components/commons/renewTimePicker/RenewTimePicker.styled.ts @@ -0,0 +1,64 @@ +import styled from "styled-components"; + +export const Wrapper = styled.div` + display: flex; + gap: 16px; +`; + +export const TimePickerContainer = styled.div` + display: flex; + flex-direction: column; + align-items: center; +`; + +export const ButtonGroup = styled.div` + display: flex; + gap: 11px; + justify-content: center; + margin-bottom: 20px; +`; + +export const StyledButton = styled.button<{ $isActive: boolean }>` + padding: 6px 50.5px; + + ${({ theme }) => theme.fonts["body2-normal-medi"]}; + color: ${({ theme, $isActive }) => ($isActive ? theme.colors.black : theme.colors.gray_400)}; + font-weight: 500; + font-size: 13.2px; + + background-color: ${({ theme, $isActive }) => ($isActive ? "#fff" : theme.colors.gray_700)}; + border-radius: 100px; +`; + +export const TimeGrid = styled.div` + display: grid; + grid-template-columns: repeat(6, 1fr); + gap: 13.4px; +`; + +export const GridTitle = styled.p` + ${({ theme }) => theme.fonts["body2-normal-medi"]}; + font-weight: 500; + + &:first-child { + margin-top: 52px; + } + + &:last-child { + margin-top: 84px; + } +`; + +export const GridButton = styled.button<{ $isActive: boolean }>` + width: 32px; + height: 32px; + + color: ${({ theme, $isActive }) => ($isActive ? theme.colors.black : theme.colors.gray_400)}; + text-align: center; + + background-color: ${({ theme, $isActive }) => + $isActive ? theme.colors.white : theme.colors.gray_700}; + cursor: pointer; + border: none; + border-radius: 5px; +`; diff --git a/src/components/commons/renewTimePicker/RenewTimePicker.tsx b/src/components/commons/renewTimePicker/RenewTimePicker.tsx new file mode 100644 index 00000000..359f3eaa --- /dev/null +++ b/src/components/commons/renewTimePicker/RenewTimePicker.tsx @@ -0,0 +1,67 @@ +import Spacing from "../spacing/Spacing"; +import * as S from "./RenewTimePicker.styled"; + +interface RenewTimePickerProps { + isPM: boolean; + selectedHour: number; + selectedMinute: number; + onChangeTime: (type: "hour" | "minute" | "isPM", value: number | boolean) => void; +} + +const RenewTimePicker = ({ + isPM, + selectedHour, + selectedMinute, + onChangeTime, +}: RenewTimePickerProps) => { + const hours = Array.from({ length: 12 }, (_, i) => ((i + 11) % 12) + 1); + const minutes = Array.from({ length: 12 }, (_, i) => i * 5); + + return ( + +
+ + +
+ + + + onChangeTime("isPM", false)}> + 오전 + + onChangeTime("isPM", true)}> + 오후 + + + + + {hours.map((hour, i) => ( + onChangeTime("hour", hour)} + > + {hour} + + ))} + + + + + + {minutes.map((min, i) => ( + onChangeTime("minute", min)} + > + {min.toString().padStart(2, "0")} + + ))} + + +
+ ); +}; + +export default RenewTimePicker; diff --git a/src/components/index.ts b/src/components/index.ts new file mode 100644 index 00000000..b11abda6 --- /dev/null +++ b/src/components/index.ts @@ -0,0 +1,36 @@ +export { default as BankBottomSheet } from "./commons/bank/bottomSheet/BankBottomSheet.tsx"; +export { default as BankBtn } from "./commons/bank/bottomSheet/BankBtn.tsx"; +export { default as InputAccountWrapper } from "./commons/bank/InputAccountWrapper.tsx"; +export { default as InputBank } from "./commons/bank/InputBank.tsx"; +export { default as ActionBottomSheet } from "./commons/bottomSheet/actionsBottomSheet/ActionBottomSheet.tsx"; +export { default as PhoneNumber } from "./commons/bottomSheet/actionsBottomSheet/phoneNumber/PhoneNumber.tsx"; +export { default as BottomSheet } from "./commons/bottomSheet/BottomSheet.tsx"; +export { default as OuterLayout } from "./commons/bottomSheet/OuterLayout.tsx"; +export { default as ViewBottomSheet } from "./commons/bottomSheet/viewBottomSheet/ViewBottomSheet.tsx"; +export { default as Button } from "./commons/button/Button.tsx"; +export { default as Chip } from "./commons/chip/Chip.tsx"; +export { default as Context } from "./commons/contextBox/Context.tsx"; +export { default as ContextBox } from "./commons/contextBox/ContextBox.tsx"; +export { default as DatePicker } from "./commons/datePicker/DatePicker.tsx"; +export { default as Hamburger } from "./commons/hamburger/Hamburger.tsx"; +export { default as ImageEditor } from "./commons/imageEditor/ImageEditor.tsx"; +export { default as TextArea } from "./commons/input/textArea/TextArea.tsx"; +export { default as TextField } from "./commons/input/textField/TextField.tsx"; +export { default as Label } from "./commons/label/Label.tsx"; +export { default as Loading } from "./commons/loading/Loading.tsx"; +export { default as LoadingAnimation } from "./commons/loading/LoadingAnimation.tsx"; +export { default as MetaTag } from "./commons/meta/MetaTag.tsx"; +export { default as Alert } from "./commons/modal/Alert.tsx"; +export { default as ModalTextBox } from "./commons/modal/components/ModalTextBox.tsx"; +export { default as ModalWrapper } from "./commons/modal/components/ModalWrapper.tsx"; +export { default as Confirm } from "./commons/modal/Confirm.tsx"; +export { default as Modal } from "./commons/modal/Modal.tsx"; +export { default as Navigation } from "./commons/navigation/Navigation.tsx"; +export { default as RenewTimePicker } from "./commons/renewTimePicker/RenewTimePicker.tsx"; +export { default as ScrollToTop } from "./commons/scrollToTop/ScrollToTop.tsx"; +export { default as Spacing } from "./commons/spacing/Spacing.tsx"; +export { default as Stepper } from "./commons/stepper/Stepper.tsx"; +export { default as TimePicker } from "./commons/timepicker/TimePicker.tsx"; +export { default as Toast } from "./commons/toast/Toast.tsx"; +export { default as AdminLayout } from "./layout/AdminLayout.tsx"; +export { default as Layout } from "./layout/Layout.tsx"; diff --git a/src/hooks/useTokenRefresher.tsx b/src/hooks/useTokenRefresher.tsx index 64fabbbb..bbaa4bb9 100644 --- a/src/hooks/useTokenRefresher.tsx +++ b/src/hooks/useTokenRefresher.tsx @@ -71,6 +71,7 @@ export default function TokenRefresher() { navigate("/main"); openAlert({ title: "장시간 미활동으로 인해 \n자동으로 로그아웃 되었습니다." }); + window.location.reload(); } } else if (status === 400 || status === 404 || status === 409) { } else if (status === 500) { diff --git a/src/pages/book/Book.tsx b/src/pages/book/Book.tsx index 47c70986..05db6e79 100644 --- a/src/pages/book/Book.tsx +++ b/src/pages/book/Book.tsx @@ -16,7 +16,6 @@ import { BookerInfo, Count, EasyPassEntry, Info, Select, TermCheck } from "@page import { SHOW_TYPE_KEY } from "@pages/gig/constants"; import NotFound from "@pages/notFound/NotFound"; import * as S from "./Book.styled"; -import { getScheduleNumberById } from "./utils"; const Book = () => { const navigate = useNavigate(); @@ -86,6 +85,13 @@ const Book = () => { const { mutateAsync: guestBook, isPending: isGuestBookingPending } = useGuestBook(); const { mutateAsync: memberBook, isPending: isMemberBookPending } = useMemberBook(); + useEffect(() => { + if (data?.scheduleList?.length === 1) { + const singleSchedule = data.scheduleList[0]; + setSelectedValue(singleSchedule.scheduleId); + } + }, [data?.scheduleList]); + const handleRadioChange = (value: number) => { setSelectedValue(value); }; diff --git a/src/pages/gig/Gig.tsx b/src/pages/gig/Gig.tsx index 83342fa5..37b384f2 100644 --- a/src/pages/gig/Gig.tsx +++ b/src/pages/gig/Gig.tsx @@ -15,6 +15,7 @@ import ShowInfo from "./components/showInfo/ShowInfo"; import { SHOW_TYPE_KEY } from "./constants"; import * as S from "./Gig.styled"; +//todo: 공연 보는 페이지, 수정 페이지에서도 변경 사항 반영해두기 const Gig = () => { const navigate = useNavigate(); const [, setNavigateUrl] = useAtom(navigateAtom); @@ -122,6 +123,11 @@ const Gig = () => { teamName={data?.performanceTeamName ?? ""} castList={data?.castList ?? []} staffList={data?.staffList ?? []} + performanceVenue={data?.performanceVenue ?? ""} + roadAddressName={data?.roadAddressName ?? ""} + placeDetailAddress={data?.placeDetailAddress ?? ""} + latitude={data?.latitude ?? ""} + longitude={data?.longitude ?? ""} /> diff --git a/src/pages/register/Register.tsx b/src/pages/register/Register.tsx index 85721869..01b7e940 100644 --- a/src/pages/register/Register.tsx +++ b/src/pages/register/Register.tsx @@ -8,10 +8,10 @@ import InputBank from "@components/commons/bank/InputBank"; import Button from "@components/commons/button/Button"; import TextArea from "@components/commons/input/textArea/TextArea"; import TextField from "@components/commons/input/textField/TextField"; +import MapInput from "@components/commons/mapInput/MapInput"; import MetaTag from "@components/commons/meta/MetaTag"; import Spacing from "@components/commons/spacing/Spacing"; import Stepper from "@components/commons/stepper/Stepper"; -import TimePicker from "@components/commons/timepicker/TimePicker"; import { NAVIGATION_STATE } from "@constants/navigationState"; import { useLogin, useModal } from "@hooks"; import Content from "@pages/gig/components/content/Content"; @@ -25,6 +25,8 @@ import { useAtom } from "jotai"; import { ChangeEvent, useEffect, useState } from "react"; import { useNavigate } from "react-router-dom"; import { useHeader } from "./../../hooks/useHeader"; +import DateTimePicker from "./components/DateTimePicker"; +import DetailImage from "./components/DetailImage"; import GenreSelect from "./components/GenreSelect"; import InputRegisterBox from "./components/InputRegisterBox"; import PosterThumbnail from "./components/PosterThumbnail"; @@ -38,7 +40,7 @@ import { handleBankClick, handleBankOpen, handleChange, - handleDateChange, + handleDateTimeChange, handleGenreSelect, handleImagesUpload, handleImageUpload, @@ -48,7 +50,6 @@ import { onMinusClick, onPlusClick, } from "./utils/handleEvent"; -import DetailImage from "./components/DetailImage"; const Register = () => { const { isLogin } = useLogin(); @@ -114,6 +115,11 @@ const Register = () => { // staffPhoto: "", // 스태프 사진 URL // }, ], + //placeName: "", + roadAddressName: "", + placeDetailAddress: "", + latitude: "", + longitude: "", }); // 구조 분해 할당 @@ -136,6 +142,10 @@ const Register = () => { scheduleList, castList, staffList, + roadAddressName, + placeDetailAddress, + latitude, + longitude, } = gigInfo; const [bankOpen, setBankOpen] = useState(false); @@ -300,6 +310,14 @@ const Register = () => { setRegisterStep((prev) => prev + 1); }; + const setLatitudeLongitude = (latitude: string, longitude: string) => { + setGigInfo((prev) => ({ + ...prev, + latitude, + longitude, + })); + }; + useEffect(() => { window.scrollTo({ top: 0 }); }, [registerStep]); @@ -390,6 +408,35 @@ const Register = () => { /> + + handleChange(e, setGigInfo)} + placeholder="ex) 홍익아트홀 303호 소극장" + cap={true} + /> + + + + handleChange(e, setGigInfo)} + setLatitudeLongitude={setLatitudeLongitude} + placeholder="지번, 도로명, 건물명으로 검색해주세요." + cap={true} + /> + + handleChange(e, setGigInfo)} + placeholder="건물명, 층 수 등의 상세주소를 입력해주세요." + /> + + handleImagesUpload(performanceImage, setGigInfo)} @@ -434,29 +481,14 @@ const Register = () => {
{index + 1}회차 - handleDateChange(index, date, setGigInfo)} - minDate={ - index > 0 ? scheduleList[index - 1].performanceDate || undefined : undefined - } + onChangeDateTime={(date) => handleDateTimeChange(index, date, setGigInfo)} />
))} - - handleChange(e, setGigInfo)} - placeholder="ex) 홍익아트홀 303호 소극장" - maxLength={15} - cap={true} - /> - - { ...cast, staffId: index + 1, }))} + performanceVenue={performanceVenue} + roadAddressName={roadAddressName} + placeDetailAddress={placeDetailAddress} + latitude={latitude} + longitude={longitude} /> + + )} + + {step === "time" && ( + <> + + + + + + + + + )} + + + + )} + + ); +}; + +export default DateTimePicker; diff --git a/src/pages/register/typings/gigInfo.ts b/src/pages/register/typings/gigInfo.ts index e2900174..5ed3b3c3 100644 --- a/src/pages/register/typings/gigInfo.ts +++ b/src/pages/register/typings/gigInfo.ts @@ -44,4 +44,9 @@ export interface GigInfo { scheduleList: Schedule[]; castList: Cast[]; staffList: Staff[]; + //placeName: string; + roadAddressName: string; + placeDetailAddress: string; + latitude: string; + longitude: string; } diff --git a/src/pages/register/utils/handleEvent.ts b/src/pages/register/utils/handleEvent.ts index 7f556db1..5b73893b 100644 --- a/src/pages/register/utils/handleEvent.ts +++ b/src/pages/register/utils/handleEvent.ts @@ -1,7 +1,7 @@ +import { SHOW_TYPE_KEY } from "@pages/gig/constants"; import { Dayjs } from "dayjs"; import { ChangeEvent, Dispatch, SetStateAction } from "react"; -import { PerformanceImage, GigInfo } from "../typings/gigInfo"; -import { SHOW_TYPE_KEY } from "@pages/gig/constants"; +import { GigInfo, PerformanceImage } from "../typings/gigInfo"; // Image 핸들링 export const handleImageUpload = ( @@ -91,7 +91,7 @@ export const onPlusClick = (setGigInfo: Dispatch>) => { }; // TimePicker 핸들링 -export const handleDateChange = ( +export const handleDateTimeChange = ( index: number, date: Dayjs | null, setGigInfo: Dispatch> @@ -162,6 +162,10 @@ export const isAllFieldsFilled = (gigInfo: GigInfo, isFree: boolean) => { "performancePeriod", "ticketPrice", "totalScheduleCount", + "roadAddressName", + "placeDetailAddress", + "latitude", + "longitude", ...(!isFree ? ["bankName", "accountNumber", "accountHolder"] : []), ]; diff --git a/src/pages/register/utils/index.ts b/src/pages/register/utils/index.ts new file mode 100644 index 00000000..37d45b83 --- /dev/null +++ b/src/pages/register/utils/index.ts @@ -0,0 +1,20 @@ +export const changeDateTime = (date: Date, hour?: number, minute?: number, isPM?: boolean) => { + const formattedDate = date + .toLocaleDateString("ko-KR", { + year: "numeric", + month: "2-digit", + day: "2-digit", + }) + .replace(/\./g, "/") + .replace(/\s/g, "") + .replace(/\/$/, ""); + + if (hour === undefined || minute === undefined || isPM === undefined) { + return formattedDate; + } + + const spaces = " ".repeat(6).replace(/ /g, "\u00A0"); + const formattedTime = `${isPM ? "오후" : "오전"} ${hour}시${minute === 0 ? "" : ` ${minute}분`}`; + + return `${formattedDate}${spaces}${formattedTime}`; +}; diff --git a/src/pages/test/KakaoMap.tsx b/src/pages/test/KakaoMap.tsx new file mode 100644 index 00000000..dfb2f2a8 --- /dev/null +++ b/src/pages/test/KakaoMap.tsx @@ -0,0 +1,107 @@ +import React, { useEffect, useState } from "react"; +import { Map, MapMarker } from "react-kakao-maps-sdk"; +// import banner_basic from "@assets/images/banner_basic.png"; +// import icon_toss_svg from "../../../public/svgs/icon_toss.svg"; + +type Position = { + lat: number; + lng: number; +}; + +type Marker = { + position: Position; + content: string; +}; + +const KakaoMap = () => { + const { kakao } = window; + const [info, setInfo] = useState(null); + const [markers, setMarkers] = useState([ + { + position: { + lat: 37.5494796485918, + lng: 126.924854360345, + }, + content: "테스트", + }, + ]); + const [map, setMap] = useState(null); + const [location, setLocation] = useState({ lat: 37.566826, lng: 126.9786567 }); + + useEffect(() => { + if (!map) { + return; + } + const ps = new kakao.maps.services.Places(); + + ps.keywordSearch("홍익대학교 제2기숙사", (data, status, _pagination) => { + if (status === kakao.maps.services.Status.OK) { + // 검색된 장소 위치를 기준으로 지도 범위를 재설정하기위해 + // LatLngBounds 객체에 좌표를 추가합니다 + const bounds = new kakao.maps.LatLngBounds(); + console.log("bounds:", bounds); + const markers = []; + + for (var i = 0; i < data.length; i++) { + console.log(data[i]); + // @ts-ignore + markers.push({ + position: { + lat: data[i].y, + lng: data[i].x, + }, + content: data[i].place_name, + }); + // @ts-ignore + bounds.extend(new kakao.maps.LatLng(data[i].y, data[i].x)); + } + setMarkers(markers); + + // 검색된 장소 위치를 기준으로 지도 범위를 재설정합니다 + map.setBounds(bounds); + } + }); + + //테스트로, 처음 위치 직접 지정 + setLocation({ lat: 37.5494796485918, lng: 126.924854360345 }); + }, [map]); + + return ( + + {markers.map((marker) => { + console.log(marker); + return ( + setInfo(marker)} + // image={{ + // src: icon_toss_svg, + // size: { + // width: 30, + // height: 30, + // }, + // }} + > + {info && info.content === marker.content && ( +
{marker.content}
+ )} +
+ ); + })} +
+ ); +}; + +export default KakaoMap; diff --git a/src/pages/test/kakao.d.ts b/src/pages/test/kakao.d.ts new file mode 100644 index 00000000..6da1ed60 --- /dev/null +++ b/src/pages/test/kakao.d.ts @@ -0,0 +1,7 @@ +declare global { + interface Window { + kakao: any; // 기본적으로 `any`로 설정. 더 상세한 타입 정의 가능. + } +} + +export {}; diff --git a/src/pages/ticketholderlist/components/narrowDropDown/NarrowDropDown.tsx b/src/pages/ticketholderlist/components/narrowDropDown/NarrowDropDown.tsx new file mode 100644 index 00000000..b99ec93a --- /dev/null +++ b/src/pages/ticketholderlist/components/narrowDropDown/NarrowDropDown.tsx @@ -0,0 +1,156 @@ +import { ReactNode, useState } from "react"; +import * as S from "./NarrowDropDown.styled"; + +interface DropdownProps { + children: ReactNode; + totalScheduleCount: number; + schedule: number; + payment: + | "CHECKING_PAYMENT" + | "BOOKING_CONFIRMED" + | "BOOKING_CANCELLED" + | "REFUND_REQUESTED" + | "BOOKING_DELETED" + | undefined; + setSchedule?: (param: number) => void; + setPayment?: ( + param: + | "CHECKING_PAYMENT" + | "BOOKING_CONFIRMED" + | "BOOKING_CANCELLED" + | "REFUND_REQUESTED" + | "BOOKING_DELETED" + | undefined + ) => void; +} + +const NarrowDropDown = ({ + children, + totalScheduleCount, + schedule, + payment, + setSchedule, + setPayment, +}: DropdownProps) => { + const [showDropdown, setShowDropdown] = useState(false); + const [isOneChoosed, setIsOneChoosed] = useState(false); + + const handleToggle = () => { + setShowDropdown(!showDropdown); + }; + + const renderDropdownContent = (count: number, childrenNode: ReactNode) => { + const items = []; + const childrenString = childrenNode?.toString(); + if (childrenString === "모든 회차") { + const handleScheduleAll = () => { + setSchedule?.(0); + setIsOneChoosed(false); + setShowDropdown(false); + }; + items.push( + + 전체 + + ); + for (let i = 1; i <= count; i++) { + const handleSchedule = () => { + setSchedule?.(i); + setIsOneChoosed(true); + setShowDropdown(false); + }; + + items.push( + + {i}회차 + + ); + } + } else if (childrenString === "입금 상태") { + const handlePaymentUndefined = () => { + setPayment?.(undefined); + setIsOneChoosed(false); + setShowDropdown(false); + }; + + const handlePaymentFalse = () => { + setPayment?.("CHECKING_PAYMENT"); + setIsOneChoosed(true); + setShowDropdown(false); + }; + + const handlePaymentTrue = () => { + setPayment?.("BOOKING_CONFIRMED"); + setIsOneChoosed(true); + setShowDropdown(false); + }; + + items.push( + + 전체 + + ); + + items.push( + + + 미입금 + + + ); + items.push( + + + 입금완료 + + + ); + } + + return items; + }; + + //공연별로 총 회차 값으로 넘겨주기로 함. + //각 공연별 회차는, DB 테이블 구조에 따라 enum값으로 넘겨주기로 함 + //필터링은 회차 번호(scheduleNumber : FIRST, SECOND, THIRD), 입금 완료 여부는 paymentStatus에 따라 나뉠거임 + // + + let changedChildren; + if (children === "입금 상태" && payment !== undefined) { + changedChildren = payment === "BOOKING_CONFIRMED" ? "입금완료" : "미입금"; + } else if (children === "모든 회차" && schedule !== 0) { + changedChildren = `${schedule}회차`; + } + + return ( + + + + + {changedChildren === undefined ? children : changedChildren} + + + + + + + {renderDropdownContent(totalScheduleCount, children)} + + + + ); +}; + +export default NarrowDropDown; diff --git a/src/pages/ticketholderlist/constants/ticketholderlist.ts b/src/pages/ticketholderlist/constants/ticketholderlist.ts new file mode 100644 index 00000000..1ccafef0 --- /dev/null +++ b/src/pages/ticketholderlist/constants/ticketholderlist.ts @@ -0,0 +1,106 @@ +export interface BookingListProps { + bookingId?: number; + bookerName?: string; + bookerPhoneNumber?: string; + scheduleId?: number; + purchaseTicketCount?: number; + createdAt?: string; + bookingStatus?: + | "CHECKING_PAYMENT" + | "BOOKING_CONFIRMED" + | "BOOKING_CANCELLED" + | "REFUND_REQUESTED" + | "BOOKING_DELETED"; + scheduleNumber?: string; +} + +export interface TicketHolderListProps { + performanceTitle: string; + isBooking: boolean; + totalScheduleCount: number; + bookingList: BookingListProps[]; +} + +export interface TicketHolderListDataProps { + data: TicketHolderListProps; +} + +export const RESPONSE_TICKETHOLDER = { + data: { + performanceTitle: "비트밴드 정기공연", + isBooking: true, + totalScheduleCount: 3, + bookingList: [ + { + bookingId: 1, + bookerName: "황혜린", + bookerPhoneNumber: "010-1234-5678", + scheduleId: 2, //scheduleId는 테이블의 특성에 따라 존재하는 것 + purchaseTicketCount: 3, + createdAt: "2024-07-07T12:34:56.789Z", + isPaymentCompleted: true, + scheduleNumber: "SECOND", + }, + { + bookingId: 2, + bookerName: "이동훈", + bookerPhoneNumber: "010-1234-0000", + scheduleId: 1, + purchaseTicketCount: 2, + createdAt: "2024-07-08T12:34:56.789Z", + isPaymentCompleted: false, + scheduleNumber: "FIRST", + }, + { + bookingId: 3, + bookerName: "공준혁", + bookerPhoneNumber: "010-1234-9999", + scheduleId: 1, + purchaseTicketCount: 9, + createdAt: "2024-07-08T12:34:56.789Z", + isPaymentCompleted: true, + scheduleNumber: "THIRD", + }, + { + bookingId: 4, + bookerName: "정도영", + bookerPhoneNumber: "010-1234-0000", + scheduleId: 3, + purchaseTicketCount: 4, + createdAt: "2024-07-11T12:34:56.789Z", + isPaymentCompleted: false, + scheduleNumber: "THIRD", + }, + { + bookingId: 5, + bookerName: "김채현", + bookerPhoneNumber: "010-1234-0000", + scheduleId: 3, + purchaseTicketCount: 6, + createdAt: "2024-07-15T12:34:56.789Z", + isPaymentCompleted: false, + scheduleNumber: "THIRD", + }, + { + bookingId: 6, + bookerName: "윤신지", + bookerPhoneNumber: "010-1234-0000", + scheduleId: 3, + purchaseTicketCount: 2, + createdAt: "2024-07-03T12:34:56.789Z", + isPaymentCompleted: true, + scheduleNumber: "THIRD", + }, + { + bookingId: 7, + bookerName: "익명인", + bookerPhoneNumber: "010-1000-0000", + scheduleId: 1, + purchaseTicketCount: 2, + createdAt: "2024-07-08T12:34:56.789Z", + isPaymentCompleted: true, + scheduleNumber: "SECOND", + }, + ], + }, +}; diff --git a/src/routes/TestRoutes.tsx b/src/routes/TestRoutes.tsx index faf0133d..ae5f2a0f 100644 --- a/src/routes/TestRoutes.tsx +++ b/src/routes/TestRoutes.tsx @@ -1,5 +1,6 @@ import ActionBottomSheetTest from "@pages/test/ActionBottomSheetTest"; import KakaoLoginTest from "@pages/test/KakaoLoginTest"; +import KakaoMap from "@pages/test/KakaoMap"; import ModalTest from "@pages/test/modalTest/ModalTest"; import ViewBottomSheetTest from "@pages/test/ViewBottomSheetTest"; @@ -23,6 +24,10 @@ const TEST_ROUTES = [ path: "view-bottom-sheet", element: , }, + { + path: "kakaomap", + element: , + }, ], }, ]; diff --git a/src/typings/api/schema/index.ts b/src/typings/api/schema/index.ts index 07e96584..8efa350c 100644 --- a/src/typings/api/schema/index.ts +++ b/src/typings/api/schema/index.ts @@ -4,7 +4,7 @@ */ export interface paths { - "/api/tickets": { + "/api/tickets/update": { parameters: { query?: never; header?: never; @@ -21,11 +21,47 @@ export interface paths { delete?: never; options?: never; head?: never; + patch?: never; + trace?: never; + }; + "/api/tickets/refund": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + get?: never; + /** + * 예매자 환불처리 API + * @description 메이커가 자신의 공연에 대한 1명 이상의 예매자의 정보를 환불완료 상태로 변경하는 PUT API입니다. + */ + put: operations["refundTickets"]; + post?: never; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/api/tickets/delete": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + get?: never; /** - * 예매자 취소 API - * @description 메이커가 자신의 공연에 대한 1명 이상의 예매자의 정보를 취소 상태로 변경하는 PATCH API입니다. + * 예매자 삭제 API + * @description 메이커가 자신의 공연에 대한 1명 이상의 예매자의 정보를 삭제하는 PUT API입니다. */ - patch: operations["cancelTickets"]; + put: operations["deleteTickets"]; + post?: never; + delete?: never; + options?: never; + head?: never; + patch?: never; trace?: never; }; "/api/performances": { @@ -176,6 +212,46 @@ export interface paths { patch?: never; trace?: never; }; + "/api/bookings/refund": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + get?: never; + put?: never; + post?: never; + delete?: never; + options?: never; + head?: never; + /** + * 유료공연 예매 환불 요청 API + * @description 유료공연 예매자가 환불 요청하는 PATCH API입니다. + */ + patch: operations["refundBookings"]; + trace?: never; + }; + "/api/bookings/cancel": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + get?: never; + put?: never; + post?: never; + delete?: never; + options?: never; + head?: never; + /** + * 무료공연/미입금 예매 취소 요청 API + * @description 무료공연/미입금 예매자가 취소 요청하는 PATCH API입니다. + */ + patch: operations["cancelBookings"]; + trace?: never; + }; "/health-check": { parameters: { query?: never; @@ -183,6 +259,10 @@ export interface paths { path?: never; cookie?: never; }; + /** + * 헬스 체크 조회 API + * @description 서버 상태를 확인하기 위한 헬스 체크 API로, 정상적으로 동작할 경우 'OK' 문자열을 반환합니다. + */ get: operations["healthcheck"]; put?: never; post?: never; @@ -232,6 +312,26 @@ export interface paths { patch?: never; trace?: never; }; + "/api/tickets/search/{performanceId}": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + /** + * 예매자 목록 검색 API + * @description 메이커가 자신의 공연에 대한 예매자 목록을 검색하는 GET API입니다. + */ + get: operations["searchTickets"]; + put?: never; + post?: never; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; "/api/schedules/{scheduleId}/availability": { parameters: { query?: never; @@ -239,6 +339,10 @@ export interface paths { path?: never; cookie?: never; }; + /** + * 티켓 구매 가능 여부 조회 API + * @description 티켓 구매 가능 여부를 확인하는 GET API입니다. + */ get: operations["getTicketAvailability"]; put?: never; post?: never; @@ -340,8 +444,8 @@ export interface paths { cookie?: never; }; /** - * 전체공연목록, 홍보목록 조회 API - * @description 홈화면에서 전체공연목록, 홍보목록을 조회하는 GET API입니다. + * 전체 공연 및 홍보 목록 조회 + * @description 홈 화면에서 전체 공연 목록 및 홍보 목록을 조회하는 GET API */ get: operations["getHomePerformanceList"]; put?: never; @@ -468,7 +572,7 @@ export interface components { /** Format: date-time */ createdAt?: string; /** @enum {string} */ - bookingStatus?: "CHECKING_PAYMENT" | "BOOKING_CONFIRMED" | "BOOKING_CANCELLED"; + bookingStatus?: "CHECKING_PAYMENT" | "BOOKING_CONFIRMED" | "BOOKING_CANCELLED" | "REFUND_REQUESTED" | "BOOKING_DELETED"; scheduleNumber?: string; }; TicketUpdateRequest: { @@ -479,12 +583,31 @@ export interface components { totalScheduleCount?: number; bookingList?: components["schemas"]["TicketUpdateDetail"][]; }; + ErrorResponse: { + /** Format: int32 */ + status?: number; + message?: string; + }; SuccessResponseVoid: { /** Format: int32 */ status?: number; message?: string; data?: Record; }; + Booking: { + /** Format: int64 */ + bookingId?: number; + }; + TicketRefundRequest: { + /** Format: int64 */ + performanceId?: number; + bookingList?: components["schemas"]["Booking"][]; + }; + TicketDeleteRequest: { + /** Format: int64 */ + performanceId?: number; + bookingList?: components["schemas"]["Booking"][]; + }; CastModifyRequest: { /** Format: int64 */ castId?: number; @@ -514,6 +637,10 @@ export interface components { posterImage?: string; performanceTeamName?: string; performanceVenue?: string; + roadAddressName?: string; + placeDetailAddress?: string; + latitude?: string; + longitude?: string; performanceContact?: string; performancePeriod?: string; /** Format: int32 */ @@ -571,6 +698,10 @@ export interface components { posterImage?: string; performanceTeamName?: string; performanceVenue?: string; + roadAddressName?: string; + placeDetailAddress?: string; + latitude?: string; + longitude?: string; performanceContact?: string; performancePeriod?: string; /** Format: int32 */ @@ -607,7 +738,7 @@ export interface components { message?: string; data?: components["schemas"]["PerformanceModifyResponse"]; }; - CarouselProcessRequest: { + CarouselHandleRequest: { carousels?: (components["schemas"]["PromotionGenerateRequest"] | components["schemas"]["PromotionModifyRequest"])[]; }; PromotionGenerateRequest: { @@ -638,16 +769,22 @@ export interface components { /** Format: int64 */ performanceId?: number; }); - ErrorResponse: { - /** Format: int32 */ - status?: number; - message?: string; + CarouselHandleAllResponse: { + modifiedPromotions?: components["schemas"]["PromotionResponse"][]; + }; + PromotionResponse: { + /** Format: int64 */ + promotionId?: number; + newImageUrl?: string; + isExternal?: boolean; + redirectUrl?: string; + carouselNumber?: string; }; - SuccessResponse: { + SuccessResponseCarouselHandleAllResponse: { /** Format: int32 */ status?: number; message?: string; - data?: Record; + data?: components["schemas"]["CarouselHandleAllResponse"]; }; MemberLoginRequest: { /** @enum {string} */ @@ -688,6 +825,10 @@ export interface components { posterImage?: string; performanceTeamName?: string; performanceVenue?: string; + roadAddressName?: string; + placeDetailAddress?: string; + latitude?: string; + longitude?: string; performanceContact?: string; performancePeriod?: string; /** Format: int32 */ @@ -743,6 +884,10 @@ export interface components { posterImage?: string; performanceTeamName?: string; performanceVenue?: string; + roadAddressName?: string; + placeDetailAddress?: string; + latitude?: string; + longitude?: string; performanceContact?: string; performancePeriod?: string; /** Format: int32 */ @@ -789,7 +934,7 @@ export interface components { bookerName?: string; bookerPhoneNumber?: string; /** @enum {string} */ - bookingStatus?: "CHECKING_PAYMENT" | "BOOKING_CONFIRMED" | "BOOKING_CANCELLED"; + bookingStatus?: "CHECKING_PAYMENT" | "BOOKING_CONFIRMED" | "BOOKING_CANCELLED" | "REFUND_REQUESTED" | "BOOKING_DELETED"; /** Format: int32 */ totalPaymentAmount?: number; }; @@ -807,7 +952,7 @@ export interface components { bookerName?: string; bookerPhoneNumber?: string; /** @enum {string} */ - bookingStatus?: "CHECKING_PAYMENT" | "BOOKING_CONFIRMED" | "BOOKING_CANCELLED"; + bookingStatus?: "CHECKING_PAYMENT" | "BOOKING_CONFIRMED" | "BOOKING_CANCELLED" | "REFUND_REQUESTED" | "BOOKING_DELETED"; /** @enum {string} */ bankName?: "NH_NONGHYUP" | "KAKAOBANK" | "KB_KOOKMIN" | "TOSSBANK" | "SHINHAN" | "WOORI" | "IBK_GIUP" | "HANA" | "SAEMAUL" | "BUSAN" | "IMBANK_DAEGU" | "SINHYEOP" | "WOOCHAEGUK" | "SCJEIL" | "SUHYEOP" | "NONE"; accountNumber?: string; @@ -836,7 +981,7 @@ export interface components { /** Format: int32 */ totalPaymentAmount?: number; /** @enum {string} */ - bookingStatus?: "CHECKING_PAYMENT" | "BOOKING_CONFIRMED" | "BOOKING_CANCELLED"; + bookingStatus?: "CHECKING_PAYMENT" | "BOOKING_CONFIRMED" | "BOOKING_CANCELLED" | "REFUND_REQUESTED" | "BOOKING_DELETED"; }; GuestBookingResponse: { /** Format: int64 */ @@ -852,7 +997,7 @@ export interface components { bookerName?: string; bookerPhoneNumber?: string; /** @enum {string} */ - bookingStatus?: "CHECKING_PAYMENT" | "BOOKING_CONFIRMED" | "BOOKING_CANCELLED"; + bookingStatus?: "CHECKING_PAYMENT" | "BOOKING_CONFIRMED" | "BOOKING_CANCELLED" | "REFUND_REQUESTED" | "BOOKING_DELETED"; /** @enum {string} */ bankName?: "NH_NONGHYUP" | "KAKAOBANK" | "KB_KOOKMIN" | "TOSSBANK" | "SHINHAN" | "WOORI" | "IBK_GIUP" | "HANA" | "SAEMAUL" | "BUSAN" | "IMBANK_DAEGU" | "SINHYEOP" | "WOOCHAEGUK" | "SCJEIL" | "SUHYEOP" | "NONE"; accountNumber?: string; @@ -897,7 +1042,7 @@ export interface components { /** Format: int32 */ dueDate?: number; /** @enum {string} */ - bookingStatus?: "CHECKING_PAYMENT" | "BOOKING_CONFIRMED" | "BOOKING_CANCELLED"; + bookingStatus?: "CHECKING_PAYMENT" | "BOOKING_CONFIRMED" | "BOOKING_CANCELLED" | "REFUND_REQUESTED" | "BOOKING_DELETED"; /** Format: date-time */ createdAt?: string; posterImage?: string; @@ -910,10 +1055,45 @@ export interface components { message?: string; data?: components["schemas"]["GuestBookingRetrieveResponse"][]; }; - TicketCancelRequest: { + BookingRefundRequest: { /** Format: int64 */ - performanceId?: number; - bookingList?: number[]; + bookingId?: number; + /** @enum {string} */ + bankName?: "NH_NONGHYUP" | "KAKAOBANK" | "KB_KOOKMIN" | "TOSSBANK" | "SHINHAN" | "WOORI" | "IBK_GIUP" | "HANA" | "SAEMAUL" | "BUSAN" | "IMBANK_DAEGU" | "SINHYEOP" | "WOOCHAEGUK" | "SCJEIL" | "SUHYEOP" | "NONE"; + accountNumber?: string; + accountHolder?: string; + }; + BookingRefundResponse: { + /** Format: int64 */ + bookingId?: number; + /** @enum {string} */ + bookingStatus?: "CHECKING_PAYMENT" | "BOOKING_CONFIRMED" | "BOOKING_CANCELLED" | "REFUND_REQUESTED" | "BOOKING_DELETED"; + /** @enum {string} */ + bankName?: "NH_NONGHYUP" | "KAKAOBANK" | "KB_KOOKMIN" | "TOSSBANK" | "SHINHAN" | "WOORI" | "IBK_GIUP" | "HANA" | "SAEMAUL" | "BUSAN" | "IMBANK_DAEGU" | "SINHYEOP" | "WOOCHAEGUK" | "SCJEIL" | "SUHYEOP" | "NONE"; + accountNumber?: string; + accountHolder?: string; + }; + SuccessResponseBookingRefundResponse: { + /** Format: int32 */ + status?: number; + message?: string; + data?: components["schemas"]["BookingRefundResponse"]; + }; + BookingCancelRequest: { + /** Format: int64 */ + bookingId?: number; + }; + BookingCancelResponse: { + /** Format: int64 */ + bookingId?: number; + /** @enum {string} */ + bookingStatus?: "CHECKING_PAYMENT" | "BOOKING_CONFIRMED" | "BOOKING_CANCELLED" | "REFUND_REQUESTED" | "BOOKING_DELETED"; + }; + SuccessResponseBookingCancelResponse: { + /** Format: int32 */ + status?: number; + message?: string; + data?: components["schemas"]["BookingCancelResponse"]; }; AccessTokenGetSuccess: { accessToken?: string; @@ -942,13 +1122,21 @@ export interface components { /** Format: date-time */ createdAt?: string; /** @enum {string} */ - bookingStatus?: "CHECKING_PAYMENT" | "BOOKING_CONFIRMED" | "BOOKING_CANCELLED"; + bookingStatus?: "CHECKING_PAYMENT" | "BOOKING_CONFIRMED" | "BOOKING_CANCELLED" | "REFUND_REQUESTED" | "BOOKING_DELETED"; scheduleNumber?: string; + bankName?: string; + accountNumber?: string; + accountHolder?: string; }; TicketRetrieveResponse: { performanceTitle?: string; + performanceTeamName?: string; /** Format: int32 */ totalScheduleCount?: number; + /** Format: int32 */ + totalPerformanceTicketCount?: number; + /** Format: int32 */ + totalPerformanceSoldTicketCount?: number; bookingList?: components["schemas"]["TicketDetail"][]; }; SuccessResponseTicketAvailabilityResponse: { @@ -990,6 +1178,10 @@ export interface components { posterImage?: string; performanceTeamName?: string; performanceVenue?: string; + roadAddressName?: string; + placeDetailAddress?: string; + latitude?: string; + longitude?: string; performanceContact?: string; performancePeriod?: string; /** Format: int32 */ @@ -1054,6 +1246,10 @@ export interface components { /** Format: int32 */ runningTime?: number; performanceVenue?: string; + roadAddressName?: string; + placeDetailAddress?: string; + latitude?: string; + longitude?: string; performanceDescription?: string; performanceAttentionNote?: string; performanceContact?: string; @@ -1121,6 +1317,10 @@ export interface components { message?: string; data?: components["schemas"]["BookingPerformanceDetailResponse"]; }; + HomeFindAllResponse: { + promotionList?: components["schemas"]["HomePromotionDetail"][]; + performanceList?: components["schemas"]["HomePerformanceDetail"][]; + }; HomePerformanceDetail: { /** Format: int64 */ performanceId?: number; @@ -1142,16 +1342,27 @@ export interface components { performanceId?: number; redirectUrl?: string; isExternal?: boolean; + /** @enum {string} */ + carouselNumber?: "ONE" | "TWO" | "THREE" | "FOUR" | "FIVE" | "SIX" | "SEVEN"; }; - HomeResponse: { - promotionList?: components["schemas"]["HomePromotionDetail"][]; - performanceList?: components["schemas"]["HomePerformanceDetail"][]; + SuccessResponseHomeFindAllResponse: { + /** Format: int32 */ + status?: number; + message?: string; + data?: components["schemas"]["HomeFindAllResponse"]; + }; + PerformanceMakerPresignedUrlFindAllResponse: { + performanceMakerPresignedUrls?: { + [key: string]: { + [key: string]: string; + }; + }; }; - SuccessResponseHomeResponse: { + SuccessResponsePerformanceMakerPresignedUrlFindAllResponse: { /** Format: int32 */ status?: number; message?: string; - data?: components["schemas"]["HomeResponse"]; + data?: components["schemas"]["PerformanceMakerPresignedUrlFindAllResponse"]; }; MemberBookingRetrieveResponse: { /** Format: int64 */ @@ -1179,7 +1390,7 @@ export interface components { /** Format: int32 */ dueDate?: number; /** @enum {string} */ - bookingStatus?: "CHECKING_PAYMENT" | "BOOKING_CONFIRMED" | "BOOKING_CANCELLED"; + bookingStatus?: "CHECKING_PAYMENT" | "BOOKING_CONFIRMED" | "BOOKING_CANCELLED" | "REFUND_REQUESTED" | "BOOKING_DELETED"; /** Format: date-time */ createdAt?: string; posterImage?: string; @@ -1192,6 +1403,60 @@ export interface components { message?: string; data?: components["schemas"]["MemberBookingRetrieveResponse"][]; }; + SuccessResponseUserFindAllResponse: { + /** Format: int32 */ + status?: number; + message?: string; + data?: components["schemas"]["UserFindAllResponse"]; + }; + UserFindAllResponse: { + users?: components["schemas"]["UserFindResponse"][]; + }; + UserFindResponse: { + /** Format: int64 */ + id?: number; + role?: string; + }; + CarouselFindAllResponse: { + carousels?: components["schemas"]["CarouselFindResponse"][]; + }; + CarouselFindResponse: { + /** Format: int64 */ + promotionId?: number; + /** @enum {string} */ + carouselNumber?: "ONE" | "TWO" | "THREE" | "FOUR" | "FIVE" | "SIX" | "SEVEN"; + newImageUrl?: string; + isExternal?: boolean; + redirectUrl?: string; + /** Format: int64 */ + performanceId?: number; + }; + SuccessResponseCarouselFindAllResponse: { + /** Format: int32 */ + status?: number; + message?: string; + data?: components["schemas"]["CarouselFindAllResponse"]; + }; + CarouselPresignedUrlFindAllResponse: { + carouselPresignedUrls?: { + [key: string]: string; + }; + }; + SuccessResponseCarouselPresignedUrlFindAllResponse: { + /** Format: int32 */ + status?: number; + message?: string; + data?: components["schemas"]["CarouselPresignedUrlFindAllResponse"]; + }; + BannerPresignedUrlFindResponse: { + bannerPresignedUrl?: string; + }; + SuccessResponseBannerPresignedUrlFindResponse: { + /** Format: int32 */ + status?: number; + message?: string; + data?: components["schemas"]["BannerPresignedUrlFindResponse"]; + }; }; responses: never; parameters: never; @@ -1214,7 +1479,7 @@ export interface operations { }; }; responses: { - /** @description OK */ + /** @description 예매자 입금여부 수정 성공 */ 200: { headers: { [name: string]: unknown; @@ -1223,9 +1488,27 @@ export interface operations { "*/*": components["schemas"]["SuccessResponseVoid"]; }; }; + /** @description 이미 결제가 완료된 티켓의 상태는 변경할 수 없습니다. */ + 400: { + headers: { + [name: string]: unknown; + }; + content: { + "*/*": components["schemas"]["ErrorResponse"]; + }; + }; + /** @description 공연 정보를 찾을 수 없습니다. */ + 404: { + headers: { + [name: string]: unknown; + }; + content: { + "*/*": components["schemas"]["ErrorResponse"]; + }; + }; }; }; - cancelTickets: { + refundTickets: { parameters: { query?: never; header?: never; @@ -1234,11 +1517,11 @@ export interface operations { }; requestBody: { content: { - "application/json": components["schemas"]["TicketCancelRequest"]; + "application/json": components["schemas"]["TicketRefundRequest"]; }; }; responses: { - /** @description OK */ + /** @description 예매자 환불처리 성공 */ 200: { headers: { [name: string]: unknown; @@ -1247,9 +1530,18 @@ export interface operations { "*/*": components["schemas"]["SuccessResponseVoid"]; }; }; + /** @description 해당 예매 내역을 찾을 수 없습니다. */ + 404: { + headers: { + [name: string]: unknown; + }; + content: { + "*/*": components["schemas"]["ErrorResponse"]; + }; + }; }; }; - updatePerformance: { + deleteTickets: { parameters: { query?: never; header?: never; @@ -1258,58 +1550,31 @@ export interface operations { }; requestBody: { content: { - "application/json": components["schemas"]["PerformanceModifyRequest"]; + "application/json": components["schemas"]["TicketDeleteRequest"]; }; }; responses: { - /** @description 공연 정보 수정 성공 */ + /** @description 예매자 삭제 성공 */ 200: { headers: { [name: string]: unknown; }; content: { - "*/*": components["schemas"]["SuccessResponsePerformanceModifyResponse"]; - }; - }; - /** @description 잘못된 요청 - 회차 최대 개수 초과 */ - 400: { - headers: { - [name: string]: unknown; - }; - content: { - "*/*": components["schemas"]["SuccessResponsePerformanceModifyResponse"]; - }; - }; - /** @description 권한 없음 - 해당 공연의 소유자가 아닙니다. */ - 403: { - headers: { - [name: string]: unknown; - }; - content: { - "*/*": components["schemas"]["SuccessResponsePerformanceModifyResponse"]; + "*/*": components["schemas"]["SuccessResponseVoid"]; }; }; - /** @description 존재하지 않는 회원 ID로 수정 요청을 보낼 수 없습니다. */ + /** @description 해당 예매 내역을 찾을 수 없습니다. */ 404: { headers: { [name: string]: unknown; }; content: { - "*/*": components["schemas"]["SuccessResponsePerformanceModifyResponse"]; - }; - }; - /** @description 서버 내부 오류 */ - 500: { - headers: { - [name: string]: unknown; - }; - content: { - "*/*": components["schemas"]["SuccessResponsePerformanceModifyResponse"]; + "*/*": components["schemas"]["ErrorResponse"]; }; }; }; }; - createPerformance: { + updatePerformance: { parameters: { query?: never; header?: never; @@ -1318,23 +1583,92 @@ export interface operations { }; requestBody: { content: { - "application/json": components["schemas"]["PerformanceRequest"]; + "application/json": components["schemas"]["PerformanceModifyRequest"]; }; }; responses: { - /** @description OK */ + /** @description 공연 정보 수정 성공 */ 200: { headers: { [name: string]: unknown; }; content: { - "*/*": components["schemas"]["SuccessResponsePerformanceResponse"]; + "*/*": components["schemas"]["SuccessResponsePerformanceModifyResponse"]; }; }; - }; - }; - readAllCarouselImages: { - parameters: { + /** @description 예매자가 존재하여 가격을 수정할 수 없습니다. */ + 400: { + headers: { + [name: string]: unknown; + }; + content: { + "*/*": components["schemas"]["ErrorResponse"]; + }; + }; + /** @description 해당 공연의 소유자가 아닙니다. */ + 403: { + headers: { + [name: string]: unknown; + }; + content: { + "*/*": components["schemas"]["ErrorResponse"]; + }; + }; + /** @description 공연 정보를 찾을 수 없습니다. */ + 404: { + headers: { + [name: string]: unknown; + }; + content: { + "*/*": components["schemas"]["ErrorResponse"]; + }; + }; + }; + }; + createPerformance: { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + requestBody: { + content: { + "application/json": components["schemas"]["PerformanceRequest"]; + }; + }; + responses: { + /** @description 공연이 성공적으로 생성되었습니다. */ + 201: { + headers: { + [name: string]: unknown; + }; + content: { + "*/*": components["schemas"]["SuccessResponsePerformanceResponse"]; + }; + }; + /** @description 필수 데이터가 누락되었습니다. */ + 400: { + headers: { + [name: string]: unknown; + }; + content: { + "*/*": components["schemas"]["ErrorResponse"]; + }; + }; + /** @description 공연 정보를 찾을 수 없습니다. */ + 404: { + headers: { + [name: string]: unknown; + }; + content: { + "*/*": components["schemas"]["ErrorResponse"]; + }; + }; + }; + }; + readAllCarouselImages: { + parameters: { query?: never; header?: never; path?: never; @@ -1348,7 +1682,7 @@ export interface operations { [name: string]: unknown; }; content: { - "*/*": components["schemas"]["SuccessResponse"]; + "*/*": components["schemas"]["SuccessResponseCarouselFindAllResponse"]; }; }; /** @description 회원이 없습니다. */ @@ -1371,7 +1705,7 @@ export interface operations { }; requestBody: { content: { - "application/json": components["schemas"]["CarouselProcessRequest"]; + "application/json": components["schemas"]["CarouselHandleRequest"]; }; }; responses: { @@ -1381,10 +1715,10 @@ export interface operations { [name: string]: unknown; }; content: { - "*/*": components["schemas"]["SuccessResponse"]; + "*/*": components["schemas"]["SuccessResponseCarouselHandleAllResponse"]; }; }; - /** @description 해당 공연 정보를 찾을 수 없습니다. */ + /** @description 해당 홍보 정보를 찾을 수 없습니다. */ 404: { headers: { [name: string]: unknown; @@ -1410,7 +1744,7 @@ export interface operations { }; }; responses: { - /** @description OK */ + /** @description 로그인 또는 회원가입 성공 */ 200: { headers: { [name: string]: unknown; @@ -1419,6 +1753,24 @@ export interface operations { "*/*": components["schemas"]["SuccessResponseLoginSuccessResponse"]; }; }; + /** @description 로그인 요청이 유효하지 않습니다. */ + 400: { + headers: { + [name: string]: unknown; + }; + content: { + "*/*": components["schemas"]["ErrorResponse"]; + }; + }; + /** @description 회원 정보를 찾을 수 없습니다. */ + 404: { + headers: { + [name: string]: unknown; + }; + content: { + "*/*": components["schemas"]["ErrorResponse"]; + }; + }; }; }; signOut: { @@ -1430,7 +1782,7 @@ export interface operations { }; requestBody?: never; responses: { - /** @description OK */ + /** @description 로그아웃 성공 */ 200: { headers: { [name: string]: unknown; @@ -1439,6 +1791,15 @@ export interface operations { "*/*": components["schemas"]["SuccessResponseVoid"]; }; }; + /** @description 회원 정보를 찾을 수 없습니다. */ + 404: { + headers: { + [name: string]: unknown; + }; + content: { + "*/*": components["schemas"]["ErrorResponse"]; + }; + }; }; }; createMemberBooking: { @@ -1454,8 +1815,8 @@ export interface operations { }; }; responses: { - /** @description OK */ - 200: { + /** @description 회원 예매가 성공적으로 완료되었습니다. */ + 201: { headers: { [name: string]: unknown; }; @@ -1463,6 +1824,24 @@ export interface operations { "*/*": components["schemas"]["SuccessResponseMemberBookingResponse"]; }; }; + /** @description 잘못된 데이터 형식입니다. */ + 400: { + headers: { + [name: string]: unknown; + }; + content: { + "*/*": components["schemas"]["ErrorResponse"]; + }; + }; + /** @description 회원 정보를 찾을 수 없습니다. */ + 404: { + headers: { + [name: string]: unknown; + }; + content: { + "*/*": components["schemas"]["ErrorResponse"]; + }; + }; }; }; createGuestBookings: { @@ -1478,8 +1857,8 @@ export interface operations { }; }; responses: { - /** @description OK */ - 200: { + /** @description 비회원 예매가 성공적으로 완료되었습니다. */ + 201: { headers: { [name: string]: unknown; }; @@ -1487,6 +1866,24 @@ export interface operations { "*/*": components["schemas"]["SuccessResponseGuestBookingResponse"]; }; }; + /** @description 잘못된 데이터 형식입니다. */ + 400: { + headers: { + [name: string]: unknown; + }; + content: { + "*/*": components["schemas"]["ErrorResponse"]; + }; + }; + /** @description 공연 정보를 찾을 수 없습니다. */ + 404: { + headers: { + [name: string]: unknown; + }; + content: { + "*/*": components["schemas"]["ErrorResponse"]; + }; + }; }; }; getGuestBookings: { @@ -1502,7 +1899,7 @@ export interface operations { }; }; responses: { - /** @description OK */ + /** @description 비회원 예매 조회가 성공적으로 완료되었습니다. */ 200: { headers: { [name: string]: unknown; @@ -1511,6 +1908,81 @@ export interface operations { "*/*": components["schemas"]["SuccessResponseListGuestBookingRetrieveResponse"]; }; }; + /** @description 입력하신 정보와 일치하는 예매 내역이 없습니다. 확인 후 다시 조회해주세요. */ + 404: { + headers: { + [name: string]: unknown; + }; + content: { + "*/*": components["schemas"]["ErrorResponse"]; + }; + }; + }; + }; + refundBookings: { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + requestBody: { + content: { + "application/json": components["schemas"]["BookingRefundRequest"]; + }; + }; + responses: { + /** @description 유료공연 예매 환불 요청이 성공적으로 완료되었습니다. */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "*/*": components["schemas"]["SuccessResponseBookingRefundResponse"]; + }; + }; + /** @description 입력하신 정보와 일치하는 예매 내역이 없습니다. 확인 후 다시 조회해주세요. */ + 404: { + headers: { + [name: string]: unknown; + }; + content: { + "*/*": components["schemas"]["ErrorResponse"]; + }; + }; + }; + }; + cancelBookings: { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + requestBody: { + content: { + "application/json": components["schemas"]["BookingCancelRequest"]; + }; + }; + responses: { + /** @description 무료공연/미입금 예매 취소 요청이 성공적으로 완료되었습니다. */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "*/*": components["schemas"]["SuccessResponseBookingCancelResponse"]; + }; + }; + /** @description 입력하신 정보와 일치하는 예매 내역이 없습니다. 확인 후 다시 조회해주세요. */ + 404: { + headers: { + [name: string]: unknown; + }; + content: { + "*/*": components["schemas"]["ErrorResponse"]; + }; + }; }; }; healthcheck: { @@ -1522,7 +1994,7 @@ export interface operations { }; requestBody?: never; responses: { - /** @description OK */ + /** @description 서버가 정상적으로 동작 중입니다. */ 200: { headers: { [name: string]: unknown; @@ -1544,7 +2016,7 @@ export interface operations { }; requestBody?: never; responses: { - /** @description OK */ + /** @description access token 재발급 성공 */ 200: { headers: { [name: string]: unknown; @@ -1553,13 +2025,22 @@ export interface operations { "*/*": components["schemas"]["SuccessResponseAccessTokenGetSuccess"]; }; }; + /** @description 유효하지 않은 토큰입니다. */ + 400: { + headers: { + [name: string]: unknown; + }; + content: { + "*/*": components["schemas"]["ErrorResponse"]; + }; + }; }; }; getTickets: { parameters: { query?: { scheduleNumber?: "FIRST" | "SECOND" | "THIRD" | "FOURTH" | "FIFTH" | "SIXTH" | "SEVENTH" | "EIGHTH" | "NINTH" | "TENTH"; - bookingStatus?: "CHECKING_PAYMENT" | "BOOKING_CONFIRMED" | "BOOKING_CANCELLED"; + bookingStatus?: "CHECKING_PAYMENT" | "BOOKING_CONFIRMED" | "BOOKING_CANCELLED" | "REFUND_REQUESTED" | "BOOKING_DELETED"; }; header?: never; path: { @@ -1569,7 +2050,7 @@ export interface operations { }; requestBody?: never; responses: { - /** @description OK */ + /** @description 예매자 목록 조회 성공 */ 200: { headers: { [name: string]: unknown; @@ -1578,6 +2059,50 @@ export interface operations { "*/*": components["schemas"]["SuccessResponseTicketRetrieveResponse"]; }; }; + /** @description 입력하신 정보와 일치하는 예매자 목록이 없습니다. */ + 404: { + headers: { + [name: string]: unknown; + }; + content: { + "*/*": components["schemas"]["ErrorResponse"]; + }; + }; + }; + }; + searchTickets: { + parameters: { + query: { + searchWord: string; + scheduleNumber?: "FIRST" | "SECOND" | "THIRD" | "FOURTH" | "FIFTH" | "SIXTH" | "SEVENTH" | "EIGHTH" | "NINTH" | "TENTH"; + bookingStatus?: "CHECKING_PAYMENT" | "BOOKING_CONFIRMED" | "BOOKING_CANCELLED" | "REFUND_REQUESTED" | "BOOKING_DELETED"; + }; + header?: never; + path: { + performanceId: number; + }; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description 예매자 목록 검색 성공 */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "*/*": components["schemas"]["SuccessResponseTicketRetrieveResponse"]; + }; + }; + /** @description 입력하신 정보와 일치하는 예매자 목록이 없습니다. */ + 404: { + headers: { + [name: string]: unknown; + }; + content: { + "*/*": components["schemas"]["ErrorResponse"]; + }; + }; }; }; getTicketAvailability: { @@ -1593,7 +2118,7 @@ export interface operations { }; requestBody?: never; responses: { - /** @description OK */ + /** @description 티켓 수량 조회가 성공적으로 완료되었습니다. */ 200: { headers: { [name: string]: unknown; @@ -1602,6 +2127,33 @@ export interface operations { "*/*": components["schemas"]["SuccessResponseTicketAvailabilityResponse"]; }; }; + /** @description 잘못된 데이터 형식입니다. */ + 400: { + headers: { + [name: string]: unknown; + }; + content: { + "*/*": components["schemas"]["ErrorResponse"]; + }; + }; + /** @description 회차 정보를 찾을 수 없습니다. */ + 404: { + headers: { + [name: string]: unknown; + }; + content: { + "*/*": components["schemas"]["ErrorResponse"]; + }; + }; + /** @description 요청한 티켓 수량이 잔여 티켓 수를 초과했습니다. */ + 409: { + headers: { + [name: string]: unknown; + }; + content: { + "*/*": components["schemas"]["ErrorResponse"]; + }; + }; }; }; getPerformanceForEdit: { @@ -1615,7 +2167,7 @@ export interface operations { }; requestBody?: never; responses: { - /** @description OK */ + /** @description 공연 수정 페이지 정보 조회 성공 */ 200: { headers: { [name: string]: unknown; @@ -1624,6 +2176,15 @@ export interface operations { "*/*": components["schemas"]["SuccessResponsePerformanceModifyDetailResponse"]; }; }; + /** @description 공연 정보를 찾을 수 없습니다. */ + 404: { + headers: { + [name: string]: unknown; + }; + content: { + "*/*": components["schemas"]["ErrorResponse"]; + }; + }; }; }; deletePerformance: { @@ -1637,7 +2198,7 @@ export interface operations { }; requestBody?: never; responses: { - /** @description OK */ + /** @description 공연 삭제 성공 */ 200: { headers: { [name: string]: unknown; @@ -1646,6 +2207,24 @@ export interface operations { "*/*": components["schemas"]["SuccessResponseVoid"]; }; }; + /** @description 공연의 소유자가 아니거나 예매자가 있어 삭제할 수 없습니다. */ + 403: { + headers: { + [name: string]: unknown; + }; + content: { + "*/*": components["schemas"]["ErrorResponse"]; + }; + }; + /** @description 공연 정보를 찾을 수 없습니다. */ + 404: { + headers: { + [name: string]: unknown; + }; + content: { + "*/*": components["schemas"]["ErrorResponse"]; + }; + }; }; }; getUserPerformances: { @@ -1657,7 +2236,7 @@ export interface operations { }; requestBody?: never; responses: { - /** @description OK */ + /** @description 회원이 등록한 공연 목록 조회 성공 */ 200: { headers: { [name: string]: unknown; @@ -1666,6 +2245,15 @@ export interface operations { "*/*": components["schemas"]["SuccessResponseMakerPerformanceResponse"]; }; }; + /** @description 회원 정보를 찾을 수 없습니다. */ + 404: { + headers: { + [name: string]: unknown; + }; + content: { + "*/*": components["schemas"]["ErrorResponse"]; + }; + }; }; }; getPerformanceDetail: { @@ -1679,7 +2267,7 @@ export interface operations { }; requestBody?: never; responses: { - /** @description OK */ + /** @description 공연 상세정보 조회 성공 */ 200: { headers: { [name: string]: unknown; @@ -1688,6 +2276,15 @@ export interface operations { "*/*": components["schemas"]["SuccessResponsePerformanceDetailResponse"]; }; }; + /** @description 공연 정보를 찾을 수 없습니다. */ + 404: { + headers: { + [name: string]: unknown; + }; + content: { + "*/*": components["schemas"]["ErrorResponse"]; + }; + }; }; }; getBookingPerformanceDetail: { @@ -1701,7 +2298,7 @@ export interface operations { }; requestBody?: never; responses: { - /** @description OK */ + /** @description 예매하기 관련 공연 정보 조회 성공 */ 200: { headers: { [name: string]: unknown; @@ -1710,12 +2307,21 @@ export interface operations { "*/*": components["schemas"]["SuccessResponseBookingPerformanceDetailResponse"]; }; }; + /** @description 공연 정보를 찾을 수 없습니다. */ + 404: { + headers: { + [name: string]: unknown; + }; + content: { + "*/*": components["schemas"]["ErrorResponse"]; + }; + }; }; }; getHomePerformanceList: { parameters: { query?: { - genre?: string; + genre?: "BAND" | "PLAY" | "DANCE" | "ETC"; }; header?: never; path?: never; @@ -1723,13 +2329,13 @@ export interface operations { }; requestBody?: never; responses: { - /** @description OK */ + /** @description 홈 화면 공연 목록 조회가 성공적으로 완료되었습니다. */ 200: { headers: { [name: string]: unknown; }; content: { - "*/*": components["schemas"]["SuccessResponseHomeResponse"]; + "*/*": components["schemas"]["SuccessResponseHomeFindAllResponse"]; }; }; }; @@ -1754,7 +2360,7 @@ export interface operations { [name: string]: unknown; }; content: { - "*/*": components["schemas"]["SuccessResponse"]; + "*/*": components["schemas"]["SuccessResponsePerformanceMakerPresignedUrlFindAllResponse"]; }; }; /** @description S3 PreSigned url을 받아오기에 실패했습니다. */ @@ -1777,7 +2383,7 @@ export interface operations { }; requestBody?: never; responses: { - /** @description OK */ + /** @description 회원 예매 조회가 성공적으로 완료되었습니다. */ 200: { headers: { [name: string]: unknown; @@ -1786,6 +2392,15 @@ export interface operations { "*/*": components["schemas"]["SuccessResponseListMemberBookingRetrieveResponse"]; }; }; + /** @description 입력하신 정보와 일치하는 예매 내역이 없습니다. 확인 후 다시 조회해주세요. */ + 404: { + headers: { + [name: string]: unknown; + }; + content: { + "*/*": components["schemas"]["ErrorResponse"]; + }; + }; }; }; readAllUsers: { @@ -1803,7 +2418,7 @@ export interface operations { [name: string]: unknown; }; content: { - "*/*": components["schemas"]["SuccessResponse"]; + "*/*": components["schemas"]["SuccessResponseUserFindAllResponse"]; }; }; /** @description 회원이 없습니다 */ @@ -1834,7 +2449,7 @@ export interface operations { [name: string]: unknown; }; content: { - "*/*": components["schemas"]["SuccessResponse"]; + "*/*": components["schemas"]["SuccessResponseCarouselPresignedUrlFindAllResponse"]; }; }; /** @description 회원이 없습니다. */ @@ -1865,7 +2480,7 @@ export interface operations { [name: string]: unknown; }; content: { - "*/*": components["schemas"]["SuccessResponse"]; + "*/*": components["schemas"]["SuccessResponseBannerPresignedUrlFindResponse"]; }; }; /** @description 회원이 없습니다. */ diff --git a/tsconfig.json b/tsconfig.json index ea9d0cd8..10e52547 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,5 +1,10 @@ { "files": [], + "compilerOptions": { + "types": [ + "kakao.maps.d.ts" + ] + }, "references": [ { "path": "./tsconfig.app.json" diff --git a/yarn.lock b/yarn.lock index c4ddc2b5..7b86625e 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1568,6 +1568,15 @@ __metadata: languageName: node linkType: hard +"@babel/runtime@npm:^7.22.15": + version: 7.26.0 + resolution: "@babel/runtime@npm:7.26.0" + dependencies: + regenerator-runtime: "npm:^0.14.0" + checksum: 10c0/12c01357e0345f89f4f7e8c0e81921f2a3e3e101f06e8eaa18a382b517376520cd2fa8c237726eb094dab25532855df28a7baaf1c26342b52782f6936b07c287 + languageName: node + linkType: hard + "@babel/template@npm:^7.24.7, @babel/template@npm:^7.25.0": version: 7.25.0 resolution: "@babel/template@npm:7.25.0" @@ -3953,6 +3962,13 @@ __metadata: languageName: node linkType: hard +"@types/lodash@npm:^4": + version: 4.17.13 + resolution: "@types/lodash@npm:4.17.13" + checksum: 10c0/c3d0b7efe7933ac0369b99f2f7bff9240d960680fdb74b41ed4bd1b3ca60cca1e31fe4046d9abbde778f941a41bc2a75eb629abf8659fa6c27b66efbbb0802a9 + languageName: node + linkType: hard + "@types/lodash@npm:^4.14.167": version: 4.17.7 resolution: "@types/lodash@npm:4.17.7" @@ -4513,6 +4529,13 @@ __metadata: languageName: node linkType: hard +"@wojtekmaj/date-utils@npm:^1.1.3, @wojtekmaj/date-utils@npm:^1.5.0": + version: 1.5.1 + resolution: "@wojtekmaj/date-utils@npm:1.5.1" + checksum: 10c0/7c213cca5ab6b84ef61b9aea2b9fb8a04bf4c9764b28a97ffc4ee46a3e81560532a74d106a6f8aeef4792e1aaa6ea3dfd3c4a639dddbea560eb3f33cd62b8d7d + languageName: node + linkType: hard + "@yarnpkg/core@npm:^4.1.2, @yarnpkg/core@npm:^4.1.3": version: 4.1.3 resolution: "@yarnpkg/core@npm:4.1.3" @@ -5357,6 +5380,7 @@ __metadata: "@storybook/test": "npm:^8.1.11" "@tanstack/react-query": "npm:^5.51.1" "@tanstack/react-query-devtools": "npm:^5.51.1" + "@types/lodash": "npm:^4" "@types/prettier-linter-helpers": "npm:^1" "@types/qs": "npm:^6" "@types/react": "npm:^18.3.3" @@ -5386,6 +5410,7 @@ __metadata: husky: "npm:^8.0.0" jotai: "npm:^2.8.4" lint-staged: "npm:^15.2.7" + lodash: "npm:^4.17.21" lottie-react: "npm:^2.4.0" openapi-typescript: "npm:^7.4.1" postcss: "npm:^8.4.38" @@ -5397,13 +5422,16 @@ __metadata: puppeteer-core: "npm:^22.15.0" react: "npm:^18.3.1" react-beautiful-dnd: "npm:^13.1.1" + react-calendar: "npm:^5.1.0" react-components: "npm:^0.5.1" react-csv: "npm:^2.2.2" react-dom: "npm:^18.3.1" react-helmet-async: "npm:^2.0.5" react-image-crop: "npm:^11.0.6" + react-kakao-maps-sdk: "npm:^1.1.27" react-lottie-player: "npm:^2.0.0" react-router-dom: "npm:^6.24.0" + react-time-picker: "npm:^7.0.0" shelljs: "npm:^0.8.5" storybook: "npm:^8.1.11" storybook-addon-theme-provider: "npm:^0.2.2" @@ -5877,7 +5905,7 @@ __metadata: languageName: node linkType: hard -"clsx@npm:^2.1.0, clsx@npm:^2.1.1": +"clsx@npm:^2.0.0, clsx@npm:^2.1.0, clsx@npm:^2.1.1": version: 2.1.1 resolution: "clsx@npm:2.1.1" checksum: 10c0/c4c8eb865f8c82baab07e71bfa8897c73454881c4f99d6bc81585aecd7c441746c1399d08363dc096c550cceaf97bd4ce1e8854e1771e9998d9f94c4fe075839 @@ -6464,6 +6492,13 @@ __metadata: languageName: node linkType: hard +"detect-element-overflow@npm:^1.4.0": + version: 1.4.2 + resolution: "detect-element-overflow@npm:1.4.2" + checksum: 10c0/dcc5f6d89c31f6035202cce64b96093338e777752b62c401c672e84908097ef01f833af5864e1c86c6440e4f843658f006d1084fb9090983ea5c438c8ad4a180 + languageName: node + linkType: hard + "devtools-protocol@npm:0.0.1312386": version: 0.0.1312386 resolution: "devtools-protocol@npm:0.0.1312386" @@ -8051,6 +8086,15 @@ __metadata: languageName: node linkType: hard +"get-user-locale@npm:^2.2.1": + version: 2.3.2 + resolution: "get-user-locale@npm:2.3.2" + dependencies: + mem: "npm:^8.0.0" + checksum: 10c0/2796b3fc3782b1f4826f31e899642cf72eeb23e296e1cf55280aab5caf7a25f4b906491ee1508a001519d6a410902ccf8fa8edaa895b7aee5dfd422ffe5523b9 + languageName: node + linkType: hard + "get-value@npm:^2.0.3, get-value@npm:^2.0.6": version: 2.0.6 resolution: "get-value@npm:2.0.6" @@ -9329,6 +9373,13 @@ __metadata: languageName: node linkType: hard +"kakao.maps.d.ts@npm:^0.1.39": + version: 0.1.40 + resolution: "kakao.maps.d.ts@npm:0.1.40" + checksum: 10c0/16eef0d9846311921ae3c5c020a8769ab8d1f894b2a6f1aef869a83648462982c2891bc8ea174665345290483bf73feb6ae264273cee38a40a1adbb17aa6862f + languageName: node + linkType: hard + "keyv@npm:^4.0.0, keyv@npm:^4.5.4": version: 4.5.4 resolution: "keyv@npm:4.5.4" @@ -9631,6 +9682,13 @@ __metadata: languageName: node linkType: hard +"make-event-props@npm:^1.6.0": + version: 1.6.2 + resolution: "make-event-props@npm:1.6.2" + checksum: 10c0/ecf0b742e43a392c07e2267baca2397e750d38cc14ef3cb72ef8bfe4a8c8b0fd99a03a2eeab84a26c2b204f7c231da6af31fa26321fbfd413ded43ba1825e867 + languageName: node + linkType: hard + "make-fetch-happen@npm:^13.0.0": version: 13.0.1 resolution: "make-fetch-happen@npm:13.0.1" @@ -9651,6 +9709,15 @@ __metadata: languageName: node linkType: hard +"map-age-cleaner@npm:^0.1.3": + version: 0.1.3 + resolution: "map-age-cleaner@npm:0.1.3" + dependencies: + p-defer: "npm:^1.0.0" + checksum: 10c0/7495236c7b0950956c144fd8b4bc6399d4e78072a8840a4232fe1c4faccbb5eb5d842e5c0a56a60afc36d723f315c1c672325ca03c1b328650f7fcc478f385fd + languageName: node + linkType: hard + "map-cache@npm:^0.2.2": version: 0.2.2 resolution: "map-cache@npm:0.2.2" @@ -9704,6 +9771,16 @@ __metadata: languageName: node linkType: hard +"mem@npm:^8.0.0": + version: 8.1.1 + resolution: "mem@npm:8.1.1" + dependencies: + map-age-cleaner: "npm:^0.1.3" + mimic-fn: "npm:^3.1.0" + checksum: 10c0/5829c404d024c1accaf76ebacbc7eae9b59e5ce5722d184aa24e8387a8097a499f6aa7e181021003c51eb87b2dcdc9a2270050c58753cce761de206643cba91c + languageName: node + linkType: hard + "memoize-one@npm:^5.1.1": version: 5.2.1 resolution: "memoize-one@npm:5.2.1" @@ -9820,6 +9897,13 @@ __metadata: languageName: node linkType: hard +"mimic-fn@npm:^3.1.0": + version: 3.1.0 + resolution: "mimic-fn@npm:3.1.0" + checksum: 10c0/a07cdd8ed6490c2dff5b11f889b245d9556b80f5a653a552a651d17cff5a2d156e632d235106c2369f00cccef4071704589574cf3601bc1b1400a1f620dff067 + languageName: node + linkType: hard + "mimic-fn@npm:^4.0.0": version: 4.0.0 resolution: "mimic-fn@npm:4.0.0" @@ -10377,6 +10461,13 @@ __metadata: languageName: node linkType: hard +"p-defer@npm:^1.0.0": + version: 1.0.0 + resolution: "p-defer@npm:1.0.0" + checksum: 10c0/ed603c3790e74b061ac2cb07eb6e65802cf58dce0fbee646c113a7b71edb711101329ad38f99e462bd2e343a74f6e9366b496a35f1d766c187084d3109900487 + languageName: node + linkType: hard + "p-limit@npm:^2.2.0": version: 2.3.0 resolution: "p-limit@npm:2.3.0" @@ -11020,6 +11111,43 @@ __metadata: languageName: node linkType: hard +"react-calendar@npm:^5.1.0": + version: 5.1.0 + resolution: "react-calendar@npm:5.1.0" + dependencies: + "@wojtekmaj/date-utils": "npm:^1.1.3" + clsx: "npm:^2.0.0" + get-user-locale: "npm:^2.2.1" + warning: "npm:^4.0.0" + peerDependencies: + "@types/react": ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 + react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 + react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 + peerDependenciesMeta: + "@types/react": + optional: true + checksum: 10c0/27673f639c5d6296342a2a888436b31a5d602faeaae01be83b2beb98ff568b0a3d1514f5cc50fcacf3ac50b9c0b9d2fb423b0c001a8f5f1a22816671409e2616 + languageName: node + linkType: hard + +"react-clock@npm:^5.0.0": + version: 5.1.0 + resolution: "react-clock@npm:5.1.0" + dependencies: + "@wojtekmaj/date-utils": "npm:^1.5.0" + clsx: "npm:^2.0.0" + get-user-locale: "npm:^2.2.1" + peerDependencies: + "@types/react": ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 + react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 + react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 + peerDependenciesMeta: + "@types/react": + optional: true + checksum: 10c0/98a9e29b7e36f56cb49c6fc9f7212b5b54fe06bb5b1a1b25bc31265ed8beaa71b7e74f566cf61290cb1c503b238342d77a86684ebabe37314b12a12e0e630a10 + languageName: node + linkType: hard + "react-colorful@npm:^5.1.2": version: 5.6.1 resolution: "react-colorful@npm:5.6.1" @@ -11117,6 +11245,25 @@ __metadata: languageName: node linkType: hard +"react-fit@npm:^2.0.0": + version: 2.0.1 + resolution: "react-fit@npm:2.0.1" + dependencies: + detect-element-overflow: "npm:^1.4.0" + warning: "npm:^4.0.0" + peerDependencies: + "@types/react": ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 + react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 + react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 + peerDependenciesMeta: + "@types/react": + optional: true + "@types/react-dom": + optional: true + checksum: 10c0/0202f4e3241bd2e967ec3728c97ab57842353ae48f218ca56d28be14737a7a759e40c3cd05700d9e328ab88e377eccb296f1e33530e6775d3acc05df97084018 + languageName: node + linkType: hard + "react-helmet-async@npm:^2.0.5": version: 2.0.5 resolution: "react-helmet-async@npm:2.0.5" @@ -11167,6 +11314,19 @@ __metadata: languageName: node linkType: hard +"react-kakao-maps-sdk@npm:^1.1.27": + version: 1.1.27 + resolution: "react-kakao-maps-sdk@npm:1.1.27" + dependencies: + "@babel/runtime": "npm:^7.22.15" + kakao.maps.d.ts: "npm:^0.1.39" + peerDependencies: + react: ^16.8 || ^17 || ^18 + react-dom: ^16.8 || ^17 || ^18 + checksum: 10c0/1cc0dd855f1b8c0ba3d089ada352ecab8569d5a6cd9344baebc3161cf64d0670ff633a422636289e549b42da6b38997ddb7a4d3ddf6b616f93f9b2e96e04eae1 + languageName: node + linkType: hard + "react-lottie-player@npm:^2.0.0": version: 2.1.0 resolution: "react-lottie-player@npm:2.1.0" @@ -11225,6 +11385,28 @@ __metadata: languageName: node linkType: hard +"react-time-picker@npm:^7.0.0": + version: 7.0.0 + resolution: "react-time-picker@npm:7.0.0" + dependencies: + "@wojtekmaj/date-utils": "npm:^1.1.3" + clsx: "npm:^2.0.0" + get-user-locale: "npm:^2.2.1" + make-event-props: "npm:^1.6.0" + react-clock: "npm:^5.0.0" + react-fit: "npm:^2.0.0" + update-input-width: "npm:^1.4.0" + peerDependencies: + "@types/react": ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 + react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 + react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 + peerDependenciesMeta: + "@types/react": + optional: true + checksum: 10c0/538a1964de1ef9021b195f856efedc207019b6da1b9bbdcb6899c876ec6b4eeb02f4f16b77677dc0edb704099ed7f204dde0821573b7a1f1f24909e02ea2aee6 + languageName: node + linkType: hard + "react-transition-group@npm:^4.4.5": version: 4.4.5 resolution: "react-transition-group@npm:4.4.5" @@ -13249,6 +13431,13 @@ __metadata: languageName: node linkType: hard +"update-input-width@npm:^1.4.0": + version: 1.4.2 + resolution: "update-input-width@npm:1.4.2" + checksum: 10c0/d3344f91c1c08a26f81d172dd774ca8834ddfaec1eb78e05280d303800a3236c4e122df14ea34fe7f0e1bdada733dec5d3676d38ce0777bafe603de0a6199473 + languageName: node + linkType: hard + "upper-case@npm:^1.1.1": version: 1.1.3 resolution: "upper-case@npm:1.1.3" @@ -13417,6 +13606,15 @@ __metadata: languageName: node linkType: hard +"warning@npm:^4.0.0": + version: 4.0.3 + resolution: "warning@npm:4.0.3" + dependencies: + loose-envify: "npm:^1.0.0" + checksum: 10c0/aebab445129f3e104c271f1637fa38e55eb25f968593e3825bd2f7a12bd58dc3738bb70dc8ec85826621d80b4acfed5a29ebc9da17397c6125864d72301b937e + languageName: node + linkType: hard + "webidl-conversions@npm:^3.0.0": version: 3.0.1 resolution: "webidl-conversions@npm:3.0.1"