diff --git a/src/pages/Result/LocationManager.tsx b/src/pages/Result/LocationManager.tsx new file mode 100644 index 0000000..719ea04 --- /dev/null +++ b/src/pages/Result/LocationManager.tsx @@ -0,0 +1,74 @@ +import { useState } from 'react'; +import { LocationType } from '../../types/location'; +import { v4 as uuid } from 'uuid'; + +interface LocationManagerProps { + recommendResults: any[]; + onAddressAvailabilityChange: (isAvailable: boolean) => void; + onInformationAvailabilityChange: (isAvailable: boolean) => void; +} + +export const useLocationManager = ({ + recommendResults, + onAddressAvailabilityChange, + onInformationAvailabilityChange, +}: LocationManagerProps) => { + const [locations, setLocations] = useState(() => { + try { + if (!recommendResults || !Array.isArray(recommendResults)) { + console.warn('Invalid recommendResults:', recommendResults); + return []; + } + + return recommendResults.map((item, index) => { + const lat = item.lat || 0; + const lng = item.lng || 0; + const address = item.address || '주소 정보 없음'; + + if (address === '주소 정보 없음') { + onAddressAvailabilityChange(false); + } + + if ( + address === '주소 정보 없음' && + item.operationHour === '영업시간 정보 없음' && + item.phoneNumber === '전화번호 정보 없음' + ) { + onInformationAvailabilityChange(false); + } + + return { + id: uuid(), + name: item.name || '이름 없음', + courseType: item.courseType || '미분류', + address, + operationHour: item.operationHour || '영업시간 정보 없음', + phoneNumber: item.phoneNumber || '전화번호 정보 없음', + lat, + lng, + isSelected: index === 0, + image: '/src/assets/image/placeholder.jpg', + }; + }); + } catch (error) { + console.error('Error transforming recommendations:', error); + return []; + } + }); + + const handleLocationSelect = (lat: number, lng: number, isMarker?: boolean) => { + if (isMarker) { + setLocations((prevLocations) => + prevLocations.map((location) => ({ + ...location, + isSelected: location.lat === lat && location.lng === lng, + })), + ); + } + }; + + return { + locations, + handleLocationSelect, + }; +}; diff --git a/src/pages/Result/WeatherForecast.tsx b/src/pages/Result/WeatherForecast.tsx new file mode 100644 index 0000000..a3e1b4b --- /dev/null +++ b/src/pages/Result/WeatherForecast.tsx @@ -0,0 +1,53 @@ +import { useEffect, useState } from 'react'; +import { getWeatherForecastApi } from '../../api/weatherForecast'; +import { LocationType } from '../../types/location'; + +interface WeatherForecastProps { + selectedDate: Date | null; + selectedLocation: LocationType | null; + onWeatherUpdate: (weather: string) => void; +} + +export const useWeatherForecast = ({ + selectedDate, + selectedLocation, + onWeatherUpdate, +}: WeatherForecastProps) => { + const [weatherForecast, setWeatherForecast] = useState('날씨 조회 중...'); + + useEffect(() => { + const fetchWeatherForecast = async () => { + if (!selectedDate || !selectedLocation?.lat || !selectedLocation?.lng) { + const defaultWeather = '화창할'; + setWeatherForecast(defaultWeather); + onWeatherUpdate(defaultWeather); + return; + } + + try { + const formattedDate = selectedDate + .toISOString() + .split('T')[0] + .replace(/-/g, ''); + const response = await getWeatherForecastApi({ + date: formattedDate, + latitude: selectedLocation.lat, + longitude: selectedLocation.lng, + }); + + const weather = response.result === '날씨 정보를 찾을 수 없습니다.' ? '화창할' : response.result; + setWeatherForecast(weather); + onWeatherUpdate(weather); + } catch (error) { + console.error('날씨 정보 조회 실패:', error); + const defaultWeather = '화창할'; + setWeatherForecast(defaultWeather); + onWeatherUpdate(defaultWeather); + } + }; + + fetchWeatherForecast(); + }, [selectedDate, selectedLocation, onWeatherUpdate]); + + return weatherForecast; +}; diff --git a/src/pages/Result/index.tsx b/src/pages/Result/index.tsx index c0b1a01..18e77b6 100644 --- a/src/pages/Result/index.tsx +++ b/src/pages/Result/index.tsx @@ -1,21 +1,20 @@ -import { useState, useRef, useCallback, useEffect } from 'react'; +import { useState, useRef, useEffect } from 'react'; import { useNavigate, useLocation } from 'react-router-dom'; -import { getWeatherForecastApi } from '../../api/weatherForecast'; import CoursePreview from '../../components/home/coursePreview/CoursePreview'; import { useMapCenter } from '../../hooks/useMapCenter'; import { useRandomCongestion } from '../../hooks/useRandomCongestion'; -import { Topbar } from '../../layouts'; +import { Topbar, Navigation } from '../../layouts'; import Map from '../../pages/Home/Map'; -import { LocationType } from '../../types/location'; +import { useWeatherForecast } from './WeatherForecast'; +import { useLocationManager } from './LocationManager'; const Result = () => { const navigate = useNavigate(); const location = useLocation(); const [isExpanded, setIsExpanded] = useState(false); const mapRef = useRef(null); - const [weatherForecast, setWeatherForecast] = - useState('날씨 조회 중...'); + const [weatherForecast, setWeatherForecast] = useState('날씨 조회 중...'); const [isAddress, setIsAddress] = useState(true); const [isInformation, setIsInformation] = useState(true); @@ -24,108 +23,20 @@ const Result = () => { const { recommendResults, selectedDate, selectedLocation, radius } = location.state || {}; - const [locations, setLocations] = useState(() => { - try { - if (!recommendResults || !Array.isArray(recommendResults)) { - console.warn('Invalid recommendResults:', recommendResults); - return []; - } - - // recommendResults가 직접 배열로 들어오므로 그대로 매핑 - return recommendResults.map((item, index) => { - // 위도, 경도 변환 - const lat = parseFloat(String(item.lat)) || 0; - const lng = parseFloat(String(item.lng)) || 0; - - if (item.address === '주소 정보 없음') { - setIsAddress(false); - } - - if ( - item.address === '주소 정보 없음' && - item.operationHour === '영업시간 정보 없음' && - item.phoneNumber === '전화번호 정보 없음' - ) { - setIsInformation(false); - } - - // 주소 정보 처리 - // const description = [ - // item.ROAD_NM_ADDR || item.LOTNO_ADDR || '주소 정보 없음', - // `영업시간: ${item.OPERATION_HOUR || '정보 없음'}`, - // `전화번호: ${item.PHONE_NUMBER || '정보 없음'}`, - // ] - // .filter(Boolean) - // .join('\n'); - - return { - id: item.id || '', - name: item.name || '이름 없음', - courseType: item.courseType || '미분류', - address: item.address, - operationHour: item.operationHour, - phoneNumber: item.phoneNumber, - lat, - lng, - isSelected: index === 0, - image: '/src/assets/image/placeholder.jpg', - }; - }); - } catch (error) { - console.error('Error transforming recommendations:', error); - return []; - } + const { locations, handleLocationSelect } = useLocationManager({ + recommendResults, + onAddressAvailabilityChange: setIsAddress, + onInformationAvailabilityChange: setIsInformation, }); - useEffect(() => { - const fetchWeatherForecast = async () => { - if (!selectedDate || !selectedLocation?.lat || !selectedLocation?.lng) { - setWeatherForecast('화창할'); - return; - } - - try { - const formattedDate = selectedDate - .toISOString() - .split('T')[0] - .replace(/-/g, ''); - const response = await getWeatherForecastApi({ - date: formattedDate, - latitude: selectedLocation.lat, - longitude: selectedLocation.lng, - }); - console.log('날씨 response : ', response); - - if (response.result === '날씨 정보를 찾을 수 없습니다.') { - setWeatherForecast('화창할'); - } else { - setWeatherForecast(`${response.result}`); - } - } catch (error) { - console.error('날씨 정보 조회 실패:', error); - setWeatherForecast('화창할'); - } - }; - - fetchWeatherForecast(); - }, [selectedDate, selectedLocation]); + useWeatherForecast({ + selectedDate, + selectedLocation, + onWeatherUpdate: setWeatherForecast, + }); const { mapCenter, mapBounds } = useMapCenter(locations); - const handleLocationSelect = useCallback( - (lat: number, lng: number, isMarker?: boolean) => { - if (isMarker) { - setLocations((prevLocations) => - prevLocations.map((location) => ({ - ...location, - isSelected: location.lat === lat && location.lng === lng, - })), - ); - } - }, - [], - ); - const handleExpand = (expanded: boolean) => { setIsExpanded(expanded); }; @@ -142,44 +53,50 @@ const Result = () => { } return ( -
+
navigate('/')} />
-
- +
+
+ +
+ +
+ + {isExpanded && ( +
setIsExpanded(false)} + /> + )} +
-
- - {isExpanded && ( -
setIsExpanded(false)} - /> - )} +
+
); diff --git a/src/types/location.ts b/src/types/location.ts index 5f9f1e5..5c7e00f 100644 --- a/src/types/location.ts +++ b/src/types/location.ts @@ -1,5 +1,5 @@ export interface LocationType { - id: number; + id: string; name: string; courseType: string; isSelected: boolean;