"use client"; import { useState } from "react"; import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query"; import { playbooksApi } from "@/lib/api"; import type { Playbook } from "@/lib/api/playbooks"; import { Card, CardContent, CardDescription, CardHeader, CardTitle, } from "@/components/ui/card"; import { Button } from "@/components/ui/button"; import { Badge } from "@/components/ui/badge"; import { Dialog, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle, } from "@/components/ui/dialog"; import { Textarea } from "@/components/ui/textarea"; import { Label } from "@/components/ui/label"; import { BookOpen, CheckCircle2, XCircle } from "lucide-react"; import { toast } from "sonner"; const _MIN_REASON = 4; // Auditor/CEO review queue for drafted playbooks. Hidden when none are pending // (mirrors the PR-review + release-proposal cards). export function PlaybookReviewQueue({ className }: { className?: string }) { const queryClient = useQueryClient(); const [rejecting, setRejecting] = useState(null); const [reason, setReason] = useState(""); const { data: drafts, isLoading } = useQuery({ queryKey: ["playbooks", "drafts"], queryFn: () => playbooksApi.listDrafts(), refetchInterval: 30000, }); const invalidate = () => queryClient.invalidateQueries({ queryKey: ["playbooks", "drafts"] }); const approveMutation = useMutation({ mutationFn: (id: string) => playbooksApi.approve(id), onSuccess: () => { invalidate(); toast.success("Playbook approved and indexed"); }, onError: (e) => toast.error( `Approve failed: ${e instanceof Error ? e.message : "error"}`, ), }); const rejectMutation = useMutation({ mutationFn: ({ id, reason }: { id: string; reason: string }) => playbooksApi.reject(id, reason), onSuccess: () => { invalidate(); toast.success("Playbook rejected (archived)"); closeReject(); }, onError: (e) => toast.error(`Reject failed: ${e instanceof Error ? e.message : "error"}`), }); const closeReject = () => { setRejecting(null); setReason(""); }; const confirmReject = () => { if (!rejecting) return; if (reason.trim().length < _MIN_REASON) { toast.error("Give a brief reason for rejecting"); return; } rejectMutation.mutate({ id: rejecting.id, reason: reason.trim() }); }; if (isLoading || !drafts || drafts.length === 0) return null; return ( <> Playbook Review {drafts.length} Drafted playbooks awaiting your approval — approved ones are indexed and auto-suggested to agents. {drafts.map((pb) => (
{pb.title} {pb.team && {pb.team}} {pb.tags.map((t) => ( {t} ))}

When: {pb.problem}

                {pb.procedure}
              
))}
closeReject()}> Reject playbook This archives the playbook. Give a brief reason (it is recorded).