From 233f81541d5bd37257fad946425c842625142dba Mon Sep 17 00:00:00 2001 From: Shinuwa Date: Mon, 24 Aug 2026 20:29:57 +0200 Subject: [PATCH] Split calendar module into form and views --- tests/static-toolboxes.test.mjs | 35 +- .../toolboxes/modules/CalendarEventForm.jsx | 392 +++++++++++ .../toolboxes/modules/CalendarModule.jsx | 664 ++---------------- .../toolboxes/modules/CalendarViews.jsx | 302 ++++++++ 4 files changed, 774 insertions(+), 619 deletions(-) create mode 100644 website/src/features/toolboxes/modules/CalendarEventForm.jsx create mode 100644 website/src/features/toolboxes/modules/CalendarViews.jsx diff --git a/tests/static-toolboxes.test.mjs b/tests/static-toolboxes.test.mjs index 91fea27..7813019 100644 --- a/tests/static-toolboxes.test.mjs +++ b/tests/static-toolboxes.test.mjs @@ -151,6 +151,9 @@ test("toolbox module registry and modules expose expected behavior", async () => const timerParts = await readFile("website/src/features/toolboxes/modules/TimerParts.jsx", "utf8"); const timerOptions = await readFile("website/src/features/toolboxes/modules/timerOptions.js", "utf8"); const calendarModule = await readFile("website/src/features/toolboxes/modules/CalendarModule.jsx", "utf8"); + const calendarEventForm = await readFile("website/src/features/toolboxes/modules/CalendarEventForm.jsx", "utf8"); + const calendarViews = await readFile("website/src/features/toolboxes/modules/CalendarViews.jsx", "utf8"); + const calendarBundle = `${calendarModule}\n${calendarEventForm}\n${calendarViews}`; const calendarUtils = await readFile("website/src/features/toolboxes/modules/calendarUtils.js", "utf8"); const taskPlannerModule = await readFile("website/src/features/toolboxes/modules/TaskPlannerModule.jsx", "utf8"); const taskPlannerItems = await readFile("website/src/features/toolboxes/modules/TaskPlannerItems.jsx", "utf8"); @@ -321,28 +324,28 @@ test("toolbox module registry and modules expose expected behavior", async () => assert.match(timerModule, /TextExchangeActions/); assert.match(calendarModule, /export function CalendarModule/); assert.match(calendarModule, /normalizeCalendarData/); - assert.match(calendarModule, /calendar-mode-choice/); - assert.match(calendarModule, /calendar-week-grid/); - assert.match(calendarModule, /calendar-month-grid/); - assert.match(calendarModule, /getMonthOccurrenceSegments/); - assert.match(calendarModule, /calendar-month-bar/); - assert.match(calendarModule, /MonthDateDropdown/); - assert.match(calendarModule, /MonthEventRepeatSwitch/); - assert.match(calendarModule, /monthlyRepeatBy/); + assert.match(calendarBundle, /calendar-mode-choice/); + assert.match(calendarBundle, /calendar-week-grid/); + assert.match(calendarBundle, /calendar-month-grid/); + assert.match(calendarBundle, /getMonthOccurrenceSegments/); + assert.match(calendarBundle, /calendar-month-bar/); + assert.match(calendarBundle, /MonthDateDropdown/); + assert.match(calendarBundle, /MonthEventRepeatSwitch/); + assert.match(calendarBundle, /monthlyRepeatBy/); assert.match(calendarModule, /displayedMonth/); assert.match(calendarModule, /deleteOccurrence/); - assert.match(calendarModule, /onPointerDown/); + assert.match(calendarBundle, /onPointerDown/); assert.match(calendarModule, /copyEvent/); - assert.match(calendarModule, /calendar-repeat-switch/); - assert.match(calendarModule, /layout-switch calendar-month-repeat-switch/); - assert.match(calendarModule, /WEEK_TIME_INPUT_STEP_MINUTES \* 60/); - assert.match(calendarModule, /durationMinutes < 120/); - assert.match(calendarModule, /is-compact/); + assert.match(calendarBundle, /calendar-repeat-switch/); + assert.match(calendarBundle, /layout-switch calendar-month-repeat-switch/); + assert.match(calendarBundle, /WEEK_TIME_INPUT_STEP_MINUTES \* 60/); + assert.match(calendarBundle, /durationMinutes < 120/); + assert.match(calendarBundle, /is-compact/); assert.match(calendarModule, /handleKeyDown/); assert.match(calendarModule, /TextExchangeActions/); assert.match(calendarModule, /type="calendar"/); - assert.match(calendarModule, /repeatLabel/); - assert.match(calendarModule, /CalendarAlertModeSwitch/); + assert.match(calendarBundle, /repeatLabel/); + assert.match(calendarBundle, /CalendarAlertModeSwitch/); assert.doesNotMatch(moduleRegistry, /calendarInitOpen/); assert.doesNotMatch(moduleRegistry, /calendar-init-modal/); assert.match(calendarUtils, /export function getCalendarOccurrences/); diff --git a/website/src/features/toolboxes/modules/CalendarEventForm.jsx b/website/src/features/toolboxes/modules/CalendarEventForm.jsx new file mode 100644 index 0000000..7b6d62c --- /dev/null +++ b/website/src/features/toolboxes/modules/CalendarEventForm.jsx @@ -0,0 +1,392 @@ +// Rôle : rend le formulaire calendrier et ses contrôles de date, répétition, couleur et alertes. +import { Icon } from "../../../components/Icon.jsx"; +import { + CALENDAR_ALERT_OFFSETS, + CALENDAR_COLORS, + WEEK_TIME_INPUT_STEP_MINUTES, + addMonths, + formatLocalDateKey, + getLocalDayIndex, + getMonthGridDays, + getMonthStart, + pad2, + parseLocalDateKey +} from "./calendarUtils.js"; +import { WEEKDAY_LABELS } from "./calendarLayout.js"; +import { ALERT_MODES } from "./timerOptions.js"; + +const CALENDAR_COLOR_VALUES = { + violet: "#8b5cf6", + gold: "#f6c453", + cyan: "#22d3ee", + pink: "#d946ef", + green: "#22c55e", + gray: "#7d879f" +}; + +function normalizeDraftDateKey(value) { + const date = parseLocalDateKey(value); + return date ? formatLocalDateKey(date) : ""; +} + +function formatMonthDateLabel(dateKey, mode) { + const date = parseLocalDateKey(dateKey); + if (!date) return ""; + const weekday = WEEKDAY_LABELS[getLocalDayIndex(date)]; + const dayLabel = date.toLocaleDateString(undefined, { day: "2-digit", month: "short", year: "numeric" }); + if (mode !== "weekday") return dayLabel; + const ordinal = Math.floor((date.getDate() - 1) / 7) + 1; + return `S${pad2(ordinal)} - ${weekday}`; +} + +function formatMonthTitle(dateLike) { + return new Date(dateLike).toLocaleDateString(undefined, { month: "long", year: "numeric" }); +} + +function getWeekdayPickerRows(monthDate) { + const monthGridDays = getMonthGridDays(monthDate).filter((day) => day.inMonth); + return Array.from({ length: 5 }, (_, rowIndex) => { + const ordinal = rowIndex + 1; + return { + ordinal, + days: WEEKDAY_LABELS.map((_, dayIndex) => { + const matches = monthGridDays.filter((day) => day.dayIndex === dayIndex); + return matches[rowIndex] || null; + }) + }; + }); +} + +export function CalendarEventForm({ + data, + draft, + error, + activeOccurrenceDate, + monthStart, + selectedAlertOffsets, + textContent, + onSubmit, + onDraftChange, + onAlertModeChange, + onAlertOffsetToggle, + onDeleteEvent, + onDeleteOccurrence, + onClose, + onDisplayedMonthChange +}) { + return ( +
+
+ + onDraftChange({ color })} /> + {draft.id &&
+ {data.viewMode === "month" && draft.repeat !== "none" && activeOccurrenceDate && ( + + )} + +
} +
+
+ {data.viewMode === "month" && ( +
+ {textContent.monthlyRepeatLabel || "Règle mensuelle"} + onDraftChange({ + monthlyRepeatBy, + repeat: monthlyRepeatBy === "monthDay" && draft.repeat === "weekly" ? "none" : draft.repeat + })} + /> +
+ )} +
+ {data.viewMode === "month" ? textContent.startDayLabel || "Date début" : textContent.dayLabel || "Jour"} + {data.viewMode === "month" ? ( + onDraftChange({ startDate: value, endDate: (draft.endDate && draft.endDate >= value) ? draft.endDate : value })} + /> + ) : ( + + )} +
+ {data.viewMode === "month" && ( +
+ {textContent.endDayLabel || "Date fin"} + onDraftChange({ endDate: value >= draft.startDate ? value : draft.startDate })} + /> +
+ )} + {data.viewMode === "week" && ( + <> + + + + )} + {data.viewMode === "month" ? ( +
+ {textContent.repeatLabel || "Répétition"} + onDraftChange({ repeat })} /> +
+ ) :
+ {textContent.repeatableTitle || "Répétable"} +