From 1d7f49217c8c5665017a392d5f6a7225ac7db460 Mon Sep 17 00:00:00 2001 From: Marco Date: Wed, 26 Aug 2026 22:48:09 +0000 Subject: [PATCH] Add PDP for Einfach anfangen. (todo-starter) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit New product page at /einfach-anfangen — Hero (breadcrumb/price/CTA), "Eine Karte. Ein Tag." 3-step section (same icon set as todo-cards' HowItWorks), a spec bullet list, and a closing pricing panel. Follows the same hand-coded structure as der-eine/todo-cards (bespoke Hero prose, not read from Products.description). detailHref updated in Payload to point here instead of the unbuilt /todo-starter. Co-Authored-By: Claude Sonnet 5 Claude-Session: https://claude.ai/code/session_01J1Hu5bZ1kZUgKhab6yNwCt --- app/einfach-anfangen/components/Focus.tsx | 42 ++++++ app/einfach-anfangen/components/Hero.tsx | 120 ++++++++++++++++++ .../components/HowItWorks.tsx | 68 ++++++++++ app/einfach-anfangen/components/Pricing.tsx | 92 ++++++++++++++ app/einfach-anfangen/page.tsx | 50 ++++++++ 5 files changed, 372 insertions(+) create mode 100644 app/einfach-anfangen/components/Focus.tsx create mode 100644 app/einfach-anfangen/components/Hero.tsx create mode 100644 app/einfach-anfangen/components/HowItWorks.tsx create mode 100644 app/einfach-anfangen/components/Pricing.tsx create mode 100644 app/einfach-anfangen/page.tsx diff --git a/app/einfach-anfangen/components/Focus.tsx b/app/einfach-anfangen/components/Focus.tsx new file mode 100644 index 0000000..92fd1e6 --- /dev/null +++ b/app/einfach-anfangen/components/Focus.tsx @@ -0,0 +1,42 @@ +import Image from "next/image"; +import { Reveal, RevealGroup, RevealItem } from "../../components/Reveal"; + +const bullets = [ + "10 ToDo-Karten", + "Platz für bis zu 7 Aufgaben", + "7,5 × 12,5 cm – passt in jede Tasche", + "Freie Rückseite für Notizen", + "Kleine Trink-Erinnerung auf jeder Karte", + "Hochwertiges Papier", + "Produziert in Deutschland", +]; + +// Spec bullet list — same "hand-written, not modeled in Products" reasoning +// as todo-cards/Pricing.tsx's own bullets. Two-column at sm+ since 7 items +// in one column ran noticeably longer than every other section on the page. +export function Focus() { + return ( +
+ +

+ 10 Karten für deinen Alltag +

+

+ Eine Karte für heute. Für das, was gerade ansteht. +

+
+ + + {bullets.map((b) => ( + + + {b} + + ))} + +
+ ); +} diff --git a/app/einfach-anfangen/components/Hero.tsx b/app/einfach-anfangen/components/Hero.tsx new file mode 100644 index 0000000..4817b77 --- /dev/null +++ b/app/einfach-anfangen/components/Hero.tsx @@ -0,0 +1,120 @@ +import Link from "next/link"; +import Image from "next/image"; +import { AddToCartButton } from "../../components/AddToCartButton"; +import { Reveal } from "../../components/Reveal"; +import { ProductName } from "../../components/ProductName"; +import { getProductBySlug, getShippingSettings, getDefaultTaxRatePercent, getKleinunternehmer } from "../../lib/payload"; +import { formatPrice, discountPercent } from "../../lib/format"; +import { effectiveTaxRate } from "../../lib/cartTotals"; + +// Same "compact early teaser + delivery-time repeated next to every buy +// button" reasoning as TodoKartenHero.tsx/der-eine's Hero.tsx. Body copy is +// hardcoded here (not read from product.description) — same convention as +// those two, see der-eine/page.tsx's own comment on why the CMS field and +// this page's tuned prose are allowed to say different things. +export async function Hero() { + const [product, shipping, defaultTaxRate, kleinunternehmer] = await Promise.all([ + getProductBySlug("todo-starter"), + getShippingSettings(), + getDefaultTaxRatePercent(), + getKleinunternehmer(), + ]); + const discount = product ? discountPercent(product.price, product.compareAtPrice) : null; + const taxRate = product ? effectiveTaxRate(product, defaultTaxRate) : null; + const fullyOutOfStock = product + ? !product.active || (product.variants.length > 0 ? product.variants.every((v) => v.outOfStock) : product.outOfStock) + : false; + const anyLowStock = product + ? product.active && (product.variants.length > 0 ? product.variants.some((v) => v.lowStock) : product.lowStock) + : false; + + return ( +
+
+ +

