Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Binary file modified app/favicon.ico
Binary file not shown.
5 changes: 5 additions & 0 deletions app/result/[id]/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,10 @@ import KakaoMapLine from '@/components/map/kakaoMapLine';
import { useMidpoint } from '@/hooks/api/query/useMidpoint';
import { useCheckMeeting } from '@/hooks/api/query/useCheckMeeting';
import { getMeetingUserId } from '@/lib/storage';
import { useQueryClient } from '@tanstack/react-query';

export default function Page() {
const queryClient = useQueryClient();
const openModal = useOpenModal();
const router = useRouter();
const params = useParams();
Expand Down Expand Up @@ -103,6 +105,9 @@ export default function Page() {
const [selectedResultId, setSelectedResultId] = useState<number>(1);

const handleModifyStart = () => {
queryClient.removeQueries({ queryKey: ['midpoint', id] });
queryClient.removeQueries({ queryKey: ['recommend', id] });

router.back();
};

Expand Down
23 changes: 16 additions & 7 deletions components/join/joinForm.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
'use client';

import { useRouter, useSearchParams } from 'next/navigation'; // [추가] useSearchParams
import { useRouter, useSearchParams } from 'next/navigation';
import { useState, useEffect } from 'react';
import { useEnterParticipant } from '@/hooks/api/mutation/useEnterParticipant';
import { useToast } from '@/hooks/useToast';
Expand All @@ -14,15 +14,15 @@

export default function JoinForm({ meetingId }: JoinFormProps) {
const router = useRouter();
const searchParams = useSearchParams(); // [추가] 쿼리 스트링 읽기용 훅
const searchParams = useSearchParams();

// meetingId는 부모(Page)에서 props로 전달받음
const { isLogin, isChecking } = useIsLoggedIn(meetingId);

const [name, setName] = useState('');
const [password, setPassword] = useState('');
const [isRemembered, setIsRemembered] = useState(true);
const [errorMessage, setErrorMessage] = useState(''); // [수정] string 타입으로 초기화
const [errorMessage, setErrorMessage] = useState('');

const participantEnter = useEnterParticipant();
const { isVisible, show } = useToast();
Expand Down Expand Up @@ -68,7 +68,7 @@
if (!isFormValid || !meetingId) return;

try {
// @ts-ignore (혹시 모를 타입 불일치 방지, API 스펙에 따라 제거 가능)
// @ts-ignore
const result = await participantEnter.mutateAsync({
meetingId,
data: {
Expand All @@ -77,15 +77,24 @@
},
});

// 1. HTTP 200 OK지만 논리적 실패인 경우 (success: false)
if (result.success) {
setMeetingUserId(meetingId, name, isRemembered);
router.push(`/meeting/${meetingId}`);
} else {
setErrorMessage('모임 참여에 실패했습니다. 다시 시도해주세요.');
setErrorMessage(result.data.message || '모임 참여에 실패했습니다. 다시 시도해주세요.');
show();
}
} catch (error) {
setErrorMessage('모임 참여에 실패했습니다. 이름과 비밀번호를 확인해주세요.');
} catch (error: any) {

Check warning on line 88 in components/join/joinForm.tsx

View workflow job for this annotation

GitHub Actions / build

Unexpected any. Specify a different type
const errorData = error.data || error.response?.data;
const serverMessage = errorData?.message;

if (serverMessage) {
setErrorMessage(serverMessage);
} else {
setErrorMessage('모임 참여에 실패했습니다. 다시 시도해주세요.');
}

show();
}
};
Expand Down
69 changes: 27 additions & 42 deletions components/map/kakaoMapLine.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,6 @@ export default function KakaoMapLine({
}: KakaoMapLineProps) {
const router = useRouter();
const [map, setMap] = useState<kakao.maps.Map | null>(null);
const [hoveredUserId, setHoveredUserId] = useState<string | null>(null);

useEffect(() => {
if (!map || !endStation || userRoutes.length === 0) return;
Expand Down Expand Up @@ -83,38 +82,28 @@ export default function KakaoMapLine({
let meetingType = '';
let category = '';

// 🔥 1순위: localStorage에서 가져오기
if (typeof window !== 'undefined') {
meetingType = localStorage.getItem(`meeting_${meetingId}_meetingType`) || '';
category = localStorage.getItem(`meeting_${meetingId}_category`) || '';
}

// 🔥 2순위: localStorage에 없으면 purposes에서 가져오기 (fallback)
if (!meetingType && purposes && purposes.length > 0) {
meetingType = purposes[0];
}
if (!category && purposes && purposes.length > 1) {
category = purposes[purposes.length - 1];
}

console.log('🔍 meetingType:', meetingType);
console.log('🔍 category:', category);

const params = new URLSearchParams({
meetingId,
midPlace: endStation.name,
lat: endStation.latitude.toString(),
lng: endStation.longitude.toString(),
});

if (meetingType) {
params.append('meetingType', meetingType);
}
if (category) {
params.append('category', category);
}
if (meetingType) params.append('meetingType', meetingType);
if (category) params.append('category', category);

console.log('🔍 final URL:', `/recommend?${params.toString()}`);
router.push(`/recommend?${params.toString()}`);
};

Expand All @@ -136,18 +125,18 @@ export default function KakaoMapLine({
level={8}
onCreate={setMap}
>
{/* 도착지 마커 */}
<CustomOverlayMap
position={{ lat: endStation.latitude, lng: endStation.longitude }}
yAnchor={1.2}
zIndex={20}
yAnchor={0.5}
zIndex={100}
>
<div className="flex items-center justify-center rounded-full border border-white bg-[#A95623] px-4 py-1.5 shadow-md">
<span className="text-sm font-semibold text-white">{endStation.name}</span>
</div>
</CustomOverlayMap>

{userRoutes.map((userRoute, index) => {
const isHovered = hoveredUserId === userRoute.nickname;
const userColor = getRandomHexColor(userRoute.nickname);

const offsetMultiplier = index - (userRoutes.length - 1) / 2;
Expand Down Expand Up @@ -181,36 +170,32 @@ export default function KakaoMapLine({
/>
)}

<CustomOverlayMap position={markerPosition} yAnchor={1} zIndex={isHovered ? 60 : 15}>
<div
className="group relative flex cursor-pointer flex-col items-center"
onMouseEnter={() => setHoveredUserId(userRoute.nickname)}
onMouseLeave={() => setHoveredUserId(null)}
>
<div
className={`absolute bottom-full mb-2 flex flex-col items-center rounded bg-gray-900 px-3 py-1 shadow-lg transition-all duration-200 ${
isHovered
? 'translate-y-0 opacity-100'
: 'pointer-events-none translate-y-2 opacity-0'
}`}
>
<span className="text-xs whitespace-nowrap text-white">
{userRoute.startStation} ({userRoute.travelTime}분)
{/* 출발지 마커 & 정보창 (항상 표시) */}
<CustomOverlayMap
position={markerPosition}
yAnchor={1}
zIndex={30} // 마커가 선보다 위에 오도록
>
<div className="flex flex-col items-center">
{/* 1. 상단 정보 말풍선 (검은색 박스) */}
<div className="relative mb-2 flex min-w-[80px] flex-col items-center justify-center rounded bg-[#2C2F36] px-3 py-2 shadow-lg">
<span className="text-[11px] leading-tight whitespace-nowrap text-white">
{userRoute.startStation}역에서
</span>
<span className="text-blue-2 mt-0.5 text-[14px] leading-tight font-semibold whitespace-nowrap">
{userRoute.travelTime}분
</span>
<div className="absolute -bottom-1 h-2 w-2 rotate-45 bg-gray-900"></div>

{/* 말풍선 꼬리 (아래쪽 화살표) */}
<div className="absolute -bottom-1.5 left-1/2 h-3 w-3 -translate-x-1/2 rotate-45 transform bg-[#2C2F36]"></div>
</div>

{/* 2. 하단 원형 프로필 아이콘 */}
<div
className={`flex items-center justify-center rounded-full border-2 border-white shadow-sm transition-transform duration-200 ${
isHovered ? 'z-50 scale-125' : 'scale-100'
}`}
style={{
backgroundColor: userColor,
width: '32px',
height: '32px',
}}
className="z-10 flex h-10 w-10 items-center justify-center rounded-full border-2 border-white shadow-md"
style={{ backgroundColor: userColor }}
>
<span className="text-xs font-bold text-white">
<span className="text-lg font-bold text-white">
{userRoute.nickname.charAt(0)}
</span>
</div>
Expand All @@ -223,7 +208,7 @@ export default function KakaoMapLine({

<div className="absolute top-4 left-1/2 z-10 -translate-x-1/2 transform">
<button
className="bg-blue-5 hover:bg-blue-8 flex h-10 items-center rounded-full px-5 text-sm font-bold text-white shadow-lg transition-colors"
className="bg-blue-5 hover:bg-blue-8 flex h-10 items-center rounded-full px-5 text-sm font-bold whitespace-nowrap text-white shadow-lg transition-colors"
onClick={handleRecommendClick}
>
{endStation.name}역 주변 장소 추천
Expand Down
2 changes: 1 addition & 1 deletion hooks/api/query/useCheckMeeting.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ export const useCheckMeeting = (meetingId: string) => {
return apiGet<MeetingStatusResponse>(`/api/meeting/${meetingId}/status`);
},
enabled: hasUserId && !!meetingId,
refetchInterval: 10000,
refetchInterval: 5000,
retry: false,
throwOnError: false,
});
Expand Down
Loading