import React, { useState, useEffect } from "react"; import { motion, AnimatePresence } from "framer-motion"; import { Edit2, AlertCircle, ChevronDown, CheckCircle2, Shield, Lock, Globe } from "lucide-react"; interface CustomState { id: string; name: string; displayName: string; description: string | null; allowMerge: boolean; requireCodeOwner: boolean; color?: string; } interface CustomPRStateProps { initialStateId: string | null; customStates: CustomState[]; repoOwner: string; repoName: string; prNumber: number; } export function CustomPRState({ initialStateId, customStates, repoOwner, repoName, prNumber }: CustomPRStateProps) { const [selectedState, setSelectedState] = useState( initialStateId ? `custom:${initialStateId}` : "builtin:open" ); const [isApplying, setIsApplying] = useState(false); const [message, setMessage] = useState<{ text: string, type: "success" | "error" } | null>(null); const [policyData, setPolicyData] = useState(null); const [isLoadingPolicy, setIsLoadingPolicy] = useState(false); useEffect(() => { if (!selectedState.startsWith("custom:")) { setPolicyData(null); return; } const fetchPolicy = async () => { setIsLoadingPolicy(true); const stateId = selectedState.replace("custom:", ""); try { const res = await fetch(`/api/repos/${repoOwner}/${repoName}/pulls/${prNumber}/required-reviewers?stateId=${encodeURIComponent(stateId)}`); const data = await res.json(); if (res.ok) { setPolicyData(data); } } catch (e) { console.error("Failed to load policy", e); } finally { setIsLoadingPolicy(false); } }; fetchPolicy(); }, [selectedState, repoOwner, repoName, prNumber]); const handleApply = async () => { setIsApplying(true); setMessage(null); try { const res = await fetch(`/api/repos/${repoOwner}/${repoName}/pulls/${prNumber}/state`, { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify(selectedState.startsWith('custom:') ? { stateId: selectedState.replace('custom:', '') } : { state: selectedState.replace('builtin:', '') }), }); const data = await res.json(); if (!res.ok) { throw new Error(data?.error?.message || data?.message || "Failed to update state"); } setMessage({ text: "State applied successfully", type: "success" }); setTimeout(() => window.location.reload(), 1000); } catch (e: any) { setMessage({ text: e.message || "Failed to update", type: "error" }); } finally { setIsApplying(false); } }; const selectedStateObj = selectedState.startsWith("custom:") ? customStates.find(s => s.id === selectedState.replace("custom:", "")) : null; return (
Workflow State
{selectedStateObj ? (
{selectedStateObj.displayName} {selectedStateObj.description && — {selectedStateObj.description}}
{selectedStateObj.allowMerge ? ( Merge enabled ) : ( Merge blocked )} {selectedStateObj.requireCodeOwner && ( CODEOWNERS enforced )}
{isLoadingPolicy ? (
Loading reviewer policy...
) : policyData ? (
Required approvals: {policyData.approvedRequired || 0}/{policyData.totalRequired || 0}
{policyData.reviewers && policyData.reviewers.length > 0 && (
Waiting on: {policyData.reviewers.filter((r: any) => !r.hasApproved).map((r: any) => r.username).join(", ")}
)}
) : null} ) : ( {selectedState === "builtin:closed" ? "Transitioning to Closed will close this pull request." : "Standard pull request state."} )} {message && ( {message.text} )}
); }