import Link from "next/link"; import { ArrowRightIcon } from "lucide-react"; import type { SearchDiagnosis } from "@/lib/data/pipeline"; import type { ResumeRow } from "@/lib/data/analytics"; import { STAGE_LABEL, STAGE_TONE } from "@/lib/stages"; import { Card, CardContent } from "@/components/ui/card"; import { cn } from "@/lib/utils"; /** * What is wrong, in a sentence. * * The rates are underneath rather than above on purpose. Six numbers with no * reading is what every job tracker already shows, and interpreting them is the * work — so the tool does that part and shows its working below. */ export function DiagnosisCard({ diagnosis, resumes, }: { diagnosis: SearchDiagnosis; /** * resume_performance's rows — the one answer to "which resume is working". * The diagnosis used to carry its own per-resume block under a different * rule, and the two disagreed about the same documents. */ resumes: ResumeRow[]; }) { const { steps, weakest, headline, detail, velocity, bySource, stalled } = diagnosis; const byResume = resumes.filter((row) => row.sent > 0).sort((a, b) => b.sent - a.sent); const busiest = Math.max(1, ...velocity.map((week) => week.count)); return (
What's working

{headline}

{detail}

{/* Gated on the diagnosis's own rule, not on "anything reached at all". At one application this drew "Applied → Screening · 0 of 1 · 0%" with a full-width empty bar, directly under a headline saying it was too early to tell them anything — and on a phone the "0 of 1" column is hidden, so the only thing left was the 0%. Same threshold as the stat tile above it, read from the same flag. */} {diagnosis.confident && steps.some((step) => step.reached > 0) && (
{steps.map((step) => { const isWeak = step.key === weakest; return (
{/* Three fixed columns plus gaps is 348px, which is wider than a 360px phone has to give. The label takes the remaining space below md instead of forcing the page sideways; the counts wait for a screen with room. */} {step.from} → {step.to} {step.advanced} of {step.reached} {step.rate === null ? "—" : `${step.rate}%`} {/* A zero-day median means "moved the same day", which is not a useful thing to print on every row. */} {step.medianDays ? `${step.medianDays}d median` : ""}
); })}
)}
Sent, by week
{velocity.map((week, index) => (
{week.count || ""}
))}
Six weeks, this week on the right
{/* A resume with one application behind it was being given a response rate — 0% or 100%, either of them a verdict on nothing, next to its name. */} {diagnosis.confident && byResume.length > 0 && (
By resume
    {byResume.slice(0, 3).map((resume) => (
  • {resume.name} {resume.responded}/{resume.sent} {resume.responseRate === null ? "—" : `${resume.responseRate}%`}
  • ))}
)} {/* Where they came from, on the same gate and in the same shape as the resumes above. This is the one of the two that changes what somebody does on Saturday: a referral rate four times a job board's is an argument for spending the morning messaging people rather than filling in forms. */} {diagnosis.confident && bySource.length > 0 && (
By source
    {bySource.slice(0, 3).map((source) => (
  • {source.name} {source.responded}/{source.sent} {source.rate === null ? "—" : `${source.rate}%`}
  • ))}
)}
{stalled.length > 0 && (
{stalled.length} gone quiet — nothing logged in{" "} {stalled[0].days === stalled[stalled.length - 1].days ? `${stalled[0].days} days` : `${stalled[stalled.length - 1].days}–${stalled[0].days} days`} Chase them
    {stalled.slice(0, 6).map((row) => (
  • {row.company} {row.days}d
  • ))}
)}
); }