- Organization (site-wide), Product (/todo-cards), BlogPosting (every
/blog/[slug]) JSON-LD via new app/lib/structuredData.ts — no new
Payload fields needed, derived from existing data. Verified locally
by curling each page and checking the rendered script tag.
- Order confirmation email gains the same "please transfer to this
account, processed after payment received" notice the invoice PDF
already had for Vorkasse orders — OrderConfirmationData's new
isManualPayment flag is set explicitly by each caller (never derived
from paymentMethodTitle, which already broke once this session after
a payment-methods rename). CompanySettings gains bankName (existed on
the backend, was missing from the frontend's type/usage).
- Newsletter signup now detects an already-subscribed email
(verified empirically: Brevo's doubleOptinConfirmation endpoint gives
identical 201 responses for new vs. already-confirmed contacts) via a
GET /v3/contacts/{email} pre-check, and shows a distinct message
instead of silently resending the confirmation mail. Success message
text centralized in useNewsletterSignup.ts instead of duplicated
across 4 forms.
- Bumped @einfach-produktiv/invoicing to the version with the
unpaid-notice layout fix (full width, more top spacing — was
squeezed into the narrow paid-badge column).
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
- Newsletter card icon: fixed an aspect-ratio distortion bug (w-16 maps
to this project's fluid --spacing-16, which floors to 40px below
768px, paired with a fixed 55px height — squished the icon on
mobile). Same root cause existed in NewsletterModal's envelope icon;
that one's now just hidden below md: instead per feedback.
- Hero: subtitle sized down and CTA checkmark icon scaled to match
below lg:, social proof text always wraps below the avatars there
instead of only when it doesn't fit.
- Werkzeuge connector arrows (todo-cards/newsletter/challenge
HowItWorks): object-contain added — the SVG has
preserveAspectRatio="none" and was stretching to fill the square
mobile box instead of keeping its thin-arrow shape.
- Challenge bottom CTA: icon now stacks above the copy, centered, below
lg: to match the Home Newsletter card's pattern.
- Homepage Werkzeuge icons: smaller below md: to match the (fluid-floor)
text size next to them.
- Blog detail "Passend dazu" card: the fixed w-[19rem] title column plus
"Entdecken" sharing its row overflowed on mobile; stacked below sm:
instead, with a little top margin on "Entdecken".
- Todo-Karten checklist checkmarks: recolored brand-orange (icon-check
.svg's fill lives in an internal CSS var that can't be overridden from
outside an <img>-loaded SVG, so switched to an inline SVG) and
top-aligned instead of vertically centered.
- AddToCartButton: added whitespace-nowrap to the stacked-label grid
(used to reserve button width across all possible label texts) — one
of those labels wrapping to two lines on a narrow w-full button was
inflating the row height for whichever label is actually showing,
leaving a tall empty gap under "Ausverkauft".
Same short "check your inbox" message everywhere instead of each form
having its own success wording (they'd drifted since each is a
separately-coded component, not a shared one).
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Extends the checkout pattern (inline red error text, refocus on
submit if invalid) to all four newsletter-signup entry points. Two of
them (WeeklyImpulsesHero's inline hero form on /newsletter, and
/challenge's EmailCapture) turned out to be completely non-functional
before this too — same static-markup-with-no-onSubmit issue as
Newsletter.tsx/NewsletterModal.tsx had, just missed in the previous
pass since they're separate components sharing only the visual
pattern, not the code.
Consolidated the shared email+consent+submit state (previously
duplicated per-component) into useNewsletterSignup.ts, and pulled the
plain email-format regex (previously duplicated in CheckoutContent.tsx
and the subscribe route) into lib/email.ts as a single source of
truth. /challenge's EmailCapture is now its own client component
(app/challenge/components/EmailCapture.tsx) since its parent page is
an async Server Component and can't hold form state itself.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Both standalone signup forms (Newsletter.tsx on Home/newsletter page,
NewsletterModal.tsx from the Navbar CTA) were previously non-functional
— static markup with no onSubmit/state at all, nothing was ever
captured. They're now real client forms posting to the new
/api/newsletter/subscribe route, which upserts the contact into
Brevo's Contacts API (list id from BREVO_LIST_ID). Checkout's existing
newsletterOptIn checkbox gets the same sync, fire-and-forget alongside
the order-confirmation email — a failed marketing sync must never
fail checkout.
lib/brevo.ts is the only thing that talks to Brevo; this app still
never sends marketing mail itself. Whatever automation Brevo has
configured on the list (Welcome Flow etc.) runs entirely on their
side — Brevo's Automation workflows aren't manageable via their
public API at all, so that part can't be wired up from here.
Needs BREVO_API_KEY and BREVO_LIST_ID set in the frontend's Coolify
environment — not yet added there.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Every email-signup form (Home/newsletter Newsletter section, /newsletter
hero, /challenge, the Newsletter overlay) now links "Datenschutzerklärung"
to /datenschutz next to its consent checkbox, opened in a new tab so a
partially filled form isn't lost. Checkout gets an equivalent AGB +
Datenschutzerklärung note under the order button, and the Versand
modal's Widerrufsbelehrung link also opens in a new tab.
Unified the "Keine Werbung. Jederzeit abbestellbar." trust note (icon +
#888 copy) across all newsletter forms, matching /challenge's existing
style instead of each form having its own wording/color.
Also converted the remaining raw <img> tags in these files to next/image
per the no-img-element lint rule (bandwidth/LCP).
- New /widerruf page: CMS-driven content via getLegalPage("widerruf"),
sidebar TOC + "Nachhaltig handeln" callout, TrustRow, and a download
card for the Muster-Widerrufsformular PDF attachment.
- New /agb page, same legal-page shell pattern as Impressum/Datenschutz.
- lib/payload.ts: getLegalPage now also resolves the optional `attachment`
media relation (depth=1) for legal pages that have a downloadable file.
- NewsletterModal: replaced the body overflow/position scroll-lock hacks
(which fought with the sticky Navbar and the global scroll-smooth CSS)
with a wheel/touch event interceptor that never touches scroll position
at all, plus `preventScroll: true` on all modal/drawer focus() calls to
stop the browser's implicit scrollIntoView from nudging the page for
elements near the sticky navbar's reserved scroll-padding-top zone.
Same preventScroll fix applied to VersandModal and the Navbar mobile
drawer for consistency.
- globals.css: scrollbar-gutter: stable, so a modal's overflow:hidden
lock never causes a scrollbar-width layout shift.
- Divider: stagger its reveal animation slightly (delay 0.3s) behind the
section above it.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
- New /shop overview, /cart (real cart state via useSyncExternalStore),
/versand (shipping policy page with TOC) and /newsletter detail page
- Cart: quantity/removal, order summary, related-products cross-sell
with randomized picks, VersandModal quick-reference instead of
navigating away, MwSt. disclosure next to unit prices
- Add-to-cart UX: inline success feedback (green state) plus a
fly-to-navbar-cart-icon animation (CartFlyProvider) with a delayed
badge count-up; AddToCartButton no longer navigates straight to /cart
- Shared lib/products.ts catalog and lib/shipping.ts constants (cost,
free-shipping threshold, handling/transit days) so cart, trust badges
and the versand page can never drift apart
- Fix Navbar smooth-scroll easing (ease-out instead of ease-in-out, no
more perceived start delay); compress newsletter modal photo
2.4MB -> 85KB to fix first-open jank