Task #275: Postpone confirm + compact 3-tab dialog

Refactor the 5-min upcoming-meeting alert's postpone sub-dialog:

- Add an explicit "Are you sure?" confirm step before any postpone
  action (matches the existing Cancel pattern). Both minute chips
  (5/10/15/30/45/60) and the manual "Apply now" button now route
  through requestPostpone() -> amber confirm block -> postponeBy().
  Switching tabs clears any armed pending-postpone state.

- Replace the three stacked bordered sections with a compact 3-tab
  layout (Postpone / Reschedule / Cancel). Default tab is Postpone.
  Reschedule grid is responsive (grid-cols-1 sm:grid-cols-3) so the
  dialog no longer overflows on common laptop viewports.

- Implement full ARIA tab pattern: role="tablist"/"tab"/"tabpanel",
  id + aria-controls + aria-labelledby, roving tabindex, and
  ArrowLeft/Right/Home/End keyboard nav (RTL-aware). Focus moves to
  the newly active tab.

- Bilingual: added EN+AR keys (tabPostpone, tabReschedule, tabCancel,
  postponeConfirmPrompt, postponeConfirmYes, rescheduleHint), trimmed
  postponeIntro, removed obsolete *Label keys.

- Spec updated: existing "Postpone by 10", "chip immediately shifts",
  "Reschedule", and both "Cancel" tests now click the new tabs and
  go through the confirm step. Added two new tests:
  "Back on postpone confirm leaves meeting unchanged" and
  "switching tabs clears armed postpone confirm".

Out of scope: server route changes, realtime alert-state push, the
floating alert panel itself.

Architect review: APPROVED. Reschedule grid widened to follow the
literal spec progression (grid-cols-1 sm:grid-cols-2 md:grid-cols-3)
per the architect's only non-blocking comment.

All 11 spec tests pass in batch (1.3m, single worker, with retry).
Earlier intermittent flakes were traced to leftover seeded test rows
from prior runs (cleaned) and pre-existing polling tightness — not
regressions from this task.
This commit is contained in:
Riyadh
2026-05-01 13:42:36 +00:00
parent a739daf28c
commit 531cee9be4
@@ -913,10 +913,10 @@ function PostponeDialog({
<p className="text-xs text-muted-foreground">
{t("executiveMeetings.alert.rescheduleHint")}
</p>
{/* Responsive: single column on narrow viewports, 3 columns
from sm-up. Prevents horizontal overflow inside the
dialog on phones / split panes. */}
<div className="grid grid-cols-1 gap-2 sm:grid-cols-3">
{/* Responsive grid: 1 col on narrow / 2 cols on sm /
3 cols on md+. Prevents horizontal overflow inside the
dialog on phones and split panes. */}
<div className="grid grid-cols-1 gap-2 sm:grid-cols-2 md:grid-cols-3">
<div className="space-y-1">
<Label className="text-xs">
{t("executiveMeetings.alert.rescheduleDate")}