import { useEffect, useMemo, useState } from "react"; type Contributor = { userId: string; userName: string; openPRs: number; pendingReviews: number; assignedIssues: number; recentCommits: number; avgReviewTime: number; workloadScore: number; trend: "increasing" | "stable" | "decreasing"; }; type Recommendation = { priority: "high" | "medium" | "low"; title: string; rationale: string; action: string; }; type WorkloadPayload = { summary: { totalContributors: number; averageWorkloadScore: number; overloadedCount: number; underutilizedCount: number; }; trendIntelligence?: { increasingTrendCount: number; stableTrendCount: number; decreasingTrendCount: number; p50WorkloadScore: number; p90WorkloadScore: number; top3LoadSharePercent: number; }; recommendations?: Recommendation[]; contributors: Contributor[]; }; type WorkloadResponse = { success: boolean; data?: WorkloadPayload; }; type Props = { owner: string; repo: string; }; function priorityStyles(priority: Recommendation["priority"]): string { if (priority === "high") return "text-red-700 bg-red-50 border-red-200"; if (priority === "medium") return "text-amber-700 bg-amber-50 border-amber-200"; return "text-blue-700 bg-blue-50 border-blue-200"; } export function DeveloperWorkloadPanel({ owner, repo }: Props) { const [days, setDays] = useState(30); const [loading, setLoading] = useState(true); const [error, setError] = useState(""); const [payload, setPayload] = useState(null); useEffect(() => { const controller = new AbortController(); async function load() { setLoading(true); setError(""); try { const response = await fetch( `/api/repos/${owner}/${repo}/analytics/workload?days=${days}&limit=20`, { signal: controller.signal } ); if (!response.ok) { throw new Error("Failed to load workload analytics"); } const parsed = (await response.json()) as WorkloadResponse; setPayload(parsed.data || null); } catch (err) { if ((err as Error).name === "AbortError") return; setError("Unable to load workload insights"); } finally { setLoading(false); } } load(); return () => controller.abort(); }, [owner, repo, days]); const topContributors = useMemo(() => payload?.contributors?.slice(0, 8) || [], [payload]); return (

Developer Workload Insights

Trend intelligence and recommendation signals from PR/review/issue load.

{loading ? (
Loading workload insights...
) : error ? (
{error}
) : !payload ? (
No workload data available.
) : (
Contributors
{payload.summary.totalContributors}
Avg workload score
{payload.summary.averageWorkloadScore}
Overloaded
{payload.summary.overloadedCount}
Top 3 load share
{payload.trendIntelligence?.top3LoadSharePercent ?? 0}%
{payload.recommendations && payload.recommendations.length > 0 && (

Recommendations

{payload.recommendations.map((recommendation, index) => (
{recommendation.title}
{recommendation.priority}

{recommendation.rationale}

{recommendation.action}

))}
)}

Top Contributors by Workload

{topContributors.map((contributor) => ( ))}
Contributor Workload Open PRs Pending Reviews Assigned Issues Trend
{contributor.userName} {contributor.workloadScore} {contributor.openPRs} {contributor.pendingReviews} {contributor.assignedIssues} {contributor.trend}
)}
); }