Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[기능] 나의 코스 플랜 페이지 구현 #12

Merged
merged 1 commit into from
Dec 22, 2024
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
92 changes: 92 additions & 0 deletions src/app/components/KakaoMap.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
'use client'

import { useEffect, useRef } from 'react'

interface Place {
name: string
address: string
latlang: number[]
}

interface KakaoMapProps {
places: Place[]
id: number
}

declare global {
interface Window {
kakao: any
}
}

export default function KakaoMap({ places, id }: KakaoMapProps) {
const mapContainerRef = useRef<HTMLDivElement>(null)

useEffect(() => {
const scriptId = `kakao-map-script-${id}`

if (!document.getElementById(scriptId)) {
const script = document.createElement('script')
script.id = scriptId
script.src = `https://dapi.kakao.com/v2/maps/sdk.js?appkey=${process.env.NEXT_PUBLIC_KAKAO_MAP_API_KEY}&autoload=false&libraries=services`
script.async = true
script.onload = initializeMap
document.head.appendChild(script)
} else {
if (window.kakao && window.kakao.maps) {
initializeMap()
}
}

function initializeMap() {
if (!mapContainerRef.current) return

window.kakao.maps.load(() => {
if (!mapContainerRef.current || !window.kakao || !window.kakao.maps)
return

const map = new window.kakao.maps.Map(mapContainerRef.current, {
center: new window.kakao.maps.LatLng(
places[0]?.latlang[0] || 37.5665,
places[0]?.latlang[1] || 126.978
),
level: 6,
})

places.forEach((place) => {
const markerPosition = new window.kakao.maps.LatLng(
place.latlang[0],
place.latlang[1]
)
const marker = new window.kakao.maps.Marker({
position: markerPosition,
})
marker.setMap(map)

// 마커 클릭 이벤트
const infoWindow = new window.kakao.maps.InfoWindow({
content: `<div class="text-sm p-2">${place.name}</div>`,
})
window.kakao.maps.event.addListener(marker, 'click', () => {
infoWindow.open(map, marker)
})
})
})
}

return () => {
const script = document.getElementById(scriptId)
if (script) {
document.head.removeChild(script)
}
}
}, [places])

return (
<div
id={`map-${id}`}
className='w-full mt-[10px] h-[180px] z-0'
ref={mapContainerRef}
/>
)
}
3 changes: 3 additions & 0 deletions src/app/schedules/[id]/update/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export default function Page() {
return null
}
75 changes: 75 additions & 0 deletions src/app/schedules/components/CourseCard.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
'use client'

import { useRouter } from 'next/navigation'
import { Course } from '../getData'
import KakaoMap from '@/app/components/KakaoMap'
import { CopyOutlined } from '@ant-design/icons'
import { message } from 'antd'

export default function CourseCard({ courseData }: { courseData: Course }) {
const { location, places, planned_for, id } = courseData
const router = useRouter()

const [messageApi, contextHolder] = message.useMessage()
const toast = (address: string) => {
// 클립보드에 복사
navigator.clipboard.writeText(address).then(() => {
// 토스트 메시지
messageApi.open({
type: 'success',
content: '주소가 클립보드에 복사되었습니다.',
duration: 1,
})
})
}

return (
<div className='w-full bg-white rounded-[8px] py-[16px] mt-[10px] items-center border-b'>
<div className='flex flex-col justify-start ml-[8px]'>
<span className='text-[12px] text-[#999999]'>{planned_for}</span>
<div className='flex flex-row justify-between items-center w-full'>
<span className='font-bold text-[16px]'>{location}</span>
<span
className='underline text-[12px] text-gray mr-[8px] cursor-pointer'
onClick={() => {
router.push('/courses/new')
}}
>
코스로 공유하기
</span>
</div>
</div>

<KakaoMap places={places} id={id} />

<section className='flex flex-col mt-[20px] text-[15px]'>
<div className='flex flex-row justify-between items-center'>
<span>| 장소 정보</span>
<button
className='w-[50px] h-[30px] text-[12px] border rounded-[10px] cursor-pointer'
onClick={() => router.push('/schedules/1/update')}
>
수정
</button>
</div>
{places.map((place, index) => (
<div
key={index}
className='flex flex-row mt-[5px] gap-[5px] bg-gray-100 px-[15px] py-[8px] rounded-[10px] justify-between items-center'
>
<div className='flex flex-col items-start'>
<span className='text-[13px] font-bold'>{place.name}</span>
<span className='text-[10px] text-gray-600'>{place.address}</span>
</div>
<CopyOutlined
className='w-[12px] h-[12px] cursor-pointer'
onClick={() => toast(place.address)}
/>
</div>
))}
</section>

{contextHolder}
</div>
)
}
173 changes: 173 additions & 0 deletions src/app/schedules/getData.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,173 @@
interface Place {
name: string
address: string
latlang: number[]
}

