[st] feat: 대시보드 일정안내 empty 구현 및 일정관리 월 이동 구현

This commit is contained in:
2026-07-23 11:33:21 +09:00
parent 1c3cc5b61b
commit f535cd1010
2 changed files with 113 additions and 67 deletions
@@ -1,7 +1,9 @@
"use client";
import { useState } from "react";
import dynamic from "next/dynamic";
import type { DatesSetArg } from "@fullcalendar/core";
import dayGridPlugin from "@fullcalendar/daygrid";
import timeGridPlugin from "@fullcalendar/timegrid";
import dayjs from "dayjs";
@@ -12,25 +14,48 @@ import { LoadingWithAnimation } from "@common/components";
import { useDragHandle } from "../sortable-panel";
// FullCalendar는 클라이언트 전용이므로 dynamic import
const FullCalendar = dynamic(() => import("@fullcalendar/react"), {
ssr: false,
});
function getMonthRange(date = dayjs()) {
return {
schdlBgngYmd: date.startOf("month").format("YYYY-MM-DD"),
schdlEndYmd: date.endOf("month").format("YYYY-MM-DD"),
};
}
export function ScheduleCalendarPanel() {
const { dragHandleProps, isDragging } = useDragHandle();
const [dateRange, setDateRange] = useState(getMonthRange);
// TBD: 초기 날짜 범위 설정 로직
const today = dayjs();
const startOfMonth = today.startOf("month").format("YYYY-MM-DD");
const endOfMonth = today.endOf("month").format("YYYY-MM-DD");
const { data, isLoading } = useSchedules({
const { data, isLoading, isFetching } = useSchedules({
siteId: SITE_ID,
schdlBgngYmd: startOfMonth,
schdlEndYmd: endOfMonth,
schdlBgngYmd: dateRange.schdlBgngYmd,
schdlEndYmd: dateRange.schdlEndYmd,
});
function handleDatesSet(dateInfo: DatesSetArg) {
const nextRange = {
schdlBgngYmd: dayjs(dateInfo.view.currentStart).format(
"YYYY-MM-DD",
),
schdlEndYmd: dayjs(dateInfo.view.currentEnd)
.subtract(1, "day")
.format("YYYY-MM-DD"),
};
setDateRange((prev) => {
if (
prev.schdlBgngYmd === nextRange.schdlBgngYmd &&
prev.schdlEndYmd === nextRange.schdlEndYmd
) {
return prev;
}
return nextRange;
});
}
return (
<div className="card card-border !m-0 h-full border-border bg-background text-foreground">
<div
@@ -40,41 +65,39 @@ export function ScheduleCalendarPanel() {
>
<h3 className="text-2xl font-semibold">일정관리</h3>
</div>
<div className="card-body max-h-[calc(100%-3.25rem)] py-2.5">
{isLoading && (
<div className="flex h-full items-center justify-center">
<div className="card-body relative max-h-[calc(100%-3.25rem)] py-2.5">
<div className="h-[48rem]">
<FullCalendar
plugins={[dayGridPlugin, timeGridPlugin]}
initialView="dayGridMonth"
locale="ko"
headerToolbar={{
left: "prev,next",
center: "title",
right: "dayGridMonth,timeGridWeek",
}}
buttonText={{
month: "월",
week: "주",
}}
datesSet={handleDatesSet}
events={(data ?? []).map((schedule) => ({
id: schedule.schdlSn.toString(),
title: schedule.schdlTtl,
start: schedule.schdlBgngDt,
end: schedule.schdlEndDt,
allDay: true,
}))}
height="100%"
/>
</div>
{(isLoading || isFetching) && (
<div className="bg-background/50 absolute inset-0 flex items-center justify-center">
<div className="relative size-[5rem]">
<LoadingWithAnimation color="primary" />
</div>
</div>
)}
{data && (
<div className="h-[48rem]">
<FullCalendar
plugins={[dayGridPlugin, timeGridPlugin]}
initialView="dayGridMonth"
locale="ko"
headerToolbar={{
left: "prev,next",
center: "title",
right: "dayGridMonth,timeGridWeek",
}}
buttonText={{
month: "월",
week: "주",
}}
events={data.map((schedule) => ({
id: schedule.schdlSn.toString(),
title: schedule.schdlTtl,
start: schedule.schdlBgngDt,
end: schedule.schdlEndDt,
// TBD: allDay 판단 기준 (1439분 이상?)
allDay: true,
}))}
height="100%"
/>
</div>
)}
</div>
</div>
);
@@ -83,36 +83,59 @@ export function ScheduleInfoPanel({ params }: ScheduleInfoPanelProps) {
</div>
<div className="space-y-2">
<div className="flex items-center justify-between rounded-sm border border-border p-3">
<Badge size="lg" className="font-semibold">
{data.inptSeCd}차 입력기간
</Badge>
<p className="text-2xl">
{dayjs(data.inptBgngYmd).format(
"YYYY.MM.DD",
)}{" "}
~{" "}
{dayjs(data.inptEndYmd).format(
"YYYY.MM.DD",
)}
</p>
{data.inptSeCd &&
data.inptBgngYmd &&
data.inptEndYmd ? (
<>
<Badge
size="lg"
className="font-semibold"
>
{data.inptSeCd}차 입력기간
</Badge>
<p className="text-2xl">
{dayjs(data.inptBgngYmd).format(
"YYYY.MM.DD",
)}{" "}
~{" "}
{dayjs(data.inptEndYmd).format(
"YYYY.MM.DD",
)}
</p>
</>
) : (
<p className="w-full text-center text-lg text-label">
등록된 입력 일정이 없습니다.
</p>
)}
</div>
<div className="flex items-center justify-between rounded-sm border border-border p-3">
<Badge
size="lg"
color="gray"
className="font-semibold"
>
{data.vrfcSeCd}차 검증기간
</Badge>
<p className="text-2xl">
{dayjs(data.vrfcBgngYmd).format(
"YYYY.MM.DD",
)}{" "}
~{" "}
{dayjs(data.vrfcEndYmd).format(
"YYYY.MM.DD",
)}
</p>
{data.vrfcSeCd &&
data.vrfcBgngYmd &&
data.vrfcEndYmd ? (
<>
<Badge
size="lg"
color="gray"
className="font-semibold"
>
{data.vrfcSeCd}차 검증기간
</Badge>
<p className="text-2xl">
{dayjs(data.vrfcBgngYmd).format(
"YYYY.MM.DD",
)}{" "}
~{" "}
{dayjs(data.vrfcEndYmd).format(
"YYYY.MM.DD",
)}
</p>
</>
) : (
<p className="w-full text-center text-lg text-label">
등록된 검증 일정이 없습니다.
</p>
)}
</div>
</div>
<div className="flex items-center justify-center gap-4 py-4 tracking-tight">