From 0a819b6a4d862db49509a2e0dca084acee3b2b3b Mon Sep 17 00:00:00 2001 From: qcigano Date: Mon, 3 Aug 2026 03:27:26 +0200 Subject: [PATCH] Kundenbereich-Vorschau, VanVans Geschichte neben ihr Foto, Emoji-Kreis entfernt 1. Neue Seite /konto/angemeldet/ (alle 4 Sprachen): zeigt, was Kund:innen nach dem Anmelden sehen - Begruessung, Kennzahlen, Bestellhistorie mit farbigem Bestellstatus, Sendungsverfolgung (echter DHL-Link), Rechnung/Nochmal- bestellen, Wunschliste, "Zuletzt angesehen" und Profildaten. Klar als Vorschau gekennzeichnet: es gibt noch kein echtes Login und keine Datenbank (Phase 2), die Bestellungen sind Beispieldaten fuer die Gestaltung. Von der Login-Seite aus verlinkt, damit die Ansicht ueberhaupt erreichbar ist. 2. "Ueber die Kreative": der erste Abschnitt ihrer Geschichte ("Meine Reise begann mit einer einzigen Masche") steht jetzt direkt neben dem Foto statt darunter. Foto bleibt beim Lesen sichtbar (sticky), die restlichen Abschnitte folgen wie bisher darunter. 3. Kategorie-Seiten: der farbige Kreis um das Emoji bei "Hauptkategorie" ist entfernt, das Emoji steht jetzt frei neben der Ueberschrift. Dabei gefundener und behobener eigener Fehler: das noetige "align-items: start" fuer das neue Nebeneinander-Layout hat im gestapelten Handy-Layout die Grid-Zeile zu niedrig berechnet, wodurch das Foto 80px in den Text darunter ragte (per Messung im Browser nachgewiesen, nicht nur per Screenshot geraten). Jetzt sauber auf Bildschirme ab 901px begrenzt - Handy misst 24px Abstand. Co-Authored-By: Claude Sonnet 5 --- src/i18n/ui.ts | 56 +++++++ src/pages/ch/konto/angemeldet.astro | 160 ++++++++++++++++++++ src/pages/ch/konto/index.astro | 3 + src/pages/ch/ueber-die-kreative/index.astro | 32 ++-- src/pages/en/konto/angemeldet.astro | 160 ++++++++++++++++++++ src/pages/en/konto/index.astro | 3 + src/pages/en/ueber-die-kreative/index.astro | 32 ++-- src/pages/fr/konto/angemeldet.astro | 160 ++++++++++++++++++++ src/pages/fr/konto/index.astro | 3 + src/pages/fr/ueber-die-kreative/index.astro | 32 ++-- src/pages/konto/angemeldet.astro | 160 ++++++++++++++++++++ src/pages/konto/index.astro | 5 + src/pages/ueber-die-kreative/index.astro | 33 ++-- src/styles/global.css | 72 ++++++++- 14 files changed, 857 insertions(+), 54 deletions(-) create mode 100644 src/pages/ch/konto/angemeldet.astro create mode 100644 src/pages/en/konto/angemeldet.astro create mode 100644 src/pages/fr/konto/angemeldet.astro create mode 100644 src/pages/konto/angemeldet.astro diff --git a/src/i18n/ui.ts b/src/i18n/ui.ts index 7ae75ca..a7ed19d 100644 --- a/src/i18n/ui.ts +++ b/src/i18n/ui.ts @@ -202,6 +202,20 @@ export const ui = { loginTitle: "Anmelden", email: "E-Mail", password: "Passwort", loginButton: "Anmelden (folgt in Phase 2)", forgotPassword: "Passwort vergessen?", newTitle: "Neu hier?", newText: "Lege ein Konto an, um Bestellhistorie, Wunschliste und Sendungsverfolgung an einem Ort zu haben – oder bestelle einfach als Gast, ganz ohne Konto.", guestButton: "Als Gast weiter einkaufen", + previewLink: "đŸ‘ïž Vorschau: So sieht der Bereich fĂŒr angemeldete Kund:innen aus", + }, + accountDash: { + eyebrow: "Mein Konto", greeting: "Hallo Marie đŸ©”", lead: "Schön, dass du wieder da bist. Hier findest du alle deine Bestellungen, deine Wunschliste und deine Daten auf einen Blick.", + previewNote: "VORSCHAU-ANSICHT (Phase 1): So sieht der persönliche Bereich aus, sobald ein:e Kund:in angemeldet ist. Die hier gezeigten Bestellungen und Daten sind reine Beispieldaten zur Gestaltung — es gibt noch kein echtes Login und keine echte Datenbank. Beides kommt in Phase 2 (Cloudflare Worker + D1), danach zeigt diese Seite die echten Daten der jeweils angemeldeten Person.", + logout: "Abmelden", + statOrders: "Bestellungen", statWishlist: "Wunschliste", statOpen: "Offene Sendungen", + ordersTitle: "📩 Meine Bestellungen", + orderNo: "Bestellung", orderDate: "Bestellt am", orderTotal: "Gesamt", orderItems: "Artikel", + trackingLabel: "Sendungsverfolgung", trackingBtn: "Sendung verfolgen →", invoiceBtn: "Rechnung (PDF)", reorderBtn: "Nochmal bestellen", + statusBezahlt: "Bezahlt", statusBearbeitung: "In Bearbeitung", statusVersandVorbereitet: "Versand vorbereitet", statusVersendet: "Versendet", statusAbgeschlossen: "Abgeschlossen", + wishlistTitle: "đŸ©” Meine Wunschliste", wishlistEmpty: "Deine Wunschliste ist noch leer — stöbere im Shop und speichere deine LieblingsstĂŒcke mit dem Herz-Symbol.", + recentTitle: "🕘 Zuletzt angesehen", + profileTitle: "đŸ‘€ Meine Daten", profileName: "Name", profileEmail: "E-Mail", profileAddress: "Lieferadresse", profileEdit: "Daten bearbeiten", }, }, en: { @@ -400,6 +414,20 @@ export const ui = { loginTitle: "Sign in", email: "Email", password: "Password", loginButton: "Sign in (coming in Phase 2)", forgotPassword: "Forgot password?", newTitle: "New here?", newText: "Create an account to keep order history, wishlist and tracking in one place – or simply check out as a guest, no account needed.", guestButton: "Continue shopping as guest", + previewLink: "đŸ‘ïž Preview: what signed-in customers see", + }, + accountDash: { + eyebrow: "My Account", greeting: "Hello Marie đŸ©”", lead: "Good to see you again. Here you'll find all your orders, your wishlist and your details at a glance.", + previewNote: "PREVIEW (Phase 1): This is what the personal area looks like once a customer is signed in. The orders and details shown here are example data for design purposes only — there is no real login or database yet. Both are coming in Phase 2 (Cloudflare Worker + D1), after which this page will show the signed-in person's real data.", + logout: "Sign out", + statOrders: "Orders", statWishlist: "Wishlist", statOpen: "Open shipments", + ordersTitle: "📩 My orders", + orderNo: "Order", orderDate: "Ordered on", orderTotal: "Total", orderItems: "Items", + trackingLabel: "Tracking", trackingBtn: "Track shipment →", invoiceBtn: "Invoice (PDF)", reorderBtn: "Order again", + statusBezahlt: "Paid", statusBearbeitung: "In progress", statusVersandVorbereitet: "Preparing shipment", statusVersendet: "Shipped", statusAbgeschlossen: "Completed", + wishlistTitle: "đŸ©” My wishlist", wishlistEmpty: "Your wishlist is still empty — browse the shop and save your favourites with the heart icon.", + recentTitle: "🕘 Recently viewed", + profileTitle: "đŸ‘€ My details", profileName: "Name", profileEmail: "Email", profileAddress: "Delivery address", profileEdit: "Edit details", }, }, ch: { @@ -598,6 +626,20 @@ export const ui = { loginTitle: "AamĂ€lde", email: "E-Mail", password: "Passwort", loginButton: "AamĂ€lde (chunnt i Phase 2)", forgotPassword: "Passwort vergĂ€sse?", newTitle: "Neu da?", newText: "Leg es Konto a, fĂŒr Bstellhistorie, Wunschlischte und SĂ€ndigsverfolgig a einem Ort z'ha – oder bstell eifach als Gast, ganz ohni Konto.", guestButton: "Als Gast wyter yichaufe", + previewLink: "đŸ‘ïž Vorschau: so gseht dr Bereich fĂŒr aagmĂ€ldeti Kund:inne us", + }, + accountDash: { + eyebrow: "Mis Konto", greeting: "Hoi Marie đŸ©”", lead: "Schön, bisch wieder da. Da findsch alli dini Bstellige, dini Wunschlischte und dini Date uf ein Blick.", + previewNote: "VORSCHAU (Phase 1): So gseht dr persönlich Bereich us, sobald öpper aagmĂ€ldet isch. D'Bstellige und Date wo da zeigt werde, sind reini Bispieldate fĂŒr d'Gstaltig — es git no kei echts Login und kei echti Datebank. Beides chunnt i Phase 2 (Cloudflare Worker + D1), denn zeigt die Syte d'echte Date vo dr aagmĂ€ldete Person.", + logout: "AbmĂ€lde", + statOrders: "Bstellige", statWishlist: "Wunschlischte", statOpen: "Offni SĂ€ndige", + ordersTitle: "📩 Mini Bstellige", + orderNo: "Bstellig", orderDate: "Bstellt am", orderTotal: "Total", orderItems: "Artikel", + trackingLabel: "SĂ€ndigsverfolgig", trackingBtn: "SĂ€ndig verfolge →", invoiceBtn: "RĂ€chnig (PDF)", reorderBtn: "Nomal bstelle", + statusBezahlt: "Zahlt", statusBearbeitung: "I dr Bearbeitig", statusVersandVorbereitet: "Versand vorbereitet", statusVersendet: "Verschickt", statusAbgeschlossen: "Abgschlosse", + wishlistTitle: "đŸ©” Mini Wunschlischte", wishlistEmpty: "Dini Wunschlischte isch no leer — lueg im Shop verbi und spicher dini LieblingssĂ€cheli mit em HĂ€rz-Symbol.", + recentTitle: "🕘 Zletscht aagluegt", + profileTitle: "đŸ‘€ Mini Date", profileName: "Name", profileEmail: "E-Mail", profileAddress: "LieferadrĂ€sse", profileEdit: "Date bearbeite", }, }, fr: { @@ -796,6 +838,20 @@ export const ui = { loginTitle: "Se connecter", email: "E-mail", password: "Mot de passe", loginButton: "Se connecter (disponible en phase 2)", forgotPassword: "Mot de passe oubliĂ© ?", newTitle: "Nouveau ici ?", newText: "CrĂ©ez un compte pour retrouver l'historique des commandes, la liste de souhaits et le suivi au mĂȘme endroit – ou commandez simplement en tant qu'invitĂ©, sans compte.", guestButton: "Continuer mes achats en tant qu'invitĂ©", + previewLink: "đŸ‘ïž Aperçu : l'espace vu par les client(e)s connectĂ©(e)s", + }, + accountDash: { + eyebrow: "Mon compte", greeting: "Bonjour Marie đŸ©”", lead: "Ravie de vous revoir. Retrouvez ici toutes vos commandes, votre liste de souhaits et vos donnĂ©es en un coup d'Ɠil.", + previewNote: "APERÇU (phase 1) : voici Ă  quoi ressemble l'espace personnel une fois le/la client(e) connectĂ©(e). Les commandes et donnĂ©es affichĂ©es ici sont des exemples destinĂ©s Ă  la conception — il n'existe encore ni connexion rĂ©elle ni base de donnĂ©es. Les deux arriveront en phase 2 (Cloudflare Worker + D1) ; cette page affichera alors les donnĂ©es rĂ©elles de la personne connectĂ©e.", + logout: "Se dĂ©connecter", + statOrders: "Commandes", statWishlist: "Liste de souhaits", statOpen: "Envois en cours", + ordersTitle: "📩 Mes commandes", + orderNo: "Commande", orderDate: "CommandĂ© le", orderTotal: "Total", orderItems: "Articles", + trackingLabel: "Suivi", trackingBtn: "Suivre l'envoi →", invoiceBtn: "Facture (PDF)", reorderBtn: "Commander Ă  nouveau", + statusBezahlt: "PayĂ©e", statusBearbeitung: "En prĂ©paration", statusVersandVorbereitet: "Envoi en prĂ©paration", statusVersendet: "ExpĂ©diĂ©e", statusAbgeschlossen: "TerminĂ©e", + wishlistTitle: "đŸ©” Ma liste de souhaits", wishlistEmpty: "Votre liste de souhaits est encore vide — parcourez la boutique et enregistrez vos coups de cƓur avec l'icĂŽne en forme de cƓur.", + recentTitle: "🕘 Vus rĂ©cemment", + profileTitle: "đŸ‘€ Mes donnĂ©es", profileName: "Nom", profileEmail: "E-mail", profileAddress: "Adresse de livraison", profileEdit: "Modifier mes donnĂ©es", }, }, } as const; diff --git a/src/pages/ch/konto/angemeldet.astro b/src/pages/ch/konto/angemeldet.astro new file mode 100644 index 0000000..a420e11 --- /dev/null +++ b/src/pages/ch/konto/angemeldet.astro @@ -0,0 +1,160 @@ +--- +import Layout from "../../../layouts/Layout.astro"; +import ProductCard from "../../../components/ProductCard.astro"; +import { products } from "../../../data/products"; +import type { Locale } from "../../../i18n/config"; +import { useTranslations } from "../../../i18n/ui"; +import { formatPrice } from "../../../i18n/format"; + +const lang: Locale = "ch"; +const t = useTranslations(lang); + +// VORSCHAU-SEITE (Phase 1): So sieht der persönliche Bereich aus, sobald jemand angemeldet ist. +// Es gibt noch kein echtes Login und keine Datenbank — die Bestellungen unten sind reine +// Beispieldaten, damit Aufbau, Status-Anzeige und Bedienung schon jetzt beurteilt und +// weiterentwickelt werden können. In Phase 2 (Cloudflare Worker + D1) werden sie durch die +// echten Daten der angemeldeten Person ersetzt; Layout und Texte bleiben dann bestehen. +const beispielBestellungen = [ + { + nummer: "VDB-2026-0184", + datum: "28.07.2026", + status: "versendet" as const, + summe: 47.5, + tracking: "00340434161094015791", + artikel: [ + { name: "\"HasiDog\" im blauen Overall", menge: 1, preis: 25 }, + { name: "Perlenarmband „Himmelblau\"", menge: 1, preis: 14.5 }, + ], + }, + { + nummer: "VDB-2026-0157", + datum: "12.07.2026", + status: "abgeschlossen" as const, + summe: 22, + tracking: null, + artikel: [{ name: "Ergonomisches HĂ€kelnadel-Set (9-teilig)", menge: 1, preis: 22 }], + }, +]; + +const statusLabel = { + bezahlt: t.accountDash.statusBezahlt, + bearbeitung: t.accountDash.statusBearbeitung, + versandVorbereitet: t.accountDash.statusVersandVorbereitet, + versendet: t.accountDash.statusVersendet, + abgeschlossen: t.accountDash.statusAbgeschlossen, +} as const; + +// "Zuletzt angesehen" zeigt in dieser Vorschau einfach die ersten echten Produkte aus dem Shop — +// in Phase 2 kommt hier die tatsĂ€chliche Verlaufs-Historie der angemeldeten Person hin. +const zuletztAngesehen = products.slice(0, 3); +const offeneSendungen = beispielBestellungen.filter((b) => b.status === "versendet").length; +--- + +
+
+

