diff --git a/src/api/usePostLogout.ts b/src/api/usePostLogout.ts index 4e61236..019cf62 100644 --- a/src/api/usePostLogout.ts +++ b/src/api/usePostLogout.ts @@ -22,6 +22,7 @@ const usePostLogout = () => { queryClient.removeQueries({ queryKey: ['member'] }) queryClient.removeQueries({ queryKey: ['favorites'] }) queryClient.removeQueries({ queryKey: ['carts'] }) + queryClient.removeQueries({ queryKey: ['address'] }) }, retry: 0, }) diff --git a/src/app/mypage/address/_components/AddressOption.tsx b/src/app/mypage/address/_components/AddressOption.tsx index 9ff7299..44e70c1 100644 --- a/src/app/mypage/address/_components/AddressOption.tsx +++ b/src/app/mypage/address/_components/AddressOption.tsx @@ -14,6 +14,7 @@ import Separator from '@/components/Separator' import LoginButtonSection from '@/components/shared/LoginButtonSection' import { useToast } from '@/hooks/useToast' import { cn } from '@/lib/utils' +import { SignupData } from '@/models/auth' import { modalStore } from '@/store/modal' import memberStore from '@/store/user' import { useQueryClient } from '@tanstack/react-query' @@ -40,6 +41,8 @@ const AddressOption = () => { const queryClient = useQueryClient() const handleComplete = async (data: { address: string }) => { + setPopup(false) + addressToGeolocation(data.address, { onSuccess: (data) => { showModal({ @@ -310,27 +313,38 @@ const AddressOption = () => { ) } -const AddressDetailModal = ({ +export const AddressDetailModal = ({ type, userAddress, addressData, + onSaveInSignup, }: { type?: AddressType userAddress?: AddressResponseData addressData?: AddressData + onSaveInSignup?: (addressData: SignupData['address']) => void }) => { const { hideModal } = modalStore() + const handleClose = () => { + hideModal() + } + return (
주소 등록
- +
- +
) diff --git a/src/app/mypage/address/_components/AddressSearchModal.tsx b/src/app/mypage/address/_components/AddressSearchModal.tsx index 3fa02d0..a413829 100644 --- a/src/app/mypage/address/_components/AddressSearchModal.tsx +++ b/src/app/mypage/address/_components/AddressSearchModal.tsx @@ -1,5 +1,5 @@ -import React, { ReactNode } from 'react' import Icon from '@/components/Icon' +import React, { ReactNode } from 'react' interface ModalProps { isOpen: boolean @@ -12,7 +12,7 @@ const AddressSearchModal: React.FC = ({ isOpen, onClose, children }) return (
-
+
diff --git a/src/app/mypage/address/detail/_components/AddressDetail.tsx b/src/app/mypage/address/detail/_components/AddressDetail.tsx index 09c5312..1eadd8c 100644 --- a/src/app/mypage/address/detail/_components/AddressDetail.tsx +++ b/src/app/mypage/address/detail/_components/AddressDetail.tsx @@ -7,6 +7,7 @@ import { useGeoLocationStore } from '@/store/geoLocation' import { useState } from 'react' import { useKakaoLoader } from 'react-kakao-maps-sdk' import MapInfo from './MapInfo' +import { SignupData } from '@/models/auth' export interface AddressData { type: AddressType | undefined @@ -24,10 +25,12 @@ const AddressDetail = ({ type = AddressType.HOME, userAddress, defaultAddressData, + onSaveInSignup, }: { type?: AddressType userAddress?: AddressResponseData defaultAddressData?: AddressData + onSaveInSignup?: (addressData: SignupData['address']) => void }) => { const [loading] = useKakaoLoader({ appkey: process.env.NEXT_PUBLIC_KAKAO_APP_KEY!, @@ -66,6 +69,7 @@ const AddressDetail = ({ addressData={addressData} onAddressChange={handleAddressChange} userAddress={userAddress} + onSaveInSignup={onSaveInSignup} />
) diff --git a/src/app/mypage/address/detail/_components/MapInfo.tsx b/src/app/mypage/address/detail/_components/MapInfo.tsx index ddf17c1..6074c61 100644 --- a/src/app/mypage/address/detail/_components/MapInfo.tsx +++ b/src/app/mypage/address/detail/_components/MapInfo.tsx @@ -9,7 +9,9 @@ import Input from '@/components/Input' import { Button } from '@/components/button' import { useToast } from '@/hooks/useToast' import { cn } from '@/lib/utils' +import { SignupData } from '@/models/auth' import { modalStore } from '@/store/modal' +import memberStore from '@/store/user' import { useQueryClient } from '@tanstack/react-query' import { useEffect, useState } from 'react' import { AddressData } from './AddressDetail' @@ -18,15 +20,18 @@ const MapInfo = ({ addressData, onAddressChange, userAddress, + onSaveInSignup, }: { addressData: AddressData onAddressChange: (data: AddressData) => void userAddress?: AddressResponseData + onSaveInSignup?: (addressData: SignupData['address']) => void }) => { const queryClient = useQueryClient() const { toast } = useToast() const { hideModal } = modalStore() + const { setMember } = memberStore() const [addressDetail, setAddressDetail] = useState('') const [alias, setAlias] = useState('') @@ -70,6 +75,19 @@ const MapInfo = ({ } } + if (onSaveInSignup) { + onSaveInSignup({ + memberAddressType: addressData.type as AddressType, + roadAddress: addressData.roadAddr || addressData.address, + jibunAddress: addressData.address, + detailAddress: addressDetail, + alias: addressData.type === AddressType.OTHERS ? alias : '', + latitude: addressData.coords.lat, + longitude: addressData.coords.lng, + }) + return + } + const _address: Address = { memberAddressType: addressData.type, roadAddress: addressData.roadAddr || addressData.address, @@ -82,7 +100,11 @@ const MapInfo = ({ const _options = { onSuccess: () => { - refetchMember() + refetchMember().then((res) => { + if (res.data) { + setMember(res.data) + } + }) queryClient.invalidateQueries({ queryKey: ['address'] }) hideModal() }, diff --git a/src/app/reviews/_components/Review.tsx b/src/app/reviews/_components/Review.tsx index 3d5e40c..52b930f 100644 --- a/src/app/reviews/_components/Review.tsx +++ b/src/app/reviews/_components/Review.tsx @@ -27,7 +27,7 @@ const Review = () => { } = useInfiniteScroll({ queryKey: 'completed-reviews', endpoint: 'reviews', - size: 2, + size: 10, }) const handleChangeTab = (tab: ReviewTabType) => { @@ -69,7 +69,7 @@ const Review = () => { /> )) ) : ( - + )} diff --git a/src/components/shared/SignupModal.tsx b/src/components/shared/SignupModal.tsx index e1c9f76..410b8a2 100644 --- a/src/components/shared/SignupModal.tsx +++ b/src/components/shared/SignupModal.tsx @@ -1,16 +1,22 @@ +import useGetAddressToGeolocation from '@/api/useGetAddressToGeolocation' +import useGetGeolocationToAddress from '@/api/useGetGeolocationToAddress' +import { Address } from '@/api/usePostAddress' import usePostSignup from '@/api/usePostSignup' +import { AddressDetailModal } from '@/app/mypage/address/_components/AddressOption' import { Button } from '@/components/button' import Icon from '@/components/Icon' import Input from '@/components/Input' import { useToast } from '@/hooks/useToast' import { ApiErrorResponse } from '@/lib/api' import { formatPhoneNumber, unformatPhoneNumber } from '@/lib/format' +import { SignupData } from '@/models/auth' +import { useGeoLocationStore } from '@/store/geoLocation' import { modalStore } from '@/store/modal' import { zodResolver } from '@hookform/resolvers/zod' -import { useState } from 'react' +import { useEffect, useState } from 'react' +import DaumPostcode from 'react-daum-postcode' import { useForm } from 'react-hook-form' import { z } from 'zod' -import Address from '@/app/mypage/address/page' const SignupModal = () => { const { hideModal } = modalStore() @@ -55,6 +61,7 @@ const signupFormSchema = z.object({ .regex(/^[a-zA-Z가-힣]+$/, '이름은 영문, 한글만 가능합니다.'), phone: z .string() + .min(1, '전화번호를 입력해주세요.') .min(10, '전화번호는 8자 이상이어야 합니다.') .max(13, '전화번호는 11자 이내여야 합니다.'), address: z.object({ @@ -69,9 +76,9 @@ const signupFormSchema = z.object({ }) const SignupForm = () => { - const { showModal, hideModal, addressData } = modalStore() + const { showModal, hideModal } = modalStore() + const { coordinates } = useGeoLocationStore() - const { mutate: signup } = usePostSignup() const { toast } = useToast() const { register, @@ -112,6 +119,13 @@ const SignupForm = () => { 'signname' | 'password' | 'nickname' | 'username' | 'phone' | null >(null) const [isClickedAddressButton, setIsClickedAddressButton] = useState(false) + const [currentAddress, setCurrentAddress] = useState
(null) + + const { mutate: addressToGeolocation } = useGetAddressToGeolocation() + const { mutate: geolocationToAddress, data: geolocationToAddressData } = + useGetGeolocationToAddress() + + const { mutate: signup } = usePostSignup() const onSubmit = handleSubmit((formData) => { const processedFormData = { @@ -159,9 +173,42 @@ const SignupForm = () => { }) } + const handleChangeAddress = (addressData: SignupData['address']) => { + setValue('address', addressData, { + shouldValidate: true, // 주소 입력 시 유효성 검사 실행 + }) + hideModal() // 주소 찾기 디테일 모달 닫기 + } + + const handleComplete = (address: { jibunAddress: string; roadAddress: string }) => { + addressToGeolocation(address.jibunAddress, { + onSuccess: (data) => { + const doc = data.documents[0] + + showAddressDetailModal({ + memberAddressType: undefined, + jibunAddress: address.jibunAddress, + roadAddress: address.roadAddress, + detailAddress: '', + alias: '', + latitude: Number(doc.y), + longitude: Number(doc.x), + }) + }, + onError: (error) => { + toast({ + title: '주소 검색에 실패했습니다.', + description: '다시 시도해주세요.', + variant: 'destructive', + position: 'center', + }) + }, + }) + } + const handleAddressClick = () => { showModal({ - content: , + content: , useAnimation: true, }) if (!isClickedAddressButton) { @@ -169,6 +216,50 @@ const SignupForm = () => { } } + const showAddressDetailModal = (address: Address) => { + showModal({ + content: ( + + ), + useAnimation: true, + }) + } + + useEffect(() => { + if (coordinates) { + geolocationToAddress({ + longitude: coordinates.longitude.toString(), + latitude: coordinates.latitude.toString(), + }) + } + }, [coordinates]) + + useEffect(() => { + if (geolocationToAddressData) { + const doc = geolocationToAddressData.documents[0] + + setCurrentAddress({ + memberAddressType: undefined, + jibunAddress: doc.address.address_name, + roadAddress: doc.road_address?.address_name || '', + detailAddress: '', + alias: '', + latitude: coordinates?.latitude || 0, + longitude: coordinates?.longitude || 0, + }) + } + }, [geolocationToAddressData]) + return (
{ height: 'calc(100vh - 94px)', }} > -
+
{ > 주소 - - - {addressData?.roadAddress ? ( -
{addressData?.roadAddress}
- ) : ( - isClickedAddressButton && ( -
주소를 입력해주세요.
- ) - )} +
+ setValue('address.roadAddress', e.target.value)} + onReset={() => setValue('address.roadAddress', '')} + icon={} + offOutline + onClick={handleAddressClick} + readOnly + /> +
{ + if (currentAddress) { + showAddressDetailModal(currentAddress) + } else { + toast({ + description: '현재 위치를 찾을 수 없습니다.', + variant: 'destructive', + position: 'center', + }) + } + }} + > + +
현재 위치로 주소 찾기
+
+
-
@@ -314,18 +416,31 @@ const SignupForm = () => { ) } -const AddressModal = () => { +const AddressModal = ({ + handleComplete, +}: { + handleComplete: (data: { jibunAddress: string; roadAddress: string }) => void +}) => { const { hideModal } = modalStore() + const handleClose = () => { + hideModal() + } + return ( -
-
-
- 대표주소 등록 +
+
+
+
- + { + hideModal() + handleComplete(data) + }} + autoClose={true} + />
-
) }