+ + Startseite + + + + Werkzeuge + + + {product ? : "Einfach anfangen."} +

+ +
+
+

+ {product ? : "Einfach anfangen."} +

+ {product?.subline && ( +

+ {product.subline} +

+ )} +
+ +
+

+ Nicht alles. Nicht irgendwann. Nur die paar Dinge, die heute wichtig sind. +

+

+ Einfach anfangen. sind ToDo-Karten für deinen Alltag – mit Platz für bis zu sieben Aufgaben, einem kleinen Feld für „Das war heute schön" und einer freien Rückseite für Notizen. +

+

+ Dazu eine kleine Trink-Erinnerung. Weil man das zwischen all den anderen Dingen gern mal vergisst. +

+
+ +
+ {product && ( +
+ {discount !== null && ( +

{formatPrice(product.compareAtPrice!)}

+ )} +

{formatPrice(product.price)}

+

+ {kleinunternehmer + ? product.noShippingCost + ? "Keine Versandkosten" + : "zzgl. Versand" + : `inkl. ${taxRate}% MwSt. ${product.noShippingCost ? "– keine Versandkosten" : "zzgl. Versand"}`} +

+
+ )} + {!product?.noShippingCost && ( +

+ Lieferzeit: {shipping.totalDays.min}–{shipping.totalDays.max} Werktage innerhalb Deutschlands +

+ )} + {anyLowStock &&

Nur noch wenige verfügbar

} +
+ + {product && ( + + )} +
+
+ +
+ {product && {product.name}} +
+
+
+ ); +} diff --git a/app/einfach-anfangen/components/HowItWorks.tsx b/app/einfach-anfangen/components/HowItWorks.tsx new file mode 100644 index 0000000..e2200da --- /dev/null +++ b/app/einfach-anfangen/components/HowItWorks.tsx @@ -0,0 +1,68 @@ +import { Fragment } from "react"; +import Image from "next/image"; +import { Reveal, RevealGroup, RevealItem } from "../../components/Reveal"; +import { StepArrow } from "../../components/StepArrow"; + +// Same icon-step-*.png set and layout as todo-cards/components/HowItWorks.tsx +// — write / choose-and-start / check off maps onto the same three glyphs. +const steps = [ + { + icon: "/icon-step-1.png", + width: 165, + height: 177, + title: "1. Aufschreiben", + desc: "Was steht heute an?", + }, + { + icon: "/icon-step-2.png", + width: 180, + height: 168, + title: "2. Anfangen", + desc: "Eine Sache auswählen und loslegen.", + }, + { + icon: "/icon-step-3.png", + width: 180, + height: 177, + title: "3. Abhaken", + desc: "Was erledigt ist, darf weg. Der Rest kommt später.", + }, +]; + +export function HowItWorks() { + return ( +
+ +

+ Eine Karte. Ein Tag. +