{t.accountDash.previewNote}

+
+
+ +
+
+ + + +
+
+ +
+
+

{t.accountDash.ordersTitle}

+
+ {beispielBestellungen.map((b) => ( +
+
+
+ {t.accountDash.orderNo} {b.nummer} + {t.accountDash.orderDate} {b.datum} +
+ {statusLabel[b.status]} +
+ +
    + {b.artikel.map((a) => ( +
  • + {a.menge}× {a.name} + {formatPrice(a.preis * a.menge, lang)} +
  • + ))} +
+ +
+ {t.accountDash.orderTotal}: {formatPrice(b.summe, lang)} + +
+ {b.tracking && ( +

{t.accountDash.trackingLabel}: {b.tracking}

+ )} +
+ ))} +
+
+
+ +
+
+

{t.accountDash.wishlistTitle}

+
+

{t.accountDash.wishlistEmpty}

+
+
+
+ + {zuletztAngesehen.length > 0 && ( +
+
+

{t.accountDash.recentTitle}

+
{zuletztAngesehen.map((p) => )}
+
+
+ )} + +
+
+

{t.accountDash.profileTitle}

+
+ + + + + + +
{t.accountDash.profileName}Marie Beispiel
{t.accountDash.profileEmail}marie@beispiel.de
{t.accountDash.profileAddress}Musterstraße 1
10115 Berlin
Deutschland
+ {t.accountDash.profileEdit} +
+
+
+
diff --git a/src/pages/ch/konto/index.astro b/src/pages/ch/konto/index.astro index c2f88cd..3105f1c 100644 --- a/src/pages/ch/konto/index.astro +++ b/src/pages/ch/konto/index.astro @@ -31,5 +31,8 @@ const t = useTranslations(lang); {t.account.guestButton} + diff --git a/src/pages/ch/ueber-die-kreative/index.astro b/src/pages/ch/ueber-die-kreative/index.astro index ff7db87..0da477c 100644 --- a/src/pages/ch/ueber-die-kreative/index.astro +++ b/src/pages/ch/ueber-die-kreative/index.astro @@ -5,32 +5,40 @@ import { useTranslations } from "../../../i18n/ui"; const lang: Locale = "ch"; const t = useTranslations(lang); + +// Der erste Story-Abschnitt steht direkt neben dem Foto von VanVan statt darunter als eigener +// Block. Alle weiteren Abschnitte bleiben wie bisher darunter. +const [ersteStory, ...weitereStories] = t.about.story; ---
-
+
VanVan i ihrer Werkstatt mit ihre sÀlber ghÀklete Plushies {t.common.handmadeBadge}
-
+
{t.about.eyebrow}

