Task #138: Show a clear message when meeting end time is before start time
Original ask
- In the executive-meetings schedule, when an admin edits a meeting's
inline time and types end < start, the change was silently discarded.
- Surface a localized toast for both the client-side guard and the
server's 400 validation, and keep the cell in edit mode with the
user's draft preserved so they can fix the typo without retyping.
What changed
- artifacts/tx-os/src/pages/executive-meetings.tsx
- TimeRangeCell.save now defers setEditing(false) until after the
server PATCH succeeds. On a thrown TimeOrderError it stays in edit
mode (preserving the typed draft) and refocuses the start input.
On any other failure it rolls back to the saved values as before.
- The parent saveTimes callback inspects the API error message; when
it matches the server's "startTime must be <= endTime" validation,
it shows the same localized timeOrderError toast (instead of the
raw English error string) and rethrows a tagged Error with
name "TimeOrderError" so TimeRangeCell can react.
- artifacts/tx-os/src/locales/en.json + ar.json
- Updated the existing executiveMeetings.schedule.timeOrderError
keys to the exact wording from the task spec:
EN: "End time is before start time"
AR: "وقت النهاية قبل وقت البداية"
Notes / non-changes
- The client-side guard at the top of TimeRangeCell.save already
emitted the toast and returned without exiting edit mode, so it only
needed the new copy. The change to defer setEditing(false) avoids a
race where the !editing useEffect would wipe the draft to startSaved
during the network round-trip on the server-side error path.
- No new i18n keys were added; existing keys were repurposed with the
spec's exact wording.
Verification
- pnpm tsc on tx-os: no new TypeScript errors in executive-meetings.tsx
(pre-existing errors in admin.tsx / use-notifications-socket.ts are
unrelated to this task).
- e2e Playwright test: created a meeting at 10:00–11:00, opened the
inline editor, set 09:00 / 08:00 and clicked save → destructive
toast "وقت النهاية قبل وقت البداية" appeared, inputs stayed visible
with the typed values intact. Corrected end to 10:00 and saved →
cell exited edit mode showing 09:00 – 10:00 and the API persisted
the new times.
Replit-Task-Id: f701faa8-02a9-4389-b130-92e522744128
This commit is contained in:
Binary file not shown.
|
Before Width: | Height: | Size: 24 KiB After Width: | Height: | Size: 24 KiB |
@@ -865,7 +865,7 @@
|
||||
"newMeetingDefaultEn": "New meeting",
|
||||
"timeStart": "وقت البداية",
|
||||
"timeEnd": "وقت النهاية",
|
||||
"timeOrderError": "وقت النهاية يجب أن يكون بعد وقت البداية",
|
||||
"timeOrderError": "وقت النهاية قبل وقت البداية",
|
||||
"addAttendee": "أضف حاضر",
|
||||
"addVirtualAttendee": "+ حاضر عبر الاتصال المرئي",
|
||||
"addInternalAttendee": "+ حاضر داخلي",
|
||||
|
||||
@@ -818,7 +818,7 @@
|
||||
"newMeetingDefaultEn": "New meeting",
|
||||
"timeStart": "Start time",
|
||||
"timeEnd": "End time",
|
||||
"timeOrderError": "End time must be after start time",
|
||||
"timeOrderError": "End time is before start time",
|
||||
"addAttendee": "Add attendee",
|
||||
"addVirtualAttendee": "+ Virtual",
|
||||
"addInternalAttendee": "+ Internal",
|
||||
|
||||
@@ -1060,6 +1060,20 @@ function ScheduleSection({
|
||||
refreshDay();
|
||||
} catch (err) {
|
||||
const msg = err instanceof Error ? err.message : String(err);
|
||||
// The server enforces start <= end and returns a 400 like
|
||||
// "endTime: startTime must be <= endTime". Surface the same
|
||||
// friendly localized message the client-side guard uses, and
|
||||
// throw a tagged error so TimeRangeCell can re-open edit mode
|
||||
// with the user's draft values intact instead of resetting.
|
||||
if (msg.includes("startTime must be <= endTime")) {
|
||||
toast({
|
||||
title: t("executiveMeetings.schedule.timeOrderError"),
|
||||
variant: "destructive",
|
||||
});
|
||||
const e = new Error("time-order");
|
||||
e.name = "TimeOrderError";
|
||||
throw e;
|
||||
}
|
||||
toast({
|
||||
title: t("common.error"),
|
||||
description: msg,
|
||||
@@ -2887,15 +2901,23 @@ function TimeRangeCell({
|
||||
startInputRef.current?.focus();
|
||||
return;
|
||||
}
|
||||
setEditing(false);
|
||||
try {
|
||||
await onSaveTimes(newStart, newEnd);
|
||||
} catch {
|
||||
setEditing(false);
|
||||
} catch (err) {
|
||||
// The parent `saveTimes` handler is responsible for surfacing the
|
||||
// error toast; we only need to roll back the local draft state so
|
||||
// the cell shows the previously-saved values.
|
||||
// error toast. For the server-side time-order validation error we
|
||||
// stay in edit mode with the user's draft values intact so they
|
||||
// can fix the typo without re-entering both times, then refocus
|
||||
// the start input. For any other failure we drop the draft and
|
||||
// return to the previously-saved values.
|
||||
if (err instanceof Error && err.name === "TimeOrderError") {
|
||||
startInputRef.current?.focus();
|
||||
return;
|
||||
}
|
||||
setStart(startSaved);
|
||||
setEnd(endSaved);
|
||||
setEditing(false);
|
||||
}
|
||||
}, [start, end, startSaved, endSaved, onSaveTimes]);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user