From 73ccf745e410f1f606454669ea673cbc88e57643 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=9D=B4=EB=8F=99=EC=9C=A8?= Date: Mon, 25 Nov 2024 16:24:10 +0900 Subject: [PATCH 01/17] =?UTF-8?q?[FE][Feat]=20#257=20:=20=EA=B2=80?= =?UTF-8?q?=EC=83=89=20=EA=B8=B0=EB=8A=A5=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- frontend/src/component/common/InputBox.tsx | 3 + .../routebutton/RouteResultButton.tsx | 25 +-- .../src/component/searchbox/SearchBox.tsx | 158 ++++++++++++++++++ frontend/src/context/ToolTypeContext.tsx | 24 +++ frontend/src/context/UserContext.tsx | 6 +- frontend/src/hooks/useFloatingButton.ts | 9 +- frontend/src/pages/AddChannel.tsx | 24 ++- frontend/src/pages/DrawRoute.tsx | 97 ++++++----- 8 files changed, 269 insertions(+), 77 deletions(-) create mode 100644 frontend/src/component/searchbox/SearchBox.tsx create mode 100644 frontend/src/context/ToolTypeContext.tsx diff --git a/frontend/src/component/common/InputBox.tsx b/frontend/src/component/common/InputBox.tsx index 3452bec7..0e1ed494 100644 --- a/frontend/src/component/common/InputBox.tsx +++ b/frontend/src/component/common/InputBox.tsx @@ -6,6 +6,7 @@ export interface IInputBoxProps { onChange?: (event: ChangeEvent) => void; onFocus?: (event: FocusEvent) => void; onBlur?: (event: FocusEvent) => void; + value?: string; className?: string; } @@ -15,6 +16,7 @@ export const InputBox = ({ onChange, onFocus, onBlur, + value = '', className = '', }: IInputBoxProps) => ( ); diff --git a/frontend/src/component/routebutton/RouteResultButton.tsx b/frontend/src/component/routebutton/RouteResultButton.tsx index 1c468564..03657c87 100644 --- a/frontend/src/component/routebutton/RouteResultButton.tsx +++ b/frontend/src/component/routebutton/RouteResultButton.tsx @@ -1,7 +1,5 @@ import { IUser } from '@/context/UserContext'; -import { getAddressFromCoordinates } from '@/utils/map/getAddress'; import classNames from 'classnames'; -import { useEffect, useState } from 'react'; import { GoArrowRight } from 'react-icons/go'; import { IoClose } from 'react-icons/io5'; import { useNavigate } from 'react-router-dom'; @@ -13,25 +11,6 @@ interface IRouteResultButtonProps { export const RouteResultButton = (props: IRouteResultButtonProps) => { const navigate = useNavigate(); - const [start, setStart] = useState(''); - const [end, setEnd] = useState(''); - useEffect(() => { - // Fetch the addresses asynchronously when component mounts - const fetchAddresses = async () => { - const startAddress = await getAddressFromCoordinates( - props.user.start_location.lat, - props.user.start_location.lng, - ); - const endAddress = await getAddressFromCoordinates( - props.user.end_location.lat, - props.user.end_location.lng, - ); - setStart(startAddress); // Set start address - setEnd(endAddress); // Set end address - }; - - fetchAddresses(); - }, [props.user.start_location, props.user.end_location]); const goToUserDrawRoute = (user: string) => { navigate(`/add-channel/${user}/draw`); @@ -50,11 +29,11 @@ export const RouteResultButton = (props: IRouteResultButtonProps) => { )} >
- {start} + {props.user.start_location.title}
- {end} + {props.user.end_location.title}
diff --git a/frontend/src/component/searchbox/SearchBox.tsx b/frontend/src/component/searchbox/SearchBox.tsx new file mode 100644 index 00000000..00ddf978 --- /dev/null +++ b/frontend/src/component/searchbox/SearchBox.tsx @@ -0,0 +1,158 @@ +import { ToolTypeContext } from '@/context/ToolTypeContext'; +import { IUser, UserContext } from '@/context/UserContext'; +import React, { useContext, useState } from 'react'; +import { IoMdClose, IoMdSearch } from 'react-icons/io'; +import { ButtonState } from '../common/enums'; + +interface ISearchResultItem { + title: string; + address: string; + link: string; + lat: number; + lng: number; +} + +interface ISearchBox { + user: IUser | undefined; +} + +export const SearchBox = (props: ISearchBox) => { + const [inputValue, setInputValue] = useState(''); // 검색 입력값 상태 + const [searchResults, setSearchResults] = useState([]); // 검색 결과 상태 + const [loading, setLoading] = useState(false); // 로딩 상태 + const [error, setError] = useState(null); // 에러 상태 + + const { users, setUsers } = useContext(UserContext); + const { toolType } = useContext(ToolTypeContext); + + const updateUser = (title: string, lat: number, lng: number) => { + const targetUser = props.user; + const updatedUsers = users.map(user => { + if (user === targetUser) { + // toolType에 따라 start_location 또는 end_location을 업데이트 + if (toolType === ButtonState.START_MARKER) { + return { + ...user, + start_location: { + title, + lat, + lng, + }, + }; + } + if (toolType === ButtonState.DESTINATION_MARKER) { + return { + ...user, + end_location: { + title, + lat, + lng, + }, + }; + } + } + return user; // 나머지 사용자들은 그대로 반환 + }); + + console.log(updatedUsers); + setUsers(updatedUsers); + }; + + const handleSearch = async () => { + if (!inputValue.trim()) return; + setLoading(true); + setError(null); + + try { + // const apiUrl = `https://cors-anywhere.herokuapp.com/https://openapi.naver.com/v1/search/local.json?query=${encodeURIComponent( + // inputValue, + // )}&display=10&start=1&sort=random`; + + const apiUrl = `https://openapi.naver.com/v1/search/local.json?query=${encodeURIComponent( + inputValue, + )}&display=10&start=1&sort=random`; + + const response = await fetch(apiUrl, { + headers: { + 'X-Naver-Client-Id': 'RwiDUxdYdlPHF1pcM0id', + 'X-Naver-Client-Secret': 'zmxxnHsoM0', + }, + }); + + if (!response.ok) { + throw new Error(`API 요청 실패: ${response.status}`); + } + + const data = await response.json(); + const formattedResults = data.items.map((item: any) => ({ + title: item.title.replace(/<\/?[^>]+(>|$)/g, ''), // HTML 태그 제거 + address: item.address || item.roadAddress || '주소 정보 없음', + link: item.link || '#', + lat: parseFloat(item.mapy) / 1e7, // 위도 값 변환 + lng: parseFloat(item.mapx) / 1e7, // 경도 값 변환 + })); + console.log(data); + setSearchResults(formattedResults); // 검색 결과 상태 업데이트 + } catch (err) { + setError(err instanceof Error ? err.message : '알 수 없는 오류'); + } finally { + setLoading(false); + } + }; + + const handleInputChange = (event: React.ChangeEvent) => { + setInputValue(event.target.value); // 상태 업데이트 + }; + + const handleSelectResult = (result: ISearchResultItem) => { + setInputValue(result.title); // 선택한 결과로 inputValue를 업데이트 + setSearchResults([]); // 결과 리스트를 닫음 + updateUser(result.title, result.lat, result.lng); + console.log(`위도: ${result.lat}, 경도: ${result.lng}`); + }; + + const handleClear = () => { + setInputValue(''); // 입력값 초기화 + setSearchResults([]); // 검색 결과 초기화 + }; + + return ( +
+ {/* 검색 입력 */} +
+ + + +
+ + {/* 검색 결과 리스트 */} + {searchResults.length > 0 && ( +
+ {loading &&

로딩 중...

} + {error &&

{error}

} + {searchResults.map(result => ( + + ))} +
+ )} +
+ ); +}; diff --git a/frontend/src/context/ToolTypeContext.tsx b/frontend/src/context/ToolTypeContext.tsx new file mode 100644 index 00000000..e2c3c85b --- /dev/null +++ b/frontend/src/context/ToolTypeContext.tsx @@ -0,0 +1,24 @@ +import { ButtonState } from '@/component/common/enums'; +import { createContext, ReactNode, useMemo, useState } from 'react'; + +interface IToolContextType { + toolType: ButtonState; + setToolType: (toolType: ButtonState) => void; +} +interface IToolTypeProps { + children: ReactNode; // ReactNode 타입 추가 +} + +const defaultToolContext: IToolContextType = { + toolType: ButtonState.CLOSE, + setToolType: () => {}, +}; + +export const ToolTypeContext = createContext(defaultToolContext); + +export const ToolTypeProvider = (props: IToolTypeProps) => { + const [toolType, setToolType] = useState(ButtonState.CLOSE); + const contextValue = useMemo(() => ({ toolType, setToolType }), [toolType, setToolType]); + + return {props.children}; +}; diff --git a/frontend/src/context/UserContext.tsx b/frontend/src/context/UserContext.tsx index 326831c0..e2c284a3 100644 --- a/frontend/src/context/UserContext.tsx +++ b/frontend/src/context/UserContext.tsx @@ -4,10 +4,12 @@ export interface IUser { id: number; name: string; start_location: { + title: string; lat: number; lng: number; }; end_location: { + title: string; lat: number; lng: number; }; @@ -38,8 +40,8 @@ export const UserProvider = (props: IUserContextProps) => { { id: 1, name: '사용자1', - start_location: { lat: 0, lng: 0 }, - end_location: { lat: 0, lng: 0 }, + start_location: { title: '', lat: 0, lng: 0 }, + end_location: { title: '', lat: 0, lng: 0 }, path: [], marker_style: { color: '' }, }, diff --git a/frontend/src/hooks/useFloatingButton.ts b/frontend/src/hooks/useFloatingButton.ts index b7a77acd..884acd52 100644 --- a/frontend/src/hooks/useFloatingButton.ts +++ b/frontend/src/hooks/useFloatingButton.ts @@ -1,9 +1,14 @@ import { ButtonState } from '@/component/common/enums'; -import { useState } from 'react'; +import { useContext, useEffect, useState } from 'react'; +import { ToolTypeContext } from '@/context/ToolTypeContext'; export const useFloatingButton = () => { const [isMenuOpen, setIsMenuOpen] = useState(false); - const [toolType, setToolType] = useState(ButtonState.CLOSE); + const { toolType, setToolType } = useContext(ToolTypeContext); + + useEffect(() => { + setIsMenuOpen(toolType === ButtonState.OPEN); + }, [toolType]); const toggleMenu = () => { setIsMenuOpen(prev => !prev); diff --git a/frontend/src/pages/AddChannel.tsx b/frontend/src/pages/AddChannel.tsx index b52e5bab..2f96e20f 100644 --- a/frontend/src/pages/AddChannel.tsx +++ b/frontend/src/pages/AddChannel.tsx @@ -1,4 +1,4 @@ -import { useContext, useEffect } from 'react'; +import { useContext, useEffect, useState } from 'react'; import { HiMiniInformationCircle } from 'react-icons/hi2'; import { FooterContext } from '@/component/layout/footer/LayoutFooterProvider'; import { RouteSettingButton } from '@/component/routebutton/RouteSettingButton'; @@ -33,8 +33,8 @@ const Divider = () =>
; */ export const AddChannel = () => { + const [channelName, setChannelName] = useState(''); const { users, setUsers } = useContext(UserContext); - const { setFooterTitle, setFooterTransparency, setFooterActive } = useContext(FooterContext); /** @@ -58,8 +58,8 @@ export const AddChannel = () => { const newUser: IUser = { id: users.length + 1, name: `사용자${users.length + 1}`, - start_location: { lat: 0, lng: 0 }, // 초기값으로 빈 좌표 - end_location: { lat: 0, lng: 0 }, // 초기값으로 빈 좌표 + start_location: { title: '', lat: 0, lng: 0 }, // 초기값으로 빈 좌표 + end_location: { title: '', lat: 0, lng: 0 }, // 초기값으로 빈 좌표 path: [], // 초기값으로 빈 배열 marker_style: { color: '' }, // 초기값으로 빈 문자열 }; @@ -71,9 +71,7 @@ export const AddChannel = () => { user.start_location.lat !== 0 && user.start_location.lng !== 0 && user.end_location.lat !== 0 && - user.end_location.lng !== 0 && - user.path.length > 0 && - user.marker_style.color !== '' + user.end_location.lng !== 0 ); }; @@ -104,13 +102,17 @@ export const AddChannel = () => { name: `사용자${index + 1}`, })); setUsers(updatedUsers); - localStorage.setItem('users', JSON.stringify(updatedUsers)); + }; + + const handleChangeChannelName = (event: React.ChangeEvent) => { + setChannelName(event.target.value); }; useEffect(() => { setFooterTitle('제작 완료'); setFooterTransparency(false); setFooterActive(buttonActiveType.PASSIVE); + console.log(users); }, []); useEffect(() => { @@ -127,7 +129,11 @@ export const AddChannel = () => { return (
- +
{users.map(user => ( diff --git a/frontend/src/pages/DrawRoute.tsx b/frontend/src/pages/DrawRoute.tsx index 88b6781e..f470d2ce 100644 --- a/frontend/src/pages/DrawRoute.tsx +++ b/frontend/src/pages/DrawRoute.tsx @@ -1,46 +1,32 @@ import { Linedrawer } from '@/component/linedrawer/Linedrawer'; -import { useContext, useEffect } from 'react'; +import { useContext, useEffect, useMemo } from 'react'; import { FooterContext } from '@/component/layout/footer/LayoutFooterProvider'; -import { useParams } from 'react-router-dom'; -import { UserContext } from '@/context/UserContext'; +import { useNavigate, useParams } from 'react-router-dom'; +import { IUser, UserContext } from '@/context/UserContext'; +import { SearchBox } from '@/component/searchbox/SearchBox'; +import { ToolTypeProvider } from '@/context/ToolTypeContext'; +import { buttonActiveType } from '@/component/layout/enumTypes'; export const DrawRoute = () => { const { users, setUsers } = useContext(UserContext); - const { setFooterTitle } = useContext(FooterContext); + const { setFooterTitle, setFooterActive, setFooterOnClick } = useContext(FooterContext); const params = useParams>(); // userName을 URL 파라미터로 가져옴 + const navigate = useNavigate(); - // URL에서 'user' 파라미터를 받아 해당 사용자를 찾음 - const getUser = (name: string | undefined) => { - return users.find(user => user.name === name); + const goToAddChannelRoute = () => { + navigate(`/add-channel/`); }; - const user = getUser(params.user); // params.user에 해당하는 사용자 찾기 + const currentUser = useMemo(() => { + return users.find(user => user.name === params.user); + }, [users, params.user]); - const addMockData = () => { - if (user) { - const updatedUser = { - ...user, - start_location: { lat: 37.5665, lng: 126.978 }, // 임시 mock 데이터 - end_location: { lat: 35.1796, lng: 129.0756 }, - path: [ - { lat: 37.5665, lng: 126.978 }, - { lat: 36.5, lng: 127.5 }, - { lat: 35.1796, lng: 129.0756 }, - ], - marker_style: { color: 'blue' }, - }; - - // 해당 user의 state를 업데이트 - const updatedUsers = users.map(u => (u.name === params.user ? updatedUser : u)); // params.user를 사용해서 업데이트 - setUsers(updatedUsers); - } - }; const resetMockData = () => { - if (user) { + if (currentUser) { const updatedUser = { - ...user, - start_location: { lat: 0, lng: 0 }, // 초기값으로 리셋 - end_location: { lat: 0, lng: 0 }, + ...currentUser, + start_location: { title: '', lat: 0, lng: 0 }, // 초기값으로 리셋 + end_location: { title: '', lat: 0, lng: 0 }, path: [], marker_style: { color: '' }, }; @@ -51,20 +37,49 @@ export const DrawRoute = () => { } }; + const isUserDataComplete = (user: IUser): boolean => { + return ( + user.start_location.lat !== 0 && + user.start_location.lng !== 0 && + user.end_location.lat !== 0 && + user.end_location.lng !== 0 + ); + }; + + useEffect(() => { + if (currentUser) { + const UsersComplete = isUserDataComplete(currentUser); + + // 모든 사용자가 완전한 데이터라면 Footer를 활성화 + if (UsersComplete) { + setFooterActive(buttonActiveType.ACTIVE); + } else { + setFooterActive(buttonActiveType.PASSIVE); + } + } + }, [users, currentUser, setFooterActive]); // currentUser가 변경될 때마다 실행 + useEffect(() => { setFooterTitle('사용자 경로 추가 완료'); - }, [setFooterTitle]); + setFooterOnClick(goToAddChannelRoute); + setFooterActive(buttonActiveType.PASSIVE); + }, []); + + if (!currentUser) { + // currentUser가 없을 경우에는 이 페이지에서 아무것도 렌더링하지 않거나, 에러 메시지를 표시할 수 있습니다. + return
사용자를 찾을 수 없습니다.
; + } return ( -
- - + +
+ + - -
+ +
+ ); }; From a2a87e2915ac3756529a5bc6480bceaa061b51af Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=9D=B4=EB=8F=99=EC=9C=A8?= Date: Mon, 25 Nov 2024 16:58:15 +0900 Subject: [PATCH 02/17] =?UTF-8?q?[FE][Feat]=20#257=20:=20=EB=B0=91?= =?UTF-8?q?=EC=A4=84=EC=9E=84=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- frontend/src/component/routebutton/RouteResultButton.tsx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/frontend/src/component/routebutton/RouteResultButton.tsx b/frontend/src/component/routebutton/RouteResultButton.tsx index 03657c87..e730be67 100644 --- a/frontend/src/component/routebutton/RouteResultButton.tsx +++ b/frontend/src/component/routebutton/RouteResultButton.tsx @@ -28,11 +28,11 @@ export const RouteResultButton = (props: IRouteResultButtonProps) => { props.user.id > 1 ? '' : 'mr-8', )} > -
+
{props.user.start_location.title}
- -
+ +
{props.user.end_location.title}
From 489e2aa582a7d3e5aeb0f6930af66179de409576 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=9D=B4=EB=8F=99=EC=9C=A8?= Date: Mon, 25 Nov 2024 17:03:05 +0900 Subject: [PATCH 03/17] =?UTF-8?q?[FE][Feat]=20#257=20:=20FloatingButton=20?= =?UTF-8?q?=EC=97=B4=EB=A6=B0=EC=83=81=ED=83=9C=EB=A1=9C=20=EC=8B=9C?= =?UTF-8?q?=EC=9E=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- frontend/src/context/ToolTypeContext.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frontend/src/context/ToolTypeContext.tsx b/frontend/src/context/ToolTypeContext.tsx index e2c3c85b..cdce41ec 100644 --- a/frontend/src/context/ToolTypeContext.tsx +++ b/frontend/src/context/ToolTypeContext.tsx @@ -17,7 +17,7 @@ const defaultToolContext: IToolContextType = { export const ToolTypeContext = createContext(defaultToolContext); export const ToolTypeProvider = (props: IToolTypeProps) => { - const [toolType, setToolType] = useState(ButtonState.CLOSE); + const [toolType, setToolType] = useState(ButtonState.OPEN); const contextValue = useMemo(() => ({ toolType, setToolType }), [toolType, setToolType]); return {props.children}; From b61177be15bcff4f31b01356b4bbdcbe6855fbb9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=9D=B4=EB=8F=99=EC=9C=A8?= Date: Mon, 25 Nov 2024 17:41:42 +0900 Subject: [PATCH 04/17] =?UTF-8?q?[FE][Feat]=20#257=20:=20ToolDescription?= =?UTF-8?q?=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../canvasWithMap/MapCanvasForDraw.tsx | 4 ++++ .../src/component/searchbox/SearchBox.tsx | 10 +++++----- .../tooldescription/ToolDescription.tsx | 19 +++++++++++++++++++ .../constant/DescriptionConst.ts | 18 ++++++++++++++++++ 4 files changed, 46 insertions(+), 5 deletions(-) create mode 100644 frontend/src/component/tooldescription/ToolDescription.tsx create mode 100644 frontend/src/component/tooldescription/constant/DescriptionConst.ts diff --git a/frontend/src/component/canvasWithMap/MapCanvasForDraw.tsx b/frontend/src/component/canvasWithMap/MapCanvasForDraw.tsx index f7e068c0..dd03effc 100644 --- a/frontend/src/component/canvasWithMap/MapCanvasForDraw.tsx +++ b/frontend/src/component/canvasWithMap/MapCanvasForDraw.tsx @@ -9,6 +9,7 @@ import { ICanvasPoint, IMapCanvasProps, IPoint } from '@/lib/types/canvasInterfa import { useUndoRedo } from '@/hooks/useUndoRedo.ts'; import startmarker from '@/assets/startmarker.png'; import endmarker from '@/assets/endmarker.png'; +import { ToolDescription } from '../tooldescription/ToolDescription'; export const MapCanvasForDraw = ({ width, @@ -431,6 +432,9 @@ export const MapCanvasForDraw = ({ handleMenuClick={handleMenuClick} />
+
+ +
{/* TODO: 줌인 줌아웃 버튼으로도 접근 가능하도록 추가 */} {/*
*/} diff --git a/frontend/src/component/searchbox/SearchBox.tsx b/frontend/src/component/searchbox/SearchBox.tsx index 00ddf978..7a1aea83 100644 --- a/frontend/src/component/searchbox/SearchBox.tsx +++ b/frontend/src/component/searchbox/SearchBox.tsx @@ -64,14 +64,14 @@ export const SearchBox = (props: ISearchBox) => { setError(null); try { - // const apiUrl = `https://cors-anywhere.herokuapp.com/https://openapi.naver.com/v1/search/local.json?query=${encodeURIComponent( - // inputValue, - // )}&display=10&start=1&sort=random`; - - const apiUrl = `https://openapi.naver.com/v1/search/local.json?query=${encodeURIComponent( + const apiUrl = `https://cors-anywhere.herokuapp.com/https://openapi.naver.com/v1/search/local.json?query=${encodeURIComponent( inputValue, )}&display=10&start=1&sort=random`; + // const apiUrl = `https://openapi.naver.com/v1/search/local.json?query=${encodeURIComponent( + // inputValue, + // )}&display=10&start=1&sort=random`; + const response = await fetch(apiUrl, { headers: { 'X-Naver-Client-Id': 'RwiDUxdYdlPHF1pcM0id', diff --git a/frontend/src/component/tooldescription/ToolDescription.tsx b/frontend/src/component/tooldescription/ToolDescription.tsx new file mode 100644 index 00000000..d0235825 --- /dev/null +++ b/frontend/src/component/tooldescription/ToolDescription.tsx @@ -0,0 +1,19 @@ +import { useContext } from 'react'; +import { ToolTypeContext } from '@/context/ToolTypeContext'; +import { ButtonStateDescriptions } from './constant/DescriptionConst'; + +export const ToolDescription = () => { + const { toolType } = useContext(ToolTypeContext); + const description = ButtonStateDescriptions[toolType]; + + return ( + // 조건부 렌더링: description이 빈 문자열이 아니면 렌더링 + description ? ( +
+

+ {description} +

+
+ ) : null + ); +}; diff --git a/frontend/src/component/tooldescription/constant/DescriptionConst.ts b/frontend/src/component/tooldescription/constant/DescriptionConst.ts new file mode 100644 index 00000000..21fd541d --- /dev/null +++ b/frontend/src/component/tooldescription/constant/DescriptionConst.ts @@ -0,0 +1,18 @@ +import { ButtonState } from '@/component/common/enums'; + +export enum DescriptionText { + CLOSE = '', + OPEN = '', + START_MARKER = '출발지를 설정합니다.\n터치를 해서 출발 지점을 선택해주세요.', + DESTINATION_MARKER = '도착지를 설정합니다.\n터치를 해서 도착 지점을 선택해주세요.', + LINE_DRAWING = '경로를 설정합니다.\n터치를 해서 경로를 그려주세요.', +} + +// 매핑 객체로 연결 +export const ButtonStateDescriptions: Record = { + [ButtonState.CLOSE]: DescriptionText.CLOSE, + [ButtonState.OPEN]: DescriptionText.OPEN, + [ButtonState.START_MARKER]: DescriptionText.START_MARKER, + [ButtonState.DESTINATION_MARKER]: DescriptionText.DESTINATION_MARKER, + [ButtonState.LINE_DRAWING]: DescriptionText.LINE_DRAWING, +}; From 57166cbc24f336b23f25a90422f35ae74df37ddb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=9D=B4=EB=8F=99=EC=9C=A8?= Date: Mon, 25 Nov 2024 18:19:52 +0900 Subject: [PATCH 05/17] =?UTF-8?q?[FE][Feat]=20#257=20:=20CurrentUser=20?= =?UTF-8?q?=EC=A0=84=EC=97=AD=EC=83=81=ED=83=9C=EB=A1=9C=20=EA=B4=80?= =?UTF-8?q?=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/component/searchbox/SearchBox.tsx | 12 ++--- frontend/src/context/CurrentUserContext.tsx | 36 ++++++++++++++ frontend/src/pages/DrawRoute.tsx | 15 ++++-- frontend/src/routes/IndexRoutes.tsx | 47 ++++++++++--------- 4 files changed, 76 insertions(+), 34 deletions(-) create mode 100644 frontend/src/context/CurrentUserContext.tsx diff --git a/frontend/src/component/searchbox/SearchBox.tsx b/frontend/src/component/searchbox/SearchBox.tsx index 7a1aea83..fd6cdc79 100644 --- a/frontend/src/component/searchbox/SearchBox.tsx +++ b/frontend/src/component/searchbox/SearchBox.tsx @@ -1,7 +1,8 @@ import { ToolTypeContext } from '@/context/ToolTypeContext'; -import { IUser, UserContext } from '@/context/UserContext'; +import { UserContext } from '@/context/UserContext'; import React, { useContext, useState } from 'react'; import { IoMdClose, IoMdSearch } from 'react-icons/io'; +import { CurrentUserContext } from '@/context/CurrentUserContext'; import { ButtonState } from '../common/enums'; interface ISearchResultItem { @@ -12,11 +13,7 @@ interface ISearchResultItem { lng: number; } -interface ISearchBox { - user: IUser | undefined; -} - -export const SearchBox = (props: ISearchBox) => { +export const SearchBox = () => { const [inputValue, setInputValue] = useState(''); // 검색 입력값 상태 const [searchResults, setSearchResults] = useState([]); // 검색 결과 상태 const [loading, setLoading] = useState(false); // 로딩 상태 @@ -24,9 +21,10 @@ export const SearchBox = (props: ISearchBox) => { const { users, setUsers } = useContext(UserContext); const { toolType } = useContext(ToolTypeContext); + const { currentUser } = useContext(CurrentUserContext); const updateUser = (title: string, lat: number, lng: number) => { - const targetUser = props.user; + const targetUser = currentUser; const updatedUsers = users.map(user => { if (user === targetUser) { // toolType에 따라 start_location 또는 end_location을 업데이트 diff --git a/frontend/src/context/CurrentUserContext.tsx b/frontend/src/context/CurrentUserContext.tsx new file mode 100644 index 00000000..cd1d3a9c --- /dev/null +++ b/frontend/src/context/CurrentUserContext.tsx @@ -0,0 +1,36 @@ +import React, { createContext, ReactNode, useMemo, useState } from 'react'; +import { IUser } from './UserContext'; + +interface ICurrentUserContextProps { + children: ReactNode; +} + +interface ICurrentUserOptionContext { + currentUser: IUser; + setCurrentUser: React.Dispatch>; +} + +const defaultUserContext: ICurrentUserOptionContext = { + currentUser: { + id: 1, + name: '사용자1', + start_location: { title: '', lat: 0, lng: 0 }, + end_location: { title: '', lat: 0, lng: 0 }, + path: [], + marker_style: { color: '' }, + }, + setCurrentUser: () => {}, +}; + +export const CurrentUserContext = createContext(defaultUserContext); + +export const CurrentUserProvider = (props: ICurrentUserContextProps) => { + const [currentUser, setCurrentUser] = useState(defaultUserContext.currentUser); + const contextValue = useMemo( + () => ({ currentUser, setCurrentUser }), + [currentUser, setCurrentUser], + ); + return ( + {props.children} + ); +}; diff --git a/frontend/src/pages/DrawRoute.tsx b/frontend/src/pages/DrawRoute.tsx index 255575f2..338a6510 100644 --- a/frontend/src/pages/DrawRoute.tsx +++ b/frontend/src/pages/DrawRoute.tsx @@ -1,4 +1,4 @@ -import { useContext, useEffect, useMemo } from 'react'; +import { useContext, useEffect } from 'react'; import { FooterContext } from '@/component/layout/footer/LayoutFooterProvider'; import { useNavigate, useParams } from 'react-router-dom'; import { IUser, UserContext } from '@/context/UserContext'; @@ -6,20 +6,21 @@ import { SearchBox } from '@/component/searchbox/SearchBox'; import { ToolTypeProvider } from '@/context/ToolTypeContext'; import { buttonActiveType } from '@/component/layout/enumTypes'; import { MapProviderForDraw } from '@/component/canvasWithMap/MapProviderForDraw'; +import { CurrentUserContext } from '@/context/CurrentUserContext'; export const DrawRoute = () => { const { users, setUsers } = useContext(UserContext); const { setFooterTitle, setFooterActive, setFooterOnClick } = useContext(FooterContext); + const { currentUser, setCurrentUser } = useContext(CurrentUserContext); const params = useParams>(); // userName을 URL 파라미터로 가져옴 const navigate = useNavigate(); const goToAddChannelRoute = () => { navigate(`/add-channel/`); }; - - const currentUser = useMemo(() => { + const getUser = () => { return users.find(user => user.name === params.user); - }, [users, params.user]); + }; const resetMockData = () => { if (currentUser) { @@ -63,6 +64,10 @@ export const DrawRoute = () => { setFooterTitle('사용자 경로 추가 완료'); setFooterOnClick(goToAddChannelRoute); setFooterActive(buttonActiveType.PASSIVE); + const user = getUser(); + if (user) { + setCurrentUser(user); + } }, []); if (!currentUser) { @@ -73,7 +78,7 @@ export const DrawRoute = () => { return (
- + diff --git a/frontend/src/routes/IndexRoutes.tsx b/frontend/src/routes/IndexRoutes.tsx index 4ae240a9..2c59bc4c 100644 --- a/frontend/src/routes/IndexRoutes.tsx +++ b/frontend/src/routes/IndexRoutes.tsx @@ -8,35 +8,38 @@ import { HostView } from '@/pages/HostView'; import { GuestView } from '@/pages/GuestView'; import { Layout } from '@/component/layout/Layout'; import { UserProvider } from '@/context/UserContext'; +import { CurrentUserProvider } from '@/context/CurrentUserContext'; export const IndexRoutes = () => ( - - }> - {/* 메인 페이지를 위한 인덱스 라우트 */} - } /> + + + }> + {/* 메인 페이지를 위한 인덱스 라우트 */} + } /> - {/* 공개 라우트 */} - } /> + {/* 공개 라우트 */} + } /> - {/* 채널 추가를 위한 중첩 라우트 */} - - } /> - - } /> - } /> + {/* 채널 추가를 위한 중첩 라우트 */} + + } /> + + } /> + } /> + - - {/* 채널별 뷰를 위한 중첩 라우트 */} - - } /> - } /> - + {/* 채널별 뷰를 위한 중첩 라우트 */} + + } /> + } /> + - {/* TODO : 정의되지 않은 경로에 대한 폴백 라우트 (선택사항) */} - {/* } /> */} - - + {/* TODO : 정의되지 않은 경로에 대한 폴백 라우트 (선택사항) */} + {/* } /> */} + + + ); From e12a3fbf045a258acb1779043744366c7caf4bc1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=9D=B4=EB=8F=99=EC=9C=A8?= Date: Mon, 25 Nov 2024 18:51:54 +0900 Subject: [PATCH 06/17] =?UTF-8?q?[FE][Feat]=20#257=20:=20Path=20=EB=B0=8F?= =?UTF-8?q?=20=EB=A7=88=EC=BB=A4=20=EC=84=A4=EC=A0=95=20=EC=8B=9C=20?= =?UTF-8?q?=EC=A0=95=EB=B3=B4=20=EB=B0=98=EC=98=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../canvasWithMap/MapCanvasForDraw.tsx | 22 ++++++++- .../src/component/searchbox/SearchBox.tsx | 23 +++++++-- frontend/src/pages/DrawRoute.tsx | 48 +++++++++++++++++-- 3 files changed, 86 insertions(+), 7 deletions(-) diff --git a/frontend/src/component/canvasWithMap/MapCanvasForDraw.tsx b/frontend/src/component/canvasWithMap/MapCanvasForDraw.tsx index dd03effc..e5b2b95b 100644 --- a/frontend/src/component/canvasWithMap/MapCanvasForDraw.tsx +++ b/frontend/src/component/canvasWithMap/MapCanvasForDraw.tsx @@ -1,4 +1,4 @@ -import React, { useEffect, useRef, useState } from 'react'; +import React, { useContext, useEffect, useRef, useState } from 'react'; import { ButtonState } from '@/component/common/enums'; import classNames from 'classnames'; import { MdArrowCircleLeft, MdArrowCircleRight } from 'react-icons/md'; @@ -9,6 +9,7 @@ import { ICanvasPoint, IMapCanvasProps, IPoint } from '@/lib/types/canvasInterfa import { useUndoRedo } from '@/hooks/useUndoRedo.ts'; import startmarker from '@/assets/startmarker.png'; import endmarker from '@/assets/endmarker.png'; +import { CurrentUserContext } from '@/context/CurrentUserContext'; import { ToolDescription } from '../tooldescription/ToolDescription'; export const MapCanvasForDraw = ({ @@ -38,6 +39,8 @@ export const MapCanvasForDraw = ({ const { isMenuOpen, toolType, toggleMenu, handleMenuClick } = useFloatingButton(); const { pathPoints, addPoint, undo, redo, undoStack, redoStack } = useUndoRedo([]); + const { setCurrentUser } = useContext(CurrentUserContext); + const startImageRef = useRef(null); const endImageRef = useRef(null); @@ -49,6 +52,23 @@ export const MapCanvasForDraw = ({ endImageRef.current.src = endmarker; }, []); + useEffect(() => { + setCurrentUser(prevUser => ({ + ...prevUser, + start_location: { + title: '', + lat: startMarker?.lat ?? 0, // startMarker가 없을 경우 0으로 처리 + lng: startMarker?.lng ?? 0, // endMarker가 없을 경우 0으로 처리 + }, + end_location: { + title: '', + lat: endMarker?.lat ?? 0, // endMarker가 없을 경우 0으로 처리 + lng: endMarker?.lng ?? 0, // endMarker가 없을 경우 0으로 처리 + }, + path: pathPoints, // 경로 포인트들 + })); + }, [startMarker, endMarker, pathPoints, setCurrentUser]); + useEffect(() => { if (!mapRef.current) return; diff --git a/frontend/src/component/searchbox/SearchBox.tsx b/frontend/src/component/searchbox/SearchBox.tsx index fd6cdc79..f1d82012 100644 --- a/frontend/src/component/searchbox/SearchBox.tsx +++ b/frontend/src/component/searchbox/SearchBox.tsx @@ -21,13 +21,17 @@ export const SearchBox = () => { const { users, setUsers } = useContext(UserContext); const { toolType } = useContext(ToolTypeContext); - const { currentUser } = useContext(CurrentUserContext); + const { currentUser, setCurrentUser } = useContext(CurrentUserContext); const updateUser = (title: string, lat: number, lng: number) => { + // `currentUser`의 정보를 사용하여 대상 사용자를 찾아서 업데이트 const targetUser = currentUser; + console.log(targetUser); + + // `users` 배열을 순회하면서 `currentUser`에 해당하는 사용자를 찾고, 업데이트 const updatedUsers = users.map(user => { - if (user === targetUser) { - // toolType에 따라 start_location 또는 end_location을 업데이트 + if (user.id === targetUser.id) { + // `targetUser`와 같은 id를 가진 사용자를 찾음 if (toolType === ButtonState.START_MARKER) { return { ...user, @@ -53,7 +57,20 @@ export const SearchBox = () => { }); console.log(updatedUsers); + + // `setUsers`를 사용해 업데이트된 사용자 목록으로 상태를 갱신 setUsers(updatedUsers); + + // `currentUser`도 갱신 (선택적, 필요 시) + setCurrentUser({ + ...currentUser, + start_location: + toolType === ButtonState.START_MARKER ? { title, lat, lng } : currentUser.start_location, + end_location: + toolType === ButtonState.DESTINATION_MARKER + ? { title, lat, lng } + : currentUser.end_location, + }); }; const handleSearch = async () => { diff --git a/frontend/src/pages/DrawRoute.tsx b/frontend/src/pages/DrawRoute.tsx index 338a6510..49ffca14 100644 --- a/frontend/src/pages/DrawRoute.tsx +++ b/frontend/src/pages/DrawRoute.tsx @@ -18,6 +18,7 @@ export const DrawRoute = () => { const goToAddChannelRoute = () => { navigate(`/add-channel/`); }; + const getUser = () => { return users.find(user => user.name === params.user); }; @@ -43,7 +44,9 @@ export const DrawRoute = () => { user.start_location.lat !== 0 && user.start_location.lng !== 0 && user.end_location.lat !== 0 && - user.end_location.lng !== 0 + user.end_location.lng !== 0 && + user.path.length > 0 && + user.marker_style.color !== '' ); }; @@ -62,14 +65,53 @@ export const DrawRoute = () => { useEffect(() => { setFooterTitle('사용자 경로 추가 완료'); - setFooterOnClick(goToAddChannelRoute); setFooterActive(buttonActiveType.PASSIVE); const user = getUser(); if (user) { - setCurrentUser(user); + // userId에 따른 Tailwind 색상 클래스를 설정 + const markerColors: { [key: number]: string } = { + 1: 'marker:user1', // tailwind의 custom color class로 설정 + 2: 'marker:user2', + 3: 'marker:user3', + 4: 'marker:user4', + 5: 'marker:user5', + }; + + // user.id에 맞는 marker 스타일 적용 + const updatedUser = { + ...user, + marker_style: { + ...user.marker_style, + color: markerColors[user.id] || 'marker:user1', // 기본값은 user1 색상 + }, + }; + + setCurrentUser(updatedUser); } }, []); + useEffect(() => { + if (currentUser) { + // currentUser가 설정된 후에 footerOnClick을 설정 + setFooterOnClick(() => { + if (currentUser) { + console.log('Footer 버튼 클릭 시 currentUser:', currentUser); + + // `users` 배열에서 `currentUser`를 찾아 업데이트 + const updatedUsers = users.map(user => { + if (user.name === currentUser.name) { + return { ...user, ...currentUser }; // currentUser 정보로 업데이트 + } + return user; + }); + + setUsers(updatedUsers); // 업데이트된 users 배열 설정 + goToAddChannelRoute(); // 경로 추가 페이지로 이동 + } + }); + } + }, [currentUser, users]); + if (!currentUser) { // currentUser가 없을 경우에는 이 페이지에서 아무것도 렌더링하지 않거나, 에러 메시지를 표시할 수 있습니다. return
사용자를 찾을 수 없습니다.
; From 4437a7a1c37a8ac86209e758e0cdf7774991c73c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=9D=B4=EB=8F=99=EC=9C=A8?= Date: Mon, 25 Nov 2024 19:08:50 +0900 Subject: [PATCH 07/17] =?UTF-8?q?[FE][Feat]=20#257=20:=20=EC=A3=BC?= =?UTF-8?q?=EC=86=8C=20=EB=B3=80=ED=99=98=20=EB=A1=9C=EC=A7=81=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../canvasWithMap/MapCanvasForDraw.tsx | 42 ++++++++++++------- frontend/src/pages/AddChannel.tsx | 4 +- frontend/src/pages/DrawRoute.tsx | 29 +------------ frontend/src/utils/map/getAddress.ts | 1 - 4 files changed, 32 insertions(+), 44 deletions(-) diff --git a/frontend/src/component/canvasWithMap/MapCanvasForDraw.tsx b/frontend/src/component/canvasWithMap/MapCanvasForDraw.tsx index e5b2b95b..f4db645b 100644 --- a/frontend/src/component/canvasWithMap/MapCanvasForDraw.tsx +++ b/frontend/src/component/canvasWithMap/MapCanvasForDraw.tsx @@ -10,6 +10,7 @@ import { useUndoRedo } from '@/hooks/useUndoRedo.ts'; import startmarker from '@/assets/startmarker.png'; import endmarker from '@/assets/endmarker.png'; import { CurrentUserContext } from '@/context/CurrentUserContext'; +import { getAddressFromCoordinates } from '@/utils/map/getAddress'; import { ToolDescription } from '../tooldescription/ToolDescription'; export const MapCanvasForDraw = ({ @@ -53,20 +54,33 @@ export const MapCanvasForDraw = ({ }, []); useEffect(() => { - setCurrentUser(prevUser => ({ - ...prevUser, - start_location: { - title: '', - lat: startMarker?.lat ?? 0, // startMarker가 없을 경우 0으로 처리 - lng: startMarker?.lng ?? 0, // endMarker가 없을 경우 0으로 처리 - }, - end_location: { - title: '', - lat: endMarker?.lat ?? 0, // endMarker가 없을 경우 0으로 처리 - lng: endMarker?.lng ?? 0, // endMarker가 없을 경우 0으로 처리 - }, - path: pathPoints, // 경로 포인트들 - })); + const updateUser = async () => { + const startLat = startMarker?.lat ?? 0; + const startLng = startMarker?.lng ?? 0; + const endLat = endMarker?.lat ?? 0; + const endLng = endMarker?.lng ?? 0; + console.log('Start coordinates:', startLat, startLng); + console.log('End coordinates:', endLat, endLng); + const startTitle = await getAddressFromCoordinates(startLat, startLng); + const endTitle = await getAddressFromCoordinates(endLat, endLng); + + setCurrentUser(prevUser => ({ + ...prevUser, + start_location: { + title: startTitle, + lat: startMarker?.lat ?? 0, + lng: startMarker?.lng ?? 0, + }, + end_location: { + title: endTitle, + lat: endMarker?.lat ?? 0, + lng: endMarker?.lng ?? 0, + }, + path: pathPoints, // 경로 포인트들 + })); + }; + + updateUser(); // 비동기 함수 호출 }, [startMarker, endMarker, pathPoints, setCurrentUser]); useEffect(() => { diff --git a/frontend/src/pages/AddChannel.tsx b/frontend/src/pages/AddChannel.tsx index 2f96e20f..548debec 100644 --- a/frontend/src/pages/AddChannel.tsx +++ b/frontend/src/pages/AddChannel.tsx @@ -71,7 +71,9 @@ export const AddChannel = () => { user.start_location.lat !== 0 && user.start_location.lng !== 0 && user.end_location.lat !== 0 && - user.end_location.lng !== 0 + user.end_location.lng !== 0 && + user.path.length > 0 && + user.marker_style.color !== '' ); }; diff --git a/frontend/src/pages/DrawRoute.tsx b/frontend/src/pages/DrawRoute.tsx index 49ffca14..4511db88 100644 --- a/frontend/src/pages/DrawRoute.tsx +++ b/frontend/src/pages/DrawRoute.tsx @@ -23,22 +23,6 @@ export const DrawRoute = () => { return users.find(user => user.name === params.user); }; - const resetMockData = () => { - if (currentUser) { - const updatedUser = { - ...currentUser, - start_location: { title: '', lat: 0, lng: 0 }, // 초기값으로 리셋 - end_location: { title: '', lat: 0, lng: 0 }, - path: [], - marker_style: { color: '' }, - }; - - // 해당 user의 state를 초기 상태로 업데이트 - const updatedUsers = users.map(u => (u.name === params.user ? updatedUser : u)); - setUsers(updatedUsers); - } - }; - const isUserDataComplete = (user: IUser): boolean => { return ( user.start_location.lat !== 0 && @@ -92,12 +76,10 @@ export const DrawRoute = () => { useEffect(() => { if (currentUser) { - // currentUser가 설정된 후에 footerOnClick을 설정 setFooterOnClick(() => { if (currentUser) { console.log('Footer 버튼 클릭 시 currentUser:', currentUser); - // `users` 배열에서 `currentUser`를 찾아 업데이트 const updatedUsers = users.map(user => { if (user.name === currentUser.name) { return { ...user, ...currentUser }; // currentUser 정보로 업데이트 @@ -112,22 +94,13 @@ export const DrawRoute = () => { } }, [currentUser, users]); - if (!currentUser) { - // currentUser가 없을 경우에는 이 페이지에서 아무것도 렌더링하지 않거나, 에러 메시지를 표시할 수 있습니다. - return
사용자를 찾을 수 없습니다.
; - } - return (
- -
{/* TODO: 동율님 mock 데이터 관련 버튼 없애고 나서, height={window.innerHeight - 180} 으로 변경해주시면 됩니다! */} - +
diff --git a/frontend/src/utils/map/getAddress.ts b/frontend/src/utils/map/getAddress.ts index 93180e92..0e9e1eda 100644 --- a/frontend/src/utils/map/getAddress.ts +++ b/frontend/src/utils/map/getAddress.ts @@ -10,7 +10,6 @@ export const getAddressFromCoordinates = (lat: number, lng: number): Promise Date: Mon, 25 Nov 2024 19:31:07 +0900 Subject: [PATCH 08/17] =?UTF-8?q?[FE][Fix]=20#257=20:=20=EC=9D=B4=EC=A0=84?= =?UTF-8?q?=20=EC=83=81=ED=83=9C=20=EA=B0=92=20=EB=B0=98=EC=98=81=20?= =?UTF-8?q?=EC=98=A4=EB=A5=98=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../canvasWithMap/MapCanvasForDraw.tsx | 48 +++++++------- .../src/component/searchbox/SearchBox.tsx | 62 ++++--------------- frontend/src/pages/DrawRoute.tsx | 58 ++++++++++++++--- 3 files changed, 83 insertions(+), 85 deletions(-) diff --git a/frontend/src/component/canvasWithMap/MapCanvasForDraw.tsx b/frontend/src/component/canvasWithMap/MapCanvasForDraw.tsx index f4db645b..550878f8 100644 --- a/frontend/src/component/canvasWithMap/MapCanvasForDraw.tsx +++ b/frontend/src/component/canvasWithMap/MapCanvasForDraw.tsx @@ -10,7 +10,6 @@ import { useUndoRedo } from '@/hooks/useUndoRedo.ts'; import startmarker from '@/assets/startmarker.png'; import endmarker from '@/assets/endmarker.png'; import { CurrentUserContext } from '@/context/CurrentUserContext'; -import { getAddressFromCoordinates } from '@/utils/map/getAddress'; import { ToolDescription } from '../tooldescription/ToolDescription'; export const MapCanvasForDraw = ({ @@ -54,34 +53,29 @@ export const MapCanvasForDraw = ({ }, []); useEffect(() => { - const updateUser = async () => { - const startLat = startMarker?.lat ?? 0; - const startLng = startMarker?.lng ?? 0; - const endLat = endMarker?.lat ?? 0; - const endLng = endMarker?.lng ?? 0; - console.log('Start coordinates:', startLat, startLng); - console.log('End coordinates:', endLat, endLng); - const startTitle = await getAddressFromCoordinates(startLat, startLng); - const endTitle = await getAddressFromCoordinates(endLat, endLng); - - setCurrentUser(prevUser => ({ - ...prevUser, - start_location: { - title: startTitle, - lat: startMarker?.lat ?? 0, - lng: startMarker?.lng ?? 0, - }, - end_location: { - title: endTitle, - lat: endMarker?.lat ?? 0, - lng: endMarker?.lng ?? 0, - }, - path: pathPoints, // 경로 포인트들 - })); + const updateUser = () => { + setCurrentUser(prevUser => { + return { + ...prevUser, + start_location: { + ...prevUser.start_location, // 기존 start_location 유지 + title: prevUser.start_location.title ?? '', + lat: startMarker?.lat ?? prevUser.start_location.lat, + lng: startMarker?.lng ?? prevUser.start_location.lng, + }, + end_location: { + ...prevUser.end_location, // 기존 end_location 유지 + title: prevUser.end_location.title ?? '', + lat: endMarker?.lat ?? prevUser.end_location.lat, + lng: endMarker?.lng ?? prevUser.end_location.lng, + }, + path: pathPoints, // 경로 포인트들 + }; + }); }; - updateUser(); // 비동기 함수 호출 - }, [startMarker, endMarker, pathPoints, setCurrentUser]); + updateUser(); // 상태 업데이트 함수 호출 + }, [startMarker, endMarker, pathPoints]); // 필요한 의존성만 포함 useEffect(() => { if (!mapRef.current) return; diff --git a/frontend/src/component/searchbox/SearchBox.tsx b/frontend/src/component/searchbox/SearchBox.tsx index f1d82012..4b665caa 100644 --- a/frontend/src/component/searchbox/SearchBox.tsx +++ b/frontend/src/component/searchbox/SearchBox.tsx @@ -1,5 +1,4 @@ import { ToolTypeContext } from '@/context/ToolTypeContext'; -import { UserContext } from '@/context/UserContext'; import React, { useContext, useState } from 'react'; import { IoMdClose, IoMdSearch } from 'react-icons/io'; import { CurrentUserContext } from '@/context/CurrentUserContext'; @@ -18,59 +17,24 @@ export const SearchBox = () => { const [searchResults, setSearchResults] = useState([]); // 검색 결과 상태 const [loading, setLoading] = useState(false); // 로딩 상태 const [error, setError] = useState(null); // 에러 상태 - - const { users, setUsers } = useContext(UserContext); const { toolType } = useContext(ToolTypeContext); const { currentUser, setCurrentUser } = useContext(CurrentUserContext); const updateUser = (title: string, lat: number, lng: number) => { - // `currentUser`의 정보를 사용하여 대상 사용자를 찾아서 업데이트 - const targetUser = currentUser; - console.log(targetUser); - - // `users` 배열을 순회하면서 `currentUser`에 해당하는 사용자를 찾고, 업데이트 - const updatedUsers = users.map(user => { - if (user.id === targetUser.id) { - // `targetUser`와 같은 id를 가진 사용자를 찾음 - if (toolType === ButtonState.START_MARKER) { - return { - ...user, - start_location: { - title, - lat, - lng, - }, - }; - } - if (toolType === ButtonState.DESTINATION_MARKER) { - return { - ...user, - end_location: { - title, - lat, - lng, - }, - }; - } - } - return user; // 나머지 사용자들은 그대로 반환 - }); - - console.log(updatedUsers); + const updatedLocation = { title, lat, lng }; - // `setUsers`를 사용해 업데이트된 사용자 목록으로 상태를 갱신 - setUsers(updatedUsers); - - // `currentUser`도 갱신 (선택적, 필요 시) - setCurrentUser({ - ...currentUser, - start_location: - toolType === ButtonState.START_MARKER ? { title, lat, lng } : currentUser.start_location, - end_location: - toolType === ButtonState.DESTINATION_MARKER - ? { title, lat, lng } - : currentUser.end_location, - }); + // toolType에 따라 start_location 또는 end_location을 업데이트 + if (toolType === ButtonState.START_MARKER) { + setCurrentUser({ + ...currentUser, + start_location: updatedLocation, // start_location만 업데이트 + }); + } else if (toolType === ButtonState.DESTINATION_MARKER) { + setCurrentUser({ + ...currentUser, + end_location: updatedLocation, // end_location만 업데이트 + }); + } }; const handleSearch = async () => { diff --git a/frontend/src/pages/DrawRoute.tsx b/frontend/src/pages/DrawRoute.tsx index 4511db88..83e1a13e 100644 --- a/frontend/src/pages/DrawRoute.tsx +++ b/frontend/src/pages/DrawRoute.tsx @@ -7,6 +7,7 @@ import { ToolTypeProvider } from '@/context/ToolTypeContext'; import { buttonActiveType } from '@/component/layout/enumTypes'; import { MapProviderForDraw } from '@/component/canvasWithMap/MapProviderForDraw'; import { CurrentUserContext } from '@/context/CurrentUserContext'; +import { getAddressFromCoordinates } from '@/utils/map/getAddress'; export const DrawRoute = () => { const { users, setUsers } = useContext(UserContext); @@ -75,20 +76,60 @@ export const DrawRoute = () => { }, []); useEffect(() => { + console.log(currentUser); if (currentUser) { setFooterOnClick(() => { if (currentUser) { - console.log('Footer 버튼 클릭 시 currentUser:', currentUser); + const updateUserLocation = async () => { + const updatedUser = { ...currentUser }; // currentUser 복사 - const updatedUsers = users.map(user => { - if (user.name === currentUser.name) { - return { ...user, ...currentUser }; // currentUser 정보로 업데이트 + // start_location.title이 비어 있으면 주소를 업데이트 + if ( + !updatedUser.start_location.title && + updatedUser.start_location.lat && + updatedUser.start_location.lng + ) { + try { + const startAddress = await getAddressFromCoordinates( + updatedUser.start_location.lat, + updatedUser.start_location.lng, + ); + updatedUser.start_location.title = startAddress; // 주소 업데이트 + } catch (error) { + console.error('Error fetching start location address:', error); + } } - return user; - }); - setUsers(updatedUsers); // 업데이트된 users 배열 설정 - goToAddChannelRoute(); // 경로 추가 페이지로 이동 + // end_location.title이 비어 있으면 주소를 업데이트 + if ( + !updatedUser.end_location.title && + updatedUser.end_location.lat && + updatedUser.end_location.lng + ) { + try { + const endAddress = await getAddressFromCoordinates( + updatedUser.end_location.lat, + updatedUser.end_location.lng, + ); + updatedUser.end_location.title = endAddress; // 주소 업데이트 + } catch (error) { + console.error('Error fetching end location address:', error); + } + } + + // user 정보를 업데이트 + const updatedUsers = users.map(user => { + if (user.name === updatedUser.name) { + return updatedUser; // 주소가 업데이트된 currentUser로 업데이트 + } + return user; + }); + + setUsers(updatedUsers); // users 배열 업데이트 + goToAddChannelRoute(); // 경로 추가 페이지로 이동 + }; + + updateUserLocation(); // 위치 업데이트 함수 호출 } }); } @@ -99,7 +140,6 @@ export const DrawRoute = () => {
- {/* TODO: 동율님 mock 데이터 관련 버튼 없애고 나서, height={window.innerHeight - 180} 으로 변경해주시면 됩니다! */}
From 0701db6e12cd5b69af041b167a2d50ef99bb22de Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=9D=B4=EB=8F=99=EC=9C=A8?= Date: Mon, 25 Nov 2024 20:01:38 +0900 Subject: [PATCH 09/17] =?UTF-8?q?[FE][Fix]=20#257=20:=20build=20=EA=B0=80?= =?UTF-8?q?=EB=8A=A5=ED=95=98=EB=8F=84=EB=A1=9D=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- frontend/src/pages/Main.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frontend/src/pages/Main.tsx b/frontend/src/pages/Main.tsx index ae446d95..18522386 100644 --- a/frontend/src/pages/Main.tsx +++ b/frontend/src/pages/Main.tsx @@ -3,13 +3,13 @@ import { MdFormatListBulleted } from 'react-icons/md'; import { FooterContext } from '@/component/layout/footer/LayoutFooterProvider'; import { useNavigate } from 'react-router-dom'; import { buttonActiveType } from '@/component/layout/enumTypes'; -import { MapProviderForDraw } from '@/component/canvasWithMap/MapProviderForDraw.tsx'; import { BottomSheet } from '@/component/bottomsheet/BottomSheet.tsx'; import { Content } from '@/component/content/Content.tsx'; import { loadLocalData, saveLocalData } from '@/utils/common/manageLocalData.ts'; import { AppConfig } from '@/lib/constants/commonConstants.ts'; import { v4 as uuidv4 } from 'uuid'; import { getUserLocation } from '@/hooks/getUserLocation.ts'; +import { MapProviderForDraw } from '@/component/canvasWithMap/canvasWithMapforDraw/MapProviderForDraw'; const contentData = [ { From 1c4e16248c1345a89ec6a4857b109f7e3c16f2d2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=9D=B4=EB=8F=99=EC=9C=A8?= Date: Mon, 25 Nov 2024 23:15:22 +0900 Subject: [PATCH 10/17] =?UTF-8?q?[FE][Feat]=20#257=20:=20=EC=B6=9C?= =?UTF-8?q?=EB=B0=9C=EC=A7=80=20=EB=B0=8F=20=EB=8F=84=EC=B0=A9=EC=A7=80=20?= =?UTF-8?q?=EA=B2=B0=EA=B3=BC=20=EC=99=BC=EC=AA=BD=20=EC=A0=95=EB=A0=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/component/routebutton/RouteResultButton.tsx | 4 ++-- frontend/src/component/searchbox/SearchBox.tsx | 10 +++++----- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/frontend/src/component/routebutton/RouteResultButton.tsx b/frontend/src/component/routebutton/RouteResultButton.tsx index e730be67..e72df861 100644 --- a/frontend/src/component/routebutton/RouteResultButton.tsx +++ b/frontend/src/component/routebutton/RouteResultButton.tsx @@ -28,11 +28,11 @@ export const RouteResultButton = (props: IRouteResultButtonProps) => { props.user.id > 1 ? '' : 'mr-8', )} > -
+
{props.user.start_location.title}
-
+
{props.user.end_location.title}
diff --git a/frontend/src/component/searchbox/SearchBox.tsx b/frontend/src/component/searchbox/SearchBox.tsx index 354c7fb2..456df245 100644 --- a/frontend/src/component/searchbox/SearchBox.tsx +++ b/frontend/src/component/searchbox/SearchBox.tsx @@ -43,14 +43,14 @@ export const SearchBox = () => { setError(null); try { - // const apiUrl = `https://cors-anywhere.herokuapp.com/https://openapi.naver.com/v1/search/local.json?query=${encodeURIComponent( - // inputValue, - // )}&display=10&start=1&sort=random`; - - const apiUrl = `https://openapi.naver.com/v1/search/local.json?query=${encodeURIComponent( + const apiUrl = `https://cors-anywhere.herokuapp.com/https://openapi.naver.com/v1/search/local.json?query=${encodeURIComponent( inputValue, )}&display=10&start=1&sort=random`; + // const apiUrl = `https://openapi.naver.com/v1/search/local.json?query=${encodeURIComponent( + // inputValue, + // )}&display=10&start=1&sort=random`; + const response = await fetch(apiUrl, { headers: { 'X-Naver-Client-Id': 'RwiDUxdYdlPHF1pcM0id', From dbe73294a3604172e2a5d66dec9f6bb46758f297 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=9D=B4=EB=8F=99=EC=9C=A8?= Date: Mon, 25 Nov 2024 23:26:45 +0900 Subject: [PATCH 11/17] =?UTF-8?q?[FE][Feat]=20#257=20:=20debounce=EB=A5=BC?= =?UTF-8?q?=20=EC=9D=B4=EC=9A=A9=ED=95=B4=201=EC=B4=88=20=EC=9D=B4?= =?UTF-8?q?=ED=9B=84=20=EC=9E=90=EB=8F=99=20=EA=B2=80=EC=83=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- frontend/src/component/searchbox/SearchBox.tsx | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/frontend/src/component/searchbox/SearchBox.tsx b/frontend/src/component/searchbox/SearchBox.tsx index 456df245..a2a50157 100644 --- a/frontend/src/component/searchbox/SearchBox.tsx +++ b/frontend/src/component/searchbox/SearchBox.tsx @@ -1,5 +1,5 @@ import { ToolTypeContext } from '@/context/ToolTypeContext'; -import React, { useContext, useState } from 'react'; +import React, { useContext, useEffect, useState } from 'react'; import { IoMdClose, IoMdSearch } from 'react-icons/io'; import { CurrentUserContext } from '@/context/CurrentUserContext'; import { ButtonState } from '../common/enums'; @@ -39,13 +39,14 @@ export const SearchBox = () => { const handleSearch = async () => { if (!inputValue.trim()) return; + setSearchResults([]); setLoading(true); setError(null); try { const apiUrl = `https://cors-anywhere.herokuapp.com/https://openapi.naver.com/v1/search/local.json?query=${encodeURIComponent( inputValue, - )}&display=10&start=1&sort=random`; + )}&display=5&start=1&sort=random`; // const apiUrl = `https://openapi.naver.com/v1/search/local.json?query=${encodeURIComponent( // inputValue, @@ -79,6 +80,16 @@ export const SearchBox = () => { } }; + useEffect(() => { + const delayDebounceFn = setTimeout(() => { + if (inputValue.trim()) { + handleSearch(); + } + }, 1000); + + return () => clearTimeout(delayDebounceFn); + }, [inputValue]); + const handleInputChange = (event: React.ChangeEvent) => { setInputValue(event.target.value); // 상태 업데이트 }; From df5878d0d31dfb71439ecf586328b392bcc99dc9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=9D=B4=EB=8F=99=EC=9C=A8?= Date: Tue, 26 Nov 2024 11:17:12 +0900 Subject: [PATCH 12/17] =?UTF-8?q?[FE][Feat]=20#257=20:=20=EB=94=94?= =?UTF-8?q?=EC=9E=90=EC=9D=B8=20=EC=88=98=EC=A0=95=20=EB=B0=8F=20=EA=B2=80?= =?UTF-8?q?=EC=83=89=20=EC=8B=9C=EA=B0=84=20=EB=8B=A8=EC=B6=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- frontend/src/component/common/InputBox.tsx | 2 +- frontend/src/component/routebutton/RouteResultButton.tsx | 2 +- frontend/src/component/routebutton/RouteSettingButton.tsx | 2 +- frontend/src/component/searchbox/SearchBox.tsx | 2 +- frontend/src/pages/AddChannel.tsx | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/frontend/src/component/common/InputBox.tsx b/frontend/src/component/common/InputBox.tsx index 0e1ed494..ad7e3da2 100644 --- a/frontend/src/component/common/InputBox.tsx +++ b/frontend/src/component/common/InputBox.tsx @@ -26,6 +26,6 @@ export const InputBox = ({ onFocus={onFocus} onBlur={onBlur} value={value} - className={`border-grayscale-75 placeholder:text-grayscale-50 focus:border-grayscale-400 text-grayscale-400 flex h-11 w-full rounded border px-3 text-xs focus:border-2 focus:outline-none ${className}`} + className={`border-grayscale-75 placeholder:text-grayscale-50 focus:border-grayscale-400 text-grayscale-400 font-nomal flex h-11 w-full rounded border px-3 text-xs focus:border-2 focus:outline-none ${className}`} /> ); diff --git a/frontend/src/component/routebutton/RouteResultButton.tsx b/frontend/src/component/routebutton/RouteResultButton.tsx index e72df861..7eb075a1 100644 --- a/frontend/src/component/routebutton/RouteResultButton.tsx +++ b/frontend/src/component/routebutton/RouteResultButton.tsx @@ -19,7 +19,7 @@ export const RouteResultButton = (props: IRouteResultButtonProps) => { return (
- {props.user.name} +

{props.user.name}

From f538d830b1bb6e76532de4dd407059bab3aed89a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=9D=B4=EB=8F=99=EC=9C=A8?= Date: Tue, 26 Nov 2024 11:19:43 +0900 Subject: [PATCH 13/17] =?UTF-8?q?[FE][Docs]=20#257=20:=20Todo=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- frontend/src/component/searchbox/SearchBox.tsx | 2 ++ 1 file changed, 2 insertions(+) diff --git a/frontend/src/component/searchbox/SearchBox.tsx b/frontend/src/component/searchbox/SearchBox.tsx index 0d6faadf..e119c66d 100644 --- a/frontend/src/component/searchbox/SearchBox.tsx +++ b/frontend/src/component/searchbox/SearchBox.tsx @@ -80,6 +80,8 @@ export const SearchBox = () => { } }; + /* TODO: 자동검색 로직 수정 필요 */ + useEffect(() => { const delayDebounceFn = setTimeout(() => { if (inputValue.trim()) { From 83d940b014fbfbf21020df171673baf13964f313 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=9D=B4=EB=8F=99=EC=9C=A8?= Date: Tue, 26 Nov 2024 13:18:27 +0900 Subject: [PATCH 14/17] =?UTF-8?q?[FE][Feat]=20#257=20:=20=EA=B2=80?= =?UTF-8?q?=EC=83=89=20=EA=B2=B0=EA=B3=BC=EB=A1=9C=20=EB=A7=88=EC=BB=A4=20?= =?UTF-8?q?=EC=B0=8D=EA=B8=B0=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../canvasWithMapforDraw/MapCanvasForDraw.tsx | 11 +++++++++++ frontend/src/component/searchbox/SearchBox.tsx | 14 +++++++++----- frontend/src/pages/DrawRoute.tsx | 4 +--- 3 files changed, 21 insertions(+), 8 deletions(-) diff --git a/frontend/src/component/canvasWithMap/canvasWithMapforDraw/MapCanvasForDraw.tsx b/frontend/src/component/canvasWithMap/canvasWithMapforDraw/MapCanvasForDraw.tsx index f1955730..66754444 100644 --- a/frontend/src/component/canvasWithMap/canvasWithMapforDraw/MapCanvasForDraw.tsx +++ b/frontend/src/component/canvasWithMap/canvasWithMapforDraw/MapCanvasForDraw.tsx @@ -11,6 +11,7 @@ import startmarker from '@/assets/startmarker.png'; import endmarker from '@/assets/endmarker.png'; import { CurrentUserContext } from '@/context/CurrentUserContext'; import { ToolDescription } from '@/component/tooldescription/ToolDescription'; +import { SearchBox } from '@/component/searchbox/SearchBox'; export const MapCanvasForDraw = ({ width, @@ -384,6 +385,11 @@ export const MapCanvasForDraw = ({ } }; + const handleMarker = (point: IPoint) => { + if (toolType === ButtonState.START_MARKER) setStartMarker(point); + setEndMarker(point); + }; + useEffect(() => { if (isDragging) { if (canvasRef.current) { @@ -415,6 +421,11 @@ export const MapCanvasForDraw = ({ onTouchMove={handleTouchMove} onTouchEnd={handleTouchEnd} > + {(toolType === ButtonState.START_MARKER || toolType === ButtonState.DESTINATION_MARKER) && ( +
+ +
+ )}
{toolType === ButtonState.LINE_DRAWING ? (
diff --git a/frontend/src/component/searchbox/SearchBox.tsx b/frontend/src/component/searchbox/SearchBox.tsx index e119c66d..ed37f84a 100644 --- a/frontend/src/component/searchbox/SearchBox.tsx +++ b/frontend/src/component/searchbox/SearchBox.tsx @@ -2,6 +2,7 @@ import { ToolTypeContext } from '@/context/ToolTypeContext'; import React, { useContext, useEffect, useState } from 'react'; import { IoMdClose, IoMdSearch } from 'react-icons/io'; import { CurrentUserContext } from '@/context/CurrentUserContext'; +import { IPoint } from '@/lib/types/canvasInterface'; import { ButtonState } from '../common/enums'; interface ISearchResultItem { @@ -12,7 +13,10 @@ interface ISearchResultItem { lng: number; } -export const SearchBox = () => { +interface ISearchBoxProps { + setMarker: (point: IPoint) => void; +} +export const SearchBox = (props: ISearchBoxProps) => { const [inputValue, setInputValue] = useState(''); // 검색 입력값 상태 const [searchResults, setSearchResults] = useState([]); // 검색 결과 상태 const [loading, setLoading] = useState(false); // 로딩 상태 @@ -100,7 +104,7 @@ export const SearchBox = () => { setInputValue(result.title); // 선택한 결과로 inputValue를 업데이트 setSearchResults([]); // 결과 리스트를 닫음 updateUser(result.title, result.lat, result.lng); - console.log(`위도: ${result.lat}, 경도: ${result.lng}`); + props.setMarker({ lat: result.lat, lng: result.lng }); }; const handleClear = () => { @@ -109,15 +113,15 @@ export const SearchBox = () => { }; return ( -
+
{/* 검색 입력 */} -
+
@@ -139,7 +177,7 @@ export const SearchBox = (props: ISearchBoxProps) => { {searchResults.map(result => (