{t.about.title}

{t.about.lead}

+

{ersteStory.title}

+ {ersteStory.paragraphs.map((p) =>

{p}

)}
-
-
- {t.about.story.map((s) => ( -
-

{s.title}

- {s.paragraphs.map((p) =>

{p}

)} -
- ))} -
-
+ {weitereStories.length > 0 && ( +
+
+ {weitereStories.map((s) => ( +
+

{s.title}

+ {s.paragraphs.map((p) =>

{p}

)} +
+ ))} +
+
+ )}
diff --git a/src/pages/en/konto/angemeldet.astro b/src/pages/en/konto/angemeldet.astro new file mode 100644 index 0000000..6f79a8b --- /dev/null +++ b/src/pages/en/konto/angemeldet.astro @@ -0,0 +1,160 @@ +--- +import Layout from "../../../layouts/Layout.astro"; +import ProductCard from "../../../components/ProductCard.astro"; +import { products } from "../../../data/products"; +import type { Locale } from "../../../i18n/config"; +import { useTranslations } from "../../../i18n/ui"; +import { formatPrice } from "../../../i18n/format"; + +const lang: Locale = "en"; +const t = useTranslations(lang); + +// VORSCHAU-SEITE (Phase 1): So sieht der persönliche Bereich aus, sobald jemand angemeldet ist. +// Es gibt noch kein echtes Login und keine Datenbank — die Bestellungen unten sind reine +// Beispieldaten, damit Aufbau, Status-Anzeige und Bedienung schon jetzt beurteilt und +// weiterentwickelt werden können. In Phase 2 (Cloudflare Worker + D1) werden sie durch die +// echten Daten der angemeldeten Person ersetzt; Layout und Texte bleiben dann bestehen. +const beispielBestellungen = [ + { + nummer: "VDB-2026-0184", + datum: "28.07.2026", + status: "versendet" as const, + summe: 47.5, + tracking: "00340434161094015791", + artikel: [ + { name: "\"HasiDog\" im blauen Overall", menge: 1, preis: 25 }, + { name: "Perlenarmband „Himmelblau\"", menge: 1, preis: 14.5 }, + ], + }, + { + nummer: "VDB-2026-0157", + datum: "12.07.2026", + status: "abgeschlossen" as const, + summe: 22, + tracking: null, + artikel: [{ name: "Ergonomisches HĂ€kelnadel-Set (9-teilig)", menge: 1, preis: 22 }], + }, +]; + +const statusLabel = { + bezahlt: t.accountDash.statusBezahlt, + bearbeitung: t.accountDash.statusBearbeitung, + versandVorbereitet: t.accountDash.statusVersandVorbereitet, + versendet: t.accountDash.statusVersendet, + abgeschlossen: t.accountDash.statusAbgeschlossen, +} as const; + +// "Zuletzt angesehen" zeigt in dieser Vorschau einfach die ersten echten Produkte aus dem Shop — +// in Phase 2 kommt hier die tatsĂ€chliche Verlaufs-Historie der angemeldeten Person hin. +const zuletztAngesehen = products.slice(0, 3); +const offeneSendungen = beispielBestellungen.filter((b) => b.status === "versendet").length; +--- + +
+
+

