Files
fivedevs.com/src/app/work/[slug]/page.tsx
Chris Smith a7c05104af flatten new-site/ to repo root and remove old hugo site
Moves the Next.js app's contents from new-site/ to the repository
root and deletes the previous Hugo site (assets/, content/, themes/,
hugo.toml, etc.). Also retires the AWS Amplify config and old
Netlify _redirects file — the new site deploys to Vercel.

Updates STRATEGY.md path references to drop the new-site/ prefix.
LICENSE preserved.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-30 13:14:01 +02:00

208 lines
6.9 KiB
TypeScript

import { notFound } from "next/navigation";
import Link from "next/link";
import { Button } from "@/components/button";
import { Container } from "@/components/container";
import { JsonLd } from "@/components/jsonld";
import { Eyebrow } from "@/components/section-heading";
import { articleSchema, breadcrumbSchema } from "@/lib/jsonld";
import { caseStudies } from "@/lib/case-studies";
import type { Metadata } from "next";
type Params = Promise<{ slug: string }>;
const bodies: Record<string, React.ReactNode> = {
"pritikin-foods": (
<>
<h2>The shape of the problem</h2>
<p>
Pritikin Foods sells direct-to-consumer prepared meals nationwide.
Every order from the e-commerce site has to be parsed, validated,
priced for shipping, and handed off to their 3PL fulfillment
partner &mdash; on time, with the right items, to the right
address, in the right shipping window.
</p>
<p>
That sounds simple. It isn&rsquo;t. There&rsquo;s SKU
normalization, address validation, transit-time math against meal
perishability, special-handling rules, batch hand-offs, and a
whole tail of reconciliation when something doesn&rsquo;t match
what the warehouse actually shipped.
</p>
<h2>What I built and what I maintain</h2>
<ul>
<li>
The order-parsing pipeline that translates e-commerce orders
into the format the 3PL accepts.
</li>
<li>
The shipping and label flow: rate calculation, label
generation, and carrier-side error handling.
</li>
<li>
Reconciliation tooling so customer service can answer
&ldquo;where is my order?&rdquo; in seconds, not minutes.
</li>
<li>
The boring, on-call work of keeping it all running as carriers,
rates, and partners change.
</li>
</ul>
<h2>Why it&rsquo;s been a multi-year relationship</h2>
<p>
This work isn&rsquo;t glamorous, but it&rsquo;s the difference
between Pritikin shipping orders today and not. Emil
Boschert&rsquo;s words say it best:
</p>
<blockquote>
&ldquo;They are vital for our e-commerce site and our 3PL.&rdquo;
</blockquote>
<p>
That&rsquo;s the bar I aim for on every long engagement &mdash;
becoming the kind of help a small team can stop worrying about.
</p>
</>
),
americold: (
<>
<h2>The shape of the problem</h2>
<p>
Americold is one of the largest temperature-controlled warehousing
and logistics networks in the world. At their scale, even
&ldquo;small&rdquo; pieces of operational tooling touch a lot of
people, a lot of pallets, and a lot of carriers.
</p>
<h2>What the engagement looked like</h2>
<p>
Senior-level PHP work on customer-facing logistics tooling.
Solution-oriented, customer-focused, delivered consistently
&mdash; in the words of Americold&rsquo;s Timothy Dorcas:
</p>
<blockquote>
&ldquo;They are solution oriented, customer focused, and
consistently deliver at a high level. Highly recommended.&rdquo;
</blockquote>
<h2>Why this kind of work fits Five Devs</h2>
<p>
Enterprise logistics teams don&rsquo;t need another agency. They
need a senior developer who can read the existing code, talk to
the operations people, ship the change, and write down what they
learned &mdash; without three layers of project management
between them and the work.
</p>
<p>
That&rsquo;s the role I&rsquo;ve played for Americold and others.
I show up, get up to speed quickly on the existing stack, and
keep the work moving.
</p>
</>
),
};
export async function generateStaticParams() {
return caseStudies.map((c) => ({ slug: c.slug }));
}
export const dynamicParams = false;
export async function generateMetadata({
params,
}: {
params: Params;
}): Promise<Metadata> {
const { slug } = await params;
const study = caseStudies.find((c) => c.slug === slug);
if (!study) return {};
return {
title: `${study.client} — Case Study`,
description: study.summary,
};
}
export default async function CaseStudyPage({
params,
}: {
params: Params;
}) {
const { slug } = await params;
const study = caseStudies.find((c) => c.slug === slug);
if (!study) notFound();
const body = bodies[slug];
return (
<article className="py-20 sm:py-28">
<JsonLd
data={articleSchema({
title: `${study.client} — Case Study`,
description: study.summary,
slug,
date: "2026-01-01",
basePath: "work",
})}
/>
<JsonLd
data={breadcrumbSchema([
{ name: "Home", path: "/" },
{ name: "Work", path: "/work" },
{ name: study.client, path: `/work/${slug}` },
])}
/>
<Container width="narrow">
<Link
href="/work"
className="text-sm text-muted hover:text-ink"
>
&larr; All work
</Link>
<Eyebrow className="mt-8">Case study</Eyebrow>
<h1 className="mt-4 font-serif text-4xl font-semibold leading-tight tracking-tight text-ink sm:text-5xl">
{study.client}
</h1>
<p
className="mt-3 text-sm text-muted"
dangerouslySetInnerHTML={{ __html: study.industry }}
/>
<dl className="mt-10 grid grid-cols-2 gap-6 border-y border-line/70 py-6 text-sm sm:grid-cols-3">
<div>
<dt className="text-xs uppercase tracking-wider text-muted">
Role
</dt>
<dd className="mt-1 text-ink">{study.role ?? "Developer"}</dd>
</div>
{study.yearsRunning ? (
<div>
<dt className="text-xs uppercase tracking-wider text-muted">
Duration
</dt>
<dd className="mt-1 text-ink">{study.yearsRunning}</dd>
</div>
) : null}
<div>
<dt className="text-xs uppercase tracking-wider text-muted">
Outcome
</dt>
<dd className="mt-1 text-ink">{study.outcome}</dd>
</div>
</dl>
<div className="prose prose-lg mt-12 max-w-none text-ink-soft prose-headings:font-serif prose-headings:text-ink prose-headings:font-semibold prose-h2:mt-12 prose-h2:text-2xl prose-strong:text-ink prose-blockquote:border-accent prose-blockquote:text-ink">
{body ?? <p>{study.summary}</p>}
</div>
<div className="mt-16 flex flex-wrap items-center gap-4">
<Button href="/contact" variant="primary">
Talk about your project
</Button>
<Button href="/work" variant="secondary">
More work
</Button>
</div>
</Container>
</article>
);
}