Skip to content

Commit

Permalink
Merge pull request #72 from Codeit-TripProduct-9team/feat/my-route-page
Browse files Browse the repository at this point in the history
✨ Feat: 나의 코스 페이지 추가 기능
  • Loading branch information
kkh000 committed Jun 12, 2024
2 parents 54e9cf7 + 83213d0 commit d053bc9
Show file tree
Hide file tree
Showing 17 changed files with 318 additions and 77 deletions.
24 changes: 24 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
"react-dom": "^18",
"react-hook-form": "^7.51.4",
"react-hook-geolocation": "^1.1.0",
"react-hot-toast": "^2.4.1",
"react-intersection-observer": "^9.10.2",
"react-kakao-maps-sdk": "^1.1.26",
"react-query": "^3.39.3",
Expand Down
Original file line number Diff line number Diff line change
@@ -1,26 +1,24 @@
const CATEGORY = ['음식점', '숙박', '관광명소'];

type CategoryListProps = {
type CategoryButtonProps = {
selectedQuery: string;
setSelectedQuery: React.Dispatch<React.SetStateAction<string>>;
};

const CategoryList = ({ selectedQuery, setSelectedQuery }: CategoryListProps) => {
const CategoryButton = ({ selectedQuery, setSelectedQuery }: CategoryButtonProps) => {
return (
<ul className="absolute bg-white rounded-s border-1 flex flex-col text-center shadow-main overflow-hidden">
<div className="absolute bg-gray-30 text-white rounded-s border-1 flex flex-col text-center shadow-main overflow-hidden">
{CATEGORY.map((category) => (
<li
<button
key={category}
onClick={() => setSelectedQuery(category)}
className={`border-b-1 last:border-none cursor-pointer p-4 ${
selectedQuery === category && 'bg-blue text-white'
}`}
className={`border-b-1 last:border-none cursor-pointer p-4 ${selectedQuery === category && 'bg-blue'}`}
>
{category}
</li>
</button>
))}
</ul>
</div>
);
};

export default CategoryList;
export default CategoryButton;
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
import Button from '../../../button';

const DISTANCE = [1000, 5000, 10000];

type DistanceButtonProps = {
Expand All @@ -9,15 +7,15 @@ type DistanceButtonProps = {

const DistanceButton = ({ selectedDistance, setSelectedDistance }: DistanceButtonProps) => {
return (
<div className="flex gap-9 justify-center">
<div className="flex flex-col justify-center absolute right-20 rounded-s overflow-hidden text-white bg-gray-30">
{DISTANCE.map((distance) => (
<Button
<button
key={distance}
className={`w-109 h-40 bg-gray-30 ${selectedDistance === distance && 'bg-blue text-white'}`}
className={`w-63 h-33 border-b-1 last:border-none rounded-0 ${selectedDistance === distance && 'bg-blue '}`}
onClick={() => setSelectedDistance(distance)}
>
{distance / 1000}km
</Button>
</button>
))}
</div>
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { mockMyCourse } from '@/src/components/MyRouteContent/mockMyRoute';
import { useEffect, useState } from 'react';
import instance from '@/src/api/axios';
import { ChevronRightIcon } from '@heroicons/react/24/outline';
import CategoryList from './CategoryList';
import CategoryButton from './CategoryButton';
import DistanceButton from './DistanceButton';

type Marker = {
Expand All @@ -30,7 +30,6 @@ type SearchedPlace = {

const AddNearbyPlaceModal = () => {
const positions = mockMyCourse.coursePlan;
const [selectedPlace, setSelectedPlace] = useState({ name: '', position: { lat: 0, lng: 0 } });
const [mapCenter, setMapCenter] = useState(positions[0].places[0].position);
const [markers, setMarkers] = useState<Marker[]>([]);
const [selectedMarker, setSelectedMarker] = useState<Marker>();
Expand All @@ -46,6 +45,11 @@ const AddNearbyPlaceModal = () => {
})
.flat();

const [selectedPlace, setSelectedPlace] = useState({
name: decomposedData[0].name,
position: decomposedData[0].position,
});

const handlePlaceClick = (id: number) => {
const place = decomposedData.find((place) => place.id === id);
if (place) {
Expand All @@ -54,6 +58,12 @@ const AddNearbyPlaceModal = () => {
}
};

const handleSearchPlace = (e: React.ChangeEvent<HTMLInputElement> | React.KeyboardEvent<HTMLInputElement>) => {
if ((e.target as HTMLInputElement).value.length > 0) {
setSelectedQuery((e.target as HTMLInputElement).value);
}
};

useEffect(() => {
const fetchPlaces = async () => {
const { data } = await instance.get(
Expand All @@ -64,7 +74,6 @@ const AddNearbyPlaceModal = () => {
},
},
);
console.log(data);

// Create a marker for each place
const newMarkers = data.documents.map((place: SearchedPlace) => ({
Expand All @@ -79,13 +88,16 @@ const AddNearbyPlaceModal = () => {

// Add the new markers to the map
setMarkers(newMarkers);
// if (newMarkers) {
// setMapCenter(newMarkers[0]);
// }
};
fetchPlaces();
}, [selectedDistance, selectedPlace, selectedQuery]);

return (
<div className="flex flex-col gap-12">
<Map center={mapCenter} level={5} className="w-558 h-204 rounded-m shadow-md z-0">
<Map center={mapCenter} level={5} className="w-558 h-304 rounded-m shadow-md z-0 relative">
{markers.map((marker, index) => (
<MapMarker
key={`${marker.name}-${index}`}
Expand Down Expand Up @@ -113,7 +125,7 @@ const AddNearbyPlaceModal = () => {
<div className="flex flex-col px-6 pb-6">
<span className="text-14">{selectedMarker.roadAddress}</span>
<span className="text-12 text-gray-50">(지번: {selectedMarker.address})</span>
<div className="flex justify-between">
<div className="flex justify-between items-center">
<span className="text-green text-12">{selectedMarker.phone || '대표번호가 없습니다.'}</span>
<span className="bg-blue rounded-s text-white text-12 px-6 py-2">일정에 추가</span>
</div>
Expand All @@ -122,14 +134,29 @@ const AddNearbyPlaceModal = () => {
</div>
</CustomOverlayMap>
)}
<input
onKeyDown={(e) => {
if (e.key === 'Enter') {
handleSearchPlace(e);
}
}}
onBlur={(e) => handleSearchPlace(e)}
className="bg-blue absolute left-1/2 transform -translate-x-1/2 text-center rounded-10 text-white placeholder:text-white py-4 px-10"
placeholder="원하시는 검색어를 입력하세요."
/>

<CategoryList setSelectedQuery={setSelectedQuery} selectedQuery={selectedQuery} />
<CategoryButton setSelectedQuery={setSelectedQuery} selectedQuery={selectedQuery} />
<DistanceButton selectedDistance={selectedDistance} setSelectedDistance={setSelectedDistance} />
</Map>
<div className="flex flex-col gap-12">
<span className="text-center text-14 text-gray-60">위치하신 곳 근방의 장소를 추천해 드려요!</span>
<DistanceButton selectedDistance={selectedDistance} setSelectedDistance={setSelectedDistance} />
</div>
<ModalPlaceList data={decomposedData} className="h-150" onClick={handlePlaceClick} />
<ModalPlaceList
data={decomposedData}
className="h-190"
onClick={handlePlaceClick}
selectedPlace={selectedPlace.name}
/>
</div>
);
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,11 @@ interface ModalPlaceListData {
description?: string;
}[];
onClick?: (id: number) => void;
selectedPlace?: string;
className?: string;
}

const ModalPlaceList = ({ data, onClick, className }: ModalPlaceListData) => {
const ModalPlaceList = ({ data, onClick, selectedPlace, className }: ModalPlaceListData) => {
const customStyle = twMerge('flex flex-col gap-10 overflow-y-auto h-230', className);
return (
<div className="flex flex-col gap-30 ">
Expand All @@ -29,7 +30,7 @@ const ModalPlaceList = ({ data, onClick, className }: ModalPlaceListData) => {
className="rounded-m "
/>
</div>
<div>
<div className={`${selectedPlace === item.name && 'text-blue'}`}>
<h1 className="font-bold">{item.name}</h1>
{item.description && <p className="text-gray-60 text-12">{item.description}</p>}
</div>
Expand Down
107 changes: 77 additions & 30 deletions src/components/MyRouteContent/KakaoMap.tsx
Original file line number Diff line number Diff line change
@@ -1,38 +1,90 @@
// import Script from 'next/script';
import { Map, MapMarker, Polyline, CustomOverlayMap } from 'react-kakao-maps-sdk';
import { mockMyRoute } from './mockMyRoute';
import React from 'react';
// const KAKAO_SDK_URL = `//dapi.kakao.com/v2/maps/sdk.js?appkey=${process.env.NEXT_PUBLIC_CLIENT_ID_KAKAO}&autoload=false`;
import React, { useEffect } from 'react';
import instance from '@/src/api/axios';
import { useState } from 'react';

// const positions = [
// {
// title: '카카오',
// latlng: { lat: 33.450705, lng: 126.570677 },
// },
// {
// title: '생태연못',
// latlng: { lat: 33.450936, lng: 126.569477 },
// },
// {
// title: '텃밭',
// latlng: { lat: 33.450879, lng: 126.56994 },
// },
// {
// title: '근린공원',
// latlng: { lat: 33.451393, lng: 126.570738 },
// },
// ];
interface Guide {
x: number;
y: number;
}

interface Section {
guides: Guide[];
}

interface Route {
sections: Section[];
}

interface ResponseData {
routes: Route[];
}

const KakaoMap = () => {
const positions = mockMyRoute.data;
const [path, setPath] = useState<{ lat: number; lng: number }[]>([]);

useEffect(() => {
const data = {
origin: {
x: positions[0].latlng.lng,
y: positions[0].latlng.lat,
},
destination: {
x: positions[positions.length - 1].latlng.lng,
y: positions[positions.length - 1].latlng.lat,
},
waypoints: positions.slice(1, positions.length - 1).map((position) => ({
name: position.place,
x: position.latlng.lng,
y: position.latlng.lat,
})),
priority: 'RECOMMEND',
car_fuel: 'GASOLINE',
car_hipass: false,
alternatives: false,
road_details: false,
};
const getPath = async () => {
try {
const response = await instance.post<ResponseData>(
'https://apis-navi.kakaomobility.com/v1/waypoints/directions',
data,
{
headers: {
'Content-Type': 'application/json',
Authorization: `KakaoAK ${process.env.NEXT_PUBLIC_CLIENT_ID_KAKAO_REST}`,
},
},
);
if (response) {
const guides = response.data.routes[0].sections
.map((section) => {
return section.guides.map((place) => {
const { x, y } = place;
return { lng: x, lat: y };
});
})
.flat();
setPath(guides);
} else {
console.log('error in calculating path');
}
} catch (error) {
console.error(error);
}
};

getPath();
}, [positions]);

return (
<>
{/* <Script src={KAKAO_SDK_URL} /> */}
<Map center={positions[0].latlng} className="w-460 h-288 rounded-8 shadow-md z-0">
<Map center={positions[0].latlng} level={10} className="w-460 h-288 rounded-8 shadow-md z-0">
{positions.map((position, index) => (
<React.Fragment key={`${index}-${position.title}`}>
<MapMarker
// key={`${index}-${position.title}`}
position={position.latlng}
title={position.title}
image={{
Expand All @@ -52,12 +104,7 @@ const KakaoMap = () => {
</React.Fragment>
))}

<Polyline
path={positions.map((position) => position.latlng)}
strokeWeight={3}
strokeColor={'#34C231'}
strokeOpacity={1}
/>
<Polyline path={path} strokeWeight={3} strokeColor={'#34C231'} strokeOpacity={1} />
</Map>
</>
);
Expand Down
Loading

0 comments on commit d053bc9

Please sign in to comment.