{t.accountDash.previewNote}

+
+
+ +
+
+ + + +
+
+ +
+
+

{t.accountDash.ordersTitle}

+
+ {beispielBestellungen.map((b) => ( +
+
+
+ {t.accountDash.orderNo} {b.nummer} + {t.accountDash.orderDate} {b.datum} +
+ {statusLabel[b.status]} +
+ +
    + {b.artikel.map((a) => ( +
  • + {a.menge}× {a.name} + {formatPrice(a.preis * a.menge, lang)} +
  • + ))} +
+ +
+ {t.accountDash.orderTotal}: {formatPrice(b.summe, lang)} + +
+ {b.tracking && ( +

{t.accountDash.trackingLabel}: {b.tracking}

+ )} +
+ ))} +
+
+
+ +
+
+

{t.accountDash.wishlistTitle}

+
+

{t.accountDash.wishlistEmpty}

+
+
+
+ + {zuletztAngesehen.length > 0 && ( +
+
+

{t.accountDash.recentTitle}

+
{zuletztAngesehen.map((p) => )}
+
+
+ )} + +
+
+

{t.accountDash.profileTitle}

+
+ + + + + + +
{t.accountDash.profileName}Marie Beispiel
{t.accountDash.profileEmail}marie@beispiel.de
{t.accountDash.profileAddress}Musterstraße 1
10115 Berlin
Deutschland
+ {t.accountDash.profileEdit} +
+
+
+
diff --git a/src/pages/en/konto/index.astro b/src/pages/en/konto/index.astro index b30afab..d137c92 100644 --- a/src/pages/en/konto/index.astro +++ b/src/pages/en/konto/index.astro @@ -31,5 +31,8 @@ const t = useTranslations(lang); {t.account.guestButton}
+
diff --git a/src/pages/en/ueber-die-kreative/index.astro b/src/pages/en/ueber-die-kreative/index.astro index 361ab05..549bc28 100644 --- a/src/pages/en/ueber-die-kreative/index.astro +++ b/src/pages/en/ueber-die-kreative/index.astro @@ -5,32 +5,40 @@ import { useTranslations } from "../../../i18n/ui"; const lang: Locale = "en"; const t = useTranslations(lang); + +// Der erste Story-Abschnitt steht direkt neben dem Foto von VanVan statt darunter als eigener +// Block. Alle weiteren Abschnitte bleiben wie bisher darunter. +const [ersteStory, ...weitereStories] = t.about.story; ---
-
+
VanVan in her workshop with her handmade crochet plushies {t.common.handmadeBadge}
-
+
{t.about.eyebrow}

