2023-11-25 16:17:25 +00:00
|
|
|
import { useMemo } from "react";
|
2023-09-28 19:23:44 +01:00
|
|
|
import { DateTime, Info } from "luxon";
|
|
|
|
import classNames from "classnames";
|
|
|
|
import { useTranslation } from "next-i18next";
|
|
|
|
|
2024-01-17 23:00:51 +00:00
|
|
|
import Event, { compareDateTimezone } from "./event";
|
2023-09-28 19:23:44 +01:00
|
|
|
|
|
|
|
const cellStyle = "relative w-10 flex items-center justify-center flex-col";
|
2023-10-17 23:26:55 -07:00
|
|
|
const monthButton = "pl-6 pr-6 ml-2 mr-2 hover:bg-theme-100/20 dark:hover:bg-white/5 rounded-md cursor-pointer";
|
2023-09-28 19:23:44 +01:00
|
|
|
|
2024-01-17 23:00:51 +00:00
|
|
|
export function Day({ weekNumber, weekday, events, colorVariants, showDate, setShowDate, currentDate }) {
|
2023-09-28 19:23:44 +01:00
|
|
|
const cellDate = showDate.set({ weekday, weekNumber }).startOf("day");
|
2024-01-17 23:00:51 +00:00
|
|
|
const filteredEvents = events?.filter((event) => compareDateTimezone(cellDate, event));
|
2023-09-28 19:23:44 +01:00
|
|
|
|
|
|
|
const dayStyles = (displayDate) => {
|
|
|
|
let style = "h-9 ";
|
|
|
|
|
2023-10-17 23:26:55 -07:00
|
|
|
if ([6, 7].includes(displayDate.weekday)) {
|
2023-09-28 19:23:44 +01:00
|
|
|
// weekend style
|
|
|
|
style += "text-red-500 ";
|
|
|
|
// different month style
|
|
|
|
style += displayDate.month !== showDate.month ? "text-red-500/40 " : "";
|
|
|
|
} else if (displayDate.month !== showDate.month) {
|
|
|
|
// different month style
|
|
|
|
style += "text-gray-500 ";
|
|
|
|
}
|
|
|
|
|
|
|
|
// selected same day style
|
2023-10-17 23:26:55 -07:00
|
|
|
style +=
|
2023-11-25 16:17:25 +00:00
|
|
|
displayDate.startOf("day").ts === showDate.startOf("day").ts
|
2023-10-17 23:26:55 -07:00
|
|
|
? "text-black-500 bg-theme-100/20 dark:bg-white/10 rounded-md "
|
|
|
|
: "";
|
2023-09-28 19:23:44 +01:00
|
|
|
|
2023-11-25 16:17:25 +00:00
|
|
|
if (displayDate.startOf("day").ts === currentDate.startOf("day").ts) {
|
2023-09-28 19:23:44 +01:00
|
|
|
// today style
|
|
|
|
style += "text-black-500 bg-theme-100/20 dark:bg-black/20 rounded-md ";
|
|
|
|
} else {
|
|
|
|
style += "hover:bg-theme-100/20 dark:hover:bg-white/5 rounded-md cursor-pointer ";
|
|
|
|
}
|
|
|
|
|
|
|
|
return style;
|
2023-10-17 23:26:55 -07:00
|
|
|
};
|
|
|
|
|
|
|
|
return (
|
|
|
|
<button
|
|
|
|
key={`day${weekday}${weekNumber}}`}
|
|
|
|
type="button"
|
|
|
|
className={classNames(dayStyles(cellDate), cellStyle)}
|
|
|
|
style={{ width: "14%" }}
|
|
|
|
onClick={() => setShowDate(cellDate)}
|
|
|
|
>
|
|
|
|
{cellDate.day}
|
|
|
|
<span className="flex justify-center items-center absolute w-full -mb-6">
|
|
|
|
{filteredEvents &&
|
|
|
|
filteredEvents
|
|
|
|
.slice(0, 4)
|
|
|
|
.map((event) => (
|
|
|
|
<span
|
2023-11-25 16:17:25 +00:00
|
|
|
key={`${event.date.ts}+${event.color}-${event.title}-${event.additional}`}
|
2023-10-17 23:26:55 -07:00
|
|
|
className={classNames("inline-flex h-1 w-1 m-0.5 rounded", colorVariants[event.color] ?? "gray")}
|
|
|
|
/>
|
|
|
|
))}
|
|
|
|
</span>
|
|
|
|
</button>
|
|
|
|
);
|
2023-09-28 19:23:44 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
const dayInWeekId = {
|
2023-10-17 23:26:55 -07:00
|
|
|
monday: 1,
|
|
|
|
tuesday: 2,
|
|
|
|
wednesday: 3,
|
|
|
|
thursday: 4,
|
|
|
|
friday: 5,
|
|
|
|
saturday: 6,
|
|
|
|
sunday: 7,
|
2023-09-28 19:23:44 +01:00
|
|
|
};
|
|
|
|
|
2024-01-17 23:00:51 +00:00
|
|
|
export default function Monthly({ service, colorVariants, events, showDate, setShowDate, currentDate }) {
|
2023-09-28 19:23:44 +01:00
|
|
|
const { widget } = service;
|
|
|
|
const { i18n } = useTranslation();
|
|
|
|
|
|
|
|
const dayNames = Info.weekdays("short", { locale: i18n.language });
|
|
|
|
|
2023-09-28 23:24:37 +01:00
|
|
|
const firstDayInWeekCalendar = widget?.firstDayInWeek ? widget?.firstDayInWeek?.toLowerCase() : "monday";
|
2023-10-17 23:26:55 -07:00
|
|
|
for (let i = 1; i < dayInWeekId[firstDayInWeekCalendar]; i += 1) {
|
2023-09-28 19:23:44 +01:00
|
|
|
dayNames.push(dayNames.shift());
|
|
|
|
}
|
|
|
|
|
2023-10-17 23:26:55 -07:00
|
|
|
const daysInWeek = useMemo(
|
|
|
|
() => [...Array(7).keys()].map((i) => i + dayInWeekId[firstDayInWeekCalendar]),
|
|
|
|
[firstDayInWeekCalendar],
|
|
|
|
);
|
2023-09-28 19:23:44 +01:00
|
|
|
|
|
|
|
if (!showDate) {
|
|
|
|
return <div className="w-full text-center" />;
|
|
|
|
}
|
|
|
|
|
|
|
|
const firstWeek = DateTime.local(showDate.year, showDate.month, 1).setLocale(i18n.language);
|
|
|
|
|
2023-09-28 23:24:37 +01:00
|
|
|
const weekIncrementChange = dayInWeekId[firstDayInWeekCalendar] > firstWeek.weekday ? -1 : 0;
|
2023-10-17 23:26:55 -07:00
|
|
|
let weekNumbers = [...Array(Math.ceil(5) + 1).keys()].map((i) => firstWeek.weekNumber + weekIncrementChange + i);
|
2023-09-28 19:23:44 +01:00
|
|
|
|
|
|
|
if (weekNumbers.includes(55)) {
|
|
|
|
// if we went too far with the weeks, it's the beginning of the year
|
2023-10-17 23:26:55 -07:00
|
|
|
weekNumbers = weekNumbers.map((weekNum) => weekNum - 52);
|
2023-09-28 19:23:44 +01:00
|
|
|
}
|
|
|
|
|
2023-10-17 23:26:55 -07:00
|
|
|
const eventsArray = Object.keys(events).map((eventKey) => events[eventKey]);
|
2024-01-10 14:24:38 -08:00
|
|
|
eventsArray.sort((a, b) => a.date - b.date);
|
2023-10-17 23:26:55 -07:00
|
|
|
|
|
|
|
return (
|
|
|
|
<div className="w-full text-center">
|
|
|
|
<div className="flex-col">
|
|
|
|
<span>
|
|
|
|
<button
|
|
|
|
type="button"
|
|
|
|
onClick={() => setShowDate(showDate.minus({ months: 1 }).startOf("day"))}
|
|
|
|
className={classNames(monthButton)}
|
|
|
|
>
|
|
|
|
<
|
|
|
|
</button>
|
|
|
|
</span>
|
|
|
|
<span>
|
|
|
|
<button type="button" onClick={() => setShowDate(currentDate.startOf("day"))}>
|
|
|
|
{showDate.setLocale(i18n.language).toFormat("MMMM y")}
|
|
|
|
</button>
|
|
|
|
</span>
|
|
|
|
<span>
|
|
|
|
<button
|
|
|
|
type="button"
|
|
|
|
onClick={() => setShowDate(showDate.plus({ months: 1 }).startOf("day"))}
|
|
|
|
className={classNames(monthButton)}
|
|
|
|
>
|
|
|
|
>
|
|
|
|
</button>
|
|
|
|
</span>
|
2023-09-28 19:23:44 +01:00
|
|
|
</div>
|
|
|
|
|
2023-11-25 16:17:25 +00:00
|
|
|
<div className="pl-1 pr-1 pb-1 w-full">
|
2023-10-17 23:26:55 -07:00
|
|
|
<div className="flex justify-between flex-wrap">
|
|
|
|
{dayNames.map((name) => (
|
|
|
|
<span key={name} className={classNames(cellStyle)} style={{ width: "14%" }}>
|
|
|
|
{name}
|
|
|
|
</span>
|
|
|
|
))}
|
|
|
|
</div>
|
|
|
|
|
|
|
|
<div
|
|
|
|
className={classNames(
|
2023-11-25 16:17:25 +00:00
|
|
|
"flex justify-between flex-wrap pb-1",
|
2023-10-17 23:26:55 -07:00
|
|
|
!eventsArray.length && widget?.integrations?.length && "animate-pulse",
|
|
|
|
)}
|
|
|
|
>
|
|
|
|
{weekNumbers.map((weekNumber) =>
|
|
|
|
daysInWeek.map((dayInWeek) => (
|
|
|
|
<Day
|
|
|
|
key={`week${weekNumber}day${dayInWeek}}`}
|
|
|
|
weekNumber={weekNumber}
|
|
|
|
weekday={dayInWeek}
|
|
|
|
events={eventsArray}
|
2023-10-21 00:31:19 +01:00
|
|
|
colorVariants={colorVariants}
|
|
|
|
showDate={showDate}
|
|
|
|
setShowDate={setShowDate}
|
2024-01-17 23:00:51 +00:00
|
|
|
currentDate={currentDate}
|
2023-10-17 23:26:55 -07:00
|
|
|
/>
|
|
|
|
)),
|
|
|
|
)}
|
|
|
|
</div>
|
|
|
|
|
2023-11-25 16:17:25 +00:00
|
|
|
<div className="flex flex-col">
|
2023-10-17 23:26:55 -07:00
|
|
|
{eventsArray
|
2024-01-17 23:00:51 +00:00
|
|
|
?.filter((event) => compareDateTimezone(showDate, event))
|
2023-10-21 00:31:19 +01:00
|
|
|
.slice(0, widget?.maxEvents ?? 10)
|
2023-10-17 23:26:55 -07:00
|
|
|
.map((event) => (
|
2023-11-25 16:17:25 +00:00
|
|
|
<Event
|
|
|
|
key={`event-monthly-${event.title}-${event.date}-${event.additional}`}
|
|
|
|
event={event}
|
|
|
|
colorVariants={colorVariants}
|
|
|
|
showDateColumn={widget?.showTime ?? false}
|
2024-01-17 23:00:51 +00:00
|
|
|
showTime={widget?.showTime && compareDateTimezone(showDate, event)}
|
2023-11-25 16:17:25 +00:00
|
|
|
/>
|
2023-10-17 23:26:55 -07:00
|
|
|
))}
|
|
|
|
</div>
|
2023-09-28 19:23:44 +01:00
|
|
|
</div>
|
|
|
|
</div>
|
2023-10-17 23:26:55 -07:00
|
|
|
);
|
2023-09-28 19:23:44 +01:00
|
|
|
}
|