feat: improve date/time fields UI and logic
- Change 'Start Date' label to 'Date' when only single date field is visible in normal mode - Fix all-day slot display: show correct start/end dates for single click and multi-day drag - Update API save logic: add +1 day to end date for all-day events (API requirement) - Migrate from moment to dayjs for date handling with AdapterDayjs - Remove manual date clamping logic - MUI/dayjs handles invalid dates automatically - Add validation guard to prevent invalid dates from clearing field values - Update test case to match new all-day event API behavior
This commit is contained in:
committed by
Benoit TELLIER
parent
41b4c41215
commit
c1f37e3ebd
@@ -11,6 +11,8 @@ export interface ValidationParams {
|
||||
endTime: string;
|
||||
allday: boolean;
|
||||
showValidationErrors: boolean;
|
||||
hasEndDateChanged?: boolean;
|
||||
showMore?: boolean;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -38,6 +40,8 @@ export function validateEventForm(params: ValidationParams): ValidationResult {
|
||||
endTime,
|
||||
allday,
|
||||
showValidationErrors,
|
||||
hasEndDateChanged = false,
|
||||
showMore = false,
|
||||
} = params;
|
||||
|
||||
const isTitleValid = title.trim().length > 0;
|
||||
@@ -46,6 +50,11 @@ export function validateEventForm(params: ValidationParams): ValidationResult {
|
||||
let isDateTimeValid = true;
|
||||
let dateTimeError = "";
|
||||
|
||||
// Determine which fields are visible based on UI mode
|
||||
const showFullFields = showMore || hasEndDateChanged;
|
||||
const showTimeOnly = !allday && !showFullFields;
|
||||
const showDateOnly = allday;
|
||||
|
||||
// Validate start date
|
||||
if (!startDate || startDate.trim() === "") {
|
||||
isDateTimeValid = false;
|
||||
@@ -54,21 +63,78 @@ export function validateEventForm(params: ValidationParams): ValidationResult {
|
||||
// Validate start time (if not all-day)
|
||||
else if (!allday && (!startTime || startTime.trim() === "")) {
|
||||
isDateTimeValid = false;
|
||||
dateTimeError = "Start time and End time is required";
|
||||
dateTimeError = "Start time is required";
|
||||
}
|
||||
// Validate end date
|
||||
else if (!endDate || endDate.trim() === "") {
|
||||
isDateTimeValid = false;
|
||||
dateTimeError = "End date is required";
|
||||
}
|
||||
// Validate end time (if not all-day)
|
||||
else if (!allday && (!endTime || endTime.trim() === "")) {
|
||||
isDateTimeValid = false;
|
||||
dateTimeError = "End time is required";
|
||||
}
|
||||
// Validate end vs start
|
||||
else {
|
||||
if (allday) {
|
||||
// Validate end fields based on UI mode
|
||||
else if (showFullFields) {
|
||||
// 4 fields mode: validate both end date and end time
|
||||
if (!endDate || endDate.trim() === "") {
|
||||
isDateTimeValid = false;
|
||||
dateTimeError = "End date is required";
|
||||
} else if (!allday && (!endTime || endTime.trim() === "")) {
|
||||
isDateTimeValid = false;
|
||||
dateTimeError = "End time is required";
|
||||
} else {
|
||||
// Validate total datetime
|
||||
if (allday) {
|
||||
const toLocalDate = (ymd: string) => {
|
||||
const [y, m, d] = ymd.split("-").map((v) => parseInt(v, 10));
|
||||
if (!y || !m || !d) return new Date(NaN);
|
||||
return new Date(y, m - 1, d);
|
||||
};
|
||||
|
||||
const startOnly = toLocalDate(startDate);
|
||||
const endOnly = toLocalDate(endDate);
|
||||
|
||||
if (isNaN(startOnly.getTime()) || isNaN(endOnly.getTime())) {
|
||||
isDateTimeValid = false;
|
||||
dateTimeError = "Invalid date";
|
||||
} else if (endOnly < startOnly) {
|
||||
isDateTimeValid = false;
|
||||
dateTimeError = "End date must be on or after start date";
|
||||
}
|
||||
} else {
|
||||
const startDateTime = new Date(combineDateTime(startDate, startTime));
|
||||
const endDateTime = new Date(combineDateTime(endDate, endTime));
|
||||
|
||||
if (isNaN(startDateTime.getTime()) || isNaN(endDateTime.getTime())) {
|
||||
isDateTimeValid = false;
|
||||
dateTimeError = "Invalid date/time";
|
||||
} else if (endDateTime <= startDateTime) {
|
||||
isDateTimeValid = false;
|
||||
dateTimeError = "End date/time must be after start date/time";
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (showTimeOnly) {
|
||||
// 3 fields mode: validate time only (end time > start time, same day)
|
||||
if (!endTime || endTime.trim() === "") {
|
||||
isDateTimeValid = false;
|
||||
dateTimeError = "End time is required";
|
||||
} else {
|
||||
// Compare times only (same day is assumed)
|
||||
const startTimeParts = startTime.split(":");
|
||||
const endTimeParts = endTime.split(":");
|
||||
if (startTimeParts.length === 2 && endTimeParts.length === 2) {
|
||||
const startMinutes =
|
||||
parseInt(startTimeParts[0]) * 60 + parseInt(startTimeParts[1]);
|
||||
const endMinutes =
|
||||
parseInt(endTimeParts[0]) * 60 + parseInt(endTimeParts[1]);
|
||||
if (endMinutes <= startMinutes) {
|
||||
isDateTimeValid = false;
|
||||
dateTimeError = "End time must be after start time";
|
||||
}
|
||||
} else {
|
||||
isDateTimeValid = false;
|
||||
dateTimeError = "Invalid time format";
|
||||
}
|
||||
}
|
||||
} else if (showDateOnly) {
|
||||
// 2 fields mode: validate date only
|
||||
if (!endDate || endDate.trim() === "") {
|
||||
isDateTimeValid = false;
|
||||
dateTimeError = "End date is required";
|
||||
} else {
|
||||
const toLocalDate = (ymd: string) => {
|
||||
const [y, m, d] = ymd.split("-").map((v) => parseInt(v, 10));
|
||||
if (!y || !m || !d) return new Date(NaN);
|
||||
@@ -85,17 +151,6 @@ export function validateEventForm(params: ValidationParams): ValidationResult {
|
||||
isDateTimeValid = false;
|
||||
dateTimeError = "End date must be on or after start date";
|
||||
}
|
||||
} else {
|
||||
const startDateTime = new Date(combineDateTime(startDate, startTime));
|
||||
const endDateTime = new Date(combineDateTime(endDate, endTime));
|
||||
|
||||
if (isNaN(startDateTime.getTime()) || isNaN(endDateTime.getTime())) {
|
||||
isDateTimeValid = false;
|
||||
dateTimeError = "Invalid date/time";
|
||||
} else if (endDateTime <= startDateTime) {
|
||||
isDateTimeValid = false;
|
||||
dateTimeError = "End time must be after start time";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user