diff --git a/package-lock.json b/package-lock.json index 93806b5e..7d48816e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -8,6 +8,10 @@ "name": "my_links_user_team", "version": "0.1.0", "dependencies": { + "@fullcalendar/core": "^6.1.15", + "@fullcalendar/daygrid": "^6.1.15", + "@fullcalendar/interaction": "^6.1.15", + "@fullcalendar/react": "^6.1.15", "moment": "2.30.1", "next": "14.2.14", "react": "^18", @@ -4483,6 +4487,15 @@ "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==", "dev": true }, + "node_modules/preact": { + "version": "10.12.1", + "resolved": "https://registry.npmjs.org/preact/-/preact-10.12.1.tgz", + "integrity": "sha512-l8386ixSsBdbreOAkqtrwqHwdvR35ID8c3rKPa8lCWuO86dBi32QWHV4vfsZK1utLLFMvw+Z5Ad4XLkZzchscg==", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/preact" + } + }, "node_modules/prelude-ls": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", diff --git a/package.json b/package.json index f8dae6ae..7b2e7e38 100644 --- a/package.json +++ b/package.json @@ -14,6 +14,10 @@ "fix": "eslint . --ext .js,.jsx,.ts,.tsx --fix && prettier --write ." }, "dependencies": { + "@fullcalendar/core": "^6.1.15", + "@fullcalendar/daygrid": "^6.1.15", + "@fullcalendar/interaction": "^6.1.15", + "@fullcalendar/react": "^6.1.15", "moment": "2.30.1", "next": "14.2.14", "react": "^18", diff --git a/src/app/admin/(block)/calendar/add/page.tsx b/src/app/admin/(block)/calendar/add/page.tsx deleted file mode 100644 index 382c0aa3..00000000 --- a/src/app/admin/(block)/calendar/add/page.tsx +++ /dev/null @@ -1,36 +0,0 @@ -"use client"; - -import React from "react"; -import { useRouter } from "next/navigation"; -import Image from "next/image"; -import AddScheduleForm from "../components/add-schedule-form"; - -export default function AddSchedulePage() { - const router = useRouter(); - - const handleClose = () => { - router.push("/calendar"); - }; - - return ( -
-
- -
-

일정 추가하기

-

- 입력하는 진행기간에 따라 -
- 전체 일정이 최근 날짜 순서로 자동 정렬됩니다. -

- -
- ); -} diff --git a/src/app/admin/(block)/calendar/components/calendar-header.tsx b/src/app/admin/(block)/calendar/components/calendar-header.tsx index 43ed3b8a..afcddde8 100644 --- a/src/app/admin/(block)/calendar/components/calendar-header.tsx +++ b/src/app/admin/(block)/calendar/components/calendar-header.tsx @@ -8,7 +8,7 @@ export default function CalendarHeader() { const router = useRouter(); const handleAddScheduleClick = () => { - router.push("calendar/add"); + router.push("calendar/manage"); }; return ( diff --git a/src/app/admin/(block)/calendar/components/calendar-view.tsx b/src/app/admin/(block)/calendar/components/calendar-view.tsx index 7a6a9d30..f3d8c131 100644 --- a/src/app/admin/(block)/calendar/components/calendar-view.tsx +++ b/src/app/admin/(block)/calendar/components/calendar-view.tsx @@ -1,5 +1,11 @@ +"use client"; + import React, { useState } from "react"; import Image from "next/image"; +import FullCalendar from "@fullcalendar/react"; +import dayGridPlugin from "@fullcalendar/daygrid"; +import interactionPlugin from "@fullcalendar/interaction"; +import { EventContentArg } from "@fullcalendar/core/index.js"; interface Schedule { id: string; @@ -13,40 +19,8 @@ interface CalendarViewProps { } const CalendarView: React.FC = ({ schedules }) => { - const [currentMonth] = useState(new Date(2023, 0, 1)); - - const getDaysInMonth = (date: Date): Date[] => { - const year = date.getFullYear(); - const month = date.getMonth(); - const firstDay = new Date(year, month, 1); - const lastDay = new Date(year, month + 1, 0); - - const daysInMonth = []; - const totalDays = 35; - - for (let i = 1 - firstDay.getDay(); daysInMonth.length < totalDays; i++) { - daysInMonth.push(new Date(year, month, i)); - } - - return daysInMonth; - }; - - const toDate = (date: string | Date): Date => { - return new Date(new Date(date).setHours(0, 0, 0, 0)); - }; - - const calculateOffsets = (start: Date, end: Date, weekStart: Date) => { - const startOffset = Math.max( - 0, - (start.getTime() - weekStart.getTime()) / (1000 * 3600 * 24), - ); - const endOffset = Math.min( - 6, - (end.getTime() - weekStart.getTime()) / (1000 * 3600 * 24), - ); - const duration = endOffset - startOffset + 1; - return { startOffset, duration }; - }; + const [currentMonth, setCurrentMonth] = useState(new Date(2023, 0, 1)); + const calendarRef = React.useRef(null); const getBackgroundColor = (start: Date, end: Date) => { const startDay = start.getDate(); @@ -61,97 +35,99 @@ const CalendarView: React.FC = ({ schedules }) => { } }; - const renderSchedules = (week: Date[]) => { - const weekStart = week[0]; - const weekEnd = week[6]; - return schedules - .filter((schedule) => { - const start = toDate(schedule.startDate); - const end = toDate(schedule.endDate); - return start <= weekEnd && end >= weekStart; - }) - .map((schedule, index) => { - const start = toDate(schedule.startDate); - const end = toDate(schedule.endDate); - const { startOffset, duration } = calculateOffsets( - start, - end, - weekStart, - ); - - return ( -
- {schedule.title} -
- ); - }); + const events = schedules.map((schedule) => ({ + id: schedule.id, + title: schedule.title, + start: schedule.startDate, + end: schedule.endDate, + backgroundColor: getBackgroundColor( + new Date(schedule.startDate), + new Date(schedule.endDate), + ), + borderColor: "transparent", + classNames: ["calendar-event"], + })); + + const handlePrevMonth = () => { + const calendarApi = calendarRef.current?.getApi(); + calendarApi?.prev(); + const newDate = new Date(currentMonth); + newDate.setMonth(newDate.getMonth() - 1); + setCurrentMonth(newDate); }; - const days = getDaysInMonth(currentMonth); - const weeks = Array.from({ length: 5 }, (_, i) => - days.slice(i * 7, (i + 1) * 7), - ); + const handleNextMonth = () => { + const calendarApi = calendarRef.current?.getApi(); + calendarApi?.next(); + const newDate = new Date(currentMonth); + newDate.setMonth(newDate.getMonth() + 1); + setCurrentMonth(newDate); + }; - return ( -
+ const CustomToolbar = () => { + return (
- Previous Month +

- {`${currentMonth.getFullYear()}.${String(currentMonth.getMonth() + 1).padStart(2, "0")}`} + {`${currentMonth.getFullYear()}.${String( + currentMonth.getMonth() + 1, + ).padStart(2, "0")}`}

- Next Month +
-
- {["일", "월", "화", "수", "목", "금", "토"].map((day) => ( -
{day}
- ))} + ); + }; + + const renderEventContent = (eventInfo: EventContentArg) => { + return ( +
+ {eventInfo.event.title}
-
-
- {weeks.map((week, weekIndex) => ( - - {week.map((day) => ( -
- {day.getDate()} -
- ))} -
- {renderSchedules(week)} -
-
- ))} -
+ ); + }; + + return ( +
+ +
+ {" "} + { + const days = ["일", "월", "화", "수", "목", "금", "토"]; + return days[date.getDay()]; + }} + />
); diff --git a/src/app/admin/(block)/calendar/components/add-schedule-form.tsx b/src/app/admin/(block)/calendar/components/schedule-form.tsx similarity index 67% rename from src/app/admin/(block)/calendar/components/add-schedule-form.tsx rename to src/app/admin/(block)/calendar/components/schedule-form.tsx index 439a770d..cdb970fc 100644 --- a/src/app/admin/(block)/calendar/components/add-schedule-form.tsx +++ b/src/app/admin/(block)/calendar/components/schedule-form.tsx @@ -3,6 +3,7 @@ import { useEffect, useState, useCallback } from "react"; import Image from "next/image"; import { useRouter } from "next/navigation"; +import { getSequence } from "lib/get-sequence"; interface Schedule { id?: number; @@ -20,7 +21,17 @@ interface CalendarBlock { schedule: Schedule[]; } -export default function AddScheduleForm() { +interface ScheduleFormProps { + mode: "add" | "edit"; + initialData?: Schedule | null; + calendarBlockId?: number | null; +} + +export default function ScheduleForm({ + mode, + initialData, + calendarBlockId, +}: ScheduleFormProps) { const [startDate, setStartDate] = useState(""); const [startTime, setStartTime] = useState(""); const [endDate, setEndDate] = useState(""); @@ -29,20 +40,29 @@ export default function AddScheduleForm() { const [url, setUrl] = useState(""); const [showStartTime, setShowStartTime] = useState(false); const [showEndTime, setShowEndTime] = useState(false); - const [isLoading, setIsLoading] = useState(false); - const [error, setError] = useState(null); const [calendarBlock, setCalendarBlock] = useState( null, ); const router = useRouter(); + useEffect(() => { + if (mode === "edit" && initialData) { + const startDateTime = new Date(initialData.dateStart); + const endDateTime = new Date(initialData.dateEnd); + + setStartDate(startDateTime.toISOString().split("T")[0]); + setStartTime(startDateTime.toTimeString().slice(0, 5)); + setEndDate(endDateTime.toISOString().split("T")[0]); + setEndTime(endDateTime.toTimeString().slice(0, 5)); + setTitle(initialData.title); + setUrl(initialData.url || ""); + } + }, [mode, initialData]); + const fetchCalendarBlock = useCallback(async () => { - try { - if (calendarBlock) { - console.log("캘린더 블록이 이미 존재합니다."); - return; - } + if (mode === "edit") return; + try { const token = sessionStorage.getItem("token"); if (!token) { throw new Error("로그인이 필요합니다."); @@ -62,36 +82,28 @@ export default function AddScheduleForm() { } const data = await response.json(); - console.log("Fetched data:", data); - - const existingCalendarBlock = data.data.find( - (item: CalendarBlock) => item.type === 7, - ); - - if (existingCalendarBlock) { - setCalendarBlock(existingCalendarBlock); - console.log("Existing calendar (block) found:", existingCalendarBlock); - } else { - console.log("캘린더 블록이 없습니다."); + if (data.code === 200 && Array.isArray(data.data)) { + const existingCalendarBlock = data.data.find( + (item: CalendarBlock) => item.type === 7, + ); + if (existingCalendarBlock) { + setCalendarBlock(existingCalendarBlock); + } } } catch (error) { - console.error("Error fetching calendar (block):", error); - setError("캘린더 블록 정보를 가져오는데 실패했습니다."); + console.error("Error fetching calendar block:", error); } - }, [calendarBlock]); + }, [mode]); useEffect(() => { - if (!calendarBlock) { - fetchCalendarBlock(); - } - }, [calendarBlock, fetchCalendarBlock]); + fetchCalendarBlock(); + }, [fetchCalendarBlock]); const handleSubmit = async (e: React.FormEvent) => { e.preventDefault(); - setIsLoading(true); - setError(null); - const schedule: Schedule = { + const newSchedule: Schedule = { + ...(mode === "edit" && initialData?.id ? { id: initialData.id } : {}), title, url: url || undefined, dateStart: `${startDate}T${startTime}:00.000Z`, @@ -100,83 +112,97 @@ export default function AddScheduleForm() { try { const token = sessionStorage.getItem("token"); - if (!token) { - throw new Error("로그인이 필요합니다."); - } + if (!token) throw new Error("인증 토큰이 없습니다. 다시 로그인해주세요."); + + const listResponse = await fetch( + `${process.env.NEXT_PUBLIC_API_URL}/api/link/list`, + { + headers: { + Authorization: `Bearer ${token}`, + }, + }, + ); - console.log("Sending schedule data:", schedule); + if (!listResponse.ok) { + throw new Error("기존 일정을 불러오는데 실패했습니다."); + } - if (calendarBlock) { - console.log("Existing calendar (block) found:", calendarBlock); - const updatedSchedule = [...calendarBlock.schedule, schedule]; + const listData = await listResponse.json(); + const existingCalendarBlock = listData.data.find( + (item: CalendarBlock) => item.type === 7, + ); - const updateResponse = await fetch( - `${process.env.NEXT_PUBLIC_API_URL}/api/link/update`, - { - method: "POST", - headers: { - "Content-Type": "application/json", - Authorization: `Bearer ${token}`, - }, - body: JSON.stringify({ - id: calendarBlock.id, - type: 7, - sequence: calendarBlock.sequence, - style: calendarBlock.style, - schedule: updatedSchedule, - }), - }, - ); + let requestBody; + const blockId = mode === "edit" ? calendarBlockId : calendarBlock?.id; - if (!updateResponse.ok) { - const errorData = await updateResponse.json(); - throw new Error(errorData.message || "일정 추가에 실패했습니다."); + if (blockId) { + let updatedSchedules; + if (mode === "edit" && existingCalendarBlock) { + updatedSchedules = existingCalendarBlock.schedule.map( + (s: Schedule) => (s.id === initialData?.id ? newSchedule : s), + ); + } else { + updatedSchedules = [ + ...(existingCalendarBlock?.schedule || []), + newSchedule, + ]; } - alert("일정이 성공적으로 추가되었습니다."); + requestBody = { + id: blockId, + type: 7, + sequence: existingCalendarBlock?.sequence || 1, + style: existingCalendarBlock?.style || 1, + schedule: updatedSchedules, + }; } else { - console.log("No calendar (block) found, creating a new one."); - - const newBlock: Omit = { + // 새로운 캘린더 블록 생성 및 일정 추가 + const nextSequence = await getSequence(token); + requestBody = { type: 7, - sequence: 8, + sequence: nextSequence + 1, style: 1, - schedule: [schedule], + schedule: [newSchedule], }; + } - const createResponse = await fetch( - `${process.env.NEXT_PUBLIC_API_URL}/api/link/add`, - { - method: "POST", - headers: { - "Content-Type": "application/json", - Authorization: `Bearer ${token}`, - }, - body: JSON.stringify(newBlock), + const response = await fetch( + `${process.env.NEXT_PUBLIC_API_URL}/api/link/${blockId ? "update" : "add"}`, + { + method: "POST", + headers: { + "Content-Type": "application/json", + Authorization: `Bearer ${token}`, }, - ); - - if (!createResponse.ok) { - const errorData = await createResponse.json(); - throw new Error( - errorData.message || "캘린더 블록 생성에 실패했습니다.", - ); - } + body: JSON.stringify(requestBody), + }, + ); - alert("캘린더 블록과 일정이 성공적으로 추가되었습니다."); + if (!response.ok) { + throw new Error( + mode === "edit" + ? "일정 수정에 실패했습니다." + : "일정 추가에 실패했습니다.", + ); } - await fetchCalendarBlock(); - router.push("/calendar"); + const data = await response.json(); + if (data.code === 200) { + alert( + mode === "edit" + ? "일정이 성공적으로 수정되었습니다." + : "일정이 성공적으로 추가되었습니다.", + ); + router.push("/admin/calendar"); + } else { + throw new Error("서버 응답 오류"); + } } catch (error) { - console.error("Error adding schedule:", error); - setError( - error instanceof Error - ? error.message - : "알 수 없는 오류가 발생했습니다.", + console.error( + mode === "edit" ? "일정 수정 중 오류 발생" : "일정 추가 중 오류 발생", + error, ); - } finally { - setIsLoading(false); + alert(error instanceof Error ? error.message : "오류가 발생했습니다."); } }; @@ -349,13 +375,10 @@ export default function AddScheduleForm() { type="submit" style={{ backgroundColor: "#FFF1ED", color: "#FFB092" }} className="button color w-full rounded-md px-4 py-2 text-white hover:bg-blue-600" - disabled={isLoading} > - {isLoading ? "처리 중..." : "추가 완료"} + {mode === "edit" ? "수정 완료" : "추가 완료"}
- - {error &&

{error}

} ); } diff --git a/src/app/admin/(block)/calendar/components/schedule-list.tsx b/src/app/admin/(block)/calendar/components/schedule-list.tsx index 1c152acf..5f45d96c 100644 --- a/src/app/admin/(block)/calendar/components/schedule-list.tsx +++ b/src/app/admin/(block)/calendar/components/schedule-list.tsx @@ -1,5 +1,6 @@ import Image from "next/image"; -import { useState } from "react"; +import { useRouter } from "next/navigation"; +import { useState, useEffect } from "react"; interface Schedule { id: number; @@ -9,43 +10,13 @@ interface Schedule { dateEnd: string; } -const scheduleData: Schedule[] = [ - { - id: 1, - title: "일정 1", - url: "https://naver.com/", - dateStart: "2024-10-01T12:26:44.000Z", - dateEnd: "2024-10-02T12:26:44.000Z", - }, - { - id: 2, - title: "일정 2", - url: "https://google.com/", - dateStart: "2024-10-01T09:00:00.000Z", - dateEnd: "2024-11-30T18:00:00.000Z", - }, - { - id: 3, - title: "일정 3", - url: "https://github.com/", - dateStart: "2024-12-01T08:00:00.000Z", - dateEnd: "2024-12-31T17:00:00.000Z", - }, - { - id: 4, - title: "일정 4", - url: "https://microsoft.com/", - dateStart: "2025-01-01T10:00:00.000Z", - dateEnd: "2025-01-31T16:00:00.000Z", - }, - { - id: 5, - title: "일정 5", - url: "https://apple.com/", - dateStart: "2025-02-01T11:00:00.000Z", - dateEnd: "2025-02-28T15:00:00.000Z", - }, -]; +interface CalendarBlock { + id: number; + type: number; + sequence: number; + style: number; + schedule: Schedule[]; +} const formatDate = (dateString: string) => { const date = new Date(dateString); @@ -80,9 +51,41 @@ const getScheduleStatus = (schedule: Schedule) => { } }; -function ScheduleItem({ schedule }: { schedule: Schedule }) { +function EmptyState({ message }: { message: React.ReactNode }) { + return ( +
+ 빈 캘린더 +

{message}

+
+ ); +} + +function ScheduleItem({ + schedule, + onDelete, +}: { + schedule: Schedule; + onDelete: (id: number) => void; +}) { + const router = useRouter(); const status = getScheduleStatus(schedule); + const handleEdit = () => { + router.push(`/admin/calendar/manage?mode=edit&id=${schedule.id}`); + }; + + const handleClick = (url: string) => { + if (url) { + window.open(url, "_blank"); + } + }; + return (
@@ -91,17 +94,26 @@ function ScheduleItem({ schedule }: { schedule: Schedule }) { > {status.text}
-
+
schedule.url && handleClick(schedule.url)} + >
{formatDate(schedule.dateStart)} ~ {formatDate(schedule.dateEnd)}
-
{schedule.title}
+
{schedule.title}
- -
@@ -114,11 +126,132 @@ function ScheduleItem({ schedule }: { schedule: Schedule }) { export default function ScheduleList() { const [isOpen, setIsOpen] = useState(true); const [activeTab, setActiveTab] = useState<"current" | "past">("current"); + const [schedules, setSchedules] = useState([]); + const [calendarBlock, setCalendarBlock] = useState( + null, + ); + const [error, setError] = useState(null); + const toggleOpen = () => setIsOpen(!isOpen); + useEffect(() => { + fetchSchedules(); + }, []); + + const fetchSchedules = async () => { + setError(null); + try { + const token = sessionStorage.getItem("token"); + if (!token) { + throw new Error("로그인이 필요합니다."); + } + + const response = await fetch( + `${process.env.NEXT_PUBLIC_API_URL}/api/link/list`, + { + headers: { + Authorization: `Bearer ${token}`, + }, + }, + ); + + if (!response.ok) { + throw new Error(`HTTP error! status: ${response.status}`); + } + + const data = await response.json(); + + if (data.code === 200 && Array.isArray(data.data)) { + const foundCalendarBlock = data.data.find( + (item: CalendarBlock) => item.type === 7, + ); + + if (foundCalendarBlock) { + setCalendarBlock(foundCalendarBlock); + if (Array.isArray(foundCalendarBlock.schedule)) { + setSchedules(foundCalendarBlock.schedule); + } else { + setSchedules([]); + } + } else { + setSchedules([]); + } + } else { + throw new Error(`Unexpected data structure: ${JSON.stringify(data)}`); + } + } catch (err) { + console.error("Error fetching schedules:", err); + setError( + err instanceof Error ? err.message : "알 수 없는 오류가 발생했습니다.", + ); + } + }; + + const handleDelete = async (scheduleId: number) => { + try { + if (!calendarBlock) { + throw new Error("캘린더 블록을 찾을 수 없습니다."); + } + + const token = sessionStorage.getItem("token"); + if (!token) { + throw new Error("로그인이 필요합니다."); + } + + const updatedSchedules = schedules.filter( + (schedule) => schedule.id !== scheduleId, + ); + + const requestBody = { + id: calendarBlock.id, + type: 7, + sequence: calendarBlock.sequence, + style: calendarBlock.style, + schedule: updatedSchedules, + }; + + const response = await fetch( + `${process.env.NEXT_PUBLIC_API_URL}/api/link/update`, + { + method: "POST", + headers: { + "Content-Type": "application/json", + Authorization: `Bearer ${token}`, + }, + body: JSON.stringify(requestBody), + }, + ); + + if (!response.ok) { + const errorData = await response.json(); + throw new Error( + `Failed to delete schedule: ${JSON.stringify(errorData)}`, + ); + } + + const responseData = await response.json(); + + if (responseData.code === 200) { + setSchedules(updatedSchedules); + alert("일정이 성공적으로 삭제되었습니다."); + } else { + throw new Error( + `Failed to delete schedule. Server response: ${JSON.stringify(responseData)}`, + ); + } + } catch (error) { + console.error("Error deleting schedule:", error); + alert( + error instanceof Error + ? error.message + : "일정 삭제 중 오류가 발생했습니다.", + ); + } + }; + const currentDate = new Date(); - const filteredSchedules = scheduleData.filter((schedule) => { + const filteredSchedules = schedules.filter((schedule) => { const endDate = new Date(schedule.dateEnd); return activeTab === "current" ? endDate >= currentDate @@ -147,7 +280,7 @@ export default function ScheduleList() {
- {filteredSchedules.map((schedule) => ( - - ))} + {filteredSchedules.length > 0 ? ( + filteredSchedules.map((schedule) => ( + + )) + ) : ( + + 진행 중이거나 예정된 일정이 없습니다. +
+ 일정을 추가하여 많은 방문자에게 알려보세요. + + ) : ( + <> + 지난 일정이 없습니다. +
+ 일정을 추가하여 많은 방문자에게 알려보세요. + + ) + } + /> + )}
)} diff --git a/src/app/admin/(block)/calendar/manage/page.tsx b/src/app/admin/(block)/calendar/manage/page.tsx new file mode 100644 index 00000000..5b13410e --- /dev/null +++ b/src/app/admin/(block)/calendar/manage/page.tsx @@ -0,0 +1,113 @@ +"use client"; + +import React, { useEffect, useState } from "react"; +import { useRouter, useSearchParams } from "next/navigation"; +import Image from "next/image"; +import ScheduleForm from "../components/schedule-form"; + +interface Schedule { + id?: number; + title: string; + url?: string; + dateStart: string; + dateEnd: string; +} + +interface CalendarBlockData { + type: number; + id: number; + schedule: Schedule[]; +} + +export default function ScheduleManagementPage() { + const router = useRouter(); + const searchParams = useSearchParams(); + const mode = searchParams.get("mode") || "add"; + const [schedule, setSchedule] = useState(null); + const [calendarBlockId, setCalendarBlockId] = useState(null); + + const handleClose = () => { + router.push("/admin/calendar"); + }; + + useEffect(() => { + if (mode === "edit") { + const fetchSchedule = async () => { + const scheduleId = searchParams.get("id"); + + if (!scheduleId) { + alert("일정 ID가 필요합니다."); + router.push("/admin/calendar"); + return; + } + + try { + const token = sessionStorage.getItem("token"); + if (!token) throw new Error("로그인이 필요합니다."); + + const response = await fetch( + `${process.env.NEXT_PUBLIC_API_URL}/api/link/list`, + { + headers: { + Authorization: `Bearer ${token}`, + }, + }, + ); + + if (!response.ok) + throw new Error("데이터를 불러오는데 실패했습니다."); + + const data = await response.json(); + if (data.code === 200 && Array.isArray(data.data)) { + const calendarBlock = data.data.find( + (block: CalendarBlockData) => block.type === 7, + ); + if (calendarBlock) { + const foundSchedule = calendarBlock.schedule.find( + (s: Schedule) => s.id === Number(scheduleId), + ); + if (foundSchedule) { + setSchedule(foundSchedule); + setCalendarBlockId(calendarBlock.id); + } + } + } + } catch (error) { + console.error("Error fetching schedule:", error); + alert("일정을 불러오는데 실패했습니다."); + router.push("/admin/calendar"); + } + }; + + fetchSchedule(); + } + }, [mode, router, searchParams]); + + return ( +
+
+ +
+

+ {mode === "edit" ? "일정 수정하기" : "일정 추가하기"} +

+

+ 입력하는 진행기간에 따라 +
+ 전체 일정이 최근 날짜 순서로 자동 정렬됩니다. +

+ +
+ ); +}