+
+ + + {steps.map((step, i) => ( + + + +

{step.title}

+

{step.desc}

+
+ {i < steps.length - 1 && ( +
+ +
+ )} +
+ ))} +
+
+ ); +} diff --git a/app/einfach-anfangen/components/Pricing.tsx b/app/einfach-anfangen/components/Pricing.tsx new file mode 100644 index 0000000..bc1261e --- /dev/null +++ b/app/einfach-anfangen/components/Pricing.tsx @@ -0,0 +1,92 @@ +import Image from "next/image"; +import { AddToCartButton } from "../../components/AddToCartButton"; +import { Reveal } from "../../components/Reveal"; +import { ProductName } from "../../components/ProductName"; +import { getProductBySlug, getShippingSettings, getDefaultTaxRatePercent, getKleinunternehmer } from "../../lib/payload"; +import { formatPrice, discountPercent } from "../../lib/format"; +import { effectiveTaxRate } from "../../lib/cartTotals"; + +// Same closing-CTA-panel pattern as todo-cards/components/Pricing.tsx. +export async function Pricing() { + const [product, shipping, defaultTaxRate, kleinunternehmer] = await Promise.all([ + getProductBySlug("todo-starter"), + getShippingSettings(), + getDefaultTaxRatePercent(), + getKleinunternehmer(), + ]); + if (!product) return null; + const discount = discountPercent(product.price, product.compareAtPrice); + const taxRate = effectiveTaxRate(product, defaultTaxRate); + const fullyOutOfStock = + !product.active || (product.variants.length > 0 ? product.variants.every((v) => v.outOfStock) : product.outOfStock); + const anyLowStock = product.active && (product.variants.length > 0 ? product.variants.some((v) => v.lowStock) : product.lowStock); + + return ( +
+ +
+ {product.name} + {fullyOutOfStock ? ( + + Ausverkauft + + ) : ( + discount !== null && ( + + -{discount}% + + ) + )} +
+ +
+

+ +

+ {product.subline &&

{product.subline}

} +
+ +
+
+
+ {discount !== null && ( +

{formatPrice(product.compareAtPrice!)}

+ )} +

{formatPrice(product.price)}

+
+

+ {kleinunternehmer + ? product.noShippingCost + ? "Keine Versandkosten" + : "zzgl. Versand" + : `inkl. ${taxRate}% MwSt. ${product.noShippingCost ? "– keine Versandkosten" : "zzgl. Versand"}`} +

+ {!product.noShippingCost && ( +

+ Lieferzeit: {shipping.totalDays.min}–{shipping.totalDays.max} Werktage innerhalb Deutschlands +

+ )} +
+ {anyLowStock &&

Nur noch wenige verfügbar

} + +
+
+
+ ); +} diff --git a/app/einfach-anfangen/page.tsx b/app/einfach-anfangen/page.tsx new file mode 100644 index 0000000..f14dad7 --- /dev/null +++ b/app/einfach-anfangen/page.tsx @@ -0,0 +1,50 @@ +import type { Metadata } from "next"; +import { Hero } from "./components/Hero"; +import { HowItWorks } from "./components/HowItWorks"; +import { Focus } from "./components/Focus"; +import { Pricing } from "./components/Pricing"; +import { Footer } from "../components/Footer"; +import { getProductBySlug, getCompanySettings } from "../lib/payload"; +import { buildProductSchema } from "../lib/structuredData"; + +const title = "Einfach anfangen. – Die Karte für die Dinge, die heute anstehen."; + +// Description reads from the live product, same pattern as der-eine/ +// tasse-die-pause — see der-eine/page.tsx's own comment on why this and +// the hand-written Hero prose are allowed to differ. +export async function generateMetadata(): Promise { + const product = await getProductBySlug("todo-starter"); + const description = product?.descriptionText || undefined; + return { + title, + description, + alternates: { canonical: "/einfach-anfangen" }, + openGraph: { title, description, url: "/einfach-anfangen" }, + twitter: { title, description }, + }; +} + +export default async function EinfachAnfangenPage() { + const [product, seller] = await Promise.all([ + getProductBySlug("todo-starter"), + getCompanySettings(), + ]); + const productSchema = product + ? buildProductSchema(product, "https://einfach-produktiv.mk360.de/einfach-anfangen", seller) + : null; + + return ( + <> + {productSchema && ( +