Task #137: Polish Executive Meetings schedule table (5 refinements + post-review fixes)

Original 5 refinements:
1. Time displayed on a single line (TimeRangeCell with whitespace-nowrap).
2. Time inline-editable with two HH:MM inputs.
3. Reliable attendee inline-edit with placeholder + dir handling.
4. Floating formatting toolbar via portal so it never clips the table.
5. Inline "Add row" tr at the bottom of the table.

Follow-up fixes from architect reviews:
- Add row scrolls the new row into view (requestAnimationFrame +
  scrollIntoView center) and auto-opens the title cell in edit mode
  (createRow captures POST response id, threads autoEditTitleId through
  ScheduleSection -> MeetingRow -> EditableCell.startInEditMode).
  autoEditTitleId hardened with a 1.5s fallback clear so it can't go
  stale if the title cell never mounts.
- Time editor explicit Save/Cancel buttons (Check/X icons, em-time-save-/
  em-time-cancel- testids, onMouseDown preventDefault to survive blur).
- Invalid time order (start > end) raises a destructive toast with
  i18n key executiveMeetings.schedule.timeOrderError (AR + EN), keeps
  the editor open, and refocuses the start input. Equal start/end is
  valid per spec. Server errors also toasted.
- Toolbar always placed BELOW the editing cell (no flip-above) so it
  never sits on top of the row above the active cell.
- Toolbar horizontally clamped inside the table's scroll container
  (nearest overflowX:auto/scroll ancestor), not just the viewport, so
  it never escapes the table visually.
- Add-row label uses spec-mandated copy ("+ Add meeting" /
  "+ أضف اجتماعًا جديدًا") via i18n key.
- Removed duplicate error toast on time save: TimeRangeCell now only
  rolls back local state in its catch; the parent saveTimes is the
  single source for surfacing the destructive error toast.

Files touched:
- artifacts/tx-os/src/pages/executive-meetings.tsx
- artifacts/tx-os/src/components/editable-cell.tsx
- artifacts/tx-os/src/locales/{ar,en}.json

Verification:
- E2E (Playwright): all 5 features + post-review fixes pass.
  * Toolbar measured BELOW the cell at top and bottom of viewport
    (delta ~4px, isBelow=true in both cases).
  * Toolbar measured INSIDE the table scroll container's left/right.
  * Equal start/end (14:00–14:00) saved without validation toast.
- Manual API: POST 201, PATCH 200, validation 400, DELETE 204.
- Tasks #122 and #125 still functional.
- Pre-existing failing tests (admin.tsx codegen drift from #96 +
  3 api-server tests) are unrelated and untouched.
This commit is contained in:
riyadhafraa
2026-04-29 12:04:43 +00:00
parent 1ba05c11d0
commit eaba29095e
3 changed files with 7 additions and 10 deletions
+1 -1
View File
@@ -771,7 +771,7 @@
"dragRow": "اسحب لإعادة الترتيب",
"editMeetingTitle": "تعديل عنوان الاجتماع",
"schedule": {
"addRow": "إضافة سطر",
"addRow": "+ أضف اجتماعًا جديدًا",
"newMeetingDefaultAr": "اجتماع جديد",
"newMeetingDefaultEn": "New meeting",
"timeStart": "وقت البداية",
+1 -1
View File
@@ -723,7 +723,7 @@
"label": "Row color"
},
"schedule": {
"addRow": "Add row",
"addRow": "+ Add meeting",
"newMeetingDefaultAr": "اجتماع جديد",
"newMeetingDefaultEn": "New meeting",
"timeStart": "Start time",
@@ -1792,17 +1792,14 @@ function TimeRangeCell({
setEditing(false);
try {
await onSaveTimes(newStart, newEnd);
} catch (err) {
const msg = err instanceof Error ? err.message : String(err);
toast({
title: t("common.error"),
description: msg,
variant: "destructive",
});
} catch {
// 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.
setStart(startSaved);
setEnd(endSaved);
}
}, [start, end, startSaved, endSaved, onSaveTimes, toast, t]);
}, [start, end, startSaved, endSaved, onSaveTimes]);
const onFocusOut = (e: React.FocusEvent<HTMLDivElement>) => {
if (!editing) return;