{t.about.title}

{t.about.lead}

+

{ersteStory.title}

+ {ersteStory.paragraphs.map((p) =>

{p}

)}
-
-
- {t.about.story.map((s) => ( -
-

{s.title}

- {s.paragraphs.map((p) =>

{p}

)} -
- ))} -
-
+ {weitereStories.length > 0 && ( +
+
+ {weitereStories.map((s) => ( +
+

{s.title}

+ {s.paragraphs.map((p) =>

{p}

)} +
+ ))} +
+
+ )}
diff --git a/src/pages/fr/konto/angemeldet.astro b/src/pages/fr/konto/angemeldet.astro new file mode 100644 index 0000000..ddce105 --- /dev/null +++ b/src/pages/fr/konto/angemeldet.astro @@ -0,0 +1,160 @@ +--- +import Layout from "../../../layouts/Layout.astro"; +import ProductCard from "../../../components/ProductCard.astro"; +import { products } from "../../../data/products"; +import type { Locale } from "../../../i18n/config"; +import { useTranslations } from "../../../i18n/ui"; +import { formatPrice } from "../../../i18n/format"; + +const lang: Locale = "fr"; +const t = useTranslations(lang); + +// VORSCHAU-SEITE (Phase 1): So sieht der persönliche Bereich aus, sobald jemand angemeldet ist. +// Es gibt noch kein echtes Login und keine Datenbank — die Bestellungen unten sind reine +// Beispieldaten, damit Aufbau, Status-Anzeige und Bedienung schon jetzt beurteilt und +// weiterentwickelt werden können. In Phase 2 (Cloudflare Worker + D1) werden sie durch die +// echten Daten der angemeldeten Person ersetzt; Layout und Texte bleiben dann bestehen. +const beispielBestellungen = [ + { + nummer: "VDB-2026-0184", + datum: "28.07.2026", + status: "versendet" as const, + summe: 47.5, + tracking: "00340434161094015791", + artikel: [ + { name: "\"HasiDog\" im blauen Overall", menge: 1, preis: 25 }, + { name: "Perlenarmband „Himmelblau\"", menge: 1, preis: 14.5 }, + ], + }, + { + nummer: "VDB-2026-0157", + datum: "12.07.2026", + status: "abgeschlossen" as const, + summe: 22, + tracking: null, + artikel: [{ name: "Ergonomisches HĂ€kelnadel-Set (9-teilig)", menge: 1, preis: 22 }], + }, +]; + +const statusLabel = { + bezahlt: t.accountDash.statusBezahlt, + bearbeitung: t.accountDash.statusBearbeitung, + versandVorbereitet: t.accountDash.statusVersandVorbereitet, + versendet: t.accountDash.statusVersendet, + abgeschlossen: t.accountDash.statusAbgeschlossen, +} as const; + +// "Zuletzt angesehen" zeigt in dieser Vorschau einfach die ersten echten Produkte aus dem Shop — +// in Phase 2 kommt hier die tatsĂ€chliche Verlaufs-Historie der angemeldeten Person hin. +const zuletztAngesehen = products.slice(0, 3); +const offeneSendungen = beispielBestellungen.filter((b) => b.status === "versendet").length; +--- + +
+
+

