286 lines
9.8 KiB
TypeScript
286 lines
9.8 KiB
TypeScript
import { useState, useRef, useEffect } from "react";
|
|
import FullCalendar from "@fullcalendar/react";
|
|
import dayGridPlugin from "@fullcalendar/daygrid";
|
|
import timeGridPlugin from "@fullcalendar/timegrid";
|
|
import interactionPlugin from "@fullcalendar/interaction";
|
|
import { EventInput, DateSelectArg, EventClickArg } from "@fullcalendar/core";
|
|
import { Modal } from "../components/ui/modal";
|
|
import { useModal } from "../hooks/useModal";
|
|
import PageMeta from "../components/common/PageMeta";
|
|
import Button from "../components/ui/button/Button";
|
|
import InputField from "../components/form/input/InputField";
|
|
|
|
interface CalendarEvent extends EventInput {
|
|
extendedProps: {
|
|
calendar: string;
|
|
};
|
|
}
|
|
|
|
const Calendar: React.FC = () => {
|
|
const [selectedEvent, setSelectedEvent] = useState<CalendarEvent | null>(
|
|
null
|
|
);
|
|
const [eventTitle, setEventTitle] = useState("");
|
|
const [eventStartDate, setEventStartDate] = useState("");
|
|
const [eventEndDate, setEventEndDate] = useState("");
|
|
const [eventLevel, setEventLevel] = useState("");
|
|
const [events, setEvents] = useState<CalendarEvent[]>([]);
|
|
const calendarRef = useRef<FullCalendar>(null);
|
|
const { isOpen, openModal, closeModal } = useModal();
|
|
|
|
const calendarsEvents = {
|
|
Danger: "danger",
|
|
Success: "success",
|
|
Primary: "primary",
|
|
Warning: "warning",
|
|
};
|
|
|
|
useEffect(() => {
|
|
// Initialize with some events
|
|
setEvents([
|
|
{
|
|
id: "1",
|
|
title: "Event Conf.",
|
|
start: new Date().toISOString().split("T")[0],
|
|
extendedProps: { calendar: "Danger" },
|
|
},
|
|
{
|
|
id: "2",
|
|
title: "Meeting",
|
|
start: new Date(Date.now() + 86400000).toISOString().split("T")[0],
|
|
extendedProps: { calendar: "Success" },
|
|
},
|
|
{
|
|
id: "3",
|
|
title: "Workshop",
|
|
start: new Date(Date.now() + 172800000).toISOString().split("T")[0],
|
|
end: new Date(Date.now() + 259200000).toISOString().split("T")[0],
|
|
extendedProps: { calendar: "Primary" },
|
|
},
|
|
]);
|
|
}, []);
|
|
|
|
const handleDateSelect = (selectInfo: DateSelectArg) => {
|
|
resetModalFields();
|
|
setEventStartDate(selectInfo.startStr);
|
|
setEventEndDate(selectInfo.endStr || selectInfo.startStr);
|
|
openModal();
|
|
};
|
|
|
|
const handleEventClick = (clickInfo: EventClickArg) => {
|
|
const event = clickInfo.event;
|
|
setSelectedEvent(event as unknown as CalendarEvent);
|
|
setEventTitle(event.title);
|
|
setEventStartDate(event.start?.toISOString().split("T")[0] || "");
|
|
setEventEndDate(event.end?.toISOString().split("T")[0] || "");
|
|
setEventLevel(event.extendedProps.calendar);
|
|
openModal();
|
|
};
|
|
|
|
const handleAddOrUpdateEvent = () => {
|
|
if (selectedEvent) {
|
|
// Update existing event
|
|
setEvents((prevEvents) =>
|
|
prevEvents.map((event) =>
|
|
event.id === selectedEvent.id
|
|
? {
|
|
...event,
|
|
title: eventTitle,
|
|
start: eventStartDate,
|
|
end: eventEndDate,
|
|
extendedProps: { calendar: eventLevel },
|
|
}
|
|
: event
|
|
)
|
|
);
|
|
} else {
|
|
// Add new event
|
|
const newEvent: CalendarEvent = {
|
|
id: Date.now().toString(),
|
|
title: eventTitle,
|
|
start: eventStartDate,
|
|
end: eventEndDate,
|
|
allDay: true,
|
|
extendedProps: { calendar: eventLevel },
|
|
};
|
|
setEvents((prevEvents) => [...prevEvents, newEvent]);
|
|
}
|
|
closeModal();
|
|
resetModalFields();
|
|
};
|
|
|
|
const resetModalFields = () => {
|
|
setEventTitle("");
|
|
setEventStartDate("");
|
|
setEventEndDate("");
|
|
setEventLevel("");
|
|
setSelectedEvent(null);
|
|
};
|
|
|
|
return (
|
|
<>
|
|
<PageMeta
|
|
title="React.js Calendar Dashboard | TailAdmin - Next.js Admin Dashboard Template"
|
|
description="This is React.js Calendar Dashboard page for TailAdmin - React.js Tailwind CSS Admin Dashboard Template"
|
|
/>
|
|
<div className="rounded-2xl border border-gray-200 bg-white dark:border-gray-800 dark:bg-white/[0.03]">
|
|
<div className="custom-calendar">
|
|
<FullCalendar
|
|
ref={calendarRef}
|
|
plugins={[dayGridPlugin, timeGridPlugin, interactionPlugin]}
|
|
initialView="dayGridMonth"
|
|
headerToolbar={{
|
|
left: "prev,next addEventButton",
|
|
center: "title",
|
|
right: "dayGridMonth,timeGridWeek,timeGridDay",
|
|
}}
|
|
events={events}
|
|
selectable={true}
|
|
select={handleDateSelect}
|
|
eventClick={handleEventClick}
|
|
eventContent={renderEventContent}
|
|
customButtons={{
|
|
addEventButton: {
|
|
text: "Add Event +",
|
|
click: openModal,
|
|
},
|
|
}}
|
|
/>
|
|
</div>
|
|
<Modal
|
|
isOpen={isOpen}
|
|
onClose={closeModal}
|
|
className="max-w-[700px] p-6 lg:p-10"
|
|
>
|
|
<div className="flex flex-col px-2 overflow-y-auto custom-scrollbar">
|
|
<div>
|
|
<h5 className="mb-2 font-semibold text-gray-800 modal-title text-theme-xl dark:text-white/90 lg:text-2xl">
|
|
{selectedEvent ? "Edit Event" : "Add Event"}
|
|
</h5>
|
|
<p className="text-sm text-gray-500 dark:text-gray-400">
|
|
Plan your next big moment: schedule or edit an event to stay on
|
|
track
|
|
</p>
|
|
</div>
|
|
<div className="mt-8">
|
|
<div>
|
|
<div>
|
|
<label className="mb-1.5 block text-sm font-medium text-gray-700 dark:text-gray-400">
|
|
Event Title
|
|
</label>
|
|
<InputField
|
|
id="event-title"
|
|
type="text"
|
|
value={eventTitle}
|
|
onChange={(e) => setEventTitle(e.target.value)}
|
|
/>
|
|
</div>
|
|
</div>
|
|
<div className="mt-6">
|
|
<label className="block mb-4 text-sm font-medium text-gray-700 dark:text-gray-400">
|
|
Event Color
|
|
</label>
|
|
<div className="flex flex-wrap items-center gap-4 sm:gap-5">
|
|
{Object.entries(calendarsEvents).map(([key, value]) => (
|
|
<div key={key} className="n-chk">
|
|
<div
|
|
className={`form-check form-check-${value} form-check-inline`}
|
|
>
|
|
<label
|
|
className="flex items-center text-sm text-gray-700 form-check-label dark:text-gray-400"
|
|
htmlFor={`modal${key}`}
|
|
>
|
|
<span className="relative">
|
|
<input
|
|
className="sr-only form-check-input"
|
|
type="radio"
|
|
name="event-level"
|
|
value={key}
|
|
id={`modal${key}`}
|
|
checked={eventLevel === key}
|
|
onChange={() => setEventLevel(key)}
|
|
/>
|
|
<span className="flex items-center justify-center w-5 h-5 mr-2 border border-gray-300 rounded-full box dark:border-gray-700">
|
|
<span
|
|
className={`h-2 w-2 rounded-full bg-white ${
|
|
eventLevel === key ? "block" : "hidden"
|
|
}`}
|
|
></span>
|
|
</span>
|
|
</span>
|
|
{key}
|
|
</label>
|
|
</div>
|
|
</div>
|
|
))}
|
|
</div>
|
|
</div>
|
|
|
|
<div className="mt-6">
|
|
<label className="mb-1.5 block text-sm font-medium text-gray-700 dark:text-gray-400">
|
|
Enter Start Date
|
|
</label>
|
|
<div className="relative">
|
|
<InputField
|
|
id="event-start-date"
|
|
type="date"
|
|
value={eventStartDate}
|
|
onChange={(e) => setEventStartDate(e.target.value)}
|
|
/>
|
|
</div>
|
|
</div>
|
|
|
|
<div className="mt-6">
|
|
<label className="mb-1.5 block text-sm font-medium text-gray-700 dark:text-gray-400">
|
|
Enter End Date
|
|
</label>
|
|
<div className="relative">
|
|
<InputField
|
|
id="event-end-date"
|
|
type="date"
|
|
value={eventEndDate}
|
|
onChange={(e) => setEventEndDate(e.target.value)}
|
|
/>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div className="flex items-center gap-3 mt-6 modal-footer sm:justify-end">
|
|
<Button
|
|
onClick={closeModal}
|
|
variant="outline"
|
|
tone="neutral"
|
|
size="md"
|
|
>
|
|
Close
|
|
</Button>
|
|
<Button
|
|
onClick={handleAddOrUpdateEvent}
|
|
variant="primary"
|
|
tone="brand"
|
|
size="md"
|
|
>
|
|
{selectedEvent ? "Update Changes" : "Add Event"}
|
|
</Button>
|
|
</div>
|
|
</div>
|
|
</Modal>
|
|
</div>
|
|
</>
|
|
);
|
|
};
|
|
|
|
const renderEventContent = (eventInfo: any) => {
|
|
const colorClass = `fc-bg-${eventInfo.event.extendedProps.calendar.toLowerCase()}`;
|
|
return (
|
|
<div
|
|
className={`event-fc-color flex fc-event-main ${colorClass} p-1 rounded-sm`}
|
|
>
|
|
<div className="fc-daygrid-event-dot"></div>
|
|
<div className="fc-event-time">{eventInfo.timeText}</div>
|
|
<div className="fc-event-title">{eventInfo.event.title}</div>
|
|
</div>
|
|
);
|
|
};
|
|
|
|
export default Calendar;
|