@@ -214,15 +230,13 @@ export function CartContent() {
+ {/* Title only — /checkout renders the same CartTrustBadges
+ docs with description too, see CheckoutContent.tsx. */}
@@ -368,17 +350,15 @@ export function CheckoutContent() {
+ {/* title + description here — /cart's sidebar renders the same
+ CartTrustBadges docs with title only, see CartContent.tsx. */}
- {[
- { icon: "/icon-lock.svg", title: "Sichere Zahlung", desc: "Deine Daten sind bei uns sicher und geschützt." },
- { icon: "/icon-trust-return.png", title: "14 Tage Rückgaberecht", desc: "Nicht zufrieden? Sende deine Bestellung innerhalb von 14 Tagen zurück." },
- { icon: "/icon-trust-leaf.png", title: "Nachhaltig verpackt", desc: "Wir achten auf umweltfreundliche Materialien und plastikfreien Versand." },
- ].map((b) => (
-
+ {trustBadges.map((b) => (
+
{b.title}
-
{b.desc}
+
{b.description}
))}
diff --git a/app/checkout/page.tsx b/app/checkout/page.tsx
index acff019..2959f7c 100644
--- a/app/checkout/page.tsx
+++ b/app/checkout/page.tsx
@@ -2,6 +2,7 @@ import type { Metadata } from "next";
import { CheckoutContent } from "./components/CheckoutContent";
import { TrustRow } from "../components/TrustRow";
import { Footer } from "../components/Footer";
+import { getShippingMethods, getPaymentMethods, getCartTrustBadges } from "../lib/payload";
// robots: noindex — transactional page, same reasoning as /cart.
export const metadata: Metadata = {
@@ -13,11 +14,17 @@ export const metadata: Metadata = {
},
};
-export default function CheckoutPage() {
+export default async function CheckoutPage() {
+ const [shippingMethods, paymentMethods, trustBadges] = await Promise.all([
+ getShippingMethods(),
+ getPaymentMethods(),
+ getCartTrustBadges(),
+ ]);
+
return (
<>
-
+
diff --git a/app/components/AddToCartButton.tsx b/app/components/AddToCartButton.tsx
index 6e986d6..58f357c 100644
--- a/app/components/AddToCartButton.tsx
+++ b/app/components/AddToCartButton.tsx
@@ -5,7 +5,6 @@ import { addToCart } from "../lib/cart";
import { useCartFly } from "./CartFly";
const FEEDBACK_MS = 2000;
-const PRODUCT_ID = "todo-karten";
/**
* Shared by /todo-cards's Hero + pricing panel and Home's product
@@ -17,9 +16,14 @@ const PRODUCT_ID = "todo-karten";
export function AddToCartButton({
label,
className,
+ productId = "todo-karten",
}: {
label: string;
className?: string;
+ /** Defaults to "todo-karten" for /todo-cards' own hardcoded usage — Home's
+ * ProductSpotlight passes the actual CMS-selected spotlight product's id
+ * explicitly, since that can now be a different product. */
+ productId?: string;
}) {
const [added, setAdded] = useState(false);
const timeoutRef = useRef | undefined>(undefined);
@@ -29,7 +33,7 @@ export function AddToCartButton({
useEffect(() => () => clearTimeout(timeoutRef.current), []);
function handleClick() {
- addToCart(PRODUCT_ID);
+ addToCart(productId);
if (buttonRef.current) fly(buttonRef.current);
setAdded(true);
clearTimeout(timeoutRef.current);
diff --git a/app/components/ProductSpotlight.tsx b/app/components/ProductSpotlight.tsx
index de48d31..3185401 100644
--- a/app/components/ProductSpotlight.tsx
+++ b/app/components/ProductSpotlight.tsx
@@ -2,36 +2,38 @@ import Image from "next/image";
import Link from "next/link";
import { AddToCartButton } from "./AddToCartButton";
import { Reveal } from "./Reveal";
-import { getProductBySlug } from "../lib/payload";
+import { getSpotlightProduct } from "../lib/payload";
import { formatPrice } from "../lib/format";
/**
- * Product teaser for ToDo-Karten, placed after the Werkzeuge section (not
- * right after the Hero — that already has its own primary CTA, the
- * 7-Tage-Challenge, and a second strong purchase CTA competing with it
- * there would dilute focus). Werkzeuge already introduces ToDo-Karten as
- * a concept with an "Entdecken" link; this is the natural next step —
- * a concrete way to buy it, right where interest was just built, rather
- * than dropped at the very top before the page has earned any trust.
- * Not derived from a Figma frame (page-home never had this section) —
- * a deliberate, code-only addition, styled to match the /todo-cards
- * pricing panel it's a teaser for. Price/photo come from Payload (same
- * "todo-karten" product the shop/cart use) rather than being duplicated
- * here as a hardcoded literal, so they can never silently drift apart —
- * the marketing headline/copy below stays hand-written, since it's
- * deliberately punchier than the plain catalog description.
+ * Product teaser for whichever product is marked `spotlight` in Payload
+ * (defaults to none — the section just doesn't render until one is set),
+ * placed after the Werkzeuge section (not right after the Hero — that
+ * already has its own primary CTA, the 7-Tage-Challenge, and a second
+ * strong purchase CTA competing with it there would dilute focus).
+ * Werkzeuge already introduces the flagship tool as a concept with an
+ * "Entdecken" link; this is the natural next step — a concrete way to buy
+ * it, right where interest was just built, rather than dropped at the
+ * very top before the page has earned any trust. Not derived from a
+ * Figma frame (page-home never had this section) — a deliberate,
+ * code-only addition, styled to match /todo-cards' pricing panel.
+ * Headline/copy/photo are the product's own dedicated spotlight* fields
+ * (deliberately separate from its plain catalog name/description/image —
+ * see Products.ts), not duplicated here as hardcoded literals.
*/
export async function ProductSpotlight() {
- const product = await getProductBySlug("todo-karten");
+ const product = await getSpotlightProduct();
if (!product) return null;
+ const image = product.spotlightImage || product.image;
+
return (
- ToDo-Karten – Kleine Karten. Große Wirkung.
+ {product.spotlightHeadline || product.name}
- 50 hochwertige Karten, die dir helfen, deinen Kopf frei zu bekommen und das Wesentliche zu sehen — analog, minimalistisch, für jeden Tag.
+ {product.spotlightText || product.description}
@@ -58,13 +63,15 @@ export async function ProductSpotlight() {
(matches Tools/Blog above/below), same as AddToCartButton's
own default styling/ring-offset, so no override is needed
here. */}
-
-
- Mehr erfahren
-
+
+ {product.href && (
+
+ Mehr erfahren
+
+ )}
diff --git a/app/components/Tools.tsx b/app/components/Tools.tsx
index cbe72de..e5259c8 100644
--- a/app/components/Tools.tsx
+++ b/app/components/Tools.tsx
@@ -1,28 +1,18 @@
import Link from "next/link";
import { Reveal, RevealGroup, RevealItem } from "./Reveal";
+import { getWerkzeugeCards } from "../lib/payload";
-const tools = [
- {
- icon: { src: "/icon-rocket.svg", w: "3.375rem", h: "4.122rem", transform: "-scale-y-100" },
- title: "Mini-Challenge",
- description: "In 7 Tagen zu mehr Klarheit. Kleine Gewohnheiten, die Großes bewirken.",
- cta: { label: "Starten", href: "/challenge" },
- },
- {
- icon: { src: "/icon-todo.svg", w: "3rem", h: "3.879rem", transform: "-scale-y-100" },
- title: "ToDo-Karten",
- description: "Das Werkzeug für Fokus im Alltag. bringe Struktur in deine Aufgaben und gewinne Zeit zurück.",
- cta: { label: "Entdecken", href: "/todo-cards" },
- },
- {
- icon: { src: "/icon-newsletter.svg", w: "3rem", h: "2.579rem", transform: "-rotate-4 -scale-y-100" },
- title: "Impulse & Tipps",
- description: "Wöchentliche Impulse mit konkreten Ideen und erprobten Tipps für weniger Reibung und mehr Leichtigkeit.",
- cta: { label: "Anmelden", href: "/newsletter" },
- },
-];
+// Content now lives in Payload (WerkzeugeCards collection). Icons use a
+// uniform box here (object-contain) rather than the previous hardcoded
+// per-card hand-tuned width/height/rotation — that only made sense for a
+// fixed, known set of 3 SVGs authored upside-down for a specific
+// hand-drawn look, which doesn't generalize to a real CMS field. The 3
+// original icons were re-exported pre-flipped/rotated as PNGs so they
+// still display correctly with plain object-contain.
+export async function Tools() {
+ const tools = await getWerkzeugeCards();
+ if (tools.length === 0) return null;
-export function Tools() {
return (
{/* Card content — self-stretch + h-full + justify-between so
@@ -85,10 +67,10 @@ export function Tools() {
- → {tool.cta.label}
+ → {tool.ctaLabel}
diff --git a/app/components/TrustRow.tsx b/app/components/TrustRow.tsx
index 40503ac..77c64e8 100644
--- a/app/components/TrustRow.tsx
+++ b/app/components/TrustRow.tsx
@@ -1,31 +1,24 @@
-import { FREE_SHIPPING_THRESHOLD, TOTAL_DAYS_DE } from "../lib/shipping";
-import { formatPrice } from "../lib/format";
+import { getTrustBadges } from "../lib/payload";
-const items = [
- {
- icon: "/icon-trust-shipping.png",
- title: "Schneller Versand",
- desc: `In ${TOTAL_DAYS_DE.min}–${TOTAL_DAYS_DE.max} Werktagen bei dir.`,
- },
- {
- icon: "/icon-trust-free-shipping.png",
- title: "Versandkostenfrei",
- desc: `Ab ${formatPrice(FREE_SHIPPING_THRESHOLD)} Bestellwert innerhalb DE.`,
- },
- { icon: "/icon-trust-heart.png", title: "Mit Liebe verpackt", desc: "Für mehr Freude beim Auspacken." },
-];
+// Content now lives in Payload (TrustBadges collection) instead of being
+// hardcoded here, so copy (e.g. the shipping timeframe/threshold numbers)
+// can be updated without a code deploy. If the fetch fails or nothing is
+// seeded yet, the row just doesn't render rather than showing stale
+// hardcoded fallback text that could drift from the real numbers.
+export async function TrustRow() {
+ const items = await getTrustBadges();
+ if (items.length === 0) return null;
-export function TrustRow() {
return (