{t.accountDash.previewNote}

+
+
+ +
+
+ + + +
+
+ +
+
+

{t.accountDash.ordersTitle}

+
+ {beispielBestellungen.map((b) => ( +
+
+
+ {t.accountDash.orderNo} {b.nummer} + {t.accountDash.orderDate} {b.datum} +
+ {statusLabel[b.status]} +
+ +
    + {b.artikel.map((a) => ( +
  • + {a.menge}× {a.name} + {formatPrice(a.preis * a.menge, lang)} +
  • + ))} +
+ +
+ {t.accountDash.orderTotal}: {formatPrice(b.summe, lang)} + +
+ {b.tracking && ( +

{t.accountDash.trackingLabel}: {b.tracking}

+ )} +
+ ))} +
+
+
+ +
+
+

{t.accountDash.wishlistTitle}

+
+

{t.accountDash.wishlistEmpty}

+
+
+
+ + {zuletztAngesehen.length > 0 && ( +
+
+

{t.accountDash.recentTitle}

+
{zuletztAngesehen.map((p) => )}
+
+
+ )} + +
+
+

{t.accountDash.profileTitle}

+
+ + + + + + +
{t.accountDash.profileName}Marie Beispiel
{t.accountDash.profileEmail}marie@beispiel.de
{t.accountDash.profileAddress}Musterstraße 1
10115 Berlin
Deutschland
+ {t.accountDash.profileEdit} +
+
+
+
diff --git a/src/pages/fr/konto/index.astro b/src/pages/fr/konto/index.astro index 0c1dbe7..d22d208 100644 --- a/src/pages/fr/konto/index.astro +++ b/src/pages/fr/konto/index.astro @@ -31,5 +31,8 @@ const t = useTranslations(lang); {t.account.guestButton}
+
diff --git a/src/pages/fr/ueber-die-kreative/index.astro b/src/pages/fr/ueber-die-kreative/index.astro index 837593f..45ae115 100644 --- a/src/pages/fr/ueber-die-kreative/index.astro +++ b/src/pages/fr/ueber-die-kreative/index.astro @@ -5,32 +5,40 @@ import { useTranslations } from "../../../i18n/ui"; const lang: Locale = "fr"; const t = useTranslations(lang); + +// Der erste Story-Abschnitt steht direkt neben dem Foto von VanVan statt darunter als eigener +// Block. Alle weiteren Abschnitte bleiben wie bisher darunter. +const [ersteStory, ...weitereStories] = t.about.story; ---
-
+
VanVan dans son atelier avec ses peluches au crochet faites main {t.common.handmadeBadge}
-
+
{t.about.eyebrow}

{t.about.title}

{t.about.lead}

+

{ersteStory.title}

+ {ersteStory.paragraphs.map((p) =>

{p}

)}
-
-
- {t.about.story.map((s) => ( -
-

{s.title}

- {s.paragraphs.map((p) =>

{p}

)} -
- ))} -
-
+ {weitereStories.length > 0 && ( +
+
+ {weitereStories.map((s) => ( +
+

{s.title}

+ {s.paragraphs.map((p) =>

{p}

)} +
+ ))} +
+
+ )}
diff --git a/src/pages/konto/angemeldet.astro b/src/pages/konto/angemeldet.astro new file mode 100644 index 0000000..30f1d40 --- /dev/null +++ b/src/pages/konto/angemeldet.astro @@ -0,0 +1,160 @@ +--- +import Layout from "../../layouts/Layout.astro"; +import ProductCard from "../../components/ProductCard.astro"; +import { products } from "../../data/products"; +import type { Locale } from "../../i18n/config"; +import { useTranslations } from "../../i18n/ui"; +import { formatPrice } from "../../i18n/format"; + +const lang: Locale = "de"; +const t = useTranslations(lang); + +// VORSCHAU-SEITE (Phase 1): So sieht der persönliche Bereich aus, sobald jemand angemeldet ist. +// Es gibt noch kein echtes Login und keine Datenbank — die Bestellungen unten sind reine +// Beispieldaten, damit Aufbau, Status-Anzeige und Bedienung schon jetzt beurteilt und +// weiterentwickelt werden können. In Phase 2 (Cloudflare Worker + D1) werden sie durch die +// echten Daten der angemeldeten Person ersetzt; Layout und Texte bleiben dann bestehen. +const beispielBestellungen = [ + { + nummer: "VDB-2026-0184", + datum: "28.07.2026", + status: "versendet" as const, + summe: 47.5, + tracking: "00340434161094015791", + artikel: [ + { name: "\"HasiDog\" im blauen Overall", menge: 1, preis: 25 }, + { name: "Perlenarmband „Himmelblau\"", menge: 1, preis: 14.5 }, + ], + }, + { + nummer: "VDB-2026-0157", + datum: "12.07.2026", + status: "abgeschlossen" as const, + summe: 22, + tracking: null, + artikel: [{ name: "Ergonomisches HĂ€kelnadel-Set (9-teilig)", menge: 1, preis: 22 }], + }, +]; + +const statusLabel = { + bezahlt: t.accountDash.statusBezahlt, + bearbeitung: t.accountDash.statusBearbeitung, + versandVorbereitet: t.accountDash.statusVersandVorbereitet, + versendet: t.accountDash.statusVersendet, + abgeschlossen: t.accountDash.statusAbgeschlossen, +} as const; + +// "Zuletzt angesehen" zeigt in dieser Vorschau einfach die ersten echten Produkte aus dem Shop — +// in Phase 2 kommt hier die tatsĂ€chliche Verlaufs-Historie der angemeldeten Person hin. +const zuletztAngesehen = products.slice(0, 3); +const offeneSendungen = beispielBestellungen.filter((b) => b.status === "versendet").length; +--- + +
+
+

