import { useState, useMemo, useEffect } from 'react'; import { useNavigate } from 'react-router-dom'; import { ChevronLeft, ChevronRight, Search } from 'lucide-react'; import { useIsMobile, useScheduleData, useCategories, useCalendar } from '@/hooks'; import { useScheduleStore } from '@/stores'; import { Loading, ScheduleCard } from '@/components'; import { cn, getTodayKST, decodeHtmlEntities } from '@/utils'; import { WEEKDAYS, MIN_YEAR } from '@/constants'; /** * PC 캘린더 컴포넌트 */ function PCCalendar({ selectedDate, schedules, categories, onSelectDate, onMonthChange }) { const { days, year, month, canGoPrev } = useCalendar(selectedDate); // 날짜별 일정 맵 const scheduleDates = useMemo(() => { const dateMap = {}; schedules?.forEach((schedule) => { const date = schedule.date?.split('T')[0]; if (!dateMap[date]) dateMap[date] = []; const cat = categories?.find((c) => c.id === (schedule.category_id || schedule.category?.id)); dateMap[date].push(cat?.color || '#6b7280'); }); return dateMap; }, [schedules, categories]); const isToday = (date) => { const today = new Date(); return ( date.getDate() === today.getDate() && date.getMonth() === today.getMonth() && date.getFullYear() === today.getFullYear() ); }; const isSelected = (date) => { const sel = new Date(selectedDate); return ( date.getDate() === sel.getDate() && date.getMonth() === sel.getMonth() && date.getFullYear() === sel.getFullYear() ); }; const formatDateStr = (date) => { const y = date.getFullYear(); const m = String(date.getMonth() + 1).padStart(2, '0'); const d = String(date.getDate()).padStart(2, '0'); return `${y}-${m}-${d}`; }; return (
{/* 헤더 */}

{year}년 {month + 1}월

{/* 요일 헤더 */}
{WEEKDAYS.map((day, i) => (
{day}
))}
{/* 날짜 그리드 */}
{days.map((item, index) => { const dayOfWeek = index % 7; const dateStr = formatDateStr(item.date); const colors = scheduleDates[dateStr] || []; return ( ); })}
); } /** * 스케줄 페이지 (PC/Mobile 통합) */ function Schedule() { const navigate = useNavigate(); const isMobile = useIsMobile(); // Zustand store const { currentDate, setCurrentDate, selectedDate, setSelectedDate } = useScheduleStore(); // 초기값 설정 const today = getTodayKST(); const actualSelectedDate = selectedDate || today; // 데이터 로드 const year = currentDate.getFullYear(); const month = currentDate.getMonth() + 1; const { data: schedules, isLoading: schedulesLoading } = useScheduleData(year, month); const { data: categories } = useCategories(); // 선택된 날짜의 일정 const selectedDateSchedules = useMemo(() => { if (!schedules) return []; const sel = new Date(actualSelectedDate); const dateStr = `${sel.getFullYear()}-${String(sel.getMonth() + 1).padStart(2, '0')}-${String(sel.getDate()).padStart(2, '0')}`; return schedules .filter((s) => s.date?.split('T')[0] === dateStr) .sort((a, b) => { const aIsBirthday = a.is_birthday || String(a.id).startsWith('birthday-'); const bIsBirthday = b.is_birthday || String(b.id).startsWith('birthday-'); if (aIsBirthday && !bIsBirthday) return -1; if (!aIsBirthday && bIsBirthday) return 1; return 0; }); }, [schedules, actualSelectedDate]); // 월 변경 const changeMonth = (delta) => { const newDate = new Date(currentDate); newDate.setMonth(newDate.getMonth() + delta); // 2017년 1월 이전으로 이동 불가 if (newDate.getFullYear() < MIN_YEAR || (newDate.getFullYear() === MIN_YEAR && newDate.getMonth() < 0)) { return; } setCurrentDate(newDate); // 이번 달이면 오늘 날짜, 다른 달이면 1일 선택 const now = new Date(); if (newDate.getFullYear() === now.getFullYear() && newDate.getMonth() === now.getMonth()) { setSelectedDate(getTodayKST()); } else { const firstDay = `${newDate.getFullYear()}-${String(newDate.getMonth() + 1).padStart(2, '0')}-01`; setSelectedDate(firstDay); } }; // 날짜 선택 const handleSelectDate = (date) => { const dateStr = `${date.getFullYear()}-${String(date.getMonth() + 1).padStart(2, '0')}-${String(date.getDate()).padStart(2, '0')}`; setSelectedDate(dateStr); // 월이 다르면 currentDate도 변경 if (date.getMonth() !== currentDate.getMonth() || date.getFullYear() !== currentDate.getFullYear()) { setCurrentDate(date); } }; // PC 레이아웃 if (!isMobile) { return (
{/* 좌측: 캘린더 */}
{/* 우측: 일정 목록 */}
{/* 헤더 */}

{new Date(actualSelectedDate).getMonth() + 1}월{' '} {new Date(actualSelectedDate).getDate()}일{' '} {WEEKDAYS[new Date(actualSelectedDate).getDay()]}요일

{selectedDateSchedules.length}개의 일정
{/* 일정 목록 */}
{schedulesLoading ? (
) : selectedDateSchedules.length === 0 ? (
{new Date(actualSelectedDate).getMonth() + 1}월{' '} {new Date(actualSelectedDate).getDate()}일 일정이 없습니다
) : (
{selectedDateSchedules.map((schedule) => ( navigate(`/schedule/${schedule.id}`)} /> ))}
)}
); } // 모바일 레이아웃 (간소화된 버전) return ( <> {/* 모바일 툴바 */}
{currentDate.getFullYear()}년 {currentDate.getMonth() + 1}월
{/* 모바일 컨텐츠 */}
{schedulesLoading ? (
) : selectedDateSchedules.length === 0 ? (
{new Date(actualSelectedDate).getMonth() + 1}월{' '} {new Date(actualSelectedDate).getDate()}일 일정이 없습니다
) : (
{selectedDateSchedules.map((schedule) => ( navigate(`/schedule/${schedule.id}`)} /> ))}
)}
); } export default Schedule;