import { useMemo, useState } from "react" import { ShieldCheck } from "lucide-react" import type { WorkflowPushApproval } from "@/lib/agents/api" import { useWorkflowApprovalDecision, useWorkflowApprovals, } from "@/lib/agents/queries" import { Button } from "@/components/ui/button" import { cn } from "@/lib/utils" function shortSha(value: string): string { return value ? value.slice(0, 7) : "unknown" } function pendingApprovals( approvals: Array | undefined ): Array { return (approvals ?? []).filter((approval) => approval.status === "pending") } function fileLabel(count: number): string { return count === 1 ? "1 file" : `${count} files` } export function WorkflowApprovalCard({ threadId, pollWhileActive = false, }: { threadId: string pollWhileActive?: boolean }) { const query = useWorkflowApprovals(threadId, { pollWhileActive }) const decision = useWorkflowApprovalDecision(threadId) const [error, setError] = useState(null) const approvals = useMemo( () => pendingApprovals(query.data?.approvals), [query.data?.approvals] ) if (approvals.length === 0) return null const isOwner = query.data?.isOwner === true const decide = async ( approval: WorkflowPushApproval, kind: "approve" | "reject" ) => { setError(null) try { await decision.mutateAsync({ fingerprint: approval.fingerprint, decision: kind, }) } catch (e) { setError((e as Error).message) } } return (
{approvals.map((approval) => { const busy = decision.isPending return (
Workflow file approval required

{approval.repo || "Repository"} on{" "} {approval.branch || "current branch"} ·{" "} {shortSha(approval.baseSha)} → {shortSha(approval.headSha)}

fingerprint: {approval.fingerprint}

{approval.approvalUrl && ( )}
{!isOwner && (

Only the thread owner can approve or reject this workflow push.

)} {error && (

{error}

)}

{fileLabel(approval.files.length)} changed

    {approval.files.slice(0, 8).map((file) => (
  • {file}
  • ))} {approval.files.length > 8 && (
  • …and {approval.files.length - 8} more
  • )}
{approval.diffStats.files} files +{approval.diffStats.additions} -{approval.diffStats.deletions}
{approval.diffPreview && (
Diff preview {approval.diffPreviewTruncated ? " (truncated)" : ""}
                    {approval.diffPreview}
                  
)}
) })}
) }