Skip to content

Commit a2bc31e

Browse files
authored
Merge pull request #37 from SWYP-mingling/hotfix/edit-departure
fix: 중간지점 UI 개선, 모임 참여 에러 핸들링
2 parents 2d6724e + ad929a3 commit a2bc31e

7 files changed

Lines changed: 69 additions & 307 deletions

File tree

app/favicon.ico

3.06 KB
Binary file not shown.

app/result/[id]/page.tsx

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,10 @@ import KakaoMapLine from '@/components/map/kakaoMapLine';
88
import { useMidpoint } from '@/hooks/api/query/useMidpoint';
99
import { useCheckMeeting } from '@/hooks/api/query/useCheckMeeting';
1010
import { getMeetingUserId } from '@/lib/storage';
11+
import { useQueryClient } from '@tanstack/react-query';
1112

1213
export default function Page() {
14+
const queryClient = useQueryClient();
1315
const openModal = useOpenModal();
1416
const router = useRouter();
1517
const params = useParams();
@@ -103,6 +105,9 @@ export default function Page() {
103105
const [selectedResultId, setSelectedResultId] = useState<number>(1);
104106

105107
const handleModifyStart = () => {
108+
queryClient.removeQueries({ queryKey: ['midpoint', id] });
109+
queryClient.removeQueries({ queryKey: ['recommend', id] });
110+
106111
router.back();
107112
};
108113

components/join/joinForm.tsx

Lines changed: 16 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
'use client';
22

3-
import { useRouter, useSearchParams } from 'next/navigation'; // [추가] useSearchParams
3+
import { useRouter, useSearchParams } from 'next/navigation';
44
import { useState, useEffect } from 'react';
55
import { useEnterParticipant } from '@/hooks/api/mutation/useEnterParticipant';
66
import { useToast } from '@/hooks/useToast';
@@ -14,15 +14,15 @@ interface JoinFormProps {
1414

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

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

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

2727
const participantEnter = useEnterParticipant();
2828
const { isVisible, show } = useToast();
@@ -68,7 +68,7 @@ export default function JoinForm({ meetingId }: JoinFormProps) {
6868
if (!isFormValid || !meetingId) return;
6969

7070
try {
71-
// @ts-ignore (혹시 모를 타입 불일치 방지, API 스펙에 따라 제거 가능)
71+
// @ts-ignore
7272
const result = await participantEnter.mutateAsync({
7373
meetingId,
7474
data: {
@@ -77,15 +77,24 @@ export default function JoinForm({ meetingId }: JoinFormProps) {
7777
},
7878
});
7979

80+
// 1. HTTP 200 OK지만 논리적 실패인 경우 (success: false)
8081
if (result.success) {
8182
setMeetingUserId(meetingId, name, isRemembered);
8283
router.push(`/meeting/${meetingId}`);
8384
} else {
84-
setErrorMessage('모임 참여에 실패했습니다. 다시 시도해주세요.');
85+
setErrorMessage(result.data.message || '모임 참여에 실패했습니다. 다시 시도해주세요.');
8586
show();
8687
}
87-
} catch (error) {
88-
setErrorMessage('모임 참여에 실패했습니다. 이름과 비밀번호를 확인해주세요.');
88+
} catch (error: any) {
89+
const errorData = error.data || error.response?.data;
90+
const serverMessage = errorData?.message;
91+
92+
if (serverMessage) {
93+
setErrorMessage(serverMessage);
94+
} else {
95+
setErrorMessage('모임 참여에 실패했습니다. 다시 시도해주세요.');
96+
}
97+
8998
show();
9099
}
91100
};

components/map/kakaoMapLine.tsx

Lines changed: 27 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,6 @@ export default function KakaoMapLine({
5252
}: KakaoMapLineProps) {
5353
const router = useRouter();
5454
const [map, setMap] = useState<kakao.maps.Map | null>(null);
55-
const [hoveredUserId, setHoveredUserId] = useState<string | null>(null);
5655

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

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

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

100-
console.log('🔍 meetingType:', meetingType);
101-
console.log('🔍 category:', category);
102-
10397
const params = new URLSearchParams({
10498
meetingId,
10599
midPlace: endStation.name,
106100
lat: endStation.latitude.toString(),
107101
lng: endStation.longitude.toString(),
108102
});
109103

110-
if (meetingType) {
111-
params.append('meetingType', meetingType);
112-
}
113-
if (category) {
114-
params.append('category', category);
115-
}
104+
if (meetingType) params.append('meetingType', meetingType);
105+
if (category) params.append('category', category);
116106

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

@@ -136,18 +125,18 @@ export default function KakaoMapLine({
136125
level={8}
137126
onCreate={setMap}
138127
>
128+
{/* 도착지 마커 */}
139129
<CustomOverlayMap
140130
position={{ lat: endStation.latitude, lng: endStation.longitude }}
141-
yAnchor={1.2}
142-
zIndex={20}
131+
yAnchor={0.5}
132+
zIndex={100}
143133
>
144134
<div className="flex items-center justify-center rounded-full border border-white bg-[#A95623] px-4 py-1.5 shadow-md">
145135
<span className="text-sm font-semibold text-white">{endStation.name}</span>
146136
</div>
147137
</CustomOverlayMap>
148138

149139
{userRoutes.map((userRoute, index) => {
150-
const isHovered = hoveredUserId === userRoute.nickname;
151140
const userColor = getRandomHexColor(userRoute.nickname);
152141

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

184-
<CustomOverlayMap position={markerPosition} yAnchor={1} zIndex={isHovered ? 60 : 15}>
185-
<div
186-
className="group relative flex cursor-pointer flex-col items-center"
187-
onMouseEnter={() => setHoveredUserId(userRoute.nickname)}
188-
onMouseLeave={() => setHoveredUserId(null)}
189-
>
190-
<div
191-
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 ${
192-
isHovered
193-
? 'translate-y-0 opacity-100'
194-
: 'pointer-events-none translate-y-2 opacity-0'
195-
}`}
196-
>
197-
<span className="text-xs whitespace-nowrap text-white">
198-
{userRoute.startStation} ({userRoute.travelTime}분)
173+
{/* 출발지 마커 & 정보창 (항상 표시) */}
174+
<CustomOverlayMap
175+
position={markerPosition}
176+
yAnchor={1}
177+
zIndex={30} // 마커가 선보다 위에 오도록
178+
>
179+
<div className="flex flex-col items-center">
180+
{/* 1. 상단 정보 말풍선 (검은색 박스) */}
181+
<div className="relative mb-2 flex min-w-[80px] flex-col items-center justify-center rounded bg-[#2C2F36] px-3 py-2 shadow-lg">
182+
<span className="text-[11px] leading-tight whitespace-nowrap text-white">
183+
{userRoute.startStation}역에서
184+
</span>
185+
<span className="text-blue-2 mt-0.5 text-[14px] leading-tight font-semibold whitespace-nowrap">
186+
{userRoute.travelTime}
199187
</span>
200-
<div className="absolute -bottom-1 h-2 w-2 rotate-45 bg-gray-900"></div>
188+
189+
{/* 말풍선 꼬리 (아래쪽 화살표) */}
190+
<div className="absolute -bottom-1.5 left-1/2 h-3 w-3 -translate-x-1/2 rotate-45 transform bg-[#2C2F36]"></div>
201191
</div>
202192

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

224209
<div className="absolute top-4 left-1/2 z-10 -translate-x-1/2 transform">
225210
<button
226-
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"
211+
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"
227212
onClick={handleRecommendClick}
228213
>
229214
{endStation.name}역 주변 장소 추천

hooks/api/query/useCheckMeeting.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ export const useCheckMeeting = (meetingId: string) => {
1414
return apiGet<MeetingStatusResponse>(`/api/meeting/${meetingId}/status`);
1515
},
1616
enabled: hasUserId && !!meetingId,
17-
refetchInterval: 10000,
17+
refetchInterval: 5000,
1818
retry: false,
1919
throwOnError: false,
2020
});

0 commit comments

Comments
 (0)