diff --git a/src/apis/walk/fetchWalkComplete.ts b/src/apis/walk/fetchWalkComplete.ts new file mode 100644 index 00000000..b8b47397 --- /dev/null +++ b/src/apis/walk/fetchWalkComplete.ts @@ -0,0 +1,80 @@ +import { AxiosError } from 'axios' +import { APIResponse, ErrorResponse } from '~types/api' +import { axiosInstance } from '~apis/axiosInstance' +import { useQuery } from '@tanstack/react-query' + +export const WALK_COMPLETE_QUERY_KEY = 'walkComplete' as const + +export type FetchWalkCompleteRequest = { + walkImgFile: File + totalDistanceMeter: number + totalWalkTimeSecond: number +} + +export type TimeDuration = { + hours: number + minutes: number + seconds: number +} + +export type WalkWithDogInfo = { + otherDogId: number + otherDogProfileImg: string + otherDogName: string + otherDogBreed: string + otherDogAge: number + otherDogGender: 'MALE' | 'FEMALE' + memberId: number +} + +export type FetchWalkCompleteResponse = { + date: string + memberName: string + dogName: string + totalDistanceMeter: number + timeDuration: TimeDuration + totalCalorie: number + walkImg: string + walkWithDogInfo: WalkWithDogInfo +} + +export const fetchWalkComplete = async (formData: FormData): Promise> => { + try { + const { data } = await axiosInstance.post>(`/walk/complete`, formData, { + headers: { + 'Content-Type': 'multipart/form-data', + }, + }) + return data + } catch (error) { + if (error instanceof AxiosError) { + const { response } = error as AxiosError + + if (response) { + const { code, message } = response.data + switch (code) { + case 400: + throw new Error(message || '잘못된 요청입니다.') + case 401: + throw new Error(message || '인증에 실패했습니다.') + case 500: + throw new Error(message || '서버 오류가 발생했습니다.') + default: + throw new Error(message || '알 수 없는 오류가 발생했습니다.') + } + } + throw new Error('네트워크 연결을 확인해주세요') + } + + console.error('예상치 못한 에러:', error) + throw new Error('다시 시도해주세요') + } +} + +export const useWalkComplete = (formData: FormData) => { + return useQuery({ + queryKey: [WALK_COMPLETE_QUERY_KEY, formData], + queryFn: () => fetchWalkComplete(formData), + enabled: false, + }) +} diff --git a/src/assets/map_pin_1.svg b/src/assets/map_pin_1.svg new file mode 100644 index 00000000..47291d34 --- /dev/null +++ b/src/assets/map_pin_1.svg @@ -0,0 +1,37 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/assets/map_pin_2.svg b/src/assets/map_pin_2.svg new file mode 100644 index 00000000..63437afd --- /dev/null +++ b/src/assets/map_pin_2.svg @@ -0,0 +1,39 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/pages/WalkCompletePage/index.tsx b/src/pages/WalkCompletePage/index.tsx index dd4ea443..58edf93c 100644 --- a/src/pages/WalkCompletePage/index.tsx +++ b/src/pages/WalkCompletePage/index.tsx @@ -2,8 +2,10 @@ import { useLocation } from 'react-router-dom' import * as S from './styles' interface WalkCompleteData { + dogName: string + memberName: string date: string - time: string + timeDuration: { hours: number; minutes: number; seconds: number } distance: string calories: string mapImage: string @@ -11,7 +13,10 @@ interface WalkCompleteData { export default function WalkCompletePage() { const location = useLocation() + console.log(location.state) const walkData: WalkCompleteData = { + dogName: location.state?.dogName || '', + memberName: location.state?.memberName || '', date: new Date() .toLocaleDateString('ko-KR', { year: 'numeric', @@ -19,27 +24,27 @@ export default function WalkCompletePage() { day: '2-digit', }) .replace(/\. /g, '.'), - time: location.state?.time || '00:00:00', - distance: location.state?.distance || '0m', - calories: '200kcal', - mapImage: location.state?.mapImage || '', + timeDuration: location.state?.timeDuration, + distance: `${location.state?.totalDistanceMeter}m` || '0m', + calories: `${location.state?.totalCalorie}kcal` || '0kcal', + mapImage: location.state?.walkImg || '', } console.log(walkData) - const getMinutesFromTime = (time: string) => { - const [hours, minutes] = time.split(':').map(Number) + const getMinutesFromTime = (time: { hours: number; minutes: number; seconds?: number }) => { + const { hours, minutes } = time return hours * 60 + minutes } - const walkTimeInMinutes = getMinutesFromTime(walkData.time) + const walkTimeInMinutes = getMinutesFromTime(walkData.timeDuration) return ( {walkData.date} - 견주닉넴과 밤톨이가 + {walkData.memberName}와(과) {walkData.dogName}가
{walkTimeInMinutes}분동안 산책했어요.
@@ -49,7 +54,7 @@ export default function WalkCompletePage() { - {walkData.time} + {`${walkData.timeDuration.hours} : ${walkData.timeDuration.minutes} : ${walkData.timeDuration.seconds}`} 산책 시간 diff --git a/src/pages/WalkPage/components/MapComponent/index.tsx b/src/pages/WalkPage/components/MapComponent/index.tsx index 8991a4c4..e31f1457 100644 --- a/src/pages/WalkPage/components/MapComponent/index.tsx +++ b/src/pages/WalkPage/components/MapComponent/index.tsx @@ -18,6 +18,8 @@ import * as S from './styles' import { MIN_ACCURACY, MIN_DISTANCE, MIN_TIME_INTERVAL } from '~types/map' import { useNavigate } from 'react-router-dom' import { useWebSocket } from '~/WebSocketContext' +import { useMutation } from '@tanstack/react-query' +import { fetchWalkComplete } from '~apis/walk/fetchWalkComplete' import WalkModal from '~pages/WalkPage/components/WalkModal' const ORS_API_URL = '/ors/v2/directions/foot-walking/geojson' @@ -33,6 +35,11 @@ export const getMarkerIconString = () => { return svgString } +export const getMarkerIconString2 = () => { + const svgString = ReactDOMServer.renderToString() + return svgString +} + interface ExtendedDeviceOrientationEvent extends DeviceOrientationEvent { webkitCompassHeading?: number } @@ -106,8 +113,8 @@ export default function MapComponent({ isModalOpen = false, setNearbyWalkers }: const [autoRotate, setAutoRotate] = useState(false) const lastHeadingRef = useRef(0) - const [showProposalModal, setShowProposalModal] = useState(true) - const [showDecisionModal, setShowDecisionModal] = useState(true) + const [showProposalModal, setShowProposalModal] = useState(false) + const [showDecisionModal, setShowDecisionModal] = useState(false) const [proposalInfo, setProposalInfo] = useState({ dogId: 1, dogName: '몽이', @@ -129,51 +136,79 @@ export default function MapComponent({ isModalOpen = false, setNearbyWalkers }: const [currentAccuracy, setCurrentAccuracy] = useState(null) + const [isWalkingTogether, setIsWalkingTogether] = useState(false) + const [, setWalkPartnerEmail] = useState(null) + const navigate = useNavigate() const { subscribe, publish, isConnected } = useWebSocket() - const memberEmail = 'mkh6793@naver.com' + const memberEmail = localStorage.getItem('email') + + // const { data } = useQuery({ + // queryKey: ['walkPage'], + // queryFn: fetchMypage, + // }) + + const walkCompleteMutation = useMutation({ + mutationFn: (walkData: FormData) => fetchWalkComplete(walkData), + }) + + const partnerMarkerRef = useRef(null) useEffect(() => { + console.log('isConnected', isConnected) if (isConnected) { const handleMessage = (message: { body: string }) => { try { const response = JSON.parse(message.body) - if (response.code === 1000 && response.data) { - if (response.data.type === 'PROPOSAL') { - const proposalData = response.data as ProposalResponse['data'] - setProposalInfo(proposalData) - setShowProposalModal(true) - } else if (response.data.type === 'DECISION') { - const decisionData = response.data as DecisionResponse['data'] - - if (decisionData.decision === 'ACCEPT') { - } else { - setDecisionInfo(decisionData) - setShowDecisionModal(true) - } - // 수락 시 같이 산책 모드 - // 거절 시 거절 모달 띄워 줌 + console.log('response', response) + const type = Array.isArray(response.data) ? response.data[0].type : response.data.type + + if (type === 'WALK_ALONE') { + const nearbyWalkersData = response.data.map((walker: NearbyWalker) => ({ + dogId: walker.dogId, + dogProfileImg: walker.dogProfileImg, + dogName: walker.dogName, + breed: walker.breed, + dogWalkCount: walker.dogWalkCount, + dogAge: walker.dogAge, + dogGender: walker.dogGender, + memberId: walker.memberId, + memberEmail: walker.memberEmail, + })) + + setNearbyWalkers(nearbyWalkersData) + } else if (type === 'PROPOSAL') { + const proposalData = response.data as ProposalResponse['data'] + setProposalInfo(proposalData) + setShowProposalModal(true) + setWalkPartnerEmail(proposalData.email) + } else if (type === 'DECISION') { + const decisionData = response.data as DecisionResponse['data'] + + if (decisionData.decision === 'DENY') { + setDecisionInfo(decisionData) + setShowDecisionModal(true) } else { - const newWalker = response.data as NearbyWalker - setNearbyWalkers((prev: NearbyWalker[]) => { - const exists = prev.some(walker => walker.dogId === newWalker.dogId) - if (!exists) { - return [...prev, newWalker] - } - return prev - }) + setIsWalkingTogether(true) } - } else { - setNearbyWalkers([]) + } else if (type === 'WALK_WITH') { + setIsWalkingTogether(true) + setWalkPartnerEmail(response.data.email) + + updatePartnerLocation(response.data.latitude, response.data.longitude) } } catch (error) { console.error('메시지 파싱 실패:', error) } } + const handleError = (message: { body: string }) => { + console.error('WebSocket 오류:', message.body) + } subscribe(`/sub/walk/${memberEmail}`, handleMessage) + subscribe('/user/queue/errors', handleError) } }, [isConnected, subscribe, memberEmail]) @@ -539,8 +574,7 @@ export default function MapComponent({ isModalOpen = false, setNearbyWalkers }: const filterPosition = (position: GeolocationPosition): boolean => { const isAccurate = position.coords.accuracy <= MIN_ACCURACY - const accuracy = position.coords.accuracy - setCurrentAccuracy(accuracy) + setCurrentAccuracy(position.coords.accuracy) return isAccurate } @@ -716,86 +750,117 @@ export default function MapComponent({ isModalOpen = false, setNearbyWalkers }: } } - const handleWalkToggle = async () => { - if (!isWalking) { - handleCompassPermission() + const base64ToFile = (base64String: string): File => { + // base64 문자열에서 데이터 URI 스키마 제거 + const base64Content = base64String.split(',')[1] + // base64를 바이너리 데이터로 변환 + const binaryData = atob(base64Content) - setIsWalking(true) - setAutoRotate(true) - setWalkDistance(0) - setEstimatedDistance(0) - accumulatedPositionsRef.current = [] + // 바이너리 데이터를 Uint8Array로 변환 + const bytes = new Uint8Array(binaryData.length) + for (let i = 0; i < binaryData.length; i++) { + bytes[i] = binaryData.charCodeAt(i) + } - routeSourceRef.current.clear() - vectorSourceRef.current.clear() + // Blob 생성 + const blob = new Blob([bytes], { type: 'image/png' }) - rotateMap(lastHeadingRef.current) - watchPositionIdRef.current = navigator.geolocation.watchPosition( - (position: GeolocationPosition) => { - if (!filterPosition(position)) return + // File 객체 생성 + const fileName = `walk-map-${new Date().getTime()}.png` + return new File([blob], fileName, { type: 'image/png' }) + } - const newPosition = { - lat: position.coords.latitude, - lng: position.coords.longitude, - } + const startWatchingPosition = () => { + return navigator.geolocation.watchPosition( + (position: GeolocationPosition) => { + if (!filterPosition(position)) return + + const newPosition = { + lat: position.coords.latitude, + lng: position.coords.longitude, + } - const coordinates = fromLonLat([newPosition.lng, newPosition.lat]) + const coordinates = fromLonLat([newPosition.lng, newPosition.lat]) - if (currentLocationMarkerRef.current) { - const point = currentLocationMarkerRef.current.getGeometry() as Point - point.setCoordinates(coordinates) - vectorSourceRef.current.changed() - } else { - currentLocationMarkerRef.current = new Feature({ - geometry: new Point(coordinates), - }) - const markerStyle = new Style({ - image: new Icon({ - anchor: [0.5, 1], - anchorXUnits: 'fraction', - anchorYUnits: 'fraction', - src: `data:image/svg+xml;utf8,${encodeURIComponent(getMarkerIconString())}`, - scale: 1, - }), - }) - currentLocationMarkerRef.current.setStyle(markerStyle) - vectorSourceRef.current.addFeature(currentLocationMarkerRef.current) + if (currentLocationMarkerRef.current) { + const point = currentLocationMarkerRef.current.getGeometry() as Point + point.setCoordinates(coordinates) + vectorSourceRef.current.changed() + } else { + currentLocationMarkerRef.current = new Feature({ + geometry: new Point(coordinates), + }) + const markerStyle = new Style({ + image: new Icon({ + anchor: [0.5, 1], + anchorXUnits: 'fraction', + anchorYUnits: 'fraction', + src: `data:image/svg+xml;utf8,${encodeURIComponent(getMarkerIconString())}`, + scale: 1, + }), + }) + currentLocationMarkerRef.current.setStyle(markerStyle) + vectorSourceRef.current.addFeature(currentLocationMarkerRef.current) + } + + if (mapRef.current) { + mapRef.current.getView().animate({ + center: coordinates, + duration: 500, + }) + } + + if (shouldCallApi(newPosition)) { + accumulatedPositionsRef.current.push(newPosition) + addWalkLocationMarker(coordinates) + + const walkData = { + latitude: newPosition.lat.toFixed(6), + longitude: newPosition.lng.toFixed(6), } - if (mapRef.current) { - mapRef.current.getView().animate({ - center: coordinates, - duration: 500, - }) + const endpoint = isWalkingTogether ? '/pub/api/v1/walk-with' : '/pub/api/v1/walk-alone' + publish(endpoint, walkData) + + if (accumulatedPositionsRef.current.length >= 2) { + const lastTwoPositions = accumulatedPositionsRef.current.slice(-2) + calculateWalkingDistance(lastTwoPositions) } - if (shouldCallApi(newPosition)) { - accumulatedPositionsRef.current.push(newPosition) - addWalkLocationMarker(coordinates) + lastApiCallTimeRef.current = Date.now() + } - try { - publish('/pub/api/v1/walk-alone', { - latitude: newPosition.lat, - longitude: newPosition.lng, - }) - console.log('웹소켓 발행 성공') - } catch (error) { - console.error('웹소켓 발행 실패:', error) - } + updateEstimatedDistance() + }, + handleLocationError, + getGeoOptions() + ) + } - if (accumulatedPositionsRef.current.length >= 2) { - const lastTwoPositions = accumulatedPositionsRef.current.slice(-2) - calculateWalkingDistance(lastTwoPositions) - } + useEffect(() => { + if (isWalking) { + if (watchPositionIdRef.current !== null) { + navigator.geolocation.clearWatch(watchPositionIdRef.current) + } + watchPositionIdRef.current = startWatchingPosition() + } + }, [isWalkingTogether, isWalking]) - lastApiCallTimeRef.current = Date.now() - } + const handleWalkToggle = async () => { + if (!isWalking) { + handleCompassPermission() - updateEstimatedDistance() - }, - handleLocationError, - getGeoOptions() - ) + setIsWalking(true) + setAutoRotate(true) + setWalkDistance(0) + setEstimatedDistance(0) + accumulatedPositionsRef.current = [] + + routeSourceRef.current.clear() + vectorSourceRef.current.clear() + + rotateMap(lastHeadingRef.current) + watchPositionIdRef.current = startWatchingPosition() walkIntervalRef.current = window.setInterval(() => { setWalkTime(prev => prev + 1) @@ -816,15 +881,35 @@ export default function MapComponent({ isModalOpen = false, setNearbyWalkers }: const mapImage = await captureMap() - const walkCompleteData = { - time: formatTime(walkTime), - distance: formatDistance(walkDistance || estimatedDistance), - mapImage: mapImage, - } + try { + const mapImageFile = base64ToFile(mapImage) + + // FormData 객체 생성 + const formData = new FormData() + formData.append('walkImgFile', mapImageFile) + formData.append( + 'request', + new Blob( + [ + JSON.stringify({ + totalDistanceMeter: walkDistance || estimatedDistance, + totalWalkTimeSecond: walkTime, + }), + ], + { type: 'application/json' } + ) + ) - navigate('/walk-complete', { - state: walkCompleteData, - }) + const response = await walkCompleteMutation.mutateAsync(formData) + + console.log(response) + + navigate('/walk-complete', { + state: response.data, + }) + } catch (error) { + console.error('산책 완료 데이터 전송 실패:', error) + } } } @@ -872,8 +957,8 @@ export default function MapComponent({ isModalOpen = false, setNearbyWalkers }: const drawRoute = (coordinates: number[][], append: boolean = false) => { const routeStyle = new Style({ stroke: new Stroke({ - color: '#FF6B6B', - width: 5, + color: '#ECB99A', + width: 10, lineCap: 'round', lineJoin: 'round', }), @@ -934,6 +1019,65 @@ export default function MapComponent({ isModalOpen = false, setNearbyWalkers }: } }, [isWalking]) + // 파트너 위치 마커 스타일 정의 + const getPartnerMarkerStyle = new Style({ + image: new Icon({ + anchor: [0.5, 1], + anchorXUnits: 'fraction', + anchorYUnits: 'fraction', + src: `data:image/svg+xml;utf8,${encodeURIComponent(getMarkerIconString2())}`, + + scale: 0.75, + }), + }) + + // 파트너 위치 업데이트 함수 + const updatePartnerLocation = (latitude: number, longitude: number) => { + const coordinates = fromLonLat([longitude, latitude]) + + if (partnerMarkerRef.current) { + // 기존 마커 위치 업데이트 + const point = partnerMarkerRef.current.getGeometry() as Point + const currentCoords = point.getCoordinates() + + // 부드러운 애니메이션으로 위치 이동 + const duration = 2000 + const start = Date.now() + const animate = () => { + const elapsed = Date.now() - start + const progress = Math.min(elapsed / duration, 1) + const easeProgress = easeOut(progress) + + const x = currentCoords[0] + (coordinates[0] - currentCoords[0]) * easeProgress + const y = currentCoords[1] + (coordinates[1] - currentCoords[1]) * easeProgress + + point.setCoordinates([x, y]) + + if (progress < 1) { + requestAnimationFrame(animate) + } + } + + animate() + } else { + // 새로운 마커 생성 + partnerMarkerRef.current = new Feature({ + geometry: new Point(coordinates), + }) + partnerMarkerRef.current.setStyle(getPartnerMarkerStyle) + vectorSourceRef.current.addFeature(partnerMarkerRef.current) + } + } + + // 컴포넌트 정리 시 파트너 마커 제거 + useEffect(() => { + return () => { + if (partnerMarkerRef.current) { + vectorSourceRef.current.removeFeature(partnerMarkerRef.current) + } + } + }, []) + return ( @@ -1031,7 +1175,7 @@ export default function MapComponent({ isModalOpen = false, setNearbyWalkers }: profileImg: proposalInfo.dogProfileImg, age: proposalInfo.dogAge, gender: proposalInfo.dogGender === 'MALE' ? '남' : '여', - email: proposalInfo.email, + memberEmail: proposalInfo.email, comment: proposalInfo.comment, }} onClose={() => setShowProposalModal(false)} diff --git a/src/pages/WalkPage/components/MapComponent/styles.ts b/src/pages/WalkPage/components/MapComponent/styles.ts index 4ce1eec6..46e96497 100644 --- a/src/pages/WalkPage/components/MapComponent/styles.ts +++ b/src/pages/WalkPage/components/MapComponent/styles.ts @@ -1,7 +1,8 @@ import { styled } from 'styled-components' import { FOOTER_HEIGHT } from '~constants/layout' import DogProfile from '~assets/walk_dog.svg?react' -import MapPin from '~assets/walk_pin.svg?react' +import MapPin from '~assets/map_pin_1.svg?react' +import MapPin2 from '~assets/map_pin_2.svg?react' import { ActionButton } from '~components/Button/ActionButton' import Center from '~assets/center.svg?react' import { Box } from '~components/Box' @@ -71,6 +72,11 @@ export const MapPinIcon = styled(MapPin)` height: 100%; ` +export const MapPinIcon2 = styled(MapPin2)` + width: 100%; + height: 100%; +` + export const WalkControlContainer = styled.div` position: absolute; bottom: 20px; diff --git a/src/pages/WalkPage/components/WalkModal/index.tsx b/src/pages/WalkPage/components/WalkModal/index.tsx index 8702986b..eb187682 100644 --- a/src/pages/WalkPage/components/WalkModal/index.tsx +++ b/src/pages/WalkPage/components/WalkModal/index.tsx @@ -13,19 +13,22 @@ const WalkModal = ({ type, userInfo, onClose, onConfirm, onCancel }: WalkModalPr const [message, setMessage] = useState('') const { publish, isConnected } = useWebSocket() + console.log(userInfo) const handleConfirm = () => { if (type === 'request') { const proposalData = { - otherMemberId: userInfo.email, - message, + otherMemberEmail: (userInfo as RequestUserInfo).memberEmail, + comment: message, } + console.log(proposalData) publish('/pub/api/v1/proposal', proposalData) onClose() } else if (type === 'accept') { const decisionData = { - otherEmail: (userInfo as RequestUserInfo).email, + otherEmail: (userInfo as RequestUserInfo).memberEmail, decision: 'ACCEPT', } + console.log(decisionData) publish('/pub/api/v1/decision', decisionData) onClose() } else { @@ -36,7 +39,7 @@ const WalkModal = ({ type, userInfo, onClose, onConfirm, onCancel }: WalkModalPr const handleCancel = () => { if (type === 'accept') { const decisionData = { - otherEmail: (userInfo as RequestUserInfo).email, + otherEmail: (userInfo as RequestUserInfo).memberEmail, decision: 'DENY', } publish('/pub/api/v1/decision', decisionData) @@ -126,7 +129,7 @@ const WalkModal = ({ type, userInfo, onClose, onConfirm, onCancel }: WalkModalPr

{userInfo.name}

- {(type === 'request' || type === 'accept' || type === 'walkRequest') ?? ( + {(type === 'request' || type === 'accept' || type === 'walkRequest') && ( <>

{(userInfo as RequestUserInfo).breed} {' '} @@ -156,7 +159,14 @@ const WalkModal = ({ type, userInfo, onClose, onConfirm, onCancel }: WalkModalPr )} {type === 'request' ? ( - + setMessage(e.target.value)} + /> ) : type === 'progress' ? ( {modalContent?.message} diff --git a/src/pages/WalkPage/index.tsx b/src/pages/WalkPage/index.tsx index 82b30abc..b2278f86 100644 --- a/src/pages/WalkPage/index.tsx +++ b/src/pages/WalkPage/index.tsx @@ -7,64 +7,6 @@ import { useState } from 'react' import WalkerListModal from '~pages/WalkPage/components/WalkerListModal' import WalkModal from '~pages/WalkPage/components/WalkModal' -const dummyWalkers: NearbyWalker[] = [ - { - dogId: 1, - dogName: '멍멍이', - dogProfileImg: 'https://placedog.net/200/200?id=1', - breed: '골든리트리버', - dogAge: 3, - dogGender: 'MALE', - dogWalkCount: 15, - memberId: 1, - memberEmail: 'test@test.com', - }, - { - dogId: 2, - dogName: '초코', - dogProfileImg: 'https://placedog.net/200/200?id=2', - breed: '푸들', - dogAge: 2, - dogGender: 'FEMALE', - dogWalkCount: 8, - memberId: 2, - memberEmail: 'test2@test.com', - }, - { - dogId: 3, - dogName: '바둑이', - dogProfileImg: 'https://placedog.net/200/200?id=3', - breed: '말티즈', - dogAge: 4, - dogGender: 'MALE', - dogWalkCount: 20, - memberId: 3, - memberEmail: 'test3@test.com', - }, - { - dogId: 4, - dogName: '뽀삐', - dogProfileImg: 'https://placedog.net/200/200?id=4', - breed: '포메라니안', - dogAge: 1, - dogGender: 'FEMALE', - dogWalkCount: 5, - memberId: 4, - memberEmail: 'test4@test.com', - }, - { - dogId: 5, - dogName: '보리', - dogProfileImg: 'https://placedog.net/200/200?id=5', - breed: '포메라니안', - dogAge: 1, - dogGender: 'FEMALE', - dogWalkCount: 5, - memberId: 5, - memberEmail: 'test5@test.com', - }, -] - export default function WalkPage() { const navigate = useNavigate() const [isModalOpen] = useState(false) @@ -111,7 +53,7 @@ export default function WalkPage() { isOpen={isWalkerListOpen} onClose={handleWalkerListClose} isClosing={isClosing} - walkers={dummyWalkers} + walkers={nearbyWalkers} onWalkRequest={handleWalkRequest} /> @@ -124,6 +66,7 @@ export default function WalkPage() { age: selectedWalker.dogAge, gender: selectedWalker.dogGender === 'MALE' ? '남' : '여', profileImg: selectedWalker.dogProfileImg, + memberEmail: selectedWalker.memberEmail, }} onClose={() => setIsWalkModalOpen(false)} onConfirm={() => { diff --git a/src/types/modal.ts b/src/types/modal.ts index cef7613f..a2691742 100644 --- a/src/types/modal.ts +++ b/src/types/modal.ts @@ -10,7 +10,7 @@ export type RequestUserInfo = { age: number gender: string profileImg?: string - email?: string + memberEmail?: string comment?: string }