diff --git a/packages/web/src/components/Dashboard.tsx b/packages/web/src/components/Dashboard.tsx index b92474c6c..f7161029a 100644 --- a/packages/web/src/components/Dashboard.tsx +++ b/packages/web/src/components/Dashboard.tsx @@ -7,6 +7,7 @@ import { type DashboardPR, type AttentionLevel, getAttentionLevel, + isPRRateLimited, } from "@/lib/types"; import { CI_STATUS } from "@composio/ao-core/types"; import { AttentionZone } from "./AttentionZone"; @@ -85,6 +86,11 @@ export function Dashboard({ sessions, stats, orchestratorId, projectName }: Dash const hasKanbanSessions = KANBAN_LEVELS.some((l) => grouped[l].length > 0); + const anyRateLimited = useMemo( + () => sessions.some((s) => s.pr && isPRRateLimited(s.pr)), + [sessions], + ); + return (
@@ -109,6 +115,19 @@ export function Dashboard({ sessions, stats, orchestratorId, projectName }: Dash )}
+ {/* Rate limit notice */} + {anyRateLimited && ( +
+ + + + + GitHub API rate limited — PR data (CI status, review state, sizes) may be stale. + {" "}Will retry automatically on next refresh. + +
+ )} + {/* Kanban columns for active zones */} {hasKanbanSessions && (
diff --git a/packages/web/src/components/PRStatus.tsx b/packages/web/src/components/PRStatus.tsx index aae10455b..6721bccb3 100644 --- a/packages/web/src/components/PRStatus.tsx +++ b/packages/web/src/components/PRStatus.tsx @@ -1,6 +1,7 @@ "use client"; import type { DashboardPR } from "@/lib/types"; +import { isPRRateLimited } from "@/lib/types"; import { CIBadge } from "./CIBadge"; function getSizeLabel(additions: number, deletions: number): string { @@ -14,6 +15,7 @@ interface PRStatusProps { export function PRStatus({ pr }: PRStatusProps) { const sizeLabel = getSizeLabel(pr.additions, pr.deletions); + const rateLimited = isPRRateLimited(pr); return (
@@ -28,10 +30,12 @@ export function PRStatus({ pr }: PRStatusProps) { #{pr.number} - {/* Size */} - - +{pr.additions} -{pr.deletions} {sizeLabel} - + {/* Size — hide when rate limited (would show +0 -0 XS) */} + {!rateLimited && ( + + +{pr.additions} -{pr.deletions} {sizeLabel} + + )} {/* Merged badge */} {pr.state === "merged" && ( @@ -47,11 +51,13 @@ export function PRStatus({ pr }: PRStatusProps) { )} - {/* CI status (only for open PRs) */} - {pr.state === "open" && !pr.isDraft && } + {/* CI status — only when we have real data */} + {pr.state === "open" && !pr.isDraft && !rateLimited && ( + + )} - {/* Review decision (only for open PRs) */} - {pr.state === "open" && pr.reviewDecision === "approved" && ( + {/* Review decision (only for open PRs with real data) */} + {pr.state === "open" && pr.reviewDecision === "approved" && !rateLimited && ( approved diff --git a/packages/web/src/components/SessionCard.tsx b/packages/web/src/components/SessionCard.tsx index dbc7064df..7e35e0fa6 100644 --- a/packages/web/src/components/SessionCard.tsx +++ b/packages/web/src/components/SessionCard.tsx @@ -5,6 +5,7 @@ import { type DashboardSession, type AttentionLevel, getAttentionLevel, + isPRRateLimited, TERMINAL_STATUSES, TERMINAL_ACTIVITIES, } from "@/lib/types"; @@ -72,6 +73,7 @@ export function SessionCard({ session, onSend, onKill, onMerge, onRestore }: Ses timerRef.current = setTimeout(() => setSendingAction(null), 2000); }; + const rateLimited = pr ? isPRRateLimited(pr) : false; const alerts = getAlerts(session); const isReadyToMerge = pr?.mergeability.mergeable && pr.state === "open"; const isTerminal = @@ -144,8 +146,20 @@ export function SessionCard({ session, onSend, onKill, onMerge, onRestore }: Ses {pr && }
+ {/* Rate limited indicator */} + {rateLimited && pr?.state === "open" && ( +
+ + + + + PR data rate limited + +
+ )} + {/* Merge button or alert tags */} - {(alerts.length > 0 || isReadyToMerge) && ( + {!rateLimited && (alerts.length > 0 || isReadyToMerge) && (
{isReadyToMerge && pr ? (