From dca4369253e5af042cf348819b5a0eaac3e0516e Mon Sep 17 00:00:00 2001 From: Bryan Helmkamp Date: Mon, 25 May 2026 12:57:05 -0400 Subject: [PATCH] feat(web): add More menu with Approve + Delete to bulk toolbar The bulk action toolbar grows: the top-level buttons stay focused on the common archive/unarchive flow, and an overflow "More" menu now houses Approve (new) and Delete (moved from the top level). Approve fans out client-side via Promise.allSettled since there's no batch approve endpoint yet; the result is reported through the same summarizeBatchLifecycleAction toast as the other batch actions. Only runs whose lifecycle.approval.state === "pending" are eligible. Co-Authored-By: Claude Opus 4.7 (1M context) --- .../runs-list/bulk-action-toolbar.tsx | 69 +++++++++++++++---- apps/fabro-web/app/lib/run-actions.ts | 25 +++++++ 2 files changed, 80 insertions(+), 14 deletions(-) diff --git a/apps/fabro-web/app/components/runs-list/bulk-action-toolbar.tsx b/apps/fabro-web/app/components/runs-list/bulk-action-toolbar.tsx index 9b5dc8d79..dc772b90a 100644 --- a/apps/fabro-web/app/components/runs-list/bulk-action-toolbar.tsx +++ b/apps/fabro-web/app/components/runs-list/bulk-action-toolbar.tsx @@ -1,15 +1,21 @@ import { useMemo, useState } from "react"; -import { ArchiveBoxIcon, ArrowUturnLeftIcon, TrashIcon, XMarkIcon } from "@heroicons/react/24/outline"; +import { + ArchiveBoxIcon, + ArrowUturnLeftIcon, + CheckIcon, + ChevronUpIcon, + EllipsisHorizontalIcon, + TrashIcon, + XMarkIcon, +} from "@heroicons/react/24/outline"; +import { Menu, MenuButton, MenuItem, MenuItems } from "@headlessui/react"; import { useSWRConfig } from "swr"; -import type { - BatchDeleteRunsResponse, - BatchRunLifecycleResponse, - BatchRunLifecycleSummary, -} from "@qltysh/fabro-api-client"; +import type { BatchRunLifecycleSummary } from "@qltysh/fabro-api-client"; import type { RunWithStatus } from "../../data/runs"; import { mutateRunListCaches } from "../../lib/board-cache"; import { + approveRuns, archiveRuns, canArchive, canDelete, @@ -21,7 +27,7 @@ import { plural } from "../settings-panel"; import { useToast } from "../toast"; import { ConfirmDialog } from "../ui"; -export type BatchLifecycleLabel = "Archive" | "Unarchive" | "Delete"; +export type BatchLifecycleLabel = "Archive" | "Unarchive" | "Delete" | "Approve"; export interface BatchLifecycleToast { message: string; @@ -69,6 +75,10 @@ export function BulkActionToolbar({ () => selectedRuns.filter((r) => canUnarchive(r.lifecycleStatus)), [selectedRuns], ); + const approvable = useMemo( + () => selectedRuns.filter((r) => r.pendingApproval === true), + [selectedRuns], + ); const deletable = useMemo( () => selectedRuns.filter((r) => canDelete(r.lifecycleStatus)), [selectedRuns], @@ -79,7 +89,7 @@ export function BulkActionToolbar({ async function runBulk( label: BatchLifecycleLabel, eligible: RunWithStatus[], - action: (ids: string[]) => Promise, + action: (ids: string[]) => Promise<{ summary: BatchRunLifecycleSummary }>, ) { if (pending) return; if (eligible.length === 0) { @@ -153,12 +163,43 @@ export function BulkActionToolbar({ disabled={pending} onClick={() => runBulk("Unarchive", unarchivable, unarchiveRuns)} /> -