"use client"; import { useState } from "react"; import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query"; import { tasksApi } from "@/lib/api"; import { Card, CardContent, CardDescription, CardHeader, CardTitle, } from "@/components/ui/card"; import { Button } from "@/components/ui/button"; import { Badge } from "@/components/ui/badge"; import { Skeleton } from "@/components/ui/skeleton"; import { Dialog, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle, } from "@/components/ui/dialog"; import { GitPullRequest, ExternalLink, Rocket, XCircle, FileText } from "lucide-react"; import Link from "next/link"; import { type Task } from "@/types"; import { toast } from "sonner"; interface PrReviewQueueProps { className?: string; } /** * The CEO's decision queue for inbound external PRs the org has reviewed. * * The PR reviewer is read-only — it posts one change-request and stops; the CEO * decides what happens next. This surfaces each reviewed PR with two actions: * Supersede (the org takes the contribution over and finishes it) or Dismiss * (drop it from the queue; the review stays on the GitHub PR). Hidden when empty. */ export function PrReviewQueue({ className }: PrReviewQueueProps) { const queryClient = useQueryClient(); const [selected, setSelected] = useState(null); const [action, setAction] = useState<"supersede" | "dismiss" | null>(null); const { data: reviews, isLoading } = useQuery({ queryKey: ["tasks", "external-pr-reviews"], queryFn: () => tasksApi.getExternalPrReviews(), refetchInterval: 30000, }); const supersedeMutation = useMutation({ mutationFn: (taskId: string) => tasksApi.supersedeExternalPr(taskId), onSuccess: (res) => { queryClient.invalidateQueries({ queryKey: ["tasks"] }); toast.success( res.ok ? "Superseding — the org is taking the PR over" : "Supersede did not start", ); close(); }, onError: (e) => toast.error( `Supersede failed: ${e instanceof Error ? e.message : "Unknown error"}`, ), }); const dismissMutation = useMutation({ mutationFn: (taskId: string) => tasksApi.dismissExternalPr(taskId), onSuccess: () => { queryClient.invalidateQueries({ queryKey: ["tasks"] }); toast.success("Review dismissed"); close(); }, onError: (e) => toast.error( `Dismiss failed: ${e instanceof Error ? e.message : "Unknown error"}`, ), }); const open = (task: Task, a: "supersede" | "dismiss") => { setSelected(task); setAction(a); }; const close = () => { setSelected(null); setAction(null); }; const confirm = () => { if (!selected) return; if (action === "supersede") supersedeMutation.mutate(selected.id); else if (action === "dismiss") dismissMutation.mutate(selected.id); }; if (isLoading) { return ( PR Reviews External PRs reviewed and awaiting your call
{[1, 2].map((i) => ( ))}
); } const items = reviews || []; if (items.length === 0) return null; // keep the dashboard clean when there's nothing to decide const isPending = supersedeMutation.isPending || dismissMutation.isPending; return ( <> PR Reviews {items.length} External PRs the org reviewed — supersede or dismiss
{items.map((task) => (
{task.title} {task.description && (

{task.description}

)}
{task.pr_url && ( )}
))}
close()}> {action === "supersede" ? "Supersede this PR" : "Dismiss this review"} {action === "supersede" ? "The org takes the contribution over: a roboco-owned branch is cut from the contributor's commits, a cell finishes and hardens it, then opens our own PR. This authorizes fetching and running the contributor's code." : "Removes this PR from your review queue. The review stays on the GitHub PR; the org takes no further action."} {selected && (

{selected.title}

{selected.pr_url && ( View PR on GitHub )}
)}
); }