diff --git a/apps/web/app/_apis/mutations/useCreateNewPlace.ts b/apps/web/app/_apis/mutations/useCreateNewPlace.ts new file mode 100644 index 0000000..a6ae49d --- /dev/null +++ b/apps/web/app/_apis/mutations/useCreateNewPlace.ts @@ -0,0 +1,25 @@ +import { useMutation } from '@tanstack/react-query' +import type { NewPlaceRequest } from '@/_apis/schemas/place' +import { createNewPlace } from '@/_apis/services/place' +import { useRouter } from 'next/navigation' +import { CLIENT_PATH } from '@/_constants/path' + +export const useCreateNewPlace = () => { + const { replace } = useRouter() + + return useMutation({ + mutationFn: async (placeData: NewPlaceRequest) => + await createNewPlace(placeData), + onSuccess: (res) => { + if (res.status === 'OK') { + replace(CLIENT_PATH.PLACE_NEW_SUCCESS) + } else { + replace(CLIENT_PATH.PLACE_NEW_FAIL) + } + }, + onError: (error) => { + console.error(error) + replace(CLIENT_PATH.PLACE_NEW_FAIL) + }, + }) +} diff --git a/apps/web/app/_apis/queries/place.ts b/apps/web/app/_apis/queries/place.ts index b59b432..10a6e31 100644 --- a/apps/web/app/_apis/queries/place.ts +++ b/apps/web/app/_apis/queries/place.ts @@ -17,7 +17,8 @@ export const PlaceQueryKeys = { [...PlaceQueryKeys.all(), 'ranking', sort, campus] as const, byCategory: (id: string, campus: CampusType) => [...PlaceQueryKeys.all(), 'category', id, campus] as const, - byMap: () => [...PlaceQueryKeys.all(), 'map'] as const, + byMap: (bounds: MapBounds | null) => + [...PlaceQueryKeys.all(), 'map', bounds] as const, byPreview: (kakaoPlaceId: string) => [...PlaceQueryKeys.all(), 'preview', kakaoPlaceId] as const, byLike: () => [...PlaceQueryKeys.all(), 'like'] as const, @@ -44,7 +45,7 @@ export const usePlaceQueries = { byMap: (bounds: MapBounds | null) => { return queryOptions({ - queryKey: PlaceQueryKeys.byMap(), + queryKey: PlaceQueryKeys.byMap(bounds), queryFn: () => { if (!bounds) return Promise.resolve([]) return getPlacesByMap(bounds) diff --git a/apps/web/app/_apis/schemas/place.ts b/apps/web/app/_apis/schemas/place.ts index be0bd82..ed02d51 100644 --- a/apps/web/app/_apis/schemas/place.ts +++ b/apps/web/app/_apis/schemas/place.ts @@ -9,6 +9,7 @@ const location = z.object({ const menus = z.array( z.object({ + menuId: z.number(), name: z.string(), price: z.number(), isRecommended: z.boolean(), @@ -17,7 +18,7 @@ const menus = z.array( const photos = z.array( z.object({ - photoId: z.number().transform(String), + photoId: z.nullable(z.number().transform(String)), photoUrl: z.url(), displayOrder: z.number(), }), @@ -77,3 +78,6 @@ export type PlaceByMap = z.infer export type PlaceDetail = z.infer export type PlaceByPreview = z.infer export type NewPlaceRequest = z.infer +export type NewPlaceResponse = { + status: 'OK' | 'ERROR' +} diff --git a/apps/web/app/_apis/services/category.ts b/apps/web/app/_apis/services/category.ts index 0e84cce..f009182 100644 --- a/apps/web/app/_apis/services/category.ts +++ b/apps/web/app/_apis/services/category.ts @@ -3,6 +3,7 @@ import { API_PATH } from '@/_constants/path' import { CategorySchema, Category } from '../schemas/category' export const getCategories = async (): Promise => { - const { data } = await axiosInstance.get(API_PATH.CATEGORY) + const { data: response } = await axiosInstance.get(API_PATH.CATEGORY) + const { data } = response return CategorySchema.array().parse(data) } diff --git a/apps/web/app/_apis/services/event.ts b/apps/web/app/_apis/services/event.ts index f8aad64..ed53ecc 100644 --- a/apps/web/app/_apis/services/event.ts +++ b/apps/web/app/_apis/services/event.ts @@ -8,7 +8,8 @@ import { } from '@/_apis/schemas/event' export const getEventInfo = async (): Promise => { - const { data } = await axiosInstance.get(API_PATH.EVENT.INFO) + const { data: response } = await axiosInstance.get(API_PATH.EVENT.INFO) + const { data } = response return EventSchema.parse(data) } @@ -16,11 +17,16 @@ export const participationEvent = async (body: { eventId: string ticketsCount: number }) => { - const { data } = await axiosInstance.post(API_PATH.EVENT.PARTICIPATIONS, body) + const { data: response } = await axiosInstance.post( + API_PATH.EVENT.PARTICIPATIONS, + body, + ) + const { data } = response return data } export const getEventResult = async (): Promise => { - const { data } = await axiosInstance.get(API_PATH.EVENT.RESULT) + const { data: response } = await axiosInstance.get(API_PATH.EVENT.RESULT) + const { data } = response return EventResultSchema.parse(data) } diff --git a/apps/web/app/_apis/services/like.ts b/apps/web/app/_apis/services/like.ts index 8ed83cf..15cb28b 100644 --- a/apps/web/app/_apis/services/like.ts +++ b/apps/web/app/_apis/services/like.ts @@ -7,13 +7,17 @@ type Response = { } export const addLike = async (placeId: string): Promise => { - const { data } = await axiosInstance.post(API_PATH.PLACES.LIKE.POST(placeId)) + const { data: response } = await axiosInstance.post( + API_PATH.PLACES.LIKE.POST(placeId), + ) + const { data } = response return data } export const removeLike = async (placeId: string): Promise => { - const { data } = await axiosInstance.delete( + const { data: response } = await axiosInstance.delete( API_PATH.PLACES.LIKE.DELETE(placeId), ) + const { data } = response return data } diff --git a/apps/web/app/_apis/services/place.ts b/apps/web/app/_apis/services/place.ts index 19ba8ad..752118e 100644 --- a/apps/web/app/_apis/services/place.ts +++ b/apps/web/app/_apis/services/place.ts @@ -9,6 +9,8 @@ import { type MapBounds, type PlaceByMap, type PlaceByPreview, + type NewPlaceRequest, + type NewPlaceResponse, BasePlaceSchema, PlaceByMapSchema, PlaceDetailSchema, @@ -23,9 +25,10 @@ export const getPlacesByRanking = async ( sort: RankingPlaceSort, campus: CampusType, ): Promise => { - const { data } = await axiosInstance.get( + const { data: response } = await axiosInstance.get( API_PATH.PLACES.BY_RANKING(sort, campus), ) + const { data } = response return BasePlaceSchema.array().parse(data) } @@ -33,9 +36,10 @@ export const getPlacesByCategory = async ( id: string, campus: CampusType, ): Promise => { - const { data } = await axiosInstance.get( + const { data: response } = await axiosInstance.get( API_PATH.PLACES.BY_CATEGORY(id, campus), ) + const { data } = response return BasePlaceSchema.array().parse(data) } @@ -45,7 +49,7 @@ export const getPlacesByMap = async ({ maxLatitude, maxLongitude, }: MapBounds): Promise => { - const { data } = await axiosInstance.get( + const { data: response } = await axiosInstance.get( API_PATH.PLACES.BY_MAP({ minLatitude, minLongitude, @@ -53,12 +57,13 @@ export const getPlacesByMap = async ({ maxLongitude, }), ) - + const { data } = response return PlaceByMapSchema.array().parse(data) } export const getPlaceDetail = async (id: string): Promise => { - const { data } = await axiosInstance.get(API_PATH.PLACES.DETAIL(id)) + const { data: response } = await axiosInstance.get(API_PATH.PLACES.DETAIL(id)) + const { data } = response return PlaceDetailSchema.parse(data) } @@ -78,20 +83,33 @@ export const getSearchPlaceByKakao = async ({ }, }, ) - return data } export const getPlaceByPreview = async ( kakaoPlaceId: string, ): Promise => { - const { data } = await axiosInstance.get( + const { data: response } = await axiosInstance.get( API_PATH.PLACES.NEW.PREVIEW(kakaoPlaceId), ) + const { data } = response return PlaceByPreviewSchema.parse(data) } export const getPlacesByLike = async (): Promise => { - const { data } = await axiosInstance.get(API_PATH.PLACES.LIKE.GET) + const { data: response } = await axiosInstance.get(API_PATH.PLACES.LIKE.GET) + const { data } = response return BasePlaceSchema.array().parse(data) } + +export const createNewPlace = async ( + placeData: NewPlaceRequest, +): Promise => { + const dataSet = { + ...placeData, + tagIds: placeData.tagIds.map(Number), + categoryIds: placeData.categoryIds.map(Number), + } + const { data } = await axiosInstance.post(API_PATH.PLACES.NEW.CREATE, dataSet) + return data +} diff --git a/apps/web/app/_apis/services/request.ts b/apps/web/app/_apis/services/request.ts index a2398f3..4f03213 100644 --- a/apps/web/app/_apis/services/request.ts +++ b/apps/web/app/_apis/services/request.ts @@ -8,11 +8,15 @@ import { } from '@/_apis/schemas/request' export const getRequests = async (): Promise => { - const { data } = await axiosInstance.get(API_PATH.REQUEST.LIST) + const { data: response } = await axiosInstance.get(API_PATH.REQUEST.LIST) + const { data } = response return RequestSchema.array().parse(data) } export const getRequestDetail = async (id: string): Promise => { - const { data } = await axiosInstance.get(API_PATH.REQUEST.DETAIL(id)) + const { data: response } = await axiosInstance.get( + API_PATH.REQUEST.DETAIL(id), + ) + const { data } = response return RequestDetailSchema.parse(data) } diff --git a/apps/web/app/_constants/campus.ts b/apps/web/app/_constants/campus.ts index e26cda0..492d6d6 100644 --- a/apps/web/app/_constants/campus.ts +++ b/apps/web/app/_constants/campus.ts @@ -1,21 +1,21 @@ import { COLOR_VARIANTS } from '@repo/ui/consts/colorVariant' export const CAMPUS = { - singwan: '신관', - cheonan: '천안', - yesan: '예산', + SINGWAN: '신관', + CHEONAN: '천안', + YESAN: '예산', } as const export const CAMPUS_LOCATION = { - singwan: { latitude: 36.469483428385914, longitude: 127.14059828594706 }, - cheonan: { latitude: 36.85101236046876, longitude: 127.15093333537096 }, - yesan: { latitude: 36.66990298154316, longitude: 126.85937468290652 }, + SINGWAN: { latitude: 36.469483428385914, longitude: 127.14059828594706 }, + CHEONAN: { latitude: 36.85101236046876, longitude: 127.15093333537096 }, + YESAN: { latitude: 36.66990298154316, longitude: 126.85937468290652 }, } as const export const CAMPUS_COLOR = { - singwan: COLOR_VARIANTS.red, - cheonan: COLOR_VARIANTS.blue, - yesan: COLOR_VARIANTS.yellow, + SINGWAN: COLOR_VARIANTS.red, + CHEONAN: COLOR_VARIANTS.blue, + YESAN: COLOR_VARIANTS.yellow, } as const export type CampusType = keyof typeof CAMPUS export const CAMPUS_LIST = Object.keys(CAMPUS) as CampusType[] diff --git a/apps/web/app/_constants/path.ts b/apps/web/app/_constants/path.ts index 7aaa000..b50f623 100644 --- a/apps/web/app/_constants/path.ts +++ b/apps/web/app/_constants/path.ts @@ -5,7 +5,7 @@ export const API_PATH = { CATEGORY: '/categories', PLACES: { BY_CATEGORY: (id: string, campus: CampusType) => - `/places?categoryId=${id}&campus=${campus}`, + `/categories/${id}/places?campus=${campus}`, BY_RANKING: (sort: RankingPlaceSort, campus: CampusType) => `/places/ranking?sort=${sort}&campus=${campus}`, BY_MAP: ({ @@ -14,11 +14,12 @@ export const API_PATH = { maxLatitude, maxLongitude, }: MapBounds) => - `/places?northEastLatitude=${maxLatitude}&northEastLongitudede=${maxLongitude}&southWestLatitude=${minLatitude}&southWestLongitude=${minLongitude}`, + `/places?maxLat=${maxLatitude}&maxLng=${maxLongitude}&minLat=${minLatitude}&minLng=${minLongitude}`, DETAIL: (id: string) => `/places/${id}`, NEW: { PREVIEW: (kakaoPlaceId: string) => `/places/preview?kakaoPlaceId=${kakaoPlaceId}`, + CREATE: '/places', }, LIKE: { GET: '/places/like', @@ -45,6 +46,8 @@ export const CLIENT_PATH = { MAIN: '/', MAP: '/map', PLACE_NEW: '/places/new', + PLACE_NEW_SUCCESS: '/places/new/success', + PLACE_NEW_FAIL: '/places/new/fail', PLACE_SEARCH: '/places/search', PLACE_DETAIL: (id: string | number) => `/places/${id}`, CATEGORY_DETAIL: (id: string | number) => `/categories/${id}`, diff --git a/apps/web/app/_mocks/handlers/index.ts b/apps/web/app/_mocks/handlers/index.ts index 6cf1065..53f0b9a 100644 --- a/apps/web/app/_mocks/handlers/index.ts +++ b/apps/web/app/_mocks/handlers/index.ts @@ -1,11 +1,11 @@ -import { CategoryHandlers } from './categoryHandlers' +// import { CategoryHandlers } from './categoryHandlers' import { PlaceHandlers } from './placeHandlers' import { EventHandlers } from './eventHandlers' import { LikeHandlers } from './likeHandlers' import { RequestHandlers } from './requestHandlers' export const handlers = [ - ...CategoryHandlers, + // ...CategoryHandlers, ...PlaceHandlers, ...EventHandlers, ...LikeHandlers, diff --git a/apps/web/app/_mocks/handlers/placeHandlers.ts b/apps/web/app/_mocks/handlers/placeHandlers.ts index 978e315..386b9c5 100644 --- a/apps/web/app/_mocks/handlers/placeHandlers.ts +++ b/apps/web/app/_mocks/handlers/placeHandlers.ts @@ -2,41 +2,41 @@ import { http, HttpResponse } from 'msw' import { API_PATH } from '@/_constants/path' import { addBaseUrl } from './addBaseUrl' import { - PlaceDetail, + // PlaceDetail, Places, - PlacesWithLocation, - PlacePreview, + // PlacesWithLocation, + // PlacePreview, } from '../data/place' export const PlaceHandlers = [ - http.get( - addBaseUrl( - API_PATH.PLACES.BY_MAP({ - maxLatitude: 36.4756348, - maxLongitude: 127.1454263, - minLatitude: 36.4633315, - minLongitude: 127.1357703, - }), - ), - () => { - return HttpResponse.json(PlacesWithLocation) - }, - ), - http.get(addBaseUrl(API_PATH.PLACES.BY_RANKING('likes', 'singwan')), () => { - return HttpResponse.json(Places) - }), - http.get(addBaseUrl(API_PATH.PLACES.BY_RANKING('views', 'singwan')), () => { - return HttpResponse.json(Places) - }), - http.get(addBaseUrl(API_PATH.PLACES.BY_CATEGORY('1', 'singwan')), () => { - return HttpResponse.json(Places) - }), - http.get(addBaseUrl(API_PATH.PLACES.DETAIL('1')), () => { - return HttpResponse.json(PlaceDetail) - }), - http.get(addBaseUrl(API_PATH.PLACES.NEW.PREVIEW('1')), () => { - return HttpResponse.json(PlacePreview) - }), + // http.get( + // addBaseUrl( + // API_PATH.PLACES.BY_MAP({ + // maxLatitude: 36.4756348, + // maxLongitude: 127.1454263, + // minLatitude: 36.4633315, + // minLongitude: 127.1357703, + // }), + // ), + // () => { + // return HttpResponse.json(PlacesWithLocation) + // }, + // ), + // http.get(addBaseUrl(API_PATH.PLACES.BY_RANKING('likes', 'singwan')), () => { + // return HttpResponse.json(Places) + // }), + // http.get(addBaseUrl(API_PATH.PLACES.BY_RANKING('views', 'singwan')), () => { + // return HttpResponse.json(Places) + // }), + // http.get(addBaseUrl(API_PATH.PLACES.BY_CATEGORY('1', 'singwan')), () => { + // return HttpResponse.json(Places) + // }), + // http.get(addBaseUrl(API_PATH.PLACES.DETAIL('1')), () => { + // return HttpResponse.json(PlaceDetail) + // }), + // http.get(addBaseUrl(API_PATH.PLACES.NEW.PREVIEW('1')), () => { + // return HttpResponse.json(PlacePreview) + // }), http.get(addBaseUrl(API_PATH.PLACES.LIKE.GET), () => { return HttpResponse.json(Places) }), diff --git a/apps/web/app/_store/campus.ts b/apps/web/app/_store/campus.ts index e91086b..f05558f 100644 --- a/apps/web/app/_store/campus.ts +++ b/apps/web/app/_store/campus.ts @@ -7,6 +7,6 @@ type CampusStore = { } export const useCampusStore = create((set) => ({ - campus: 'singwan', + campus: 'SINGWAN', setCampus: (campus: CampusType) => set({ campus: campus }), })) diff --git a/apps/web/app/_utils/formatPrice/formatPrice.ts b/apps/web/app/_utils/formatPrice/formatPrice.ts new file mode 100644 index 0000000..e5b2e22 --- /dev/null +++ b/apps/web/app/_utils/formatPrice/formatPrice.ts @@ -0,0 +1,4 @@ +export const formatPrice = (price: number): string => { + if (price === 0) return '미등록' + return `${price.toLocaleString()}원` +} diff --git a/apps/web/app/_utils/formatPrice/index.ts b/apps/web/app/_utils/formatPrice/index.ts new file mode 100644 index 0000000..f0021ec --- /dev/null +++ b/apps/web/app/_utils/formatPrice/index.ts @@ -0,0 +1 @@ +export { formatPrice } from './formatPrice' diff --git a/apps/web/app/map/MapComponent.tsx b/apps/web/app/map/MapComponent.tsx index 83d6dee..866b6cd 100644 --- a/apps/web/app/map/MapComponent.tsx +++ b/apps/web/app/map/MapComponent.tsx @@ -13,16 +13,18 @@ import { cn } from '@repo/ui/utils/cn' import { toLatLng } from './_utils/toLatLng' import { useWatchLocation } from './_hooks/useWatchLocation' import { PlaceList } from './_components/PlaceList' -import { CampusButtonBax } from './_components/CampusButtom' +import { CampusButtonBox } from './_components/CampusButtom' import { UserMarker, PlaceMarker } from './_components/Marker' import { CurrentLocationButton } from './_components/CurrentLocationButton' import { PreviewPlace } from './_components/PreviewPlace' +import { RefreshButton } from './_components/RefreshButton' const MapComponent = () => { const [map, setMap] = useState(null) const [isCenteredOnUser, setIsCenteredOnUser] = useState(false) const [currentBounds, setCurrentBounds] = useState(null) const [previewPlaceId, setPreviewPlaceId] = useState(null) + const [showUpdateButton, setShowUpdateButton] = useState(false) const { campus } = useCampusStore() const { userLocation } = useWatchLocation() @@ -32,7 +34,8 @@ const MapComponent = () => { ? data.find((place) => place.placeId === previewPlaceId)! : null - const updateBoundsFromMap = useCallback(() => { + const refreshMapBounds = useCallback(() => { + // bounds 업데이트 시 query update if (!map) return const bounds = map.getBounds() setCurrentBounds({ @@ -43,36 +46,52 @@ const MapComponent = () => { }) }, [map]) + const handleRefreshClick = () => { + setShowUpdateButton(false) + refreshMapBounds() + } + const centerMapToUserLocation = () => { if (!map || !userLocation) return map.setCenter(toLatLng(userLocation)) - updateBoundsFromMap() setIsCenteredOnUser(true) + handleRefreshClick() + } + + const centerMapToCampus = () => { + handleRefreshClick() + setIsCenteredOnUser(false) } const onCenterChanged = () => { setIsCenteredOnUser(false) - setPreviewPlaceId(null) + setShowUpdateButton(true) } const handlePreviewPlace = (placeId: string) => { setPreviewPlaceId(placeId) } - useEffect(() => { - updateBoundsFromMap() - }, [updateBoundsFromMap]) + const resetPreviewPlace = () => { + setPreviewPlaceId(null) + } + + useEffect(refreshMapBounds, [refreshMapBounds]) return ( <> + {showUpdateButton && ( + + )} - + diff --git a/apps/web/app/map/_components/CampusButtom/CampusButtonBax.tsx b/apps/web/app/map/_components/CampusButtom/CampusButtonBox.tsx similarity index 89% rename from apps/web/app/map/_components/CampusButtom/CampusButtonBax.tsx rename to apps/web/app/map/_components/CampusButtom/CampusButtonBox.tsx index 547dbd1..0148486 100644 --- a/apps/web/app/map/_components/CampusButtom/CampusButtonBax.tsx +++ b/apps/web/app/map/_components/CampusButtom/CampusButtonBox.tsx @@ -13,7 +13,7 @@ import { Column } from '@repo/ui/components/Layout' type Props = { map: naver.maps.Map | null - onCenterChanged: VoidFunction + centerMapToCampus: VoidFunction } /** @@ -22,7 +22,7 @@ type Props = { * * @example * ```tsx - * + * * ``` * * @param {object} props @@ -30,7 +30,7 @@ type Props = { * * @returns 캠퍼스 선택 버튼 그룹 UI */ -export const CampusButtonBax = ({ map, onCenterChanged }: Props) => { +export const CampusButtonBox = ({ map, centerMapToCampus }: Props) => { const { campus: initCampus } = useCampusStore() const [activeCampus, setActiveCampus] = useState(initCampus) @@ -39,7 +39,7 @@ export const CampusButtonBax = ({ map, onCenterChanged }: Props) => { setActiveCampus(campus) map.setCenter(toLatLng(CAMPUS_LOCATION[campus])) - onCenterChanged() + centerMapToCampus() } return ( diff --git a/apps/web/app/map/_components/CampusButtom/index.tsx b/apps/web/app/map/_components/CampusButtom/index.tsx index ee2e785..2458bfb 100644 --- a/apps/web/app/map/_components/CampusButtom/index.tsx +++ b/apps/web/app/map/_components/CampusButtom/index.tsx @@ -1,2 +1,2 @@ export { CampusButton } from './CampusButton' -export { CampusButtonBax } from './CampusButtonBax' +export { CampusButtonBox } from './CampusButtonBox' diff --git a/apps/web/app/map/_components/Marker/Marker.tsx b/apps/web/app/map/_components/Marker/Marker.tsx index b9d28bf..727ed44 100644 --- a/apps/web/app/map/_components/Marker/Marker.tsx +++ b/apps/web/app/map/_components/Marker/Marker.tsx @@ -36,7 +36,7 @@ export const UserMarker = ({ position }: { position: Coord }) => { export const PlaceMarker = ({ icon, position, - handlePreviewPlace, + handlePreviewPlace = () => {}, }: { icon: IconType position: Coord @@ -52,7 +52,10 @@ export const PlaceMarker = ({ return ( { + e.pointerEvent.stopPropagation() + handlePreviewPlace() + }} position={new naverMaps.LatLng(toLatLng(position))} icon={{ content: MarkerIcon, diff --git a/apps/web/app/map/_components/PreviewPlace/PreviewPlace.tsx b/apps/web/app/map/_components/PreviewPlace/PreviewPlace.tsx index 93bf268..a879262 100644 --- a/apps/web/app/map/_components/PreviewPlace/PreviewPlace.tsx +++ b/apps/web/app/map/_components/PreviewPlace/PreviewPlace.tsx @@ -37,7 +37,7 @@ export const PreviewPlace = ({ place }: { place: PlaceByMap }) => { {photos.map((photo) => ( ( + + + 현재 지도에서 찾기 + +) diff --git a/apps/web/app/map/_components/RefreshButton/index.tsx b/apps/web/app/map/_components/RefreshButton/index.tsx new file mode 100644 index 0000000..a5fac4a --- /dev/null +++ b/apps/web/app/map/_components/RefreshButton/index.tsx @@ -0,0 +1 @@ +export { RefreshButton } from './RefreshButton' diff --git a/apps/web/app/places/[id]/PlaceDetailPage.tsx b/apps/web/app/places/[id]/PlaceDetailPage.tsx index d7322de..9311d57 100644 --- a/apps/web/app/places/[id]/PlaceDetailPage.tsx +++ b/apps/web/app/places/[id]/PlaceDetailPage.tsx @@ -23,20 +23,23 @@ export const PlaceDetailPage = ({ id }: { id: string }) => { right={} /> - ( - place-photo - ))} - minHeight={180} - showIndicator={true} - /> + {photos.length > 0 && ( + ( + place-photo + ))} + minHeight={180} + showIndicator={true} + /> + )} + diff --git a/apps/web/app/places/[id]/_components/Menus/Menu.tsx b/apps/web/app/places/[id]/_components/Menus/Menu.tsx index b7a959f..96eb93b 100644 --- a/apps/web/app/places/[id]/_components/Menus/Menu.tsx +++ b/apps/web/app/places/[id]/_components/Menus/Menu.tsx @@ -1,4 +1,5 @@ -import { PlaceDetail } from '@/_apis/schemas/place' +import type { PlaceDetail } from '@/_apis/schemas/place' +import { formatPrice } from '@/_utils/formatPrice' import { Flex, JustifyBetween } from '@repo/ui/components/Layout' import { Text } from '@repo/ui/components/Text' import { Icon } from '@repo/ui/components/Icon' @@ -20,7 +21,7 @@ export const Menu = ({ menu }: Props) => { - {price.toLocaleString()} 원 + {formatPrice(price)} ) diff --git a/apps/web/app/places/new/_components/Step/Category/Category.tsx b/apps/web/app/places/new/_components/Step/Category/Category.tsx index e5a33d0..cd3c11d 100644 --- a/apps/web/app/places/new/_components/Step/Category/Category.tsx +++ b/apps/web/app/places/new/_components/Step/Category/Category.tsx @@ -12,16 +12,10 @@ import { Button } from '@repo/ui/components/Button' type Props = { setValue: UseFormSetValue getValues: () => NewPlaceRequest - isSubmitting: boolean - nextStep: VoidFunction + isLoading: boolean } -export const Category = ({ - setValue, - getValues, - isSubmitting, - nextStep, -}: Props) => { +export const Category = ({ setValue, getValues, isLoading }: Props) => { const { data: categories } = useSuspenseQuery(useCategoryQueries.list()) const initialValues = getValues().categoryIds const initialCategory = categories.filter((category) => @@ -75,13 +69,10 @@ export const Category = ({ includeInCategories={includeInCategories} /> diff --git a/apps/web/app/places/new/_components/Step/Fail/index.tsx b/apps/web/app/places/new/_components/Step/Fail/index.tsx deleted file mode 100644 index 372c595..0000000 --- a/apps/web/app/places/new/_components/Step/Fail/index.tsx +++ /dev/null @@ -1 +0,0 @@ -export { Fail } from './Fail' diff --git a/apps/web/app/places/new/_components/Step/PlacePreview/PlacePreview.tsx b/apps/web/app/places/new/_components/Step/PlacePreview/PlacePreview.tsx index f4fa863..dc4f6ce 100644 --- a/apps/web/app/places/new/_components/Step/PlacePreview/PlacePreview.tsx +++ b/apps/web/app/places/new/_components/Step/PlacePreview/PlacePreview.tsx @@ -13,13 +13,15 @@ import { AlreadyRegistered } from './AlreadyRegistered' import { Title } from '@/places/new/_components/Title' type Props = { + getValues: () => NewPlaceRequest setValue: UseFormSetValue nextStep: VoidFunction } -export const PlacePreview = ({ setValue, nextStep }: Props) => { - // Todo: 테스트용 api 요청 삭제 예정 (id prop으로 전달받을 예정) - const { data } = useSuspenseQuery(usePlaceQueries.byPreview('1')) +export const PlacePreview = ({ getValues, setValue, nextStep }: Props) => { + const { data } = useSuspenseQuery( + usePlaceQueries.byPreview(getValues().kakaoPlaceId), + ) const { alreadyRegistered, placeName, photos, menus, location } = data setValue('menus', menus) diff --git a/apps/web/app/places/new/_components/Step/RecommendedMenu/RecommendedMenu.tsx b/apps/web/app/places/new/_components/Step/RecommendedMenu/RecommendedMenu.tsx index 8665ee5..fe74983 100644 --- a/apps/web/app/places/new/_components/Step/RecommendedMenu/RecommendedMenu.tsx +++ b/apps/web/app/places/new/_components/Step/RecommendedMenu/RecommendedMenu.tsx @@ -1,6 +1,7 @@ import { CheckboxGroup, Checkbox } from '@heroui/react' import { type Control, Controller } from 'react-hook-form' import type { NewPlaceRequest } from '@/_apis/schemas/place' +import { formatPrice } from '@/_utils/formatPrice' import { Title } from '@/places/new/_components/Title' import { Text } from '@repo/ui/components/Text' import { JustifyBetween, VerticalScrollArea } from '@repo/ui/components/Layout' @@ -67,7 +68,7 @@ export const RecommendedMenu = ({ control, nextStep }: Props) => { fontWeight='semibold' className='text-gray-300' > - {menu.price.toLocaleString()} 원 + {formatPrice(menu.price)} diff --git a/apps/web/app/places/new/_components/Step/Success/index.tsx b/apps/web/app/places/new/_components/Step/Success/index.tsx deleted file mode 100644 index e198e71..0000000 --- a/apps/web/app/places/new/_components/Step/Success/index.tsx +++ /dev/null @@ -1 +0,0 @@ -export { Success } from './Success' diff --git a/apps/web/app/places/new/_components/Step/index.tsx b/apps/web/app/places/new/_components/Step/index.tsx index 70fc47c..3748e5c 100644 --- a/apps/web/app/places/new/_components/Step/index.tsx +++ b/apps/web/app/places/new/_components/Step/index.tsx @@ -5,5 +5,3 @@ export * from './PlacePreview' export * from './RecommendedMenu' export * from './Description' export * from './Category' -export * from './Success' -export * from './Fail' diff --git a/apps/web/app/places/new/_components/Step/Fail/Fail.tsx b/apps/web/app/places/new/fail/page.tsx similarity index 87% rename from apps/web/app/places/new/_components/Step/Fail/Fail.tsx rename to apps/web/app/places/new/fail/page.tsx index f8079ae..733b7ee 100644 --- a/apps/web/app/places/new/_components/Step/Fail/Fail.tsx +++ b/apps/web/app/places/new/fail/page.tsx @@ -1,11 +1,11 @@ -import { Icon } from '@repo/ui/components/Icon' import { Column } from '@repo/ui/components/Layout' +import { Icon } from '@repo/ui/components/Icon' import { Text } from '@repo/ui/components/Text' import { Button } from '@repo/ui/components/Button' import { CLIENT_PATH } from '@/_constants/path' -export const Fail = () => ( - +const FailPage = () => ( + @@ -24,9 +24,11 @@ export const Fail = () => ( as={'a'} href={CLIENT_PATH.MAIN} size={'medium'} - className={'ui:min-w-full'} + className={'w-full'} > 홈으로 ) + +export default FailPage diff --git a/apps/web/app/places/new/page.tsx b/apps/web/app/places/new/page.tsx index 5a26834..79ec352 100644 --- a/apps/web/app/places/new/page.tsx +++ b/apps/web/app/places/new/page.tsx @@ -8,6 +8,7 @@ import { NewPlaceRequestSchema, } from '@/_apis/schemas/place' import { useFunnel } from '@/_hooks/useFunnel' +import { useCreateNewPlace } from '@/_apis/mutations/useCreateNewPlace' import { useCampusStore } from '@/_store/campus' import { Header } from '@repo/ui/components/Header' import { Column, Flex } from '@repo/ui/components/Layout' @@ -23,8 +24,6 @@ import { RecommendedMenu, Description, Category, - Success, - Fail, } from './_components/Step' export type StepType = @@ -52,10 +51,9 @@ const STEP_ORDER: Record = { const PlaceNewPage = () => { const { Step, nextStep } = useFunnel(STEP_ORDER) - const { campus: initCampus } = useCampusStore() + const { mutate, isPending } = useCreateNewPlace() const { - // register, handleSubmit, control, setValue, @@ -74,7 +72,10 @@ const PlaceNewPage = () => { }, }) - const onSubmit: SubmitHandler = (data) => console.log(data) + const onSubmit: SubmitHandler = async (data) => { + mutate(data) + } + const onError = (errors: FieldErrors) => { if (errors.categoryIds) { addToast({ @@ -126,6 +127,7 @@ const PlaceNewPage = () => { { const step = @@ -164,19 +166,9 @@ const PlaceNewPage = () => { { - // Todo: api 요청 후 status boolean 값으로 success 또는 fail로 이동 - nextStep('SUCCESS') - }} + isLoading={isSubmitting || isPending} /> - - - - - - ) diff --git a/apps/web/app/places/new/_components/Step/Success/Success.tsx b/apps/web/app/places/new/success/page.tsx similarity index 87% rename from apps/web/app/places/new/_components/Step/Success/Success.tsx rename to apps/web/app/places/new/success/page.tsx index 2c2cf63..cccff5e 100644 --- a/apps/web/app/places/new/_components/Step/Success/Success.tsx +++ b/apps/web/app/places/new/success/page.tsx @@ -1,11 +1,11 @@ -import { Icon } from '@repo/ui/components/Icon' import { Column } from '@repo/ui/components/Layout' +import { Icon } from '@repo/ui/components/Icon' import { Text } from '@repo/ui/components/Text' import { Button } from '@repo/ui/components/Button' import { CLIENT_PATH } from '@/_constants/path' -export const Success = () => ( - +const SuccessPage = () => ( + @@ -24,9 +24,11 @@ export const Success = () => ( as={'a'} href={CLIENT_PATH.MAIN} size={'medium'} - className={'ui:min-w-full'} + className={'w-full'} > 홈으로 ) + +export default SuccessPage diff --git a/apps/web/next.config.ts b/apps/web/next.config.ts index 66d1cd5..6e6143f 100644 --- a/apps/web/next.config.ts +++ b/apps/web/next.config.ts @@ -11,7 +11,23 @@ const nextConfig: NextConfig = { remotePatterns: [ { protocol: 'https', - hostname: 'search.pstatic.net', //테스트용 주소 + hostname: '*.pstatic.net', + port: '', + pathname: '/**', + }, + { + protocol: 'https', + hostname: 't1.daumcdn.net', + pathname: '/**', + }, + { + protocol: 'http', + hostname: 't1.daumcdn.net', + pathname: '/**', + }, + { + protocol: 'https', + hostname: 'blog.kakaocdn.net', pathname: '/**', }, ], diff --git a/packages/ui/src/components/Icon/IconMap.ts b/packages/ui/src/components/Icon/IconMap.ts index 2dd82b7..272a1b6 100644 --- a/packages/ui/src/components/Icon/IconMap.ts +++ b/packages/ui/src/components/Icon/IconMap.ts @@ -63,6 +63,7 @@ import { Gift } from './assets/icons/gift' import { Pencil } from './assets/icons/pencil' import { Paper } from './assets/icons/paper' import { X } from './assets/icons/x' +import { Refresh } from './assets/icons/refresh' export const iconMap = { // 메뉴 @@ -133,6 +134,7 @@ export const iconMap = { pencil: Pencil, paper: Paper, x: X, + refresh: Refresh, } export type IconType = keyof typeof iconMap diff --git a/packages/ui/src/components/Icon/assets/icons/refresh.tsx b/packages/ui/src/components/Icon/assets/icons/refresh.tsx new file mode 100644 index 0000000..1a688cd --- /dev/null +++ b/packages/ui/src/components/Icon/assets/icons/refresh.tsx @@ -0,0 +1,27 @@ +import { SVGProps } from 'react' + +export const Refresh = ({ + width, + height, + ...props +}: SVGProps) => ( + + + + +)