diff --git a/app/meeting/[id]/page.tsx b/app/meeting/[id]/page.tsx index 7d10e77..0279f20 100644 --- a/app/meeting/[id]/page.tsx +++ b/app/meeting/[id]/page.tsx @@ -11,6 +11,8 @@ import { useCheckMeeting } from '@/hooks/api/query/useCheckMeeting'; // [추가] import StationDataRaw from '@/database/stations_info.json'; import { getRandomHexColor } from '@/lib/color'; import MeetingInfoSection from '@/components/meeting/MeetingInfoSection'; +import { useToast } from '@/hooks/useToast'; +import Toast from '@/components/ui/toast'; // 로컬 데이터 타입 정의 interface StationInfo { @@ -36,6 +38,8 @@ export default function Page() { // [API Hook] 모임 정보 조회 & 출발지 등록 const { data: meetingData } = useCheckMeeting(id); const { mutate: setDeparture } = useSetDeparture(id); + const { isVisible, show } = useToast(); + const [errorMessage, setErrorMessage] = useState(''); const [myName] = useState(() => { if (typeof window === 'undefined') return ''; return localStorage.getItem('userId') || sessionStorage.getItem('userId') || ''; @@ -113,9 +117,11 @@ export default function Page() { const handleSubmit = () => { if (!selectedStation) { - alert('출발지를 먼저 선택해주세요!'); + setErrorMessage('출발지를 먼저 선택해주세요!'); + show(); return; } + // 결과 페이지로 이동 router.push(`/result/${id}`); }; @@ -201,7 +207,7 @@ export default function Page() { className="flex h-7 w-7 items-center justify-center rounded-full text-xs font-normal text-white" style={{ backgroundColor: `${user.hexColor}` }} > - {user.name} + {user.name.charAt(0)} {user.name} @@ -212,10 +218,11 @@ export default function Page() { + diff --git a/app/result/[id]/page.tsx b/app/result/[id]/page.tsx index 007a335..f15e1fc 100644 --- a/app/result/[id]/page.tsx +++ b/app/result/[id]/page.tsx @@ -1,11 +1,11 @@ 'use client'; -import { useState } from 'react'; +import { useState, useMemo } from 'react'; import Image from 'next/image'; import { useOpenModal } from '@/hooks/useOpenModal'; import { useParams, useRouter } from 'next/navigation'; import KakaoMapLine from '@/components/map/kakaoMapLine'; -import { MOCK_LOCATION_RESULTS } from '@/mock/mockData'; +import { useMidpoint } from '@/hooks/api/query/useMidpoint'; export default function Page() { const openModal = useOpenModal(); @@ -13,23 +13,164 @@ export default function Page() { const params = useParams(); const id = params?.id as string; - // 현재 선택된 결과 카드 관리 (기본값: 첫 번째) + // 현재 사용자 닉네임 가져오기 + const [myNickname] = useState(() => { + if (typeof window === 'undefined') return ''; + return localStorage.getItem('userId') || sessionStorage.getItem('userId') || ''; + }); + + const { data: midpointData, isLoading, isError } = useMidpoint(id); + + const locationResults = useMemo(() => { + if (!midpointData?.success || !midpointData.data || !Array.isArray(midpointData.data)) { + return []; + } + + return midpointData.data.map((midpoint, index) => { + const { endStation, endStationLine, userRoutes } = midpoint; + + const myRoute = userRoutes.find((route) => route.nickname === myNickname); + + const travelTime = myRoute?.travelTime + + // 호선 번호 추출 함수 (숫자가 있으면 숫자만, 없으면 앞 글자만) + const extractLineNumber = (linenumber: string): string => { + if (!linenumber) return ''; + + // "호선" 제거 + const cleaned = linenumber.replace('호선', '').trim(); + + // 숫자가 있는지 확인 + const hasNumber = /\d/.test(cleaned); + + if (hasNumber) { + // 숫자만 추출 (예: "4호선" → "4") + return cleaned.replace(/\D/g, ''); + } else { + // 숫자가 없으면 앞 글자만 추출 (예: "수인분당선" → "수") + return cleaned.charAt(0); + } + }; + + + const transferPathLines: Array<{ display: string; text: string }> = []; + + if (myRoute?.transferPath && Array.isArray(myRoute.transferPath) && myRoute.transferPath.length > 0) { + for (const path of myRoute.transferPath) { + if (path?.linenumber) { + const lineNumber = extractLineNumber(path.linenumber); + if (lineNumber) { + transferPathLines.push({ + display: lineNumber, // 원 안에 표시할 값 + text: path.linenumber, // 텍스트로 표시할 원래 값 + }); + } + } + } + } + + + if (endStationLine) { + const endLineNumber = extractLineNumber(endStationLine); + if (endLineNumber) { + // transferPathLines의 마지막 항목과 비교 + const lastLine = transferPathLines[transferPathLines.length - 1]; + if (lastLine?.display !== endLineNumber) { + transferPathLines.push({ + display: endLineNumber, // 원 안에 표시할 값 + text: endStationLine, // 텍스트로 표시할 원래 값 + }); + } + } + } + + return { + id: index + 1, + endStation, + endStationLine, + latitude: midpoint.latitude, + longitude: midpoint.longitude, + travelTime, + transferPath: myRoute?.transferPath || [], + transferPathLines, + userRoutes, + }; + }); + }, [midpointData, myNickname]); + + const [selectedResultId, setSelectedResultId] = useState(1); const handleModifyStart = () => { - // 출발지 수정 로직 (이전 페이지로 이동 등) router.back(); }; - // 호선별 색상 반환 함수 (예시) - const getLineColor = (line: string) => { - switch (line) { - case '1': - return 'bg-[#0052A4]'; // 1호선 파랑 - case '2': - return 'bg-[#3CB44A]'; // 2호선 초록 - case '6': - return 'bg-[#CD7C2F]'; // 6호선 갈색 + const getLineColor = (fullLineName: string) => { + const cleaned = fullLineName.replace('호선', '').trim(); + + // 숫자 호선 처리 (1~9) + if (/^\d+$/.test(cleaned)) { + switch (cleaned) { + case '1': + return 'bg-[#004A85]'; // 1호선 파랑 + case '2': + return 'bg-[#00A23F]'; // 2호선 초록 + case '3': + return 'bg-[#ED6C00]'; // 3호선 파랑 + case '4': + return 'bg-[#009BCE]'; // 4호선 파랑 + case '5': + return 'bg-[#794698]'; // 5호선 보라색 + case '6': + return 'bg-[#7C4932]'; // 6호선 빨강 + case '7': + return 'bg-[#6E7E31]'; // 7호선 초록 + case '8': + return 'bg-[#D11D70]'; // 8호선 빨강 + case '9': + return 'bg-[#A49D87]'; // 9호선 회색 + default: + return 'bg-gray-400'; + } + } + + // 전체 호선명으로 처리 (앞 글자가 겹치는 경우 구분) + switch (fullLineName) { + // 수도권 도시철도(경전철) + case '우이신설선': + return 'bg-[#B0CE18]'; // 우이신설 노랑 + case '신림선': + return 'bg-[#5E7DBB]'; // 신림선 하늘 + case '의정부경전철': + return 'bg-[#F0831E]'; // 의정부경전철 주황 + case '용인에버라인': + return 'bg-[#44A436]'; // 용인에버라인 초록 + case '인천2호선': + return 'bg-[#F4A462]'; // 인천2호선 살색 + case '김포골드라인': + return 'bg-[#F4A462]'; // 김포골드라인 금색 + + // 수도권 도시철도(중전철) + case '경의선': + case '경의중앙선': + return 'bg-[#6AC2B3]'; // 경의중앙선 민트색 + case '수인분당선': + return 'bg-[#ECA300]'; // 수인분당선 노란색 + case '신분당선': + return 'bg-[#B81B30]'; // 신분당선 빨강색 + case '인천1호선': + return 'bg-[#B4C7E7]'; // 인천1호선 연한 하늘색 + case '공항철도': + return 'bg-[#0079AC]'; // 공항철도 파랑색 + + // 광역철도 + case '경춘선': + return 'bg-[#007A62]'; // 경춘선 초록 + case '경강산': + return 'bg-[#0B318F]'; // 경강산 파란 + case '서해선': + return 'bg-[#5EAC41]'; // 서해선 초록 + default: return 'bg-gray-400'; } @@ -65,68 +206,84 @@ export default function Page() { {/* 리스트 스크롤 영역 */}
- {MOCK_LOCATION_RESULTS.map((result) => ( -
setSelectedResultId(result.id)} - className={`flex cursor-pointer flex-col gap-3.75 rounded border bg-white p-5 ${ - selectedResultId === result.id - ? 'border-blue-5 border-2' - : 'border-gray-2 hover:bg-gray-1' - }`} - > - {/* 카드 헤더: 역 이름 & 시간 */} -
- {result.station} - - 이동시간 - - {result.time} + {isLoading ? ( +
+ 로딩 중... +
+ ) : isError || locationResults.length === 0 ? ( +
+ + {isError ? '데이터를 불러오는데 실패했습니다.' : '결과가 없습니다.'} + +
+ ) : ( + locationResults.map((result) => ( +
setSelectedResultId(result.id)} + className={`flex cursor-pointer flex-col gap-3.75 rounded border bg-white p-5 ${ + selectedResultId === result.id + ? 'border-blue-5 border-2' + : 'border-gray-2 hover:bg-gray-1' + }`} + > + {/* 카드 헤더: 역 이름 & 시간 */} +
+ {result.endStation} + + 이동시간 + + {result.travelTime}분 + - -
- - {/* 환승 경로 (호선 아이콘) */} -
- 내 환승경로 -
- {result.lines.map((line, idx) => ( -
- {/* 호선 원형 아이콘 */} - - {line} - - - {line}호선 - - {idx < result.lines.length - 1 && ( - 화살표 - )} -
- ))}
-
- {/* 모임원 경로 보기 버튼 (카드 내부) */} - -
- ))} + {/* 환승 경로 (호선 아이콘) */} +
+ 내 환승경로 +
+
+ {result.transferPathLines.map((line: { display: string; text: string }, idx: number) => ( +
+ {/* 호선 원형 아이콘 */} + + {line.display} + + + {line.text} + + {idx < result.transferPathLines.length - 1 && ( + 화살표 + )} +
+ ))} +
+ + + {/* 모임원 경로 보기 버튼 (카드 내부) */} + +
+ )) + )}
@@ -142,7 +299,20 @@ export default function Page() { {/* [RIGHT PANEL] 데스크탑 지도 영역 */}
- + {locationResults.length > 0 && (() => { + const selectedResult = locationResults.find((r) => r.id === selectedResultId) || locationResults[0]; + return ( + + ); + })()}
diff --git a/components/map/kakaoMapLine.tsx b/components/map/kakaoMapLine.tsx index 5c903f1..f3036f6 100644 --- a/components/map/kakaoMapLine.tsx +++ b/components/map/kakaoMapLine.tsx @@ -1,160 +1,187 @@ 'use client'; -import { useState, useMemo } from 'react'; -import { Map, Polyline, CustomOverlayMap, Circle } from 'react-kakao-maps-sdk'; -import { REAL_SUBWAY_PATHS, HAPJUNG_STATION } from '@/mock/mockData'; +import { useState, useEffect } from 'react'; +import { Map, Polyline, CustomOverlayMap } from 'react-kakao-maps-sdk'; import { useRouter } from 'next/navigation'; import ZoomControl from './zoomControl'; +import { getRandomHexColor } from '@/lib/color'; -export default function KakaoMapLine({ className }: { className?: string }) { +interface EndStation { + name: string; + latitude: number; + longitude: number; +} + +interface UserRoute { + nickname: string; + startStation: string; + startStationLine: string; + latitude: number; + longitude: number; + travelTime: number; + transferPath: Array<{ + linenumber: string; + station: string; + latitude: number; + longitude: number; + }>; + stations: Array<{ + linenumber: string; + station: string; + latitude: number; + longitude: number; + }>; +} + +interface KakaoMapLineProps { + className?: string; + endStation?: EndStation; + userRoutes?: UserRoute[]; +} + +export default function KakaoMapLine({ className, endStation, userRoutes = [] }: KakaoMapLineProps) { const router = useRouter(); const [map, setMap] = useState(null); - const [hoveredRouteId, setHoveredRouteId] = useState(null); - const [tooltipStation, setTooltipStation] = useState<{ - lat: number; - lng: number; - name: string; - } | null>(null); - - // 데이터가 로드되면 지도의 범위를 모든 경로가 보이도록 조정 - const initBounds = useMemo(() => { - if (typeof kakao === 'undefined') return null; + const [hoveredUserId, setHoveredUserId] = useState(null); + + // 지도가 생성되고 Kakao Maps SDK가 로드된 후 범위 설정 + useEffect(() => { + if (!endStation || userRoutes.length === 0) return; + if (!map || typeof kakao === 'undefined' || !kakao.maps || !kakao.maps.LatLngBounds) return; + const bounds = new kakao.maps.LatLngBounds(); // 도착지 추가 - bounds.extend(new kakao.maps.LatLng(HAPJUNG_STATION.latitude, HAPJUNG_STATION.longitude)); - - // 모든 경로의 정차역 추가 - REAL_SUBWAY_PATHS.forEach((route) => { - route.stations.forEach((station) => { - bounds.extend(new kakao.maps.LatLng(station.latitude, station.longitude)); - }); + bounds.extend(new kakao.maps.LatLng(endStation.latitude, endStation.longitude)); + + // 사용자 경로 기준으로 범위 설정 + userRoutes.forEach((userRoute) => { + // 출발역 추가 + bounds.extend(new kakao.maps.LatLng(userRoute.latitude, userRoute.longitude)); + + // transferPath의 모든 역 추가 + if (userRoute.transferPath && userRoute.transferPath.length > 0) { + userRoute.transferPath.forEach((path) => { + bounds.extend(new kakao.maps.LatLng(path.latitude, path.longitude)); + }); + } + + // stations의 모든 역 추가 + if (userRoute.stations && userRoute.stations.length > 0) { + userRoute.stations.forEach((station) => { + bounds.extend(new kakao.maps.LatLng(station.latitude, station.longitude)); + }); + } }); - return bounds; - }, []); - // 특정 경로 클릭 시 해당 경로로 줌인 - const handleRouteClick = (route: (typeof REAL_SUBWAY_PATHS)[0]) => { - if (!map) return; - const bounds = new kakao.maps.LatLngBounds(); - route.stations.forEach((s) => bounds.extend(new kakao.maps.LatLng(s.latitude, s.longitude))); - map.setBounds(bounds, 100); // padding 100 - }; + map.setBounds(bounds); + }, [map, endStation, userRoutes]); + + // endStation이나 userRoutes가 없으면 빈 화면 표시 + if (!endStation || userRoutes.length === 0) { + return ( +
+
+ 지도 정보가 없습니다. +
+
+ ); + } return (
{ - setMap(mapInstance); - if (initBounds) mapInstance.setBounds(initBounds); - }} + onCreate={setMap} > - {/* 도착지(합정역) 마커 */} + {/* 도착지 마커 */}
- {HAPJUNG_STATION.name} + {endStation.name}
- {/* 경로 루프 */} - {REAL_SUBWAY_PATHS.map((route) => { - const isHovered = hoveredRouteId === route.id; + {/* 사용자 경로 표시 */} + {userRoutes.map((userRoute, index) => { + const isHovered = hoveredUserId === userRoute.nickname; + const userColor = getRandomHexColor(userRoute.nickname); + + // 경로 좌표 생성 (출발역 -> transferPath -> 도착지) + const pathCoordinates: Array<{ lat: number; lng: number }> = []; + + // 출발역 추가 + pathCoordinates.push({ lat: userRoute.latitude, lng: userRoute.longitude }); + + // transferPath 추가 + if (userRoute.transferPath && userRoute.transferPath.length > 0) { + userRoute.transferPath.forEach((path) => { + pathCoordinates.push({ lat: path.latitude, lng: path.longitude }); + }); + } + + // 도착지 추가 + pathCoordinates.push({ lat: endStation.latitude, lng: endStation.longitude }); return ( -
- {/*지하철 경로 선 */} - ({ lat: s.latitude, lng: s.longitude }))} - strokeWeight={4} - strokeColor={route.color} - strokeStyle={'solid'} - zIndex={isHovered ? 50 : 1} - /> - - {/* 정차역 점 (Circle) & 툴팁 이벤트 */} - {route.stations.slice(1, -1).map((station, idx) => ( - - setTooltipStation({ - lat: station.latitude, - lng: station.longitude, - name: station.name, - }) - } - onMouseout={() => setTooltipStation(null)} +
+ {/* 경로 선 */} + {pathCoordinates.length > 1 && ( + - ))} + )} {/* 출발지 마커 & 인터랙션 영역 */} - {/* 마우스 이벤트 감지용 그룹 */}
setHoveredRouteId(route.id)} - onMouseLeave={() => setHoveredRouteId(null)} - onClick={() => handleRouteClick(route)} + onMouseEnter={() => setHoveredUserId(userRoute.nickname)} + onMouseLeave={() => setHoveredUserId(null)} > {/* 말풍선 */}
- {route.originName}에서 + {userRoute.startStation}에서 - {route.time} + {userRoute.travelTime}분
- {/* 원형 아이콘 */} + {/* 원형 아이콘 (닉네임 앞글자) */}
- {route.name} + {userRoute.nickname.charAt(0)}
- {/* 클릭/호버 감지 범위 확장용 투명 원 (Click Hit Area) */} + {/* 클릭/호버 감지 범위 확장용 투명 원 */}
); })} - - {/* 툴팁 오버레이 (조건부 렌더링) */} - {tooltipStation && ( - -
- {tooltipStation.name} -
-
- )} {/* 상단 고정 버튼 (지도 밖) */} @@ -163,7 +190,7 @@ export default function KakaoMapLine({ className }: { className?: string }) { className="bg-blue-5 hover:bg-blue-8 relative flex h-9 cursor-pointer items-center rounded-full px-4 py-1.75 text-sm font-semibold text-white transition-colors" onClick={() => router.push('/recommend')} > - {HAPJUNG_STATION.name} 주변 장소 추천 + {endStation.name} 주변 장소 추천
diff --git a/components/modal/globalModal.tsx b/components/modal/globalModal.tsx index 3f04c5a..08536d9 100644 --- a/components/modal/globalModal.tsx +++ b/components/modal/globalModal.tsx @@ -21,7 +21,7 @@ export default function GlobalModal() { case 'NUDGE': return ; case 'TRANSFER': - return ; + return ; default: return null; } diff --git a/components/modal/transferModal.tsx b/components/modal/transferModal.tsx index 4280d5b..e4e103f 100644 --- a/components/modal/transferModal.tsx +++ b/components/modal/transferModal.tsx @@ -8,24 +8,124 @@ import { DialogTitle, DialogDescription, } from '@/components/ui/dialog'; -import { MOCK_TRANSFER_ROUTES } from '@/mock/mockData'; + +interface UserRoute { + nickname: string; + startStation: string; + startStationLine: string; + latitude: number; + longitude: number; + travelTime: number; + transferPath: Array<{ + linenumber: string; + station: string; + latitude: number; + longitude: number; + }>; + stations: Array<{ + linenumber: string; + station: string; + latitude: number; + longitude: number; + }>; +} interface TransferModalProps { isOpen: boolean; onClose: () => void; + userRoutes?: UserRoute[]; + endStation?: string; } -export default function TransferModal({ isOpen, onClose }: TransferModalProps) { - // 호선별 뱃지 색상 (필요시 추가) - const getLineBadgeStyle = (line: string) => { - switch (line) { +export default function TransferModal({ isOpen, onClose, userRoutes = [], endStation = '' }: TransferModalProps) { + +// 호선별 색상 반환 함수 (전체 호선명을 받아서 처리) +const getLineBadgeStyle = (fullLineName: string) => { + // "호선" 제거 + const cleaned = fullLineName.replace('호선', '').trim(); + + // 숫자 호선 처리 (1~9) + if (/^\d+$/.test(cleaned)) { + switch (cleaned) { case '1': - return 'bg-[#0052A4]'; // 1호선 파랑 + return 'bg-[#004A85]'; // 1호선 파랑 case '2': - return 'bg-[#3CB44A]'; // 2호선 초록 + return 'bg-[#00A23F]'; // 2호선 초록 + case '3': + return 'bg-[#ED6C00]'; // 3호선 파랑 + case '4': + return 'bg-[#009BCE]'; // 4호선 파랑 + case '5': + return 'bg-[#794698]'; // 5호선 보라색 + case '6': + return 'bg-[#7C4932]'; // 6호선 빨강 + case '7': + return 'bg-[#6E7E31]'; // 7호선 초록 + case '8': + return 'bg-[#D11D70]'; // 8호선 빨강 + case '9': + return 'bg-[#A49D87]'; // 9호선 회색 default: return 'bg-gray-400'; } + } + + // 전체 호선명으로 처리 (앞 글자가 겹치는 경우 구분) + switch (fullLineName) { + // 수도권 도시철도(경전철) + case '우이신설선': + return 'bg-[#B0CE18]'; // 우이신설 노랑 + case '신림선': + return 'bg-[#5E7DBB]'; // 신림선 하늘 + case '의정부경전철': + return 'bg-[#F0831E]'; // 의정부경전철 주황 + case '용인에버라인': + return 'bg-[#44A436]'; // 용인에버라인 초록 + case '인천2호선': + return 'bg-[#F4A462]'; // 인천2호선 살색 + case '김포골드라인': + return 'bg-[#F4A462]'; // 김포골드라인 금색 + + // 수도권 도시철도(중전철) + case '경의선': + case '경의중앙선': + return 'bg-[#6AC2B3]'; // 경의선/경의중앙선 민트색 + case '수인분당선': + return 'bg-[#ECA300]'; // 수인분당선 노란색 + case '신분당선': + return 'bg-[#B81B30]'; // 신분당선 빨강색 + case '인천1호선': + return 'bg-[#B4C7E7]'; // 인천1호선 연한 하늘색 + case '공항철도': + return 'bg-[#0079AC]'; // 공항철도 파랑색 + + // 광역철도 + case '경춘선': + return 'bg-[#007A62]'; // 경춘선 초록 + case '경강산': + return 'bg-[#0B318F]'; // 경강산 파란 + case '서해선': + return 'bg-[#5EAC41]'; // 서해선 초록 + + default: + return 'bg-gray-400'; + } +}; + + // transferPath에서 호선 추출 함수 + const extractTransferLines = (route: UserRoute): string[] => { + const lines: string[] = []; + + // transferPath의 모든 항목에서 호선 추출 + if (route.transferPath && Array.isArray(route.transferPath)) { + route.transferPath.forEach((path) => { + if (path.linenumber && !lines.includes(path.linenumber)) { + lines.push(path.linenumber); + } + }); + } + + return lines; }; return ( @@ -40,61 +140,75 @@ export default function TransferModal({ isOpen, onClose }: TransferModalProps) { 모임원 환승경로 보기 - 합정역 도착 + {endStation ? `${endStation} 도착` : '도착역'} {/* 리스트 영역 (스크롤) */}
- {MOCK_TRANSFER_ROUTES.map((route, index) => ( -
- {/* 상단: 이름 및 출발역 */} -
- {route.name} - - {route.startStation} - -
+ {userRoutes.length === 0 ? ( +
+ 환승 경로 정보가 없습니다. +
+ ) : ( + userRoutes.map((route, index) => { + const transferLines = extractTransferLines(route); + + return ( +
+ {/* 상단: 이름 및 출발역 */} +
+ {route.nickname} + + {route.startStation} + +
- {/* 하단: 호선 정보 및 시간 */} -
- {/* 호선 뱃지 */} -
- {route.lines.map((line, idx) => ( -
- - {line}호선 - - {/* 화살표 아이콘 (마지막 요소 제외) */} - {idx < route.lines.length - 1 && ( - - 오른쪽 화살표 - + {/* 하단: 호선 정보 및 시간 */} +
+ {/* 호선 뱃지 */} +
+ {transferLines.length === 0 ? ( + 환승 경로 없음 + ) : ( + transferLines.map((line, idx) => ( +
+ + {line} + + {/* 화살표 아이콘 (마지막 요소 제외) */} + {idx < transferLines.length - 1 && ( + + 오른쪽 화살표 + + )} +
+ )) )}
- ))} -
- {/* 이동 시간 */} - - 이동시간 - {route.time} - -
-
- ))} + {/* 이동 시간 */} + + 이동시간 + {route.travelTime}분 + +
+
+ ); + }) + )}
diff --git a/hooks/api/query/useMidpoint.ts b/hooks/api/query/useMidpoint.ts new file mode 100644 index 0000000..4b66afa --- /dev/null +++ b/hooks/api/query/useMidpoint.ts @@ -0,0 +1,13 @@ +import { useQuery } from '@tanstack/react-query'; +import { apiGet } from '@/lib/api'; +import type { MidpointResponse } from '@/types/api'; + +export const useMidpoint = (meetingId: string) => { + return useQuery({ + queryKey: ['midpoint', meetingId], + queryFn: async () => { + return apiGet(`/api/meeting/${meetingId}/midpoint`); + }, + enabled: !!meetingId, + }); +}; diff --git a/store/useModalStore.ts b/store/useModalStore.ts index eeff5da..b26fa2a 100644 --- a/store/useModalStore.ts +++ b/store/useModalStore.ts @@ -6,6 +6,27 @@ export type ModalType = 'FEEDBACK' | 'SHARE' | 'NUDGE' | 'TRANSFER'; export interface ModalData { meetingId?: string; // SHARE, NUDGE 모달에서 사용 + userRoutes?: Array<{ + nickname: string; + startStation: string; + startStationLine: string; + latitude: number; + longitude: number; + travelTime: number; + transferPath: Array<{ + linenumber: string; + station: string; + latitude: number; + longitude: number; + }>; + stations: Array<{ + linenumber: string; + station: string; + latitude: number; + longitude: number; + }>; + }>; // TRANSFER 모달에서 사용 + endStation?: string; // TRANSFER 모달에서 사용 } export const useModalStore = create( diff --git a/types/api.ts b/types/api.ts index 5c5c221..0a3df20 100644 --- a/types/api.ts +++ b/types/api.ts @@ -65,3 +65,39 @@ export interface MeetingStatusData { // 모임 참여 현황 조회 API 조립 export type MeetingStatusResponse = ApiResponse; + +// 중간지점 조회 API 응답 데이터 타입 +export interface MidpointData { + endStation: string; + endStationLine: string; + latitude: number; + longitude: number; + userRoutes: { + nickname: string; + startStation: string; + startStationLine: string; + latitude: number; + longitude: number; + travelTime: number; + transferPath: { + linenumber: string; + station: string; + latitude: number; + longitude: number; + }[]; + stations: { + linenumber: string; + station: string; + latitude: number; + longitude: number; + }[]; + }[]; +} + + + + + +// 중간지점 조회 API 조립 +// 실제 응답: { success: true, data: MidpointData[] } +export type MidpointResponse = ApiResponse;