import { Clock, RotateCcw, AlertCircle, Loader2, CheckCircle2 } from "lucide-react"; import { Link } from "@/lib/router"; import { Button } from "@/components/ui/button"; import { cn, formatDateTime } from "@/lib/utils"; import { formatMonitorOffset } from "@/lib/issue-monitor"; import { formatRetryReason } from "@/lib/runRetryState"; import type { IssueScheduledRetry } from "@paperclipai/shared"; import { useRetryNowMutation, type RetryNowError } from "../hooks/useRetryNowMutation"; const MAX_TURN_CONTINUATION = "max_turns_continuation"; function isContinuationReason(reason: string | null | undefined) { return reason === MAX_TURN_CONTINUATION; } function shortRunId(runId: string | null | undefined) { return typeof runId === "string" && runId.length >= 8 ? runId.slice(0, 8) : runId ?? ""; } interface IssueScheduledRetryCardProps { issueId: string | null | undefined; scheduledRetry: IssueScheduledRetry | null | undefined; } export function IssueScheduledRetryCard({ issueId, scheduledRetry, }: IssueScheduledRetryCardProps) { const retryNow = useRetryNowMutation(issueId); if (!scheduledRetry || !issueId) return null; if (scheduledRetry.status !== "scheduled_retry") return null; const continuation = isContinuationReason(scheduledRetry.scheduledRetryReason); const dueAtIso = scheduledRetry.scheduledRetryAt ? new Date(scheduledRetry.scheduledRetryAt).toISOString() : null; const relative = dueAtIso ? formatMonitorOffset(dueAtIso) : null; const absolute = scheduledRetry.scheduledRetryAt ? formatDateTime(scheduledRetry.scheduledRetryAt) : null; const reason = formatRetryReason(scheduledRetry.scheduledRetryReason); const attempt = typeof scheduledRetry.scheduledRetryAttempt === "number" && Number.isFinite(scheduledRetry.scheduledRetryAttempt) && scheduledRetry.scheduledRetryAttempt > 0 ? scheduledRetry.scheduledRetryAttempt : null; const badgeLabel = continuation ? "Continuation scheduled" : "Retry scheduled"; const titleAction = continuation ? "Automatic continuation" : "Automatic retry"; let titleSuffix: string; if (relative === "now") { titleSuffix = "due now"; } else if (relative) { titleSuffix = relative; } else { titleSuffix = "pending schedule"; } const title = `${titleAction} ${titleSuffix}`; const helperIdle = continuation ? "Pulls continuation forward immediately" : "Pulls retry forward immediately"; const isError = retryNow.isError || retryNow.lastError !== null; const isSuccessTransient = retryNow.isSuccess && (retryNow.data?.outcome === "promoted" || retryNow.data?.outcome === "already_promoted"); return (