Commit Graph

5 Commits

Author SHA1 Message Date
Marco 1706da8598 Add schema.org structured data, Vorkasse email notice, newsletter duplicate detection
- 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>
2026-07-25 17:11:48 +00:00
Marco 3328f30a06 Add /newsletter-confirmed double-opt-in confirmation page
Brevo's redirectionUrl now points here instead of the homepage — a
static page matching /bestellbestaetigung's visual language (brand-
tinted checkmark circle, serif display heading, thin brand divider).
No query params to read; Brevo's confirmation redirect carries nothing
this page needs.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-25 14:08:52 +00:00
Marco df4bd700e6 Switch newsletter signup to Brevo double opt-in
Was a plain POST /v3/contacts upsert (single opt-in — straight onto the
list, no confirmation required). Now calls doubleOptinConfirmation
instead, so a signup only requests subscription; Brevo sends its own
confirmation email and adds the contact to the real list only once they
click through. Needs BREVO_DOUBLE_OPTIN_TEMPLATE_ID set in Coolify
before this works — not yet configured, signups will fail closed with a
logged reason until it is.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-25 13:40:27 +00:00
Marco 789a818c6b Add on-blur email validation to every newsletter signup form
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>
2026-07-23 20:48:13 +00:00
Marco 6a4539bf9b Sync newsletter opt-ins to Brevo
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>
2026-07-23 20:38:41 +00:00