import Link from "next/link"; import { format } from "date-fns"; import { cn } from "@/lib/utils"; import type { CountdownVerdict, EpisodeCountdown } from "@/lib/dashboard/ops"; /** * Are we going to make the date? One card per episode, sorted by how soon. * * The bar is the episode's shots by where they actually are, and the row under * it is the only comparison that matters: shots per day still needed against * shots per day the studio has managed this week. */ const VERDICT: Record = { overdue: { rail: "bg-red-500", chip: "text-red-400 bg-red-500/10", label: "Overdue" }, behind: { rail: "bg-red-500", chip: "text-red-400 bg-red-500/10", label: "Behind" }, stalled: { rail: "bg-red-500", chip: "text-red-400 bg-red-500/10", label: "Nothing delivered this week" }, tight: { rail: "bg-amber-500", chip: "text-amber-400 bg-amber-500/10", label: "No slack" }, ahead: { rail: "bg-green-500", chip: "text-green-400 bg-green-500/10", label: "On track" }, complete: { rail: "bg-emerald-500", chip: "text-emerald-400 bg-emerald-500/10", label: "All delivered" }, }; /** Two decimals is false precision for a shot count; one is the real resolution. */ const perDay = (n: number) => n.toFixed(1); export function DeliveryCountdown({ episodes }: { episodes: EpisodeCountdown[] }) { if (episodes.length === 0) { return (
No delivery dates set. Add them on a project's Episodes tab to see the countdown here.
); } return (
{episodes.map((ep) => { const verdict = VERDICT[ep.verdict]; const pct = (n: number) => (ep.total === 0 ? 0 : (n / ep.total) * 100); return ( {/* Severity rail — the card itself stays neutral so only the episode in trouble pulls the eye. */}
); }