Sending no longer needs a user password for Microsoft 365. When
SMTP_OAUTH_TENANT_ID, SMTP_OAUTH_CLIENT_ID and SMTP_OAUTH_CLIENT_SECRET
are set, the shop fetches an app-only token from Entra ID and signs in to
smtp.office365.com with XOAUTH2. This works with security defaults and
MFA, survives the end of SMTP basic auth (default off from end of
December 2026) and limits the app to the one mailbox Exchange grants it
(SMTP.SendAsApp + Add-MailboxPermission). Password login stays available
for other providers; a partial OAuth setup never silently falls back.
- lib/ms365-oauth.ts: token cached until 5 minutes before expiry,
concurrent requests share one fetch, forced refresh after a server
rejection, 15 s timeout, tenant id validated before it enters the URL,
client secret redacted from every error.
- lib/smtp-diagnose.ts: explains AADSTS7000215 (secret ID instead of
value), 7000222 (expired), 700016 (unknown app), 90002/900023 (tenant)
and maps a rejected XOAUTH2 login to missing mailbox access.
- Admin e-mail test shows the auth mode and lists missing settings.
- k8s manifests, preflight check and README (step-by-step Entra and
Exchange setup) updated; misplaced SMTP_PORT comment moved.
Verified against Entra ID and smtp.office365.com with fake values:
AADSTS90002 is classified as tenant error without leaking the secret,
an invalid token yields 535 5.7.3 and is classified as mailbox access.
Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
Mail failures were only logged as a bare error, so a broken Microsoft
365 setup showed up as "no emails arrive" with no cause. Now:
- lib/smtp-diagnose.ts maps SMTP errors to a cause with German
instructions: security defaults, SMTP AUTH disabled for tenant or
mailbox, MFA/conditional access, SendAsDenied (SMTP_FROM differs from
the mailbox), wrong credentials, basic-auth retirement, network, TLS.
The password is redacted from any echoed server message.
- /admin/e-mail-test verifies login and sends a real test email to the
signed-in admin only (no free recipient, rate limited, audit logged)
and shows the server message, the cause and the settings in use.
- The transport now requires STARTTLS on port 587 so credentials are
never sent unencrypted, strips a trailing newline from secrets (a
common kubectl/base64 mistake) and logs the diagnosed cause.
Verified against smtp.office365.com with a fake account: STARTTLS
succeeds and "535 5.7.3" is classified as wrong credentials.
Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
Customer input (first name, address, product names), order data, admin
fields (company data, carrier, tracking number) and URLs were inserted
into email HTML unescaped. A first name such as <a href="…">…</a> ended
up as a working link in a genuine shop email, usable for phishing.
All variable values in email HTML now go through escapeHtml
(lib/escape-html.ts, shared with the withdrawal receipt). Button links
only accept http(s), so a javascript: tracking URL is dropped. The plain
text part stays unescaped. Tests cover order confirmation, shipment,
status mails, layout company data and back-in-stock mails.
Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
Withdrawal function (§ 356a BGB, mandatory since 19 June 2026):
- /vertrag-widerrufen: two-step flow "Vertrag widerrufen" ->
"Widerruf bestätigen", no login, only name, order number and e-mail
required. Prominent footer button on every page, prefilled link per
order in the customer account.
- /api/widerruf stores every declaration (matched to the order only if
order number and e-mail fit), mails a receipt with the declaration and
the exact time of receipt, notifies ADMIN_EMAIL. Same response for
matching and non-matching orders (no enumeration), origin check and
rate limit instead of a CAPTCHA that could block a statutory right.
User input is HTML-escaped and stripped of line breaks in headers.
- /admin/widerrufe lists declarations, open ones first, with an
open-count badge and "mark as done".
- Withdrawal notice gains the statutory sentence on the online function.
Other fixes:
- Newsletter stores confirmedAt as proof of consent (Art. 7(1) GDPR).
- "Recently viewed" is only persisted with consent (§ 25(1) TDDDG);
choosing "essential only" or withdrawing consent deletes it. The
banner no longer promises audience-measurement cookies that do not
exist.
- Admin rejects an invalid company e-mail; an obfuscated stored value
falls back to the configured address so the legal notice stays valid.
- Privacy policy: Microsoft 365 as mail processor, log retention of at
most 14 days, withdrawal-function processing, consent timestamps.
Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
Legal pages rendered admin text as bare paragraphs. LegalPage now has a
honeycomb header, a sticky table of contents with scroll tracking (a
collapsible one on mobile), anchor links, a reading progress bar and a
revision date pulled from a "Stand:" line. lib/legal-blocks.ts derives
lists, cookie/provider entries with labelled facts and safe links from
plain text, so the pages stay editable in the admin without markup.
Legal corrections (German law as of 2026):
- Widerrufsbelehrung now follows the official model (Anlage 1 und 2 zu
Art. 246a EGBGB): recipient name/address/e-mail filled from company
data, deadline rule, return period, refund details, diminished value
and the model withdrawal form. The incomplete notice extended the
withdrawal period to up to 12 months and 14 days. Honey is no longer
declared perishable.
- Impressum: TMG references replaced by DDG; editorial contact gains an
address.
- Datenschutz: removes the self-hosted vs. hosting-provider
contradiction, adds PayPal/Klarna, reviews, back-in-stock
notifications, storage period, no automated decisions, withdrawal of
consent and the Art. 21 objection notice; newsletter double opt-in.
- AGB: payment methods and delivery area point to the live shipping
page; adds contract-text and input-correction information.
- Cookie policy: storage period and provider for every entry.
Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
- Hero: Titel gross auf dem Bienenfoto, darunter "Fuer ..." mit
wechselnder Zielgruppe (Unternehmen, Kanzleien, Praxen, Hotels,
Agenturen, Handwerksbetriebe - die Woerter aus dem Untertitel), eine
Biene fliegt durchs Bild, Knopf springt zur Anfrage.
- Die drei Pakete wieder als Karten, jetzt mit gezeichnetem Honigglas:
Klassik mit Banderole, Geschenk im Karton mit Schleife, Individuell mit
Stift und Funkeln. Beim Hover hebt sich das Glas, die Schleife wippt.
Ein Klick springt zum Formular und waehlt das Paket dort vor
(?paket=, nur bekannte Schluessel).
- Anfrage: links Ueberschrift und das Glaeser-Foto (mitlaufend, leichte
Parallaxe), rechts das Formular.
Neu im Woerterbuch nur Ueberschrift, Knopftext und die Zielgruppenliste;
keine neuen Aussagen. Dunkelmodus und reduzierte Bewegung beruecksichtigt.
Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
The policy only named wabenhain_session, although the site sets ten
cookies and three localStorage entries. § 25 TDDDG requires the
information for every access to the user's device, so each entry now
states purpose, provider, storage period, flags and legal basis.
Also adds the legal-basis section (§ 25 Abs. 1/2 TDDDG, Art. 6 DSGVO),
third-party cookies (Stripe __stripe_mid/__stripe_sid, Microsoft,
Cloudflare Turnstile), browser controls, data subject rights, withdrawal
under Art. 7(3) DSGVO and a revision date. Corrects the claim that the
cart never leaves the device: it is sent when an order is submitted.
Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
Rollen-Farben statt reiner Markenfarben, damit hell und dunkel aus einem
Satz Klassen entstehen:
- heading (Ueberschriften, Links, Rahmen): hell Waldgruen, dunkel Creme
- surface (abgesetzte Flaeche): hell Creme, dunkel angehobenes Dunkelgruen
- forest bleibt die dunkle Markenflaeche (Hero, Baender, Knoepfe,
Admin-Leiste), cream der helle Text darauf
- honey-deep als fester Honigton fuer Flaechen; honey-dark (Text) wird
im Dunkelmodus heller und bleibt lesbar
Codemod ueber src: text-/border-forest -> *-heading, bg-cream ->
bg-surface, bg-honey-dark -> bg-honey-deep. Im hellen Modus haben die
Rollen exakt die bisherigen Werte - das Erscheinungsbild bleibt gleich.
Umschalter (components/theme-toggle.tsx) Automatisch -> Hell -> Dunkel in
Kopfzeile, Mobilmenue und Admin-Leiste. Die Wahl liegt im Cookie "theme"
(nur light/dark, geprueft in lib/theme.ts); der Server setzt data-theme
schon beim Ausliefern, nichts blitzt hell auf. "Automatisch" folgt der
Systemeinstellung. Gewaehlter und automatischer Dunkelblock in
globals.css werden per Test auf Gleichheit geprueft.
Logo, Biene und Select-Pfeil nutzen die Rollenfarben. In der Kopfzeile
steht "Honig entdecken" zwischen 1280 und 1535 px nicht mehr doppelt
neben "Shop" (sonst stiess die Navigation an), der Slogan erscheint erst
ab 1680 px.
Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
- Eine gezeichnete Biene fliegt beim Scrollen eine geschwungene Bahn ueber
"Aktuelle Ernte" und zieht eine gepunktete Honigspur hinter sich her;
Fluegelschlag und leichtes Schweben per CSS (components/bee.tsx).
- Neuer Abschnitt nach der Philosophie: Portraet des Imkers in Wabenform,
die Punkte des Wabenrings wandern an der Kante entlang, eine Biene
umkreist das Bild. Daneben der eigene Satz von der Imkerei-Seite als
Zitat - kein neuer Text.
- Herkunft-, Region- und Firmenkunden-Foto bewegen sich beim Scrollen
leicht mit (Zoom und Parallax, nur transform; components/parallax-media).
Alles rein dekorativ (aria-hidden) und mit Pfad fuer reduzierte Bewegung:
die Biene sitzt dann still, Ring und Fotos stehen.
Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
Das Speichern schrieb den Formularwert immer fest in den Lagerbestand.
Wurde zwischen Oeffnen und Speichern ein Glas verkauft, kam es dadurch
wieder in den Bestand, ohne dass es je zurueckkam.
- Das Formular schickt den Bestand beim Oeffnen mit (quantityLoaded)
- Feld unveraendert: Bestand wird nicht angefasst
- Feld geaendert: Schreiben nur, wenn der Bestand noch dem geladenen Stand
entspricht (Bedingung atomar im UPDATE); sonst wird nichts gespeichert
und der aktuelle Wert angezeigt
- Fehlt quantityLoaded (alter Tab), muss neu geladen werden
Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
Wurde eine Retoure erstattet und die Bestellung danach auf "STORNIERT"
gesetzt, buchten beide Schritte die Ware zurueck. Bei einer Testbestellung
stieg der Bestand so von 105 auf 106 Glaeser.
- Stornieren bucht nur noch zurueck, wenn keine Retoure erstattet ist
(needsRestockOnCancel in lib/inventory.ts)
- Retoure einer bereits stornierten Bestellung laesst sich nicht mehr
erstatten, auch nicht im Wettlauf (Bedingung im atomaren Claim)
Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
Bisher stand dort nur ein Satz und ein Knopf zum Shop. Jetzt steht der
Hinweis als Spalte links, daneben bis zu zwei aktuell verfuegbare
Produkte (Honig zuerst) - dasselbe Muster wie "Aktuelle Ernte" auf der
Startseite. Nutzt die bereits geladenen availableProducts.
Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
Der Knopf stand unter beiden Spalten (Anmelden und Konto erstellen),
obwohl er zur Anmeldung gehoert, und erschien auch ohne eingerichtete
Entra-Konfiguration - der Klick endete dann in der Meldung "SSO ist noch
nicht konfiguriert". Jetzt steht er unter dem Anmeldeformular und nur,
wenn isEntraConfigured() zutrifft. Die serverseitige Pruefung in der
Login-Route bleibt als zweite Absicherung.
Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
Die drei Pakete standen doppelt: als Deko-Kacheln (mit grossem
Waben-Wasserzeichen) ueber dem Formular und darin noch einmal als
Auswahlliste. Jetzt waehlt man sie direkt im Formular als Karten mit
sichtbarem Auswahl- und Fokuszustand; die Kacheln entfallen. Feldname
und Werte (package = KLASSIK/GESCHENK/INDIVIDUELL) sind unveraendert.
Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
- Imkerei, Herkunft und Ernten endeten mit dem letzten Absatz direkt vor
der Fusszeile. Neuer PageOutro: zwei naechste Wege als grosse
Serif-Links mit Kurztext (Ernten/Shop bzw. Herkunft/Shop).
- Kopfzeile: bei 1280 px lief sie 54 px ueber (horizontale Scrollleiste),
weil die nicht umbrechbare Slogan-Zeile unter dem Logo schon ab xl
erschien. Jetzt erst ab 2xl; geprueft 1024 bis 1920 px ohne Ueberlauf.
- Wissen: Inhaltsverzeichnis statt zehn gleicher Bloecke mit je einem
"Weiterlesen" - ganze Zeile ist der Link, Titel und Kurztext
nebeneinander. Ungenutzter Schluessel readMore entfernt.
- Produktseite: bei ein, zwei Empfehlungen steht die Ueberschrift als
erste Rasterspalte statt ueber einer einzelnen verlorenen Karte.
Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
Die Galerie lief im Querformat 4:3 neben einer langen Detailspalte und
wirkte daneben schmaechtig. Ab md jetzt 4:5, das Glas traegt die Seite;
auf dem Handy bleibt 4:3, damit der Kaufen-Knopf oben bleibt.
Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
Fehlt ein Foto, zeigte die Seite einen Farbverlauf mit Diagonallinien -
ein generisches Muster, das auf Produkt- und Inhaltskacheln wie
unfertige Vorlage wirkte. Jetzt: ruhige Markenflaeche mit leisem
Wabenraster, das gezeichnete Waben-Zeichen und darunter der Name in
gesperrten Serif-Versalien, wie auf dem Etikett. In kleinen Groessen
(Warenkorb, Galerie-Vorschau) bleibt per Container Query nur das Zeichen.
Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
- Aktuelle Ernte: bei ein, zwei Produkten steht die Ueberschrift als
eigene Spalte daneben, statt ueber einem halb leeren Dreierraster.
- Herkunft: Foto randlos ueber die halbe Seitenbreite, Text daneben,
ohne Rahmen.
- Philosophie: typografische Reihe mit Trennlinien und grossen
Serif-Titeln statt vier Icon-Karten.
- Region: breites Bild, der Text liegt als Tafel ueberlappend darueber.
- Geschaeftskunden: kompakte Tafel mit randlosem Bild statt eines dritten
grossen Bild/Text-Wechsels.
Inhalte, Texte und Content-Schluessel bleiben unveraendert.
Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
Jede zweite Ueberschrift trug eine kleine Versalien-Zeile mit Wabenpunkt
("• STIMMEN", "• AKTUELL VERFUEGBAR", "• KUNDENKONTO" ...). Auf 29 Seiten
und Abschnitten wiederholt las sich das wie eine Vorlage. SectionHeading
rendert sie nicht mehr, die Prop ist entfernt; die Ueberschrift traegt den
Abschnitt allein.
Die eyebrow-Texte, die weiterhin als Seitentitel, Bildbeschriftung oder
Breadcrumb dienen, bleiben; 18 danach ungenutzte Eintraege sind aus Typ und
beiden Woerterbuechern entfernt.
Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
- Admin-Leiste (Desktop und Mobilmenue): Link "Zur Website" oberhalb von
"Angemeldet als". Der Shop-Kopf ist im Admin ausgeblendet, bisher fuehrte
von dort kein sichtbarer Weg zurueck.
- Startseite, Kundenstimmen: das grosse Schriftzeichen "„" sass auf der
Grundlinie und ragte mit leading-[0.6] in die Sternebewertung. Ersetzt
durch ein gezeichnetes SVG mit fester Hoehe und Abstand.
Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
Sechs Route-Handler (Hinweis-Weiterleitung, E-Mail-Bestaetigung,
Newsletter bestaetigen/abmelden, Entra-Login und -Callback) bauten ihr
Weiterleitungsziel mit getBaseUrl() absolut zusammen. Ohne gesetztes
NEXT_PUBLIC_SITE_URL (lokal, Staging, Vorschau-Host) sprang man damit auf
https://wabenhain.com, und die gerade gesetzte Flash-Meldung ging auf dem
fremden Host verloren - aufgefallen, als die Admin-Pruefung nach
/api/hinweis umleitete.
Neuer Helfer lib/redirect.ts (redirectWithinSite) mit relativem
Location-Header, analog zu redirect() in Server Actions. Er lehnt alles
ausser Pfaden dieser Seite ab ("//host", "/\host", absolute URLs), damit
daraus keine offene Weiterleitung werden kann. getSiteUrl() bleibt fuer
Adressen, die die Seite verlassen (E-Mails, Stripe, Entra-redirect_uri,
Sitemap, Canonicals).
proxy.ts bleibt bei new URL("/konto", request.url) (eigener Host): der
Proxy verlangt einen absoluten Location-Header, ein relativer endet dort
in "Invalid URL" - als Kommentar festgehalten.
Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
Neue Gruppe "Logo und Favicon" unter Admin > Inhalte mit drei Bildfeldern:
Logo (Kopfzeile), optionales Logo fuer dunkle Flaechen (Mobilmenue,
Fusszeile, Admin-Leiste; faellt sonst auf das normale Logo zurueck) und
Favicon. Leer bleibt alles wie bisher (gezeichnetes Waben-Zeichen bzw.
Standard-Icon).
- lib/brand.ts liest die Pfade und prueft sie erneut gegen das
Upload-Muster, bevor sie in ein src-Attribut gelangen.
- Statische app/icon.svg und app/favicon.ico ersetzt durch dynamische
app/icon.tsx (512 px PNG) und app/apple-icon.tsx (180 px auf Creme);
lib/brand-icon.ts rendert Upload oder Standard-Icon per sharp und liest
Dateien nur ueber resolveUploadPath. Cache-Control no-cache, damit ein
neues Favicon ohne Tage Verzoegerung erscheint.
- /favicon.ico per Rewrite auf /icon, Manifest nutzt /icon.
- Hochgeladenes Logo mit fester Hoehe und begrenzter Breite (Kopfzeile
mobil 56 px, ab sm 112 px), damit breite Wortmarken nichts sprengen.
Uploads bleiben auf Rasterbilder beschraenkt (kein SVG, XSS-Schutz).
Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
Derselbe "Alles synchronisieren"-Knopf stand auf /admin/produkte und
/admin/ernten und sah dadurch wie zwei verschiedene Abgleiche aus. Er
sitzt jetzt einmal auf dem Dashboard, das Ergebnis des Laufs erscheint
dort oben. Die Action springt standardmaessig nach /admin zurueck; die
alten Ziele bleiben in der Whitelist erlaubt.
Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
Sicherheit: "Jetzt per Stripe erstatten" loeste die Rueckerstattung bisher
mit einem einzigen Klick aus. Erstatten und Ablehnen einer Retoure laufen
jetzt ueber ConfirmForm; die Rueckfrage nennt Bestellnummer und Betrag.
Admin: Produkte, Bestellungen und Retouren nutzen dieselben Bausteine wie
die uebrigen Listen (Seitenkopf, Suche mit Zusatzfiltern und Archiv-
Parameter, Status-Badges, Leerzustaende). Produkttabelle ohne seitliches
Scrollen bei 1440 px: Bearbeiten ueber den Produktnamen, Charge ohne
Umbruch. Geschenk-Markierung als SVG statt Emoji. Letzte rohe Knoepfe
(Bestelldetail, ApiCore, Inhalte) auf Button umgestellt.
Mobil (390 px): Ueberlauf auf /konto (Datenexport) und /kontakt
(E-Mail-Adresse) behoben. Tippziele unter 24 px vergroessert
(Zurueck-Link, "Zum Honig", Links auf der Produktseite). Wunschliste mit
SVG-Stern statt Unicode-Zeichen.
Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
Alle Anlege- und Bearbeiten-Formulare nutzen jetzt die Bausteine aus
components/admin-form.tsx wie das Produktformular: Rahmen mit fester
Speicherleiste, Abschnitte mit Serif-Ueberschrift (Gutschein: Code und
Rabatt / Bedingungen / Zeitraum und Status; Ernte: Angaben / Bilder /
Sichtbarkeit; Charge: Abfuellung und Bestand / Bilder), Schalter statt
Checkbox, buendige Eingabefelder in Rastern.
Benutzer bearbeiten: Sicherheitsbereich mit Status-Badges, Passwort-Reset
als Sekundaerknopf, Sperren/Loeschen in eigenem abgesetzten Bereich.
AdminField/AdminSelect koennen jetzt min und einen Hinweistext.
Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
Neue gemeinsame Bausteine in components/admin-list.tsx: Seitenkopf mit
Hauptaktion, Suchleiste, Status-Badge (gleiche Form wie der Bestellstatus,
wartende Eintraege mit pulsierendem Punkt), Leerzustand mit Wabe und
naechstem Schritt sowie Zeilenaktionen mit einheitlichem Unterstrich-
Hover (Loeschen rot, Sperren/Zurueckziehen gedaempft).
Umgestellt: Bewertungen (SVG-Sterne statt Unicode), Benutzer, Gutscheine
(neu: Status "Abgelaufen"), Versand, Ernten, Chargen (Anlegeformulare auf
admin-form), Firmenanfragen (Status als Badge, beschriftete Auswahl) und
Audit-Log. Zahlen und Daten in Tabellen mit tabular-nums.
Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
Die Kategorie kam bisher nur per Typ-Cast aus dem Formular; ein
manipulierter Wert endete als unbehandelte Prisma-Ausnahme. Ein eigener
Slug wurde ungeprueft zur oeffentlichen Adresse, inklusive Schraegstrichen
und Sonderzeichen. Neues adminProductIdentitySchema: Kategorie per Enum,
Slug auf a-z, 0-9 und Bindestrich (Umlaute umschrieben, max. 80 Zeichen),
leere Adresse wird mit Meldung abgelehnt.
Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
Gemeinsame Formularbausteine in components/admin-form.tsx (Feld,
Auswahl, Abschnitt, Schalter, Formularrahmen mit fester Speicherleiste).
Das Anlegen-Formular nutzt dieselben Abschnitte wie das Bearbeiten und
blendet Ernte/Charge sowie Geschmack per :has() aus, sobald keine
Honig-Kategorie gewaehlt ist. Eingabefelder in Rastern stehen buendig,
auch wenn Beschriftungen mobil umbrechen.
Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
- Bestelldetail: Status-Badge neben der Nummer, Waben-Zeitleiste
(dieselbe wie im Kundenkonto) zeigt auf einen Blick den Stand
- Produkt bearbeiten: Abschnitte (Text und Bilder, Geschmack, Preis und
Bestand, Sichtbarkeit), "Im Shop sichtbar" als Schalter (gleiches Feld
isActive), Speicherleiste bleibt beim Scrollen unten erreichbar
- Kartentitel im gesamten Admin als lesbare Serifentitel statt winziger
Versal-Etiketten (zentral per .admin-content, farbige Titel bleiben)
Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
- Admin ohne Shop-Kopf-/Fusszeile (HideOnAdmin); kein doppeltes <main> mehr
- Seitenleiste gruppiert (Verkauf, Sortiment, Kundschaft, Seite), aktive
Seite als wandernde Flaeche mit Wabe, Zaehler fuer Wartendes: bezahlte
offene Bestellungen, neue Retouren, unfreigegebene Bewertungen, neue
Firmenanfragen (COUNT-Abfragen erst nach Admin-/2FA-Pruefung)
- Schnellsuche (Strg/Cmd+K): Seiten und haeufige Aktionen per Tastatur,
tolerante Wortsuche, Ziele nur aus fester Liste
- Dashboard: Begruessung nach Tageszeit (Berliner Zeit), eine Leitzahl
"Umsatz heute" mit Vergleich zu gestern und Mini-Verlauf, "Zu erledigen"
mit direkten Links bzw. "Alles erledigt"-Siegel, ruhigere Kennzahl-Kacheln
- Diagramme nach dataviz-Skill: per Validator gepruefter Diagrammton
#a0722f (>=3:1; #b88a44 erreichte nur 2,93:1), gerundete Balkenkappen,
2px Luecke, Tooltip je Balken (auch per Tastatur), Meter mit Spur im
selben Ton, Rang der Top-Produkte als Wabe
- Einheitliche Admin-Details zentral: groessere Ueberschriften,
Tabellenkopf als Etikett, feste Ziffernbreite, eigene Auswahlpfeile
Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
Aktive Seite mit Honig-Wabe statt farbigem border-left, feines Wabenraster
in der Seitenleiste (liegt per isolate/-z-10 unter dem Menue).
Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
- Schrittanzeige "Deine Daten -> Bezahlen" mit Waben (rein anzeigend,
gesteuert weiter ueber den bestehenden Zahlungszustand)
- Lieferart als Karten mit Symbol, Auswahl mit Waben-Haekchen, aria-pressed
- Bestelluebersicht abgehoben, Namen und Gesamtsumme in Serifenschrift
- Keine Aenderung an Bezahllogik oder Formularfeldern
Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
- Adressen im Zweierraster, Standardadresse mit Honigrahmen und Wabenmarke,
neues Adressformular in der Formular-Karte
- Retoure: Positionen als Liste mit Waben, Formular in der Karte
- Suche: Vorschlaege gleiten gestaffelt herein, Suchbegriff hervorgehoben,
Lupe wird zum Schliessen-Symbol; das Feld legt sich ueber die Navigation
statt sie zu verschieben (vorher: waagerechter Scrollbalken)
- Cookie-Hinweis als schwebende Karte; "Nur notwendige" und "Alle
akzeptieren" gleich gewichtet (vorher war Akzeptieren Waldgruen auf
Waldgruen und kaum als Knopf erkennbar)
Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
- DHL-Verlauf: neuestes Ereignis als Wabe in Statusfarbe (pulsiert, solange
unterwegs), aeltere als leere Waben, Eintraege erscheinen gestaffelt
- Bestellung verfolgen: Suchformular in der Formular-Karte, Ergebnis als
abgehobene Karte
Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
- Gemeinsame FormPanel-Karte fuer Passwort- und Zwei-Faktor-Seiten
- Zwei-Faktor-Code: grosses, zentriertes Ziffernfeld
- Backup-Codes als nummerierte Kacheln statt langer Liste
- Newsletter-Bestaetigung: Willkommensrabatt als Gutschein mit
gestricheltem Rand und Kopier-Knopf (bestaetigt "Kopiert")
Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
- Herkunft: atmendes Titelbild mit Wabenraster, Ueberschrift als Karte,
die ins Bild ragt; Aspekte als Liste mit Waben
- Imkerei: Bild mit Honigrahmen, erster Absatz als Einstieg
- Fuer Unternehmen: Pakete als Karten mit Waben-Wasserzeichen, das beim
Hover einrueckt; Anfrage mit mitlaufender Ueberschrift neben dem Formular
- Kontakt: E-Mail gross auf Wald-Karte mit Wabenraster, Abholung daneben
Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
- Artikel: Lesefortschritt als feine Honiglinie am oberen Rand, Initiale
in Serifenschrift und Honig, groesserer Titel, angenehme Zeilenlaenge
- Wissen-Uebersicht und "Weitere Artikel" als ruhige Liste mit grossen
Serifen-Titeln, Unterstrich und gezeichnetem Pfeil statt "→"
- Rechtliche Seiten mit klarerer Ueberschriften-Hierarchie
Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
- Weg zum Mengenrabatt als sich fuellender Balken (gleiche Schwelle wie
bisher der Texthinweis), erreichter Rabatt als Bestaetigung mit Wabe
- Positionen gleiten herein und beim Entfernen hinaus, Entfernen-Knopf
als gezeichnetes Symbol statt Textzeichen
- Artikelnamen und Preise in Serifenschrift, Gesamtsumme tickt bei jeder
Aenderung, Zusammenfassung laeuft beim Scrollen mit
Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
- Ernten: Jahrgang als grosse Serifenzahl mit auslaufender Linie, Karten
heben sich mit Honigschimmer, Pfeil und Unterstrich
- Ernte-Detail: grosser Titel, mitlaufendes Bild, Fuellstandsbalken je
Charge (verfuegbare von abgefuellten Glaesern)
- Chargen-Seite (QR-Code auf dem Glas): Echtheitssiegel stempelt sich ein,
Zeitleiste mit Waben und sich fuellender Linie, Werte in Serifenschrift
- Produktverweise heben sich, gezeichneter Pfeil statt Textzeichen
- Fix: aktive Seite in der Kopfzeile zeigte den Unterstrich in Textfarbe
statt in Honig (unlayered CSS ueberstimmte die Utility-Klasse)
Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
Bisher fiel der fehlende Schluessel nur in der Kasse auf ("Zahlung gerade
nicht verfuegbar"), nie im Log des Pods.
Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
- Kopfzeile: aktive Seite mit Honig-Unterstrich (auch im Handy-Menue),
einlaufende Unterstriche beim Hover, beim Scrollen Glas und Schatten;
weicht beim Runterscrollen aus und ist beim Hochscrollen sofort zurueck
(steht still, solange Warenkorb oder Menue offen sind)
- Fusszeile mit Wabenraster und einlaufenden Link-Unterstrichen
- Mengensteller: gezeichnete Plus/Minus, taktiles Druecken, tickende Zahl
- Newsletter als eigene Flaeche mit Wabenraster; Erfolg zeichnet ein
Haekchen in eine Wabe, Fehler werden als Alert angesagt
- 404: Wabe mit einer fehlenden Zelle als Bild fuer die fehlende Seite
Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
Bezahlt wird jetzt direkt auf /checkout: die Checkout-Session laeuft mit
ui_mode "embedded_page", das Zahlungsformular ist ein Stripe-iframe
(PCI SAQ A bleibt). PayPal, Karte, SEPA und Klarna kommen weiterhin
dynamisch aus dem Dashboard.
- Zahlungsschritt ersetzt das Formular; "Zurueck zu deinen Angaben" gibt
Sitzung und Bestand sofort frei (POST /api/checkout/cancel) und behaelt
alle Eingaben, Turnstile wird neu geladen
- Rueckkehrseite prueft serverseitig bei Stripe und gleicht die Sitzung mit
der eigenen Bestellung ab; bei abgebrochener PayPal-Zahlung wird dieselbe
Sitzung erneut eingebettet
- Erneuter Versuch und Abbruch sind per HttpOnly-Cookie an den bestellenden
Browser gebunden, die session_id in der URL allein reicht nicht
- Alte /checkout/cancel?ref= Links aendern per GET nichts mehr
- CSP um die Stripe-Hosts fuer Embedded Checkout und 3-D-Secure erweitert
- STRIPE_PUBLISHABLE_KEY zur Laufzeit gelesen (kein NEXT_PUBLIC_), mit
Preflight-Pruefung auf pk_live und Schutz gegen versehentliche sk_/rk_
Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
- Konto-Kacheln: Icon in einer Wabe, die sich beim Hover mit Honig fuellt,
einlaufender Pfeil, Serifen-Titel
- Bestell-Statusleiste: Waben statt Punkte, erledigte Abschnitte fliessen
nacheinander voll, aktueller Schritt pulsiert sanft
- Status-Badge mit pulsierendem Punkt bei laufenden Bestellungen
- Leere Zustaende mit gestrichelter Wabe und Serifen-Titel
- Formularfelder: einheitlicher Honig-Schein beim Fokus
- Groessere Abschnittstitel im Konto, Bestellsummen in Serifen-Preisschrift
- Nur CSS-Animationen (Komponenten rendern auch auf Admin-Serverseiten);
reduzierte Bewegung schaltet Puls und Fuellen ab
Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
- Hero: deutlich groessere Headline, langsam atmendes Bild, Wabenraster,
Honigtropfen als Scroll-Hinweis, Hauptaktion in Honig
- "Sechs Schritte" als Weg: eine Honiglinie fuellt sich beim Scrollen und
laesst die Waben der Schritte nacheinander aufleuchten (waagerecht ab lg,
senkrecht auf dem Handy; reduzierte Bewegung: sofort gefuellt)
- Philosophie mit Waben statt Nummern, Verantwortung als geteiltes Layout
mit mitlaufender Ueberschrift
- Bilder mit versetztem Honigrahmen, Wabenraster auf dunklen Flaechen,
grosse Anfuehrungszeichen bei den Kundenstimmen
- Knopf-Varianten "inverted"/"accent" fuer dunkle Flaechen ersetzen
!-Overrides, die den Fuell-Effekt ausgehebelt haetten
- Fix: Ueberschrift der Kundenstimmen war Waldgruen auf Waldgruen
Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
- Fundament: feine Papierkoernung, Scrollbar/Cursor/Fokusring/Markierung in
Markenfarben, gleich breite Ziffern fuer Preise, einlaufende Unterstriche
- Knoepfe fuellen sich beim Hover von unten wie ein Glas (Honig/Waldgruen),
geben beim Klick nach
- Wabenmotiv aus dem Logo als Komponente (Raster, Markierung, Haekchen)
- In den Warenkorb: ein Honigtropfen fliegt im Bogen zum Korb, der Korb
wippt, der Zaehler springt, erst dann oeffnet die Schublade; Knopf zeigt
gezeichnetes Haekchen. Reduzierte Bewegung: Schublade oeffnet sofort.
- Produktkarten: Honigschimmer, Anheben, Serifen-Preis, gestaffeltes
Erscheinen, Pfeil-Links
- Produktseite: mitlaufendes Bild auf Wabenraster, groesserer Titel und
Preis, sich fuellende Geschmacksskala, Waben-Haekchen statt "✓"
- Shop-Filter als einheitliche Werkzeugleiste mit Schalter
- Warenkorb-Schublade: Dialog-Semantik, animierte Positionen, tickende
Summe, gestalteter leerer Zustand
Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
Viele Aktionen gaben keine Rueckmeldung (Retoure annehmen, Archiv,
Bewertungen, Adressen, Newsletter, Kundensperre ...) oder meldeten Erfolg,
obwohl die zugehoerige E-Mail nie rausging.
- lib/flash: Meldung als kurzlebiges Cookie; Toast im Root-Layout
(FlashToast). Ersetzt ?message=/?error= in der URL — darueber liess sich
bisher beliebiger Text als Systemmeldung im Wabenhain-Layout anzeigen
(Phishing-Hebel). Server Components leiten ueber /api/hinweis mit festen
Codes und festen Zielen weiter.
- fail()/succeed() zentral und asynchron; ein Quelltext-Test stellt sicher,
dass jeder Aufruf mit await/return erfolgt (sonst liefe eine Action nach
einem Fehler weiter).
- sendMail meldet, ob die Mail angenommen wurde; Aktionen mit Mail zeigen
ehrlich an, wenn der Versand scheitert (z.B. "Link erneut senden").
- Jede Server Action meldet jetzt Erfolg oder den Grund des Abbruchs.
- SubmitFeedback: geklickter Knopf zeigt sofort einen Ladezustand und ist
bis zur Antwort gegen Doppelklick gesperrt.
- 2FA: nach dem Einrichten zurueck ins Profil statt auf die Seite mit
"Deaktivieren".
Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
Abgeschlossene Eintraege wandern automatisch in einen Tab "Archiv", damit
die aktiven Listen nur zeigen, was noch zu tun ist:
- Bestellungen: versendet/abgeholt/storniert, keine offene Retoure,
seit einem Monat nicht bearbeitet
- Retouren: angenommen/abgelehnt/erstattet, seit einem Monat nicht bearbeitet
- Produkte: seit einem Monat Bestand 0 (nur Admin-Sicht, Shop unveraendert)
Die Automatik laeuft beim Oeffnen der jeweiligen Liste (hoechstens alle
5 Minuten) und holt Eintraege zurueck, sobald sie wieder offen sind.
Zusaetzlich "Archivieren"/"Wiederherstellen" per Hand; von Hand
archivierte Eintraege fasst die Automatik nicht an. Alle Verschiebungen
landen im Audit-Log.
Neue Felder archivedAt/archivedManually samt Index an orders,
return_requests und products (Migration 20260925010000_archive_fields).
Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
/bestellung-verfolgen holt den Sendungsverlauf serverseitig ueber die
DHL Shipment Tracking - Unified API und zeigt ihn im eigenen Layout an,
statt Kundschaft auf dhl.de weiterzuleiten.
- lib/dhl-tracking.ts: Abfrage mit API-Key, Zod-Validierung, Timeout,
Cache pro Sendung (unterwegs 10 min, zugestellt 12 h, Fehler 2 min)
- Abfrage erst nach erfolgreicher Pruefung von Bestellnummer + E-Mail,
damit die Seite nicht als offener Proxy zu DHL dient
- Ohne freigeschalteten Key bleibt die Seite unveraendert (Nummer + Link)
- Optional DHL_TRACKING_API_KEY, falls das Tracking in eigener DHL-App liegt
- Datenschutzerklaerung um die Statusabfrage bei DHL ergaenzt
Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
Haelt die freigegebene Loesung fest: Versandstaffeln haengen kuenftig an der
Produktkategorie statt an einer einzigen globalen Mengenstaffel, weil ein
Honigglas deutlich mehr Versandkosten verursacht als eine Kerze. Bei
gemischten Warenkoerben gilt die teuerste Kategorie, nicht die Summe.
Ausserdem wird die Hoechstbestellmenge je Position im Adminportal pflegbar
(globaler Standardwert plus optionale Uebersteuerung je Produkt) statt als
widerspruechliche Konstanten im Stepper und im Checkout-Schema zu stehen.
Co-Authored-By: Claude Opus 5 <[email protected]>