refactor: Split EventFormFields.tsx into smaller modules

- Extract helper functions to utils/dateTimeHelpers.ts and utils/dateTimeFormatters.ts
- Extract validation logic to utils/formValidation.ts
- Extract FieldWithLabel component to components/FieldWithLabel.tsx
- Extract DateTimeFields component to components/DateTimeFields.tsx
- Extract all-day toggle logic to hooks/useAllDayToggle.ts
- Update imports in EventModal.tsx, EventUpdateModal.tsx, EventDisplay.tsx, eventHandlers.ts
- Maintain all existing functionality without breaking changes
This commit is contained in:
lenhanphung
2025-10-24 16:23:10 +07:00
committed by Benoit TELLIER
parent 701f2f2b71
commit 01f79553c0
11 changed files with 657 additions and 441 deletions
@@ -0,0 +1,193 @@
import React from "react";
import { combineDateTime } from "../utils/dateTimeHelpers";
import { getRoundedCurrentTime } from "../utils/dateTimeFormatters";
/**
* Parameters for all-day toggle hook
*/
export interface AllDayToggleParams {
allday: boolean;
start: string;
end: string;
startDate: string;
startTime: string;
endDate: string;
endTime: string;
setStartTime: (time: string) => void;
setEndTime: (time: string) => void;
setEndDate: (date: string) => void;
setStart: (start: string) => void;
setEnd: (end: string) => void;
setAllDay: (allday: boolean) => void;
onAllDayChange?: (allday: boolean, start: string, end: string) => void;
}
/**
* Handlers returned by all-day toggle hook
*/
export interface AllDayToggleHandlers {
originalTimeRef: React.MutableRefObject<{
start: string;
end: string;
endDate?: string;
fromAllDaySlot?: boolean;
} | null>;
handleAllDayToggle: () => void;
}
/**
* Custom hook for managing all-day toggle logic
* Handles saving/restoring time values and endDate logic
*/
export function useAllDayToggle(
params: AllDayToggleParams
): AllDayToggleHandlers {
const {
allday,
start,
end,
startDate,
startTime,
endDate,
endTime,
setStartTime,
setEndTime,
setEndDate,
setStart,
setEnd,
setAllDay,
onAllDayChange,
} = params;
// Store original time before toggling to all-day
const originalTimeRef = React.useRef<{
start: string;
end: string;
endDate?: string;
fromAllDaySlot?: boolean;
} | null>(null);
const handleAllDayToggle = React.useCallback(() => {
const newAllDay = !allday;
let newStart = start;
let newEnd = end;
if (newAllDay) {
// OFF => ON: Save original time AND original end date
if (start.includes("T")) {
originalTimeRef.current = {
start: startTime,
end: endTime,
endDate: endDate, // Save original end date
};
}
// Reset time to empty, keep only date part
setStartTime("");
setEndTime("");
newStart = startDate;
newEnd = endDate;
// If same day, extend end to next day
if (startDate === endDate) {
const nextDay = new Date(endDate);
nextDay.setDate(nextDay.getDate() + 1);
newEnd = nextDay.toISOString().split("T")[0];
setEndDate(newEnd); // Update internal endDate state
}
} else {
// ON => OFF: Restore original time AND original end date
if (originalTimeRef.current) {
// Check if this came from all-day slot click
if (originalTimeRef.current.fromAllDaySlot) {
// From all-day slot: set endDate = startDate, use default time
const currentTime = getRoundedCurrentTime();
const hours = String(currentTime.getHours()).padStart(2, "0");
const minutes = String(currentTime.getMinutes()).padStart(2, "0");
const timeStr = `${hours}:${minutes}`;
newStart = combineDateTime(startDate, timeStr);
// End time = start time + 1 hour
const endTimeDate = new Date(currentTime);
endTimeDate.setHours(endTimeDate.getHours() + 1);
const endHours = String(endTimeDate.getHours()).padStart(2, "0");
const endMinutes = String(endTimeDate.getMinutes()).padStart(2, "0");
const endTimeStr = `${endHours}:${endMinutes}`;
newEnd = combineDateTime(startDate, endTimeStr); // Use startDate as endDate
// Update internal states
setStartTime(timeStr);
setEndTime(endTimeStr);
setEndDate(startDate); // Set endDate = startDate
} else {
// Normal case: restore original time AND original end date
const restoredEndDate = originalTimeRef.current.endDate || endDate;
newStart = combineDateTime(startDate, originalTimeRef.current.start);
newEnd = combineDateTime(
restoredEndDate,
originalTimeRef.current.end
);
// Update internal states
setStartTime(originalTimeRef.current.start);
setEndTime(originalTimeRef.current.end);
setEndDate(restoredEndDate);
}
originalTimeRef.current = null;
} else {
// No original time: use rounded current time with 1 hour duration
const currentTime = getRoundedCurrentTime();
const hours = String(currentTime.getHours()).padStart(2, "0");
const minutes = String(currentTime.getMinutes()).padStart(2, "0");
const timeStr = `${hours}:${minutes}`;
newStart = combineDateTime(startDate, timeStr);
// End time = start time + 1 hour (not 30 mins)
const endTimeDate = new Date(currentTime);
endTimeDate.setHours(endTimeDate.getHours() + 1);
const endHours = String(endTimeDate.getHours()).padStart(2, "0");
const endMinutes = String(endTimeDate.getMinutes()).padStart(2, "0");
const endTimeStr = `${endHours}:${endMinutes}`;
newEnd = combineDateTime(endDate, endTimeStr);
// Update internal states
setStartTime(timeStr);
setEndTime(endTimeStr);
}
}
if (!onAllDayChange) {
setStart(newStart);
setEnd(newEnd);
setAllDay(newAllDay);
} else {
onAllDayChange(newAllDay, newStart, newEnd);
}
}, [
allday,
start,
end,
startDate,
startTime,
endDate,
endTime,
setStartTime,
setEndTime,
setEndDate,
setStart,
setEnd,
setAllDay,
onAllDayChange,
]);
return {
originalTimeRef,
handleAllDayToggle,
};
}