export interface Course {
id: number
location: string
places: Place[]
created_at: string
updated_at: string
planned_for: string
}

interface Data {
user_name: string
courses: Course[]
}

export default function getData() {
const data: Data = {
user_name: '홍인데유',
courses: [
{
id: 1,
location: '강남구',
places: [
{
name: '알베르',
address: '서울 강남구 강남대로102길 34',
latlang: [37.50304, 127.028098],
},
{
name: '땀땀 본점',
address: '서울 강남구 강남대로98길 12-5',
latlang: [37.500398, 127.027992],
},
{
name: '마녀주방',
address: '서울 강남구 강남대로94길 9',
latlang: [37.499486, 127.028079],
},
{
name: 'CGV 강남',
address: '서울 강남구 강남대로 438',
latlang: [37.501678, 127.026387],
},
],
created_at: '2024.12.16',
updated_at: '',
planned_for: '2024.12.16',
},
{
id: 2,
location: '강남구',
places: [
{
name: '알베르',
address: '서울 강남구 강남대로102길 34',
latlang: [37.50304, 127.028098],
},
{
name: '땀땀 본점',
address: '서울 강남구 강남대로98길 12-5',
latlang: [37.500398, 127.027992],
},
{
name: '마녀주방',
address: '서울 강남구 강남대로94길 9',
latlang: [37.499486, 127.028079],
},
{
name: 'CGV 강남',
address: '서울 강남구 강남대로 438',
latlang: [37.501678, 127.026387],
},
],
created_at: '2024.12.16',
updated_at: '',
planned_for: '2024.12.16',
},
{
id: 3,
location: '강남구',
places: [
{
name: '알베르',
address: '서울 강남구 강남대로102길 34',
latlang: [37.50304, 127.028098],
},
{
name: '땀땀 본점',
address: '서울 강남구 강남대로98길 12-5',
latlang: [37.500398, 127.027992],
},
{
name: '마녀주방',
address: '서울 강남구 강남대로94길 9',
latlang: [37.499486, 127.028079],
},
{
name: 'CGV 강남',
address: '서울 강남구 강남대로 438',
latlang: [37.501678, 127.026387],
},
],
created_at: '2024.12.16',
updated_at: '',
planned_for: '2024.12.16',
},
{
id: 4,
location: '강남구',
places: [
{
name: '알베르',
address: '서울 강남구 강남대로102길 34',
latlang: [37.50304, 127.028098],
},
{
name: '땀땀 본점',
address: '서울 강남구 강남대로98길 12-5',
latlang: [37.500398, 127.027992],
},
{
name: '마녀주방',
address: '서울 강남구 강남대로94길 9',
latlang: [37.499486, 127.028079],
},
{
name: 'CGV 강남',
address: '서울 강남구 강남대로 438',
latlang: [37.501678, 127.026387],
},
],
created_at: '2024.12.16',
updated_at: '',
planned_for: '2024.12.16',
},
{
id: 5,
location: '강남구',
places: [
{
name: '알베르',
address: '서울 강남구 강남대로102길 34',
latlang: [37.50304, 127.028098],
},
{
name: '땀땀 본점',
address: '서울 강남구 강남대로98길 12-5',
latlang: [37.500398, 127.027992],
},
{
name: '마녀주방',
address: '서울 강남구 강남대로94길 9',
latlang: [37.499486, 127.028079],
},
{
name: 'CGV 강남',
address: '서울 강남구 강남대로 438',
latlang: [37.501678, 127.026387],
},
],
created_at: '2024.12.16',
updated_at: '',
planned_for: '2024.12.16',
},
],
}
return data
}
3 changes: 3 additions & 0 deletions src/app/schedules/new/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export default function Page() {
return null
}
34 changes: 33 additions & 1 deletion src/app/schedules/page.tsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,35 @@
'use client'

import { useRouter } from 'next/navigation'
import getData from './getData'
import CourseCard from './components/CourseCard'
import { PlusOutlined } from '@ant-design/icons'

export default function Page() {
return null
const router = useRouter()
const courseData = getData()
const userName = courseData.user_name
const planList = courseData.courses

return (
<div className='w-full pt-[20px] pb-[32px] px-[16px] flex flex-col'>
<span className='border-b text-[18px] inline-flex items-center'>
<p className='font-bold'>{userName}</p>
<p>님의 코스 플랜</p>
</span>

{planList.map((course, index) => (
<CourseCard key={index} courseData={course} />
))}

<div className='fixed bottom-[60px] right-[20px] flex items-center justify-center cursor-pointer z-[50]'>
<button
className='w-[50px] h-[50px] bg-white rounded-full flex items-center justify-center shadow-lg border border-blue-800 border-opacity-20'
onClick={() => router.push('/schedules/new')}
>
<PlusOutlined className='text-[20px] text-blue-800' />
</button>
</div>
</div>
)
}
Loading