{t.accountDash.previewNote}

+
+
+ +
+
+ + + +
+
+ +
+
+

{t.accountDash.ordersTitle}

+
+ {beispielBestellungen.map((b) => ( +
+
+
+ {t.accountDash.orderNo} {b.nummer} + {t.accountDash.orderDate} {b.datum} +
+ {statusLabel[b.status]} +
+ +
    + {b.artikel.map((a) => ( +
  • + {a.menge}× {a.name} + {formatPrice(a.preis * a.menge, lang)} +
  • + ))} +
+ +
+ {t.accountDash.orderTotal}: {formatPrice(b.summe, lang)} + +
+ {b.tracking && ( +

{t.accountDash.trackingLabel}: {b.tracking}

+ )} +
+ ))} +
+
+
+ +
+
+

{t.accountDash.wishlistTitle}

+
+

{t.accountDash.wishlistEmpty}

+
+
+
+ + {zuletztAngesehen.length > 0 && ( +
+
+

{t.accountDash.recentTitle}

+
{zuletztAngesehen.map((p) => )}
+
+
+ )} + +
+
+

{t.accountDash.profileTitle}

+
+ + + + + + +
{t.accountDash.profileName}Marie Beispiel
{t.accountDash.profileEmail}marie@beispiel.de
{t.accountDash.profileAddress}Musterstraße 1
10115 Berlin
Deutschland
+ {t.accountDash.profileEdit} +
+
+
+
diff --git a/src/pages/konto/index.astro b/src/pages/konto/index.astro index 2c717a3..0946630 100644 --- a/src/pages/konto/index.astro +++ b/src/pages/konto/index.astro @@ -31,5 +31,10 @@ const t = useTranslations(lang); {t.account.guestButton}
+ {/* Direkter Weg zur Vorschau des eingeloggten Kundenbereichs — solange es noch kein echtes + Login gibt (Phase 2), wĂ€re diese Ansicht sonst gar nicht erreichbar. */} +
diff --git a/src/pages/ueber-die-kreative/index.astro b/src/pages/ueber-die-kreative/index.astro index 1f10379..17ec1a8 100644 --- a/src/pages/ueber-die-kreative/index.astro +++ b/src/pages/ueber-die-kreative/index.astro @@ -5,32 +5,41 @@ import { useTranslations } from "../../i18n/ui"; const lang: Locale = "de"; const t = useTranslations(lang); + +// Der erste Story-Abschnitt ("Meine Reise begann mit einer einzigen Masche") steht direkt neben +// dem Foto von VanVan, statt darunter als eigener Block — so liest man ihre Geschichte sofort +// gemeinsam mit ihrem Bild. Alle weiteren Abschnitte bleiben wie bisher darunter. +const [ersteStory, ...weitereStories] = t.about.story; ---
-
+
VanVan in ihrer Werkstatt mit selbstgehÀkelten Plushies {t.common.handmadeBadge}
-
+
{t.about.eyebrow}

{t.about.title}

{t.about.lead}

+

{ersteStory.title}

