feat: 일정 등록 기능

This commit is contained in:
2025-05-27 13:23:56 +09:00
parent d351b8e24e
commit a9887eaac6
16 changed files with 792 additions and 3 deletions
@@ -0,0 +1,2 @@
import { SchedulePage } from "@/pages/schedule";
export default SchedulePage;
@@ -0,0 +1,2 @@
import { CreatePage } from "@/pages/schedule";
export default CreatePage;
+5
View File
@@ -14,6 +14,10 @@
"@common/config": "workspace:*",
"@fontsource-variable/noto-sans-kr": "^5.2.5",
"@frontend-opensource/use-react-hooks": "^1.1.4",
"@fullcalendar/core": "^6.1.17",
"@fullcalendar/daygrid": "^6.1.17",
"@fullcalendar/interaction": "^6.1.17",
"@fullcalendar/react": "^6.1.17",
"@hookform/resolvers": "^4.1.3",
"@minoru/react-dnd-treeview": "3.5.1-alpha.1",
"@radix-ui/react-icons": "^1.3.2",
@@ -26,6 +30,7 @@
"chart.js": "^4.4.8",
"dayjs": "^1.11.13",
"js-cookie": "^3.0.5",
"lucide-react": "^0.474.0",
"next": "15.3.0",
"react-chartjs-2": "^5.3.0",
"react-dnd": "^16.0.1",
@@ -0,0 +1,24 @@
import { client } from "@/shared/api";
import { ENDPOINT } from "@/shared/config";
import type {
Schedule,
ScheduleFormData,
ScheduleParams,
ScheduleService,
} from "../model/schedule-interface";
class ScheduleApiService implements ScheduleService {
public async getSchedules(params?: ScheduleParams): Promise<Schedule[]> {
const response = await client.get<Schedule[]>(
ENDPOINT.CMS_SERVICE.SCHEDULES,
{ params },
);
return response.data;
}
public async createSchedule(data: ScheduleFormData): Promise<void> {
await client.post(ENDPOINT.CMS_SERVICE.SCHEDULES, data);
}
}
export const ScheduleApi = new ScheduleApiService();
+2
View File
@@ -0,0 +1,2 @@
export { SchedulePage } from "./ui/schedule-page";
export { CreatePage } from "./ui/create-page";
@@ -0,0 +1,55 @@
import { z } from "zod";
export interface ScheduleParams {
siteId?: string;
schdlTtl?: string;
schdlSeCd?: string;
schdlBgngYmd?: string;
schdlEndYmd?: string;
reptYn?: string;
commSchdlYn?: string;
useYn?: string;
}
export interface Schedule {
schdlSn: number;
siteId: string;
schdlTtl: string;
schdlSeCd: string;
schdlSeNm: string;
schdlBgngDt: string;
schdlEndDt: string;
reptYn: string;
commSchdlYn: string;
}
const scheduleRepeatSchema = z.object({
schdlSn: z.number().nullish(),
reptCycleCd: z.string().nullish(),
reptDayVl: z.number().nullish(),
reptDowVl: z.number().nullish(),
reptMmVl: z.number().nullish(),
reptQuVl: z.number().nullish(),
reptEndDt: z.string().nullish(),
});
export const scheduleFormSchema = z.object({
siteId: z.string(),
schdlTtl: z.string(),
schdlSeCd: z.string(),
schdlBgngDt: z.string(),
schdlEndDt: z.string(),
schdlCn: z.string().nullish(),
reptYn: z.string().nullish(),
commSchdlYn: z.string().nullish(),
useYn: z.string().nullish(),
atflCd: z.string().nullish(),
reptDto: scheduleRepeatSchema.nullish(),
});
export type ScheduleFormData = z.infer<typeof scheduleFormSchema>;
export interface ScheduleService {
getSchedules(params?: ScheduleParams): Promise<Schedule[]>;
createSchedule(data: ScheduleFormData): Promise<void>;
}
@@ -0,0 +1,66 @@
"use client";
import dayjs from "dayjs";
import { useForm } from "react-hook-form";
import { useRouter, useSearchParams } from "next/navigation";
import { useMutation } from "@tanstack/react-query";
import { zodResolver } from "@hookform/resolvers/zod";
import { useAlertStore } from "@/shared/lib";
import { SITE_ID } from "@/shared/config";
import {
scheduleFormSchema,
type ScheduleFormData,
} from "../model/schedule-interface";
import { ScheduleApi } from "../api/schedule-service";
import { ScheduleForm } from "./schedule-form";
function CreatePage() {
const router = useRouter();
const searchParams = useSearchParams();
const dateStr = searchParams?.get("dateStr");
const { setMessage: alert } = useAlertStore((state) => state);
const { mutateAsync: createSchedule } = useMutation({
mutationFn: ScheduleApi.createSchedule,
onSuccess: () => {
alert("일정이 등록되었습니다.", router.back);
},
});
const form = useForm<ScheduleFormData>({
resolver: zodResolver(scheduleFormSchema),
defaultValues: {
siteId: SITE_ID,
schdlBgngDt: dateStr
? dayjs(dateStr).format("YYYY-MM-DD 00:00:00")
: undefined,
schdlEndDt: dateStr
? dayjs(dateStr).format("YYYY-MM-DD 23:59:59")
: undefined,
reptYn: "N",
commSchdlYn: "N",
useYn: "Y",
atflCd: null,
reptDto: null,
},
});
const handleSave = async (data: ScheduleFormData) => {
// const reptDtoValid = Object.values(data.reptDto ?? {}).every(
// (value) => value === null,
// );
// if (reptDtoValid) data.reptDto = null;
await createSchedule(data);
};
return (
<div className="card card-border !mb-0 !mt-6">
<div className="card-header">
<h3 className="text-2xl font-medium text-label">스케줄 등록</h3>
</div>
<ScheduleForm form={form} handleSave={handleSave} />
</div>
);
}
export { CreatePage };
@@ -0,0 +1,123 @@
"use client";
import dayjs from "dayjs";
import { useState } from "react";
import { CalendarIcon } from "lucide-react";
import {
Button,
Calendar,
Popover,
PopoverContent,
PopoverTrigger,
ScrollArea,
ScrollBar,
cn,
} from "@common/components";
type DateTimePickerProps = {
date?: Date;
classes?: { trigger?: string };
onDateChange?: (date: Date) => void;
};
function DateTimePicker({ date, classes, onDateChange }: DateTimePickerProps) {
const [isOpen, setIsOpen] = useState(false);
const hours = Array.from({ length: 24 }, (_, i) => i);
const minutes = Array.from({ length: 60 }, (_, i) => i);
const handleDateSelect = (selectedDate: Date | undefined) => {
if (!selectedDate) return;
onDateChange?.(selectedDate);
};
const handleTimeChange = (type: "hour" | "minute", value: string) => {
if (date) {
const newDate = new Date(date);
if (type === "hour") {
newDate.setHours(parseInt(value));
} else if (type === "minute") {
newDate.setMinutes(parseInt(value));
}
onDateChange?.(newDate);
}
};
return (
<Popover open={isOpen} onOpenChange={setIsOpen}>
<PopoverTrigger asChild>
<Button
variant="outline"
className={cn(
"date-picker-trigger",
!date && "text-muted-foreground",
classes?.trigger,
)}
>
<CalendarIcon className="!size-[1.6rem]" />
{date ? (
dayjs(date).format("YYYY/MM/DD HH:mm")
) : (
<span>YYYY/MM/DD HH:mm</span>
)}
</Button>
</PopoverTrigger>
<PopoverContent className="w-auto p-0">
<div className="sm:flex">
<Calendar
mode="single"
selected={date}
onSelect={handleDateSelect}
initialFocus
/>
<div className="flex flex-col divide-y sm:h-[300px] sm:flex-row sm:divide-x sm:divide-y-0">
<ScrollArea className="w-64 sm:w-auto">
<div className="flex p-2 sm:flex-col">
{hours.reverse().map((hour) => (
<Button
key={hour}
size="icon"
className={cn(
"aspect-square shrink-0 text-label sm:w-full",
date && date.getHours() === hour
? "!bg-primary text-primary-foreground"
: "!bg-background",
)}
onClick={() => handleTimeChange("hour", hour.toString())}
>
{hour}
</Button>
))}
</div>
<ScrollBar orientation="horizontal" className="sm:hidden" />
</ScrollArea>
<ScrollArea className="w-64 sm:w-auto">
<div className="flex p-2 sm:flex-col">
{minutes.map((minute) => (
<Button
key={minute}
size="icon"
className={cn(
"aspect-square shrink-0 text-label sm:w-full",
date && date.getMinutes() === minute
? "!bg-primary text-primary-foreground"
: "!bg-background",
)}
onClick={() =>
handleTimeChange("minute", minute.toString())
}
>
{minute.toString().padStart(2, "0")}
</Button>
))}
</div>
<ScrollBar orientation="horizontal" className="sm:hidden" />
</ScrollArea>
</div>
</div>
</PopoverContent>
</Popover>
);
}
export { DateTimePicker };
@@ -0,0 +1,331 @@
import dayjs from "dayjs";
import { useState } from "react";
import { type UseFormReturn } from "react-hook-form";
import {
Checkbox,
DatePicker,
Input,
Label,
RadioGroup,
RadioGroupItem,
Table,
TableBody,
TableCell,
TableHead,
TableRow,
Textarea,
} from "@common/components";
import { GeneralFileUploader } from "@/widgets/post/ui/general-file-uploader";
import { FormWrapper } from "@/widgets/form-wrapper";
import { CodeSelect } from "@/entities/code";
import { FormItem, FormControl, FormField } from "@/shared/ui/form";
import type { ScheduleFormData } from "../model/schedule-interface";
import { DateTimePicker } from "./date-time-picker";
type ScheduleFormProps = {
form: UseFormReturn<ScheduleFormData>;
scheduleId?: string;
handleSave: (data: ScheduleFormData) => Promise<void>;
handleDelete?: () => Promise<void>;
};
function ScheduleForm({ form, scheduleId, ...props }: ScheduleFormProps) {
const isModifyMode = !!scheduleId;
const [beginDate, setBeginDate] = useState<Date | undefined>(
dayjs(form?.getValues("schdlBgngDt"))?.toDate(),
);
const [endDate, setEndDate] = useState<Date | undefined>(
dayjs(form?.getValues("schdlEndDt"))?.toDate(),
);
const [isAllDay, setIsAllDay] = useState(false);
const [deleteAttachmentIds, setDeleteAttachmentIds] = useState<string[]>([]);
const handleDateTimeChange =
(name: "schdlBgngDt" | "schdlEndDt") => (date?: Date) => {
if (!date) return;
form.setValue(name, dayjs(date).format("YYYY-MM-DD HH:mm:ss"));
setIsAllDay(false);
};
return (
<FormWrapper form={form} isModifyMode={isModifyMode} {...props}>
<div className="card">
<Table variant="secondary">
<colgroup>
<col width="15%" />
<col width="85%" />
</colgroup>
<TableBody>
<TableRow>
<TableHead>제목</TableHead>
<TableCell className="border">
<FormField
control={form.control}
name="schdlTtl"
render={({ field }) => (
<FormItem>
<FormControl>
<Input
{...field}
value={field.value ?? ""}
className="!text-[1.3rem]"
placeholder="일정 제목을 입력하십시오."
/>
</FormControl>
</FormItem>
)}
/>
</TableCell>
</TableRow>
<TableRow>
<TableHead>일정 기간</TableHead>
<TableCell className="border">
<div className="flex items-center gap-4">
<div className="flex items-center gap-2">
<FormField
control={form.control}
name="schdlBgngDt"
render={({ field }) => (
<DateTimePicker
date={beginDate}
classes={{ trigger: "w-auto px-4 justify-start" }}
onDateChange={(date) => {
setBeginDate(date);
handleDateTimeChange("schdlBgngDt")(date);
}}
{...field}
/>
)}
/>
~
<FormField
control={form.control}
name="schdlEndDt"
render={({ field }) => (
<DateTimePicker
date={endDate}
classes={{ trigger: "w-auto px-4 justify-start" }}
onDateChange={(date) => {
setEndDate(date);
handleDateTimeChange("schdlEndDt")(date);
}}
{...field}
/>
)}
/>
</div>
<div className="flex items-center gap-2">
<Checkbox
id="allDay"
checked={isAllDay}
onCheckedChange={(checked) => {
setIsAllDay(checked === true);
if (checked === true) {
const baseDate = form.getValues("schdlBgngDt");
if (baseDate) {
const newStartDate = dayjs(baseDate).format(
"YYYY-MM-DD 00:00:00",
);
const newEndDate = dayjs(baseDate).format(
"YYYY-MM-DD 23:59:59",
);
setBeginDate(dayjs(newStartDate).toDate());
setEndDate(dayjs(newEndDate).toDate());
form.setValue("schdlBgngDt", newStartDate);
form.setValue("schdlEndDt", newEndDate);
}
}
}}
/>
<Label htmlFor="allDay">종일</Label>
</div>
</div>
</TableCell>
</TableRow>
<TableRow>
<TableHead>일정 반복</TableHead>
<TableCell className="border">
<div className="flex items-center gap-4">
<div className="flex items-center gap-2">
<FormField
control={form.control}
name="reptYn"
render={({ field }) => (
<FormItem>
<FormControl>
<div className="mr-2 flex items-center gap-2">
<Checkbox
id="reptY"
checked={field.value === "Y"}
onCheckedChange={(checked) => {
field.onChange(checked ? "Y" : "N");
}}
/>
<Label htmlFor="reptY">반복</Label>
</div>
</FormControl>
</FormItem>
)}
/>
<FormField
control={form.control}
name="reptDto.reptCycleCd"
render={({ field }) => (
<FormItem>
<FormControl>
<CodeSelect
{...field}
upCd="SCHDLRPTCD"
value={field.value ?? ""}
onValueChange={(value) =>
value && field.onChange(value)
}
placeholder="반복 주기 선택"
disabled={form.watch("reptYn") === "N"}
/>
</FormControl>
</FormItem>
)}
/>
<FormField
control={form.control}
name="reptDto.reptEndDt"
render={({ field }) => (
<FormItem>
<FormControl>
<DatePicker
mode="single"
selected={
field.value
? dayjs(field.value).toDate()
: undefined
}
onSelect={(value) =>
value && field.onChange(value)
}
className="w-auto px-4"
placeholder="반복 종료일 선택"
disabled={form.watch("reptYn") === "N"}
/>
</FormControl>
</FormItem>
)}
/>
</div>
</div>
</TableCell>
</TableRow>
<TableRow>
<TableHead>일정 유형</TableHead>
<TableCell className="border">
<FormField
control={form.control}
name="schdlSeCd"
render={({ field }) => (
<FormItem>
<FormControl>
<CodeSelect
{...field}
upCd="SCHDLCLCD"
value={field.value ?? ""}
onValueChange={field.onChange}
/>
</FormControl>
</FormItem>
)}
/>
</TableCell>
</TableRow>
<TableRow>
<TableHead>일정 내용</TableHead>
<TableCell className="border">
<FormField
control={form.control}
name="schdlCn"
render={({ field }) => (
<FormItem>
<FormControl>
<Textarea
{...field}
value={field.value ?? ""}
className="min-h-32 !text-[1.3rem]"
placeholder="일정 내용을 입력하십시오."
/>
</FormControl>
</FormItem>
)}
/>
</TableCell>
</TableRow>
<TableRow>
<TableHead>대상 사이트</TableHead>
<TableCell className="border">
<FormField
control={form.control}
name="commSchdlYn"
render={({ field }) => (
<FormItem>
<FormControl>
<div className="flex items-center gap-2">
<Checkbox
id="commSchdlY"
checked={field.value === "Y"}
onCheckedChange={(checked) => {
field.onChange(checked ? "Y" : "N");
}}
/>
<Label htmlFor="commSchdlY">공용</Label>
</div>
</FormControl>
</FormItem>
)}
/>
</TableCell>
</TableRow>
<TableRow>
<TableHead>첨부 파일</TableHead>
<TableCell className="border">
<GeneralFileUploader
form={form}
deleteAttachmentIds={deleteAttachmentIds}
setDeleteAttachmentIds={setDeleteAttachmentIds}
/>
</TableCell>
</TableRow>
<TableRow>
<TableHead>사용 여부</TableHead>
<TableCell className="border">
<FormField
control={form.control}
name="useYn"
render={({ field }) => (
<FormItem>
<FormControl>
<RadioGroup
className="flex items-center gap-4"
onValueChange={field.onChange}
>
<div className="flex items-center gap-2">
<RadioGroupItem value="Y" id="useY" />
<Label htmlFor="useY">사용</Label>
</div>
<div className="flex items-center gap-2">
<RadioGroupItem value="N" id="useN" />
<Label htmlFor="useN">미사용</Label>
</div>
</RadioGroup>
</FormControl>
</FormItem>
)}
/>
</TableCell>
</TableRow>
</TableBody>
</Table>
</div>
</FormWrapper>
);
}
export { ScheduleForm };
@@ -0,0 +1,63 @@
"use client";
import dayjs from "dayjs";
import { useState } from "react";
import { useRouter } from "next/navigation";
import FullCalendar from "@fullcalendar/react";
import dayGridPlugin from "@fullcalendar/daygrid";
import interactionPlugin, {
type DateClickArg,
} from "@fullcalendar/interaction";
import { useQuery } from "@tanstack/react-query";
import { ENDPOINT, SITE_ID } from "@/shared/config";
import { ScheduleApi } from "../api/schedule-service";
import type { ScheduleParams } from "../model/schedule-interface";
function SchedulePage() {
const router = useRouter();
const [scheduleParams, setScheduleParams] = useState<ScheduleParams>({
siteId: SITE_ID,
schdlBgngYmd: "",
schdlEndYmd: "",
useYn: "Y",
});
const { data } = useQuery({
queryKey: [ENDPOINT.CMS_SERVICE.SCHEDULES, scheduleParams],
queryFn: () => ScheduleApi.getSchedules(scheduleParams),
select: (data) => {
return data.map((item) => ({
title: item.schdlTtl,
start: item.schdlBgngDt,
end: item.schdlEndDt,
}));
},
enabled: !!scheduleParams.schdlBgngYmd && !!scheduleParams.schdlEndYmd,
});
const handleDateClick = (info: DateClickArg) => {
router.push(`/schedule/register?dateStr=${info.dateStr}`);
};
return (
<div className="card card-border">
<div className="p-5">
<FullCalendar
plugins={[dayGridPlugin, interactionPlugin]}
events={data}
locale="ko"
dateClick={handleDateClick}
datesSet={(dateInfo) => {
setScheduleParams({
...scheduleParams,
schdlBgngYmd: dayjs(dateInfo.startStr).format("YYYY-MM-DD"),
schdlEndYmd: dayjs(dateInfo.endStr).format("YYYY-MM-DD"),
});
}}
/>
</div>
</div>
);
}
export { SchedulePage };
+1
View File
@@ -39,6 +39,7 @@ export const ENDPOINT = {
LWPO_CODE: "/cms-service/api/v1/lwpo-cds",
UP_CODE: "/cms-service/api/v1/up-codes",
POPUPS: "/cms-service/api/v1/popups",
SCHEDULES: "/cms-service/api/v1/schdls",
},
BOARD_SERVICE: {
BBS: "/board-service/api/v1/bbs",
+1
View File
@@ -40,6 +40,7 @@
"@radix-ui/react-popover": "^1.1.6",
"@radix-ui/react-progress": "^1.1.2",
"@radix-ui/react-radio-group": "^1.2.2",
"@radix-ui/react-scroll-area": "^1.2.3",
"@radix-ui/react-select": "^2.1.6",
"@radix-ui/react-separator": "^1.1.2",
"@radix-ui/react-slider": "^1.2.3",
@@ -9,21 +9,30 @@ import { Button } from "../button";
import { Calendar } from "../calendar";
import { Popover, PopoverContent, PopoverTrigger } from "../popover";
type DatePickerProps = DayPickerSingleProps;
type DatePickerProps = {
placeholder?: string;
disabled?: boolean;
} & DayPickerSingleProps;
export function DatePicker({ className, ...props }: DatePickerProps) {
export function DatePicker({
className,
placeholder = "날짜 선택",
disabled,
...props
}: DatePickerProps) {
return (
<Popover>
<PopoverTrigger asChild>
<Button
variant="outline"
className={cn("date-picker-trigger", className)}
disabled={disabled}
>
<CalendarIcon className="!size-[1.6rem]" />
{props.selected ? (
format(props.selected, "yyyy-MM-dd")
) : (
<span>날짜 선택</span>
<span>{placeholder}</span>
)}
</Button>
</PopoverTrigger>
@@ -20,6 +20,7 @@ export * from "./page-title";
export * from "./popover";
export * from "./progress";
export * from "./radio-group";
export * from "./scroll-area";
export * from "./select";
export * from "./slider";
export * from "./skeleton";
@@ -0,0 +1,48 @@
"use client";
import * as React from "react";
import * as ScrollAreaPrimitive from "@radix-ui/react-scroll-area";
import { cn } from "@/lib/utils";
const ScrollArea = React.forwardRef<
React.ElementRef<typeof ScrollAreaPrimitive.Root>,
React.ComponentPropsWithoutRef<typeof ScrollAreaPrimitive.Root>
>(({ className, children, ...props }, ref) => (
<ScrollAreaPrimitive.Root
ref={ref}
className={cn("relative overflow-hidden", className)}
{...props}
>
<ScrollAreaPrimitive.Viewport className="h-full w-full rounded-[inherit]">
{children}
</ScrollAreaPrimitive.Viewport>
<ScrollBar />
<ScrollAreaPrimitive.Corner />
</ScrollAreaPrimitive.Root>
));
ScrollArea.displayName = ScrollAreaPrimitive.Root.displayName;
const ScrollBar = React.forwardRef<
React.ElementRef<typeof ScrollAreaPrimitive.ScrollAreaScrollbar>,
React.ComponentPropsWithoutRef<typeof ScrollAreaPrimitive.ScrollAreaScrollbar>
>(({ className, orientation = "vertical", ...props }, ref) => (
<ScrollAreaPrimitive.ScrollAreaScrollbar
ref={ref}
orientation={orientation}
className={cn(
"flex touch-none select-none transition-colors",
orientation === "vertical" &&
"h-full w-2.5 border-l border-l-transparent p-[1px]",
orientation === "horizontal" &&
"h-2.5 flex-col border-t border-t-transparent p-[1px]",
className,
)}
{...props}
>
<ScrollAreaPrimitive.ScrollAreaThumb className="relative flex-1 rounded-full bg-border" />
</ScrollAreaPrimitive.ScrollAreaScrollbar>
));
ScrollBar.displayName = ScrollAreaPrimitive.ScrollAreaScrollbar.displayName;
export { ScrollArea, ScrollBar };
+56
View File
@@ -178,6 +178,18 @@ importers:
'@frontend-opensource/use-react-hooks':
specifier: ^1.1.4
version: 1.1.4
'@fullcalendar/core':
specifier: ^6.1.17
version: 6.1.17
'@fullcalendar/daygrid':
specifier: ^6.1.17
version: 6.1.17(@fullcalendar/core@6.1.17)
'@fullcalendar/interaction':
specifier: ^6.1.17
version: 6.1.17(@fullcalendar/core@6.1.17)
'@fullcalendar/react':
specifier: ^6.1.17
version: 6.1.17(@fullcalendar/core@6.1.17)(react-dom@18.3.1)(react@18.3.1)
'@hookform/resolvers':
specifier: ^4.1.3
version: 4.1.3(react-hook-form@7.54.2)
@@ -214,6 +226,9 @@ importers:
js-cookie:
specifier: ^3.0.5
version: 3.0.5
lucide-react:
specifier: ^0.474.0
version: 0.474.0(react@18.3.1)
next:
specifier: 15.3.0
version: 15.3.0(react-dom@18.3.1)(react@18.3.1)
@@ -538,6 +553,9 @@ importers:
'@radix-ui/react-radio-group':
specifier: ^1.2.2
version: 1.2.3(@types/react-dom@18.3.5)(@types/react@18.3.18)(react-dom@18.3.1)(react@18.3.1)
'@radix-ui/react-scroll-area':
specifier: ^1.2.3
version: 1.2.3(@types/react-dom@18.3.5)(@types/react@18.3.18)(react-dom@18.3.1)(react@18.3.1)
'@radix-ui/react-select':
specifier: ^2.1.6
version: 2.1.6(@types/react-dom@18.3.5)(@types/react@18.3.18)(react-dom@18.3.1)(react@18.3.1)
@@ -1756,6 +1774,40 @@ packages:
react-dom: 18.3.1(react@18.3.1)
dev: false
/@fullcalendar/core@6.1.17:
resolution: {integrity: sha512-0W7lnIrv18ruJ5zeWBeNZXO8qCWlzxDdp9COFEsZnyNjiEhUVnrW/dPbjRKYpL0edGG0/Lhs0ghp1z/5ekt8ZA==}
dependencies:
preact: 10.12.1
dev: false
/@fullcalendar/daygrid@6.1.17(@fullcalendar/core@6.1.17):
resolution: {integrity: sha512-K7m+pd7oVJ9fW4h7CLDdDGJbc9szJ1xDU1DZ2ag+7oOo1aCNLv44CehzkkknM6r8EYlOOhgaelxQpKAI4glj7A==}
peerDependencies:
'@fullcalendar/core': ~6.1.17
dependencies:
'@fullcalendar/core': 6.1.17
dev: false
/@fullcalendar/interaction@6.1.17(@fullcalendar/core@6.1.17):
resolution: {integrity: sha512-AudvQvgmJP2FU89wpSulUUjeWv24SuyCx8FzH2WIPVaYg+vDGGYarI7K6PcM3TH7B/CyaBjm5Rqw9lXgnwt5YA==}
peerDependencies:
'@fullcalendar/core': ~6.1.17
dependencies:
'@fullcalendar/core': 6.1.17
dev: false
/@fullcalendar/react@6.1.17(@fullcalendar/core@6.1.17)(react-dom@18.3.1)(react@18.3.1):
resolution: {integrity: sha512-AA8soHhlfRH5dUeqHnfAtzDiXa2vrgWocJSK/F5qzw/pOxc9MqpuoS/nQBROWtHHg6yQUg3DoGqOOhi7dmylXQ==}
peerDependencies:
'@fullcalendar/core': ~6.1.17
react: ^18.3.1
react-dom: ^18.3.1
dependencies:
'@fullcalendar/core': 6.1.17
react: 18.3.1
react-dom: 18.3.1(react@18.3.1)
dev: false
/@hookform/resolvers@4.1.3(react-hook-form@7.54.2):
resolution: {integrity: sha512-Jsv6UOWYTrEFJ/01ZrnwVXs7KDvP8XIo115i++5PWvNkNvkrsTfGiLS6w+eJ57CYtUtDQalUWovCZDHFJ8u1VQ==}
peerDependencies:
@@ -7665,6 +7717,10 @@ packages:
picocolors: 1.1.1
source-map-js: 1.2.1
/preact@10.12.1:
resolution: {integrity: sha512-l8386ixSsBdbreOAkqtrwqHwdvR35ID8c3rKPa8lCWuO86dBi32QWHV4vfsZK1utLLFMvw+Z5Ad4XLkZzchscg==}
dev: false
/precinct@12.2.0:
resolution: {integrity: sha512-NFBMuwIfaJ4SocE9YXPU/n4AcNSoFMVFjP72nvl3cx69j/ke61/hPOWFREVxLkFhhEGnA8ZuVfTqJBa+PK3b5w==}
engines: {node: '>=18'}