import { useEffect, useMemo, useState } from "react"; import { getExperience } from "../api"; import Icon from "../components/Icon"; import PageHeader from "../components/PageHeader"; import { ErrorState, LoadingState } from "../components/Status"; function asDate(value) { const [year, month] = value.split("-").map(Number); return new Date(year, month - 1, 1); } function monthLabel(value) { return new Intl.DateTimeFormat("en-US", { month: "short", year: "numeric", }).format(asDate(value)); } function durationLabel(start, end) { const startDate = asDate(start); const endDate = end ? asDate(end) : new Date(); const months = Math.max( 1, (endDate.getFullYear() - startDate.getFullYear()) * 12 + endDate.getMonth() - startDate.getMonth(), ); const years = Math.floor(months / 12); const remainingMonths = months % 12; const parts = []; if (years) parts.push(`${years} yr${years === 1 ? "" : "s"}`); if (remainingMonths) parts.push(`${remainingMonths} mo`); return parts.join(" ") || "1 mo"; } function TimelineItem({ item, index, isExpanded, onToggle }) { const isCurrent = !item.end; const year = item.start.slice(0, 4); return (

{item.summary}

    {item.highlights.map((highlight) => (
  • {highlight}
  • ))}
{item.projects && (
{item.projects.map((project) => ( {project.name} {project.period} ))}
)}
{item.technologies.map((technology) => ( {technology} ))}
); } export default function ExperiencePage() { const [experience, setExperience] = useState([]); const [expandedId, setExpandedId] = useState("independent"); const [loading, setLoading] = useState(true); const [error, setError] = useState(""); useEffect(() => { const controller = new AbortController(); getExperience(controller.signal) .then(setExperience) .catch((requestError) => { if (requestError.name !== "AbortError") setError(requestError.message); }) .finally(() => setLoading(false)); return () => controller.abort(); }, []); const visibleYears = useMemo(() => { if (!experience.length) return "2013 — now"; return `${experience.at(-1).start.slice(0, 4)} — now`; }, [experience]); return (
{experience.length || "6"} career chapters
{visibleYears}
} /> {loading && } {error && } {!loading && !error && (
{experience.map((item, index) => ( setExpandedId((active) => (active === item.id ? "" : item.id))} /> ))}
)}
); }