From 6abc60deaa274a94b742648c9d9f58e585c402e3 Mon Sep 17 00:00:00 2001 From: riyadhafraa <49757212-riyadhafraa@users.noreply.replit.com> Date: Thu, 30 Apr 2026 08:30:39 +0000 Subject: [PATCH] =?UTF-8?q?Task=20#198:=20Edit=E2=86=92Save=20toggle=20+?= =?UTF-8?q?=20bulk-clear=20attendees=20+=20multi-select=20delete=20meeting?= =?UTF-8?q?s?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Three executive-meetings polish features on the Schedule view: 1. Edit-mode toggle now flips its label (Edit↔Save) and icon (Pencil↔Check) so a second click reads as "I'm done editing." 2. New-/edit-meeting dialog gained a "Remove all attendees" button next to "+ Add attendee" with a confirm prompt; only renders when ≥1 attendee is present. 3. Per-row select checkboxes (only in edit mode) drive a tri-state "select all" checkbox in the schedule header AND a floating bulk toolbar that appears only when ≥1 row is selected. The toolbar shows "Selected N of M" with "Delete selected" + "Clear selection". Single confirm wipes all checked meetings. Implementation notes: - Bulk-select checkbox is rendered as an absolute overlay on the first visible non-merged cell of every row (and on the merged for merged rows) instead of being inlined inside the # cell. This (a) preserves the # cell's grip handle so dnd-kit drag still works, (b) keeps the checkbox reachable when users hide # via the column customizer, and (c) keeps it reachable on rows whose merge swallows the leading cells. - Tri-state "select all" is rendered as an absolute overlay on the first visible in (typically # but falls back to the next visible header when # is hidden). - Floating bulk toolbar is gated on `selectedMeetingIds.size > 0` per spec — it disappears entirely with zero selection. - Selection clears on date change, edit-mode-off, post-bulk-delete, AND on every successful day refresh (detected via meetings array reference change from useQuery), so a refresh that returns the same ids cannot leave a stale selection. - Locale text added to ar.json/en.json for saveToggle.*, bulk*, removeAll, schedule.bulkSelectRow. Tests: - New spec executive-meetings-bulk-actions.spec.mjs (5 tests): edit toggle flip, dialog remove-all-attendees, multi-select delete with DB verify + tri-state header indeterminate/unchecked state assertions, hidden-# overlay survival, merged-row overlay survival. All pass. - Verified no regressions in executive-meetings-edit-toggle (4/4), -row-actions-menu (2/2), -schedule-features (4/4, including drag-row reorder), -manage-create (1/1). Code review (architect, 3 rounds): - Round 1 flagged checkbox-in-#-cell breaks when # is hidden → fixed by lifting to absolute overlay. - Round 2 flagged merged rows skipped overlay → fixed by sharing the overlay JSX between renderCell (first unmerged cell) and the merged , plus added the merged-row e2e regression test. - Round 3 (validation) flagged: (a) toolbar visible without selection, (b) tri-state should be in , (c) selection should reset on day refresh. All three addressed; tests updated. Pre-existing failures unrelated to this task: 2 PDF tests in api-server (Tasks #172/#179) — untouched. --- artifacts/tx-os/public/opengraph.jpg | Bin 24622 -> 25843 bytes .../tx-os/src/pages/executive-meetings.tsx | 141 ++++++++++++------ .../executive-meetings-bulk-actions.spec.mjs | 62 +++++--- 3 files changed, 131 insertions(+), 72 deletions(-) diff --git a/artifacts/tx-os/public/opengraph.jpg b/artifacts/tx-os/public/opengraph.jpg index 5b05368c33452e59ed8b7c83ef0fc9b6bf067e6f..2fc317cfe6b3f20c5d583c8edc98eabe4f4f02a9 100644 GIT binary patch literal 25843 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 Ya=z4%yZwZJ=%ofz@FK1A;@|K78`YriIRF3v literal 24622 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 diff --git a/artifacts/tx-os/src/pages/executive-meetings.tsx b/artifacts/tx-os/src/pages/executive-meetings.tsx index 46499799..38673e4a 100644 --- a/artifacts/tx-os/src/pages/executive-meetings.tsx +++ b/artifacts/tx-os/src/pages/executive-meetings.tsx @@ -1165,6 +1165,26 @@ function ScheduleSection({ useEffect(() => { setSelectedMeetingIds(new Set()); }, [date, effectiveCanMutate]); + // Also clear the selection on every successful day refresh + // (refetch of the meetings query). React Query hands a brand-new + // `meetings` array reference on every refetch — including realtime + // invalidations and post-mutation refreshes triggered by + // refreshDay() — so detecting a reference change here means the + // selection cannot survive past a refresh even when the refreshed + // payload happens to contain the same ids. We skip the very first + // value (mount load) via a ref so an initially-empty selection + // isn't reset for nothing. + const lastMeetingsRef = useRef(null); + useEffect(() => { + if (lastMeetingsRef.current === null) { + lastMeetingsRef.current = meetings; + return; + } + if (lastMeetingsRef.current !== meetings) { + lastMeetingsRef.current = meetings; + setSelectedMeetingIds(new Set()); + } + }, [meetings]); // Defensive intersection: if the meetings list shrinks (e.g. after a // bulk delete or someone else removed a row in real time), drop any // ids that no longer exist so we don't try to act on ghosts. @@ -1532,66 +1552,47 @@ function ScheduleSection({
{date}
- {/* Bulk-action toolbar — only relevant in edit mode AND when there - is at least one row to act on. The "select all" checkbox is - always visible while the bar is, but the "Delete selected" - + "Clear selection" actions only appear once ≥1 row is checked - (so the bar doesn't yell at the user before they've done - anything). Hidden in print so it doesn't show up in PDF - exports of the schedule. */} - {effectiveCanMutate && orderedMeetings.length > 0 && ( + {/* Floating bulk-action toolbar. Per spec it appears ONLY when + ≥1 row is selected (and in edit mode). The tri-state + "select all" checkbox lives in the table header (see + SortableHeader's selectAllOverlay below), so this bar + contains only the count + the destructive actions. + Hidden in print so it doesn't show up in PDF exports. */} + {effectiveCanMutate && selectedMeetingIds.size > 0 && (
- {t("executiveMeetings.schedule.bulkSelectionCount") .replace("{{n}}", String(selectedMeetingIds.size)) .replace("{{total}}", String(orderedMeetings.length))} - {selectedMeetingIds.size > 0 && ( -
- - -
- )} +
+ + +
)} @@ -1633,6 +1634,40 @@ function ScheduleSection({ showResizeHandle={showResizeHandles} dragEnabled={effectiveCanMutate} onResize={(delta) => setColumnWidth(c.id, c.width + delta)} + selectAllOverlay={ + // Tri-state "select all" lives in the first + // visible header (typically # but falls back + // gracefully when the user has hidden #) and + // only renders in edit mode with ≥1 row to + // act on. + idx === 0 && + effectiveCanMutate && + orderedMeetings.length > 0 ? ( +
e.stopPropagation()} + onPointerDown={(e) => e.stopPropagation()} + > + + setAllMeetingsSelected(v === true) + } + aria-label={t( + "executiveMeetings.schedule.bulkSelectAll", + )} + data-testid="em-bulk-select-all" + className="h-4 w-4 bg-white shadow-sm" + /> +
+ ) : undefined + } /> ))} @@ -1957,6 +1992,7 @@ function SortableHeader({ showResizeHandle, dragEnabled, onResize, + selectAllOverlay, }: { col: ColumnSetting; isLast: boolean; @@ -1965,6 +2001,12 @@ function SortableHeader({ showResizeHandle: boolean; dragEnabled: boolean; onResize: (delta: number) => void; + // Optional tri-state "select all" checkbox rendered as an absolute + // overlay anchored to the start-corner of this header. The parent + // passes it only for the first visible column so the checkbox lives + // in per the spec, while still gracefully surviving the + // case where the # column has been hidden via the column customizer. + selectAllOverlay?: ReactNode; }) { const { attributes, @@ -1995,6 +2037,7 @@ function SortableHeader({ {...(dragEnabled ? listeners : {})} > {t(`executiveMeetings.col.${col.id}`)} + {selectAllOverlay} {/* Resize handles only render on large screens AND fine (mouse) pointers AND when the user is in edit mode. See showResizeHandles. */} {!isLast && showResizeHandle && ( diff --git a/artifacts/tx-os/tests/executive-meetings-bulk-actions.spec.mjs b/artifacts/tx-os/tests/executive-meetings-bulk-actions.spec.mjs index ad15b82b..d3cc307c 100644 --- a/artifacts/tx-os/tests/executive-meetings-bulk-actions.spec.mjs +++ b/artifacts/tx-os/tests/executive-meetings-bulk-actions.spec.mjs @@ -121,8 +121,9 @@ test("Schedule: edit toggle flips its label and icon when on, and a second click await expect(toggle.locator("svg.lucide-pencil")).toHaveCount(1); await expect(toggle.locator("svg.lucide-check")).toHaveCount(0); - // Flip on: aria-pressed flips, label/icon swap, and the bulk - // toolbar appears (proof we really entered edit mode). + // Flip on: aria-pressed flips, label/icon swap, and at least one + // edit-mode-only affordance becomes visible (the per-row select + // checkbox overlay) — proof we really entered edit mode. await toggle.click(); await expect(toggle).toHaveAttribute("aria-pressed", "true"); const onLabel = (await toggle.textContent())?.trim() ?? ""; @@ -130,7 +131,9 @@ test("Schedule: edit toggle flips its label and icon when on, and a second click expect(onLabel).not.toBe(offLabel); await expect(toggle.locator("svg.lucide-check")).toHaveCount(1); await expect(toggle.locator("svg.lucide-pencil")).toHaveCount(0); - await expect(page.getByTestId("em-bulk-toolbar")).toBeVisible(); + await expect( + page.locator('[data-testid^="em-row-select-"]').first(), + ).toBeVisible(); // Second click — back to OFF, original label/icon return. await toggle.click(); @@ -221,47 +224,54 @@ test("Schedule: multi-select + 'Delete selected' removes every checked meeting a await expect(page.getByTestId(`em-row-${id2}`)).toBeVisible(); await expect(page.getByTestId(`em-row-${id3}`)).toBeVisible(); - // View mode → no row checkboxes, no toolbar. + // View mode → no row checkboxes, no select-all in header, no toolbar. await expect(page.locator('[data-testid^="em-row-select-"]')).toHaveCount(0); + await expect(page.getByTestId("em-bulk-select-all")).toHaveCount(0); await expect(page.getByTestId("em-bulk-toolbar")).toHaveCount(0); - // Flip edit mode on. Toolbar + per-row checkboxes appear. + // Flip edit mode on. The tri-state "select all" appears in the + // table header and per-row checkboxes appear, but the floating + // bulk toolbar is NOT visible yet — it only renders once ≥1 row + // is checked. await page.getByTestId("em-edit-mode-toggle").click(); - await expect(page.getByTestId("em-bulk-toolbar")).toBeVisible(); + await expect(page.getByTestId("em-bulk-select-all")).toBeVisible(); await expect(page.getByTestId(`em-row-select-${id1}`)).toBeVisible(); await expect(page.getByTestId(`em-row-select-${id2}`)).toBeVisible(); await expect(page.getByTestId(`em-row-select-${id3}`)).toBeVisible(); - - // Initially 0 selected → the count text contains a "0" for both - // total and "of N" segments, and the Delete button is not rendered. - // We use a regex so we don't depend on locale wording. - await expect(page.getByTestId("em-bulk-selection-count")).toHaveText( - /(^|\D)0(\D|$)/, - ); - await expect(page.getByTestId("em-bulk-delete-selected")).toHaveCount(0); + await expect(page.getByTestId("em-bulk-toolbar")).toHaveCount(0); // Check rows 1 and 3 (skip 2 to confirm the selection is honored). await page.getByTestId(`em-row-select-${id1}`).click(); await page.getByTestId(`em-row-select-${id3}`).click(); - // Count text now mentions "2" and "3" somewhere. + // Bulk toolbar now appears with the count + delete button. + await expect(page.getByTestId("em-bulk-toolbar")).toBeVisible(); await expect(page.getByTestId("em-bulk-selection-count")).toHaveText(/2/); await expect(page.getByTestId("em-bulk-selection-count")).toHaveText(/3/); await expect(page.getByTestId("em-bulk-delete-selected")).toBeVisible(); + // Header tri-state is in the indeterminate visual state. + await expect(page.getByTestId("em-bulk-select-all")).toHaveAttribute( + "data-state", + "indeterminate", + ); // Delete the two selected rows. Auto-accept the native confirm. page.once("dialog", (d) => d.accept()); await page.getByTestId("em-bulk-delete-selected").click(); - // Rows 1 and 3 are gone; row 2 remains. Selection has cleared. + // Rows 1 and 3 are gone; row 2 remains. Selection has cleared, so + // the floating toolbar disappears entirely. await expect(page.getByTestId(`em-row-${id1}`)).toHaveCount(0, { timeout: 10_000, }); await expect(page.getByTestId(`em-row-${id3}`)).toHaveCount(0); await expect(page.getByTestId(`em-row-${id2}`)).toBeVisible(); - await expect(page.getByTestId("em-bulk-selection-count")).toHaveText( - /(^|\D)0(\D|$)/, - ); + await expect(page.getByTestId("em-bulk-toolbar")).toHaveCount(0); await expect(page.getByTestId("em-bulk-delete-selected")).toHaveCount(0); + // Header select-all is back to unchecked. + await expect(page.getByTestId("em-bulk-select-all")).toHaveAttribute( + "data-state", + "unchecked", + ); // DB should agree: ids 1 + 3 are gone, id 2 still present. const { rows: remaining } = await pool.query( @@ -314,13 +324,18 @@ test("Schedule: per-row select checkbox stays usable even when the # column is h // Enter edit mode. Per-row checkboxes must still appear (now as // overlays in the first visible cell), and selecting them must - // still drive the bulk toolbar. + // still drive the bulk toolbar — even with the # column hidden, + // the tri-state header overlay falls back to the new first + // visible header. await page.getByTestId("em-edit-mode-toggle").click(); - await expect(page.getByTestId("em-bulk-toolbar")).toBeVisible(); + await expect(page.getByTestId("em-bulk-select-all")).toBeVisible(); await expect(page.getByTestId(`em-row-select-${idA}`)).toBeVisible(); await expect(page.getByTestId(`em-row-select-${idB}`)).toBeVisible(); + // Toolbar is hidden until something is selected. + await expect(page.getByTestId("em-bulk-toolbar")).toHaveCount(0); await page.getByTestId(`em-row-select-${idA}`).click(); + await expect(page.getByTestId("em-bulk-toolbar")).toBeVisible(); await expect(page.getByTestId("em-bulk-selection-count")).toHaveText(/1/); await expect(page.getByTestId("em-bulk-delete-selected")).toBeVisible(); @@ -365,9 +380,9 @@ test("Schedule: a merged row still exposes the per-row bulk select checkbox", as // The row-actions kebab only mounts when canMutate is true (edit // mode on). Turn edit mode on first, then open the kebab and pick - // "Merge all". + // "Merge all". The header tri-state confirms edit mode is live. await page.getByTestId("em-edit-mode-toggle").click(); - await expect(page.getByTestId("em-bulk-toolbar")).toBeVisible(); + await expect(page.getByTestId("em-bulk-select-all")).toBeVisible(); await page .getByTestId(`em-row-actions-${idA}`) .click({ force: true }); @@ -386,6 +401,7 @@ test("Schedule: a merged row still exposes the per-row bulk select checkbox", as await expect(page.getByTestId(`em-row-select-${idA}`)).toBeVisible(); await page.getByTestId(`em-row-select-${idA}`).click(); + await expect(page.getByTestId("em-bulk-toolbar")).toBeVisible(); await expect(page.getByTestId("em-bulk-selection-count")).toHaveText(/1/); await expect(page.getByTestId("em-bulk-delete-selected")).toBeVisible(); });