+ {ersteStory.paragraphs.map((p) =>

{p}

)}
-
-
- {t.about.story.map((s) => ( -
-

{s.title}

- {s.paragraphs.map((p) =>

{p}

)} -
- ))} -
-
+ {weitereStories.length > 0 && ( +
+
+ {weitereStories.map((s) => ( +
+

{s.title}

+ {s.paragraphs.map((p) =>

{p}

)} +
+ ))} +
+
+ )}
diff --git a/src/styles/global.css b/src/styles/global.css index b9622fe..66b8ca9 100644 --- a/src/styles/global.css +++ b/src/styles/global.css @@ -679,6 +679,29 @@ a:focus-visible, button:focus-visible { border-radius: var(--radius-s); } +/* "Über die Kreative": VanVans Geschichte steht direkt NEBEN ihrem Foto (statt darunter als + eigener Block). Weil die Textspalte dadurch deutlich höher wird als das Foto, wird hier oben + ausgerichtet statt gestreckt — sonst wĂŒrde die Fotokarte mit viel leerem Raum mitwachsen. + Das Foto bleibt beim Lesen sichtbar (sticky), solange genug Platz da ist. */ +/* NUR im zweispaltigen Layout (ab 901px): oben ausrichten statt strecken + Foto bleibt beim + Lesen sichtbar. Bewusst NICHT global gesetzt — unterhalb von 900px stehen Foto und Text + ohnehin untereinander, und dort fĂŒhrt `align-items: start` zu einem echten Fehler: die + Grid-Zeile wird dann anhand des geschĂ€tzten Bild-SeitenverhĂ€ltnisses berechnet und fĂ€llt + ~100px zu niedrig aus, wodurch das Foto in den Text darunter hineinragt (gemessen: 80px + Überlappung). Mit dem Standardverhalten (stretch) tritt das nicht auf. */ +@media (min-width: 901px) { + .intro-story { align-items: start; } + .intro-story .portrait-frame.portrait { position: sticky; top: 6.5rem; } +} +/* text-wrap: balance verteilt die Zeilen der Überschrift gleichmĂ€ĂŸig — sonst rutscht bei + breiten Fenstern nur das đŸ©” allein in eine eigene Zeile. */ +.intro-story-text h1 { margin-bottom: 0.6rem; text-wrap: balance; } +.intro-story-text h2 { + margin-top: 2rem; + font-size: clamp(1.4rem, 2.4vw, 1.8rem); +} +.intro-story-text p { max-width: 60ch; } + /* Wechselt innerhalb einer Tier-Familie ab: Foto links/rechts, Text jeweils gegenĂŒber — sorgt fĂŒr optische Abwechslung, sobald mehrere Varianten einer Serie eigene Seiten bekommen. */ .intro-reverse .portrait-frame.portrait { order: 2; } @@ -1074,16 +1097,15 @@ a:focus-visible, button:focus-visible { color: var(--c-accent); margin: 2.2rem 0 0; } +/* Das Kategorie-Emoji steht frei neben der Überschrift — vorher saß es in einem farbigen + Kreis, der auf Wunsch entfernt wurde (der Kreis hat das Emoji eher eingesperrt als + hervorgehoben und passte nicht zum ruhigen Rest der Seite). */ .kategorie-section-label .label-icon { display: inline-flex; align-items: center; justify-content: center; - width: 1.9rem; - height: 1.9rem; - border-radius: 50%; - font-size: 1rem; - background: linear-gradient(135deg, var(--c-accent), var(--c-purple-glow) 45%, var(--c-sale) 100%); - color: #08181c; + font-size: 1.35rem; + line-height: 1; } .kategorie-section-label-rule { height: 2px; @@ -1126,6 +1148,44 @@ a:focus-visible, button:focus-visible { .thumb.product-photo:hover { box-shadow: 0 0 0 3px rgba(10, 9, 16, 0.6), 0 0 0 4px var(--c-accent); } .badge-row { display: flex; gap: 0.5rem; margin-bottom: 0.8rem; flex-wrap: wrap; } +/* Persönlicher Kundenbereich (/konto/angemeldet/) — Ansicht fĂŒr angemeldete Kund:innen mit + Bestellhistorie, Sendungsverfolgung, Wunschliste und Profildaten. Nutzt durchgehend die + bestehende Karten-/Badge-Optik der Seite, damit sich nichts fremd anfĂŒhlt. */ +.account-head { + display: flex; + align-items: flex-start; + justify-content: space-between; + gap: 1.5rem; + flex-wrap: wrap; +} +.account-head h1 { margin: 0.2rem 0 0.5rem; } +.account-head .lead { margin: 0; max-width: 52ch; } +.account-stats { display: grid; grid-template-columns: repeat(3, 1fr); gap: 1rem; margin-top: 1.2rem; } +.account-stat { text-align: center; display: flex; flex-direction: column; gap: 0.25rem; padding: 1.2rem 1rem; } +.account-stat-value { font-family: var(--font-head); font-size: 1.9rem; font-weight: 700; color: var(--c-accent); line-height: 1; } +@media (max-width: 620px) { .account-stats { grid-template-columns: 1fr; } } + +.order-list { display: flex; flex-direction: column; gap: 1.1rem; } +.order-card { display: flex; flex-direction: column; gap: 0.9rem; } +.order-card-head { display: flex; align-items: flex-start; justify-content: space-between; gap: 1rem; flex-wrap: wrap; } +.order-no { display: block; font-weight: 700; font-size: 1.02rem; } +.order-meta { color: var(--c-text-muted); } +/* Bestellstatus in den Farben der jeweiligen Phase (siehe Pflichtenheft Punkt 12): unterwegs = + Akzentfarbe, abgeschlossen = ruhiges GrĂŒn, in Arbeit = neutral. */ +.order-status-versendet { background: color-mix(in srgb, var(--c-accent) 34%, transparent); border-color: color-mix(in srgb, var(--c-accent) 55%, transparent); } +.order-status-abgeschlossen { background: rgba(126, 200, 160, 0.28); border-color: rgba(126, 200, 160, 0.5); color: #cfeee0; } +.order-items { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 0.4rem; } +.order-items li { display: flex; justify-content: space-between; gap: 1rem; font-size: 0.95rem; color: var(--c-text-muted); } +.order-item-price { color: var(--c-text); white-space: nowrap; } +.order-card-foot { display: flex; align-items: center; justify-content: space-between; gap: 1rem; flex-wrap: wrap; border-top: 1px dashed rgba(244, 241, 247, 0.14); padding-top: 0.9rem; } +.order-total { font-size: 1.05rem; } +.order-total strong { color: var(--c-accent); } +.order-actions { display: flex; gap: 0.5rem; flex-wrap: wrap; } +.order-tracking-no { margin: 0; color: var(--c-text-muted); } +.order-tracking-no code { font-size: 0.9em; letter-spacing: 0.02em; } +.profile-card { display: flex; flex-direction: column; align-items: flex-start; gap: 0.5rem; } +.profile-card .specs { margin-top: 0; width: 100%; } + /* Kundenrezensionen — Herzen in Lila-Babyblau statt Sterne (siehe HeartRating.astro), damit Bewertungen sich erkennbar in die restliche Seite einfĂŒgen statt wie ein fremdes Bewertungs- Widget zu wirken. */