diff --git a/apps/fabro-web/app/data/runs.ts b/apps/fabro-web/app/data/runs.ts index d72b88706..46c151fe6 100644 --- a/apps/fabro-web/app/data/runs.ts +++ b/apps/fabro-web/app/data/runs.ts @@ -33,6 +33,7 @@ export interface RunItem { actionDisabled?: boolean; comments?: number; question?: string; + pendingApproval?: boolean; sandboxId?: string; sandboxWorkingDirectory?: string; sourceDirectory?: string; @@ -98,6 +99,9 @@ export function mapRunListItem(item: Run): RunItem { elapsed: item.timing != null ? formatDurationMs(item.timing.wall_time_ms) : undefined, resources: undefined, question: item.current_question?.text, + pendingApproval: + item.lifecycle.status.kind === "pending" + && item.lifecycle.approval?.state === "pending", sandboxId: runtime?.id ?? undefined, sandboxWorkingDirectory: runtime?.working_directory ?? undefined, sourceDirectory: item.source_directory ?? undefined, diff --git a/apps/fabro-web/app/routes/run-detail.tsx b/apps/fabro-web/app/routes/run-detail.tsx index 604ad3621..01711366b 100644 --- a/apps/fabro-web/app/routes/run-detail.tsx +++ b/apps/fabro-web/app/routes/run-detail.tsx @@ -8,6 +8,7 @@ import { } from "react"; import { ArrowPathIcon, + CheckIcon, ChevronDownIcon, ChevronRightIcon, ClockIcon, @@ -44,6 +45,7 @@ import { PopoverHeader, PopoverRow, PopoverRows, + PRIMARY_BUTTON_CLASS, SECONDARY_BUTTON_CLASS, Tooltip, } from "../components/ui"; @@ -701,6 +703,22 @@ export default function RunDetail({ params }: { params: { id: string } }) { )} + {approvalActionVisible && ( + + )} + void archiveMutation.trigger()} - canApprove={approvalActionVisible} approvePending={approvePending} - onApprove={() => void approveMutation.trigger()} canDeny={approvalActionVisible} denyPending={denyPending} onDeny={() => void denyMutation.trigger()} @@ -961,9 +977,7 @@ interface ActionsMenuProps { canArchive: boolean; archivePending: boolean; onArchive: () => void; - canApprove: boolean; approvePending: boolean; - onApprove: () => void; canDeny: boolean; denyPending: boolean; onDeny: () => void; @@ -988,7 +1002,7 @@ function ActionsMenu(props: ActionsMenuProps) { canFocusSteer, onFocusSteer, canPreview, previewPending, onPreview, canArchive, archivePending, onArchive, - canApprove, approvePending, onApprove, + approvePending, canDeny, denyPending, onDeny, canRetry, retryPending, onRetry, canUnarchive, unarchivePending, onUnarchive, @@ -998,7 +1012,7 @@ function ActionsMenu(props: ActionsMenuProps) { const [runIdCopied, setRunIdCopied] = useState(false); - const hasLifecycle = canApprove || canRetry || canArchive || canUnarchive; + const hasLifecycle = canRetry || canArchive || canUnarchive; const hasDestructive = canDeny || canCancel || canDelete; const anyPending = previewPending || @@ -1083,18 +1097,6 @@ function ActionsMenu(props: ActionsMenuProps) { {separators.afterOperations && (
)} - {canApprove && ( - - - - )} {canRetry && (
); } +function ApproveBoardButton({ runId }: { runId: string }) { + const { mutate } = useSWRConfig(); + const { push } = useToast(); + const [pending, setPending] = useState(false); + + async function handleClick(event: React.MouseEvent) { + event.stopPropagation(); + event.preventDefault(); + if (pending) return; + setPending(true); + try { + await approveRun(runId); + mutateRunListCaches(mutate); + push({ message: "Run approved." }); + } catch (error) { + push({ message: mapError(error, "approve"), tone: "error" }); + } finally { + setPending(false); + } + } + + return ( + + ); +} + function SortablePrCard({ pr, iconColor,