2025-10-14 10:48:17 +09:00
|
|
|
"use client";
|
|
|
|
|
|
|
|
|
|
import { useState } from "react";
|
|
|
|
|
import { DashboardElement, CalendarConfig } from "../types";
|
|
|
|
|
import { MonthView } from "./MonthView";
|
|
|
|
|
import { CalendarSettings } from "./CalendarSettings";
|
|
|
|
|
import { generateCalendarDays, getMonthName, navigateMonth } from "./calendarUtils";
|
|
|
|
|
import { Popover, PopoverContent, PopoverTrigger } from "@/components/ui/popover";
|
|
|
|
|
import { Button } from "@/components/ui/button";
|
|
|
|
|
import { Settings, ChevronLeft, ChevronRight, Calendar } from "lucide-react";
|
2025-10-17 09:49:02 +09:00
|
|
|
import { useDashboard } from "@/contexts/DashboardContext";
|
2025-10-14 10:48:17 +09:00
|
|
|
|
|
|
|
|
interface CalendarWidgetProps {
|
|
|
|
|
element: DashboardElement;
|
|
|
|
|
onConfigUpdate?: (config: CalendarConfig) => void;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 달력 위젯 메인 컴포넌트
|
|
|
|
|
* - 월간/주간/일간 뷰 지원
|
|
|
|
|
* - 네비게이션 (이전/다음 월, 오늘)
|
|
|
|
|
* - 내장 설정 UI
|
|
|
|
|
*/
|
|
|
|
|
export function CalendarWidget({ element, onConfigUpdate }: CalendarWidgetProps) {
|
2025-10-17 09:49:02 +09:00
|
|
|
// Context에서 선택된 날짜 관리
|
|
|
|
|
const { selectedDate, setSelectedDate } = useDashboard();
|
|
|
|
|
|
2025-10-14 10:48:17 +09:00
|
|
|
// 현재 표시 중인 년/월
|
|
|
|
|
const today = new Date();
|
|
|
|
|
const [currentYear, setCurrentYear] = useState(today.getFullYear());
|
|
|
|
|
const [currentMonth, setCurrentMonth] = useState(today.getMonth());
|
|
|
|
|
const [settingsOpen, setSettingsOpen] = useState(false);
|
2025-10-17 09:49:02 +09:00
|
|
|
|
|
|
|
|
// 날짜 클릭 핸들러
|
|
|
|
|
const handleDateClick = (date: Date) => {
|
|
|
|
|
setSelectedDate(date);
|
|
|
|
|
};
|
2025-10-14 10:48:17 +09:00
|
|
|
|
|
|
|
|
// 기본 설정값
|
|
|
|
|
const config = element.calendarConfig || {
|
|
|
|
|
view: "month",
|
|
|
|
|
startWeekOn: "sunday",
|
|
|
|
|
highlightWeekends: true,
|
|
|
|
|
highlightToday: true,
|
|
|
|
|
showHolidays: true,
|
|
|
|
|
theme: "light",
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// 설정 저장 핸들러
|
|
|
|
|
const handleSaveSettings = (newConfig: CalendarConfig) => {
|
|
|
|
|
onConfigUpdate?.(newConfig);
|
|
|
|
|
setSettingsOpen(false);
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// 이전 월로 이동
|
|
|
|
|
const handlePrevMonth = () => {
|
|
|
|
|
const { year, month } = navigateMonth(currentYear, currentMonth, "prev");
|
|
|
|
|
setCurrentYear(year);
|
|
|
|
|
setCurrentMonth(month);
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// 다음 월로 이동
|
|
|
|
|
const handleNextMonth = () => {
|
|
|
|
|
const { year, month } = navigateMonth(currentYear, currentMonth, "next");
|
|
|
|
|
setCurrentYear(year);
|
|
|
|
|
setCurrentMonth(month);
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// 오늘로 돌아가기
|
|
|
|
|
const handleToday = () => {
|
|
|
|
|
setCurrentYear(today.getFullYear());
|
|
|
|
|
setCurrentMonth(today.getMonth());
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// 달력 날짜 생성
|
|
|
|
|
const calendarDays = generateCalendarDays(currentYear, currentMonth, config.startWeekOn);
|
|
|
|
|
|
|
|
|
|
// 크기에 따른 컴팩트 모드 판단
|
|
|
|
|
const isCompact = element.size.width < 400 || element.size.height < 400;
|
|
|
|
|
|
|
|
|
|
return (
|
|
|
|
|
<div className="relative flex h-full w-full flex-col">
|
|
|
|
|
{/* 헤더 - 네비게이션 */}
|
2025-10-29 17:53:03 +09:00
|
|
|
<div className="flex items-center justify-between border-b border-border p-2">
|
2025-10-14 10:48:17 +09:00
|
|
|
{/* 이전 월 버튼 */}
|
|
|
|
|
<Button variant="ghost" size="icon" className="h-7 w-7" onClick={handlePrevMonth}>
|
|
|
|
|
<ChevronLeft className="h-4 w-4" />
|
|
|
|
|
</Button>
|
|
|
|
|
|
|
|
|
|
{/* 현재 년월 표시 */}
|
|
|
|
|
<div className="flex items-center gap-2">
|
|
|
|
|
<span className="text-sm font-semibold">
|
|
|
|
|
{currentYear}년 {getMonthName(currentMonth)}
|
|
|
|
|
</span>
|
|
|
|
|
{!isCompact && (
|
|
|
|
|
<Button variant="outline" size="sm" className="h-6 px-2 text-xs" onClick={handleToday}>
|
|
|
|
|
오늘
|
|
|
|
|
</Button>
|
|
|
|
|
)}
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
{/* 다음 월 버튼 */}
|
|
|
|
|
<Button variant="ghost" size="icon" className="h-7 w-7" onClick={handleNextMonth}>
|
|
|
|
|
<ChevronRight className="h-4 w-4" />
|
|
|
|
|
</Button>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
{/* 달력 콘텐츠 */}
|
|
|
|
|
<div className="flex-1 overflow-hidden">
|
2025-10-17 09:49:02 +09:00
|
|
|
{config.view === "month" && (
|
|
|
|
|
<MonthView
|
|
|
|
|
days={calendarDays}
|
|
|
|
|
config={config}
|
|
|
|
|
isCompact={isCompact}
|
|
|
|
|
selectedDate={selectedDate}
|
|
|
|
|
onDateClick={handleDateClick}
|
|
|
|
|
/>
|
|
|
|
|
)}
|
2025-10-14 10:48:17 +09:00
|
|
|
{/* 추후 WeekView, DayView 추가 가능 */}
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
{/* 설정 버튼 - 우측 하단 */}
|
|
|
|
|
<div className="absolute bottom-2 right-2">
|
|
|
|
|
<Popover open={settingsOpen} onOpenChange={setSettingsOpen}>
|
|
|
|
|
<PopoverTrigger asChild>
|
2025-10-29 17:53:03 +09:00
|
|
|
<Button variant="ghost" size="icon" className="h-8 w-8 bg-background/80 hover:bg-background">
|
2025-10-14 10:48:17 +09:00
|
|
|
<Settings className="h-4 w-4" />
|
|
|
|
|
</Button>
|
|
|
|
|
</PopoverTrigger>
|
|
|
|
|
<PopoverContent className="w-[450px] p-0" align="end">
|
|
|
|
|
<CalendarSettings config={config} onSave={handleSaveSettings} onClose={() => setSettingsOpen(false)} />
|
|
|
|
|
</PopoverContent>
|
|
|
|
|
</Popover>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|