import { IS_CORRESPONDENCE, LETTER_LABEL } from "@/lib/letter-kinds"; import type { LetterKind } from "@prisma/client"; /** * A letter, on a page. * * Deliberately not ResumePaper. A resume is a structured document with a * schema, six templates and a design popover; a letter is prose somebody types, * and the only questions worth answering about printing one are whether the * contact details are at the top and whether the paragraphs break sensibly. * Giving it templates and an accent colour would be inventing decisions nobody * asked to make. * * The margin is stated here in inches rather than read from a setting for the * same reason. One inch is what a letter has. */ export type LetterPaperProps = { kind: LetterKind; title: string; recipient: string; body: string; /** The letterhead, from their profile. Any field that is empty is skipped. */ from: { name: string; email: string; phone: string; location: string; website: string; }; /** * The date as a civil day already formatted in the person's own zone. A Date * would be formatted here in the server's, which is the bug the time-zone * sweep spent a day removing. */ date: string; }; export function LetterPaper({ kind, title, recipient, body, from, date }: LetterPaperProps) { const contact = [from.email, from.phone, from.location, from.website].filter(Boolean); // A blank line is a paragraph break; a single newline is a line break inside // one. That is how people type into a textarea, so it is how this reads it. const paragraphs = body.split(/\n{2,}/).map((block) => block.trim()).filter(Boolean); // A LinkedIn About, a headline, a self-review and a brag doc are documents // ABOUT the person rather than letters to anybody, so they get no date, no // recipient line and no rule — a brag doc dated like a letter looks like a // mistake. The letterhead stays: their name at the top of a LinkedIn About is // right, and the contact block already drops every empty field. const correspondence = IS_CORRESPONDENCE[kind]; return (

{from.name || title || LETTER_LABEL[kind]}

{contact.length > 0 &&
{contact.join(" · ")}
}

{correspondence && date &&
{date}
} {correspondence && recipient &&

{recipient},

} {/* The title is the person's own label for the draft ("Stripe cover letter"), which is a filing name rather than a subject line — so it only appears when there is no recipient to address, where a page with nothing but body text reads as a fragment. On the four self-facing kinds it is the document's heading rather than a subject, and it is always shown. */} {(!correspondence || !recipient) && title && (

{title}

)} {paragraphs.map((paragraph, index) => (

{paragraph.split("\n").map((line, lineIndex, lines) => ( {line} {lineIndex < lines.length - 1 &&
}
))}

))}
); }