Follows Posts.content's BlocksFeature update (docker/payload commit
5426a02) — mirrors PageBlocks.tsx's own JSX for each block field-for-field
since it's the same Block config reused a second time. Works in both the
server-rendered page and the client-side Live Preview renderer (shared
converters), unlike testimonialsRef which needs an async fetch and stays
page-builder only.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01J1Hu5bZ1kZUgKhab6yNwCt
- RichText.tsx: Lexical's auto-detected "autolink" nodes (a typed-out
email/URL, as opposed to an editor-inserted link) fell through to
Payload's unstyled default converter — only "link" was overridden.
AGB's Vertragspartner email rendered as plain black text because of
this. Both node types now get the same text-brand/hover:underline
treatment.
- Breadcrumb color revert: the breadcrumb Startseite links across the
4 legal pages/shop should stay their original hover:text-brand
treatment — only the actual content/contact links needed the
always-brand-colored style, not page-level breadcrumbs.
- /blog was the one page missing both a breadcrumb and the
text-h-feature heading size every other section page (/shop, legal
pages) already uses — added to match.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018unaXmuzVA8ct1b6WoyP1U
RichText.tsx switched to Payload's official React renderer + custom
JSXConverters (same call signature, LiveRichText/LivePostContent
untouched) — needed to render the new Lexical Blocks the Payload repo's
Posts.content just gained. Converters follow the existing CMS-image
convention (relative + aspect-[...] + fill + object-cover); the video
block resolves YouTube/Vimeo links to an iframe embed.
New getSeoSettings() fetcher (same pattern as getKleinunternehmer()),
app/layout.tsx now generateMetadata() reading it with the same fallback
values it used to hardcode. Per-post SEO overrides (seoTitle/
seoDescription/seoImage) wired into the blog detail page's metadata,
falling back to title/excerpt/thumbnail when empty.
Also fixed while auditing every page's metadata: missing descriptions on
3 konto pages, a static title on the dynamic order-detail route, and
missing OG images on /shop and /blog.
"Angaben zum Anbieter"/"Umsatzsteuer"/"Handelsregister"/"Geschäftsführung"/
"Verantwortlich für den Inhalt" used to be hand-typed into the Impressum's
richText content (seed-legal-pages.ts) with no connection to the same
seller data the invoice PDFs and every email footer already pull from
company-settings — an admin updating one had no reason to remember the
other existed, and the old text was already stale in one concrete way:
it never showed Handelsregister/Geschäftsführung at all even though
company-settings has modeled both since the legal-form work shipped.
Now rendered by a new AnbieterAngaben component, straight from
getCompanySettings(), positioned above the CMS richText (which keeps
only genuinely editorial content: Kontakt, Haftung für Inhalte, Haftung
für Links, Urheberrecht). Same "structural/brand elements in code, only
pull the actual numbers/copy that need single-sourcing from data"
pattern this page's own Nachhaltigkeit sidebar card already used.
RichText.tsx's headingId() is now exported so the new block's headings
get the exact same id-assignment logic as CMS-driven ones, keeping the
SectionTOC sidebar's ids in sync with both sources.
/impressum moves from static to dynamic rendering (it now fetches live
company-settings data, cache: "no-store") — an acceptable tradeoff for
a legally-required page to never show stale seller info.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Bug fixes:
- Navbar login/logout state now updates immediately (custom ep-auth-changed
event) instead of requiring a hard reload
- Status-change email links were broken by an un-encoded "#" in the order
number; fixed for all 4 status emails
- Cart discount code: manual input field restored (was removed entirely)
- Quote-label underline now scales with the label's actual text width
- Number Ranges admin list now shows the invoice prefix/counter columns
Pricing & VAT:
- Prices show the real per-product VAT rate ("inkl. X% MwSt.") instead of
a generic disclosure
- Cart/checkout/confirmation totals show the actual € amount of VAT
included, broken down per rate when a cart spans more than one
(new lib/taxBreakdown.ts, shared with the invoice PDF's own math)
- Account order pages gained product thumbnails and the same VAT breakdown
Low-stock warning: a "Nur noch wenige verfügbar" badge/hint across the
shop grid, spotlight, and add-to-cart variant pickers, driven by the
existing lowStockThreshold field (still never exposes raw stock counts).
Invoice PDFs: product thumbnails on every line item, a plain "Netto"
label (rate was redundant, already stated on the MwSt. line below), no
more duplicate USt-IdNr. in the header, and — for a Stornorechnung
specifically — an explicit "Versand" line that was previously only
folded silently into the tax totals.
Checkout:
- Optional deviating shipping address (separate from the billing address
used for the invoice), with its own toggle + address form
- Full checkout draft persistence (name/address/shipping/payment
selections) survives navigating away and back, via localStorage
- Invoice PDF shows a third "Lieferadresse" block when the shipping
address differs from billing
Mobile navigation: fullscreen panel with a circular reveal animation from
the hamburger's corner, replacing the old in-flow accordion drawer; no
login CTA inside it (redundant with the always-visible header icon).
Admin-facing (Payload backend, mirrored where the frontend has a ported
copy of the same renderer): dashboard rebuilt as individual cards, split
into 3 task queues (received/processing/returns) instead of 2, revenue
and order counts now exclude cancelled/returned orders immediately, and
the low-stock alert links to the specific affected product(s) instead of
the unfiltered list. A new immediate email notifies the shop owner the
moment an order comes in, instead of only via the daily digest.
Testimonials admin list now groups by page instead of interleaving all
three grids' entries. ~45 English admin field descriptions translated to
German for consistency.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Both were fully hardcoded before: every blockquote showed a static
"Merke dir:" label, and every post's "Passend dazu" card always linked
the same flagship product. Now driven by two new Posts fields —
quoteLabel (empty hides the label/icon/underline, blockquote still
renders) and relatedProduct (empty hides the card entirely) — mirroring
Products.spotlight but per-post instead of a single site-wide flag.
README's collection table updated to match today's Payload changes
(shipping-settings, the new Posts fields, trust-badges' placeholder
tokens, admin sidebar grouping) — also fixed a stale claim that `media`
isn't tenant-scoped; it already was.
Clears every remaining @next/next/no-img-element warning — automatic
responsive srcset, lazy-loading, and format optimization instead of
always loading the original file at full size. Fixed-size icons got
explicit width/height; dynamic-aspect photos got fill inside a
relative wrapper.
QuoteNode's children are flat text/linebreak nodes directly (confirmed
by reading @lexical/rich-text's source — insertNewAfter exits the quote
into a new paragraph on Enter, it doesn't nest one inside). The "quote"
case assumed nested paragraph children, which only happened to work for
this session's own hand-authored seed JSON; any blockquote actually
typed in the Payload admin editor rendered blank, since child.children
was undefined on a plain text/linebreak node.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
- app/blog/[slug]/page.tsx: article header (category/readTime, Playfair
display title, excerpt, byline), full-bleed hero image, RichText body,
"Passend dazu: ToDo-Karten" cross-promo, author bio card, "Weiterlesen"
related-post card. Widths, fonts, and the Passend-dazu card's
border/padding/spacing were corrected against the actual built Figma
frame (page-blog-detail, node 4667:344, jCCZyh1DGwdjpv1wGge9To) via
get_design_context after a few visually-wrong guesses.
- RichText.tsx: new "quote" case renders Lexical's blockquote feature as
the "Merke dir:" pull-quote — label+underline+divider+Caveat lines,
using the real exported sparkle/underline assets, matching Figma's
actual (background-less) structure instead of a guessed bordered card.
- lib/payload.ts: getPostBySlug() for single-post fetches.
- lib/format.ts: formatDate() — "03. Juli 2025"-style German dates.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
- lib/payload.ts: getLegalPage(type) alongside the existing
getBlogPosts/getProducts fetchers
- New RichText component: small dependency-free Lexical JSON → JSX
renderer for Payload's richText fields (headings get stable
"section-N" ids for anchor/TOC linking)
- New SectionTOC: generalized from VersandTOC into a reusable
scroll-spy sidebar driven by any {id, title}[] — Datenschutz needs one
built from CMS-authored headings, not a hardcoded array. VersandTOC is
now a thin wrapper around it. Sticky positioning moved from the nav
itself to each page's sidebar wrapper, so a TOC and an extra card
(Impressum/Datenschutz both have one) scroll together as one unit
instead of the card drifting away independently
- TOC clicks set the active item immediately and hold it for ~1s over
the scroll-spy observer, covering both "target already fully visible,
nothing to scroll" and "an unrelated section flickers through the
intersection band mid-scroll"
- Both pages follow the same breadcrumb+H1+TOC-sidebar+content layout
established for /versand