diff --git a/src/app/components/(course)/CoursePlanNewLayout.tsx b/src/app/components/(course)/CoursePlanNewLayout.tsx new file mode 100644 index 0000000..4fc86a4 --- /dev/null +++ b/src/app/components/(course)/CoursePlanNewLayout.tsx @@ -0,0 +1,160 @@ +'use client' + +import { ChevronLeft, Plus } from 'lucide-react' +import { useRouter } from 'next/navigation' +import Spacer from '@components/(layout)/Spacer' +import { Place } from '@components/SearchPlace' +import SearchPlace from '@components/SearchPlace' +import SelectCategories from '@components/SelectCategories' +import { useEffect, useState } from 'react' +import { Input } from 'antd' +import SortableList from '@components/SortableList' +import getData from '@/app/schedules/getData' +import KakaoMap from '@components/KakaoMap' +import type { DatePickerProps } from 'antd' +import { DatePicker } from 'antd' +import RegionCascader from '@components/RegionCascader' + +const LAYOUT_TYPE = { + course: 'course' as const, + plan: 'plan' as const, +} + +type LayoutType = keyof typeof LAYOUT_TYPE + +export default function CoursePlanNewLayout({ type }: { type: LayoutType }) { + const router = useRouter() + const [places, setPlaces] = useState([]) + const [selectedRegion, setSelectedRegion] = useState('') + const [openSearchPlace, setOpenSearchPlace] = useState(false) + + const pageType = type === LAYOUT_TYPE.course ? '코스' : '플랜' + + useEffect(() => { + const data = getData() + setPlaces(data.courses[0].places) + }, []) + + const onChange: DatePickerProps['onChange'] = (date, dateString) => { + console.log('날짜 선택') + } + + const onChangePlaces = (place: Place) => { + setPlaces((prevPlaces) => [...prevPlaces, place]) + } + + const headerTitle = + type === LAYOUT_TYPE.course + ? '나만의 코스 작성하기' + : '좋아하는 장소로 채우는 나의 플랜' + + return ( +
+
router.back()} /> + +
+ +
+ +
+ +
+ +
+
+ {places.length > 0 && } + + +
+ {openSearchPlace && ( + + )} +
+ +
+ +
+ +
+ +
+ +
+ +
+ + +
+ ) +} + +function Header({ title, onBack }: { title: string; onBack: () => void }) { + return ( +
+ +

{title}

+
+
+ ) +} + +function Section({ + title, + children, + padding, +}: { + title: string + children: React.ReactNode + padding?: boolean +}) { + return ( +
+ + {title} + + {children} +
+ ) +} + +function Divider() { + return ( + <> + + + + + ) +} diff --git a/src/app/components/RegionCascader.tsx b/src/app/components/RegionCascader.tsx index 465e2b4..26f298f 100644 --- a/src/app/components/RegionCascader.tsx +++ b/src/app/components/RegionCascader.tsx @@ -33,7 +33,7 @@ export default function RegionCascader({ size='large' showSearch={{ filter }} style={{ - width: '300px', + width: '100%', }} expandTrigger='hover' /> diff --git a/src/app/components/SearchPlace.tsx b/src/app/components/SearchPlace.tsx index 414abe0..f94d776 100644 --- a/src/app/components/SearchPlace.tsx +++ b/src/app/components/SearchPlace.tsx @@ -1,5 +1,7 @@ import { useEffect, useState } from 'react' import { Input } from 'antd' +import { ChevronLeft } from 'lucide-react' +import Spacer from '@components/(layout)/Spacer' export interface Place { id: string @@ -21,18 +23,29 @@ interface Meta { } interface SearchPlaceProps { - onOpenDrawer?: (open: boolean) => void - onSelectPlace?: (place: Place) => void + setOpenSearchPlace: (open: boolean) => void + onChangePlaces: (place: Place) => void } export default function SearchPlace({ - onOpenDrawer, - onSelectPlace, + setOpenSearchPlace, + onChangePlaces, }: SearchPlaceProps) { const [results, setResults] = useState([]) const [meta, setMeta] = useState() // TODO: 페이지네이션 추가 필요 const [inputValue, setInputValue] = useState('') + useEffect(() => { + getResult(inputValue) + }, [inputValue]) + + useEffect(() => { + document.body.style.overflow = 'hidden' + return () => { + document.body.style.overflow = 'unset' + } + }, []) + const getResult = async (value: string) => { if (!value) return @@ -50,46 +63,56 @@ export default function SearchPlace({ } const selectPlace = (place: Place) => { - if (onOpenDrawer && onSelectPlace) { - onOpenDrawer(false) - onSelectPlace(place) + if (onChangePlaces) { + setOpenSearchPlace(false) + onChangePlaces(place) setInputValue('') } } - useEffect(() => { - getResult(inputValue) - }, [inputValue]) - return ( -
-
- 장소명 - setInputValue(e.target.value)} - /> -
-
-
- {results.map((result) => { - return ( -
{ - selectPlace(result) - }} - > - {result.place_name} - - {result.address_name} - -
- ) - })} +
+
+ +

장소 추가하기

+
+
+ +
+
+ 장소명 + setInputValue(e.target.value)} + /> +
+
+
+
+ {results.map((result) => { + return ( +
{ + selectPlace(result) + }} + > + + {result.place_name} + + + {result.address_name} + +
+ ) + })} +
+
) diff --git a/src/app/components/SelectCategories.tsx b/src/app/components/SelectCategories.tsx new file mode 100644 index 0000000..231b64f --- /dev/null +++ b/src/app/components/SelectCategories.tsx @@ -0,0 +1,32 @@ +import { useState } from 'react' +import { categories } from '@/types/Categories' + +export default function SelectCategories() { + const [clickedCategory, setClickedCategory] = useState([]) + + const handleCategoryClick = (id: number) => { + setClickedCategory((prev) => + prev.includes(id) + ? prev.filter((categoryId) => categoryId !== id) + : [...prev, id] + ) + } + + return ( +
+ {categories.map((category) => ( + + ))} +
+ ) +} diff --git a/src/app/components/SortableItem.tsx b/src/app/components/SortableItem.tsx index 18c92f1..2ea8fa0 100644 --- a/src/app/components/SortableItem.tsx +++ b/src/app/components/SortableItem.tsx @@ -1,8 +1,7 @@ -import { Button } from 'antd' import { useSortable } from '@dnd-kit/sortable' import { CSS } from '@dnd-kit/utilities' import { Place } from '@/app/components/SearchPlace' -import { AlignJustify } from 'lucide-react' +import { AlignJustify, X, Image } from 'lucide-react' interface SortableItemProps { id: string @@ -33,29 +32,29 @@ export default function SortableItem({ transition, touchAction: 'none', }} - className='flex items-center justify-between gap-[10px] h-[40px] px-[10px] py-[10px] border text-[15px] rounded-[5px] bg-white' + className='flex items-center justify-between gap-[10px] h-[40px] px-[10px] py-[10px] text-[15px] rounded-full bg-bright-gray' >
- +
{place.place_name}
{onEdit && ( - + + )} - + +
) diff --git a/src/app/components/SortableList.tsx b/src/app/components/SortableList.tsx new file mode 100644 index 0000000..74092c9 --- /dev/null +++ b/src/app/components/SortableList.tsx @@ -0,0 +1,60 @@ +import { DndContext, closestCenter, DragEndEvent } from '@dnd-kit/core' +import { Place } from '@components/SearchPlace' +import SortableItem from '@components/SortableItem' +import { + SortableContext, + arrayMove, + verticalListSortingStrategy, +} from '@dnd-kit/sortable' + +export default function SortableList({ + places, + setPlaces, +}: { + places: Place[] + setPlaces: React.Dispatch> +}) { + const onChangePlaces = (place: Place) => { + setPlaces((prevPlaces: Place[]): Place[] => [...prevPlaces, place]) + } + + const handleDragEnd = (event: DragEndEvent) => { + const { active, over } = event + + if (active.id !== over?.id) { + setPlaces((items: Place[]): Place[] => { + const oldIndex = items.findIndex((item) => item.id === active.id) + const newIndex = items.findIndex((item) => item.id === over?.id) + return arrayMove(items, oldIndex, newIndex) as Place[] + }) + } + } + + const handleDelete = (id: string) => { + setPlaces((prev: Place[]): Place[] => + prev.filter((place) => place.id !== id) + ) + } + + const handleEdit = (id: string) => { + alert(`${id}번 장소를 수정합니다.`) + } + + return ( +
+ + + {places.map((place) => ( + + ))} + + +
+ ) +} diff --git a/src/app/courses/new/page.tsx b/src/app/courses/new/page.tsx index 1484301..f8d12ae 100644 --- a/src/app/courses/new/page.tsx +++ b/src/app/courses/new/page.tsx @@ -1,174 +1,5 @@ -'use client' - -import { useEffect, useState } from 'react' -import { Input, Drawer } from 'antd' -import { DndContext, closestCenter, DragEndEvent } from '@dnd-kit/core' -import { - SortableContext, - arrayMove, - verticalListSortingStrategy, -} from '@dnd-kit/sortable' -import SortableItem from '@/app/components/SortableItem' -import SearchPlace, { Place } from '@/app/components/SearchPlace' -import { categories } from '@/types/Categories' -import getData from '@/app/schedules/getData' -import { ChevronLeft } from 'lucide-react' -import { useRouter } from 'next/navigation' -import Spacer from '@/app/components/(layout)/Spacer' +import CoursePlanNewLayout from '@/app/components/(course)/CoursePlanNewLayout' export default function Page() { - const router = useRouter() - const [clickedCategory, setClickedCategory] = useState([]) - const [places, setPlaces] = useState([]) - const [open, setOpen] = useState(false) - - useEffect(() => { - const data = getData() - setPlaces(data.courses[0].places) - }, []) - - const showDrawer = () => { - setOpen(true) - } - - const onClose = () => { - setOpen(false) - } - - const onChangePlaces = (place: Place) => { - setPlaces((prevPlaces) => [...prevPlaces, place]) - } - - const handleCategoryClick = (id: number) => { - setClickedCategory((prev) => - prev.includes(id) - ? prev.filter((categoryId) => categoryId !== id) - : [...prev, id] - ) - } - - const handleDragEnd = (event: DragEndEvent) => { - const { active, over } = event - - if (active.id !== over?.id) { - setPlaces((items) => { - const oldIndex = items.findIndex((item) => item.id === active.id) - const newIndex = items.findIndex((item) => item.id === over?.id) - return arrayMove(items, oldIndex, newIndex) - }) - } - } - - const handleDelete = (id: string) => { - setPlaces((prev) => prev.filter((place) => place.id !== id)) - } - - const handleEdit = (id: string) => { - alert(`${id}번 장소를 수정합니다.`) - } - - return ( - <> -
- -

나만의 코스 작성하기

-
-
- -
-
-
- - 코스 제목을 만들어주세요 - - - - -
- -
- - 코스 지역을 검색하세요. - - -
-
- 코스 카테고리 -
- {categories.map((category) => ( - - ))} -
-
-
-
- 코스 내 장소 정보 - - - {places.map((place) => ( - - ))} - - - - - - -
-
- -
- - ) + return } diff --git a/src/app/schedules/[id]/update/page.tsx b/src/app/schedules/[id]/update/page.tsx index 801c9be..69dc4b8 100644 --- a/src/app/schedules/[id]/update/page.tsx +++ b/src/app/schedules/[id]/update/page.tsx @@ -127,7 +127,10 @@ export default function Page() { open={open} maskClosable > - +
) diff --git a/src/app/schedules/new/page.tsx b/src/app/schedules/new/page.tsx index 8b54701..ad2d87c 100644 --- a/src/app/schedules/new/page.tsx +++ b/src/app/schedules/new/page.tsx @@ -160,7 +160,10 @@ export default function Page() { open={open} maskClosable > - +