Files
com.gregkilmer.www/services/site/src/lib/resume.ts
T
Claude 963ad0df23 Resume from Greg's interview, one-page PDF, store slots closed
The experience notes, skills, education and a new /resume Page text now come
from Greg's Resume Interview answers (tools/migrate/src/seed.ts). Comcast and
Drexel move to a short Earlier list, and the page and PDF gain Selected
projects. Carrier Connect's numbers are Greg's corrected ones.

The PDF fits on one page and carries the email from site settings. Store
lines migrate with no open slots until Greg opens orders.

Schema: experience earlier, page projects, site-settings email.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012aSbo2D1qP8cMFQCykf7kx
2026-10-11 21:58:21 +00:00

61 lines
2.5 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/** The resume's data, shared by the page and the PDF so they never disagree. */
import type { Note } from "./content.ts";
import type { SiteIndex } from "./site.ts";
export interface ResumeData {
name: string;
headline: string;
location?: string;
summary?: string;
availability?: string;
email?: string;
stats: { value: string; label: string; detail?: string }[];
/** Roles shown in full, current first. */
roles: Note<"experience">[];
/** Roles marked earlier: a short list of title, company, dates and focus. */
earlier: Note<"experience">[];
projects: { title: string; detail: string; link?: string }[];
skills?: Note<"skills">["data"];
education?: Note<"education">["data"];
socials: { label: string; handle: string; url: string }[];
/** The newest change to any note the resume is built from (YYYY-MM-DD). */
updated: string;
}
/** "Mar 2019", from "2019-03". */
export const month = (ym: string | undefined) => {
if (!ym) return "Present";
const [y, m] = ym.split("-").map(Number);
return new Date(Date.UTC(y!, (m ?? 1) - 1, 1)).toLocaleDateString("en-US", { timeZone: "UTC", month: "short", year: "numeric" });
};
export const period = (r: Note<"experience">) => `${month(r.data.start)} – ${month(r.data.end)}`;
/** "A, B and C". */
export const listOf = (items: string[]) => new Intl.ListFormat("en-US", { style: "long", type: "conjunction" }).format(items);
export function resumeData(s: SiteIndex): ResumeData {
const settings = s.settings();
const page = s.pageText("/resume");
const roles = [...s.all("experience")].sort((a, b) => (b.data.end ?? "9999").localeCompare(a.data.end ?? "9999") || b.data.start.localeCompare(a.data.start));
const current = roles.find((r) => !r.data.end) ?? roles[0];
const sources: Note[] = [...roles, ...(page ? [page] : []), ...[s.one("skills"), s.one("education")].filter((n): n is Note<"skills"> | Note<"education"> => !!n)];
const updated = sources.map((n) => n.acceptedAt?.slice(0, 10) ?? "").sort().at(-1) ?? "";
return {
name: settings?.name ?? "Greg Kilmer",
headline: page?.data.lede ?? current?.data.role ?? "",
location: settings?.location,
summary: page?.body?.trim() || undefined,
availability: settings?.availability,
email: settings?.email,
stats: page?.data.stats ?? [],
roles: roles.filter((r) => !r.data.earlier),
earlier: roles.filter((r) => r.data.earlier),
projects: page?.data.projects ?? [],
skills: s.one("skills")?.data,
education: s.one("education")?.data,
socials: settings?.socials ?? [],
updated,
};
}