import React, { useState, useEffect } from "react"; import { motion, AnimatePresence } from "framer-motion"; import { Shield, AlertTriangle, CheckCircle2, ChevronRight, XCircle, Clock } from "lucide-react"; interface CodeOwnersEnforcementProps { repoOwner: string; repoName: string; prNumber: number; } export function CodeOwnersEnforcement({ repoOwner, repoName, prNumber }: CodeOwnersEnforcementProps) { const [data, setData] = useState(null); const [isLoading, setIsLoading] = useState(true); const [error, setError] = useState(null); useEffect(() => { const fetchEnforcement = async () => { try { const res = await fetch(`/api/repos/${repoOwner}/${repoName}/pulls/${prNumber}/codeowner-enforcement`); const payload = await res.json(); if (!res.ok) { throw new Error(payload?.error?.message || payload?.message || "Failed to load policy"); } setData(payload.data || payload); } catch (e: any) { setError(e.message); } finally { setIsLoading(false); } }; fetchEnforcement(); }, [repoOwner, repoName, prNumber]); if (isLoading) { return (
); } if (error || !data) { return (
Failed to load CODEOWNERS enforcement: {error}
); } const { enforced, hasCodeOwners, rules, missingApprovals } = data; if (!hasCodeOwners) { return null; // Don't show anything if no codeowners exist } const isSatisfied = missingApprovals === 0; return (
CODEOWNERS Review
{enforced ? ( {isSatisfied ? "Satisfied" : "Pending"} ) : ( Optional )}
{rules && rules.length > 0 ? (
{rules.map((rule: any, idx: number) => { const ruleSatisfied = rule.missingApprovals === 0; return (
{rule.pattern}
{ruleSatisfied ? ( ) : ( )}
Required: {rule.owners.map((owner: string) => (
{owner.replace('@', '')[0]?.toUpperCase()}
{owner}
))}
{!ruleSatisfied && enforced && (
Waiting for {rule.missingApprovals} approval{rule.missingApprovals !== 1 ? 's' : ''}
)}
); })}
) : (
No CODEOWNERS rules apply to these changes.
)}
); }