feat(portfolio): rebuild w-make.com as a bilingual editorial studio
Give clients a real IA, SMTP contact to eldov@w-make.de, and live Batch evidence instead of a single-page placeholder. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -0,0 +1,181 @@
|
||||
import Link from "next/link";
|
||||
import { HomeJsonLd } from "@/components/json-ld";
|
||||
import { getOffers } from "@/content/offers";
|
||||
import { getNotes } from "@/content/notes";
|
||||
import { getPerson } from "@/content/person";
|
||||
import { getProjects } from "@/content/projects";
|
||||
import { getDictionary } from "@/i18n/dictionaries";
|
||||
import { publicPath } from "@/i18n/routes";
|
||||
import { pageMetadata } from "@/lib/metadata";
|
||||
import { requireLocale } from "@/lib/locale";
|
||||
import { getUpdates } from "@/lib/updates";
|
||||
|
||||
export async function generateMetadata({ params }: PageProps<"/[lang]">) {
|
||||
const locale = requireLocale((await params).lang);
|
||||
const dict = getDictionary(locale);
|
||||
return pageMetadata(
|
||||
locale,
|
||||
"home",
|
||||
locale === "de"
|
||||
? "Jan Wagner — Software für anspruchsvolle Prozesse"
|
||||
: "Jan Wagner — Software for demanding processes",
|
||||
dict.home.lede,
|
||||
);
|
||||
}
|
||||
|
||||
export default async function HomePage({ params }: PageProps<"/[lang]">) {
|
||||
const locale = requireLocale((await params).lang);
|
||||
const dict = getDictionary(locale);
|
||||
const person = getPerson(locale);
|
||||
const projects = getProjects(locale);
|
||||
const offers = getOffers(locale);
|
||||
const notes = getNotes(locale).slice(0, 2);
|
||||
const updates = await getUpdates();
|
||||
|
||||
return (
|
||||
<main>
|
||||
<HomeJsonLd />
|
||||
<section className="mx-auto grid w-full max-w-6xl gap-16 px-6 py-20 md:grid-cols-[1.35fr_0.65fr] md:items-end md:px-10 md:py-28">
|
||||
<div>
|
||||
<p className="kicker">{dict.home.kicker}</p>
|
||||
<h1 className="display mt-6 max-w-4xl text-5xl text-paper md:text-7xl">{dict.home.title}</h1>
|
||||
<p className="mt-8 max-w-2xl text-lg leading-8 text-mute-strong md:text-xl">{dict.home.lede}</p>
|
||||
<div className="mt-10 flex flex-wrap gap-8 text-sm">
|
||||
<Link href={publicPath(locale, "work")} className="border-b border-copper pb-1 text-copper">
|
||||
{dict.actions.viewWork}
|
||||
</Link>
|
||||
<Link href={publicPath(locale, "contact")} className="border-b border-line-strong pb-1 text-paper">
|
||||
{dict.actions.startConversation}
|
||||
</Link>
|
||||
</div>
|
||||
</div>
|
||||
<aside className="border-l border-copper/50 pl-6">
|
||||
<p className="kicker">{dict.home.asideLabel}</p>
|
||||
<p className="mt-4 font-serif text-2xl leading-snug text-paper">{dict.home.asideTitle}</p>
|
||||
<p className="mt-5 text-sm leading-7 text-mute-strong">{dict.home.asideBody}</p>
|
||||
</aside>
|
||||
</section>
|
||||
|
||||
<section className="border-t border-line">
|
||||
<div className="mx-auto w-full max-w-6xl px-6 py-20 md:px-10">
|
||||
<div className="mb-12 flex flex-wrap items-end justify-between gap-6">
|
||||
<div>
|
||||
<p className="kicker">{dict.home.workKicker}</p>
|
||||
<h2 className="display mt-4 text-4xl md:text-5xl">{dict.home.workTitle}</h2>
|
||||
</div>
|
||||
<p className="max-w-md text-sm leading-7 text-mute-strong">{dict.home.workLede}</p>
|
||||
</div>
|
||||
<div className="grid gap-12 md:grid-cols-2">
|
||||
{projects.map((project) => (
|
||||
<article key={project.slug} className="border-t border-line pt-8">
|
||||
<div className="flex items-baseline justify-between gap-4">
|
||||
<p className="kicker">{project.label}</p>
|
||||
<span className="font-mono text-xs text-mute">{project.number}</span>
|
||||
</div>
|
||||
<h3 className="mt-5 font-serif text-3xl">{project.title}</h3>
|
||||
<p className="mt-4 leading-7 text-mute-strong">{project.summary}</p>
|
||||
<p className="mt-6 font-mono text-xs uppercase tracking-[0.14em] text-mute">{project.status}</p>
|
||||
<Link
|
||||
href={publicPath(locale, "workItem", project.slug)}
|
||||
className="mt-6 inline-block border-b border-copper pb-1 text-sm text-copper"
|
||||
>
|
||||
{dict.actions.openCase} →
|
||||
</Link>
|
||||
</article>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section className="border-t border-line">
|
||||
<div className="mx-auto grid w-full max-w-6xl gap-12 px-6 py-20 md:grid-cols-[0.8fr_1.2fr] md:px-10">
|
||||
<div>
|
||||
<p className="kicker">{dict.home.methodKicker}</p>
|
||||
<h2 className="display mt-4 text-4xl md:text-5xl">{dict.home.methodTitle}</h2>
|
||||
<p className="mt-6 max-w-md leading-7 text-mute-strong">{dict.home.methodLede}</p>
|
||||
</div>
|
||||
<ol className="grid gap-8">
|
||||
{person.principles.map((principle, index) => (
|
||||
<li key={principle.title} className="grid gap-3 border-b border-line pb-8 md:grid-cols-[3rem_1fr]">
|
||||
<span className="font-mono text-sm text-copper">0{index + 1}</span>
|
||||
<div>
|
||||
<h3 className="font-serif text-2xl">{principle.title}</h3>
|
||||
<p className="mt-2 max-w-xl leading-7 text-mute-strong">{principle.text}</p>
|
||||
</div>
|
||||
</li>
|
||||
))}
|
||||
</ol>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section className="border-t border-line">
|
||||
<div className="mx-auto w-full max-w-6xl px-6 py-20 md:px-10">
|
||||
<p className="kicker">{dict.home.offerKicker}</p>
|
||||
<h2 className="display mt-4 max-w-2xl text-4xl md:text-5xl">{dict.home.offerTitle}</h2>
|
||||
<div className="mt-12 grid gap-10 md:grid-cols-3">
|
||||
{offers.map((offer, index) => (
|
||||
<article key={offer.title} className="border-l border-line-strong pl-5">
|
||||
<span className="font-mono text-xs text-copper">0{index + 1}</span>
|
||||
<h3 className="mt-4 font-serif text-2xl">{offer.title}</h3>
|
||||
<p className="mt-3 text-sm leading-7 text-mute-strong">{offer.text}</p>
|
||||
</article>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section className="border-t border-line">
|
||||
<div className="mx-auto w-full max-w-6xl px-6 py-20 md:px-10">
|
||||
<p className="kicker">{dict.home.notesKicker}</p>
|
||||
<h2 className="display mt-4 text-4xl md:text-5xl">{dict.home.notesTitle}</h2>
|
||||
<div className="mt-12 grid gap-10 md:grid-cols-2">
|
||||
{notes.map((note) => (
|
||||
<article key={note.slug}>
|
||||
<time className="font-mono text-xs text-mute" dateTime={note.date}>
|
||||
{new Date(note.date).toLocaleDateString(locale === "de" ? "de-DE" : "en-GB")}
|
||||
</time>
|
||||
<h3 className="mt-3 font-serif text-2xl">{note.title}</h3>
|
||||
<p className="mt-3 text-sm leading-7 text-mute-strong">{note.summary}</p>
|
||||
<Link
|
||||
href={publicPath(locale, "noteItem", note.slug)}
|
||||
className="mt-5 inline-block text-sm text-copper"
|
||||
>
|
||||
{dict.actions.openNote} →
|
||||
</Link>
|
||||
</article>
|
||||
))}
|
||||
</div>
|
||||
{updates.length ? (
|
||||
<div className="mt-16 border-t border-line pt-10">
|
||||
<p className="kicker">Batchmaker</p>
|
||||
<div className="mt-6 grid gap-6 md:grid-cols-3">
|
||||
{updates.map((update) => (
|
||||
<article key={update.slug}>
|
||||
<h3 className="font-serif text-xl">{update.title}</h3>
|
||||
<p className="mt-2 text-sm leading-6 text-mute-strong">{update.summary}</p>
|
||||
</article>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
) : null}
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section className="border-t border-line">
|
||||
<div className="mx-auto w-full max-w-6xl px-6 py-20 md:px-10">
|
||||
<div className="border border-copper/30 bg-copper/5 px-8 py-12 md:px-12">
|
||||
<p className="kicker">{dict.home.ctaKicker}</p>
|
||||
<h2 className="display mt-4 max-w-3xl text-4xl md:text-6xl">{dict.home.ctaTitle}</h2>
|
||||
<p className="mt-6 max-w-2xl leading-7 text-mute-strong">{dict.home.ctaBody}</p>
|
||||
<Link
|
||||
href={publicPath(locale, "contact")}
|
||||
className="mt-8 inline-block border border-copper bg-copper px-6 py-3 text-sm font-medium text-ink"
|
||||
>
|
||||
{dict.actions.startConversation}
|
||||
</Link>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</main>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user