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 <[email protected]>
This commit is contained in:
co-authored by
Claude Sonnet 5
parent
b384938b92
commit
0a819b6a4d
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
---
|
||||
<Layout title="Mis Konto – aagmäldet (Vorschau)" description="Vorschau vom persönliche Kundebereich vo Van's DIY & Bastelbedarf." lang={lang} path="/konto/angemeldet/">
|
||||
<section class="section-tight">
|
||||
<div class="container">
|
||||
<p class="todo-note">{t.accountDash.previewNote}</p>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="section-tight">
|
||||
<div class="container">
|
||||
<div class="account-head card">
|
||||
<div>
|
||||
<span class="eyebrow">{t.accountDash.eyebrow}</span>
|
||||
<h1>{t.accountDash.greeting}</h1>
|
||||
<p class="lead">{t.accountDash.lead}</p>
|
||||
</div>
|
||||
<a class="btn btn-outline btn-sm" href="/ch/konto/">{t.accountDash.logout}</a>
|
||||
</div>
|
||||
|
||||
<div class="account-stats">
|
||||
<div class="card account-stat">
|
||||
<span class="account-stat-value">{beispielBestellungen.length}</span>
|
||||
<span class="small">{t.accountDash.statOrders}</span>
|
||||
</div>
|
||||
<div class="card account-stat">
|
||||
<span class="account-stat-value">0</span>
|
||||
<span class="small">{t.accountDash.statWishlist}</span>
|
||||
</div>
|
||||
<div class="card account-stat">
|
||||
<span class="account-stat-value">{offeneSendungen}</span>
|
||||
<span class="small">{t.accountDash.statOpen}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="section-tight">
|
||||
<div class="container">
|
||||
<h2>{t.accountDash.ordersTitle}</h2>
|
||||
<div class="order-list">
|
||||
{beispielBestellungen.map((b) => (
|
||||
<div class="card order-card">
|
||||
<div class="order-card-head">
|
||||
<div>
|
||||
<span class="order-no">{t.accountDash.orderNo} {b.nummer}</span>
|
||||
<span class="small order-meta">{t.accountDash.orderDate} {b.datum}</span>
|
||||
</div>
|
||||
<span class={`badge order-status order-status-${b.status}`}>{statusLabel[b.status]}</span>
|
||||
</div>
|
||||
|
||||
<ul class="order-items">
|
||||
{b.artikel.map((a) => (
|
||||
<li>
|
||||
<span>{a.menge}× {a.name}</span>
|
||||
<span class="order-item-price">{formatPrice(a.preis * a.menge, lang)}</span>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
|
||||
<div class="order-card-foot">
|
||||
<span class="order-total">{t.accountDash.orderTotal}: <strong>{formatPrice(b.summe, lang)}</strong></span>
|
||||
<div class="order-actions">
|
||||
{b.tracking && <a class="btn btn-outline btn-sm" href={`https://www.dhl.de/de/privatkunden/dhl-sendungsverfolgung.html?piececode=${b.tracking}`} target="_blank" rel="noopener">{t.accountDash.trackingBtn}</a>}
|
||||
<a class="btn btn-outline btn-sm" href="#">{t.accountDash.invoiceBtn}</a>
|
||||
<a class="btn btn-outline btn-sm" href="/ch/shop/">{t.accountDash.reorderBtn}</a>
|
||||
</div>
|
||||
</div>
|
||||
{b.tracking && (
|
||||
<p class="small order-tracking-no">{t.accountDash.trackingLabel}: <code>{b.tracking}</code></p>
|
||||
)}
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="section-tight">
|
||||
<div class="container">
|
||||
<h2>{t.accountDash.wishlistTitle}</h2>
|
||||
<div class="card">
|
||||
<p class="small" style="margin:0;">{t.accountDash.wishlistEmpty}</p>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{zuletztAngesehen.length > 0 && (
|
||||
<section class="section-tight">
|
||||
<div class="container">
|
||||
<h2>{t.accountDash.recentTitle}</h2>
|
||||
<div class="grid grid-3">{zuletztAngesehen.map((p) => <ProductCard product={p} lang={lang} />)}</div>
|
||||
</div>
|
||||
</section>
|
||||
)}
|
||||
|
||||
<section class="section-tight">
|
||||
<div class="container">
|
||||
<h2>{t.accountDash.profileTitle}</h2>
|
||||
<div class="card profile-card">
|
||||
<table class="specs">
|
||||
<tbody>
|
||||
<tr><th>{t.accountDash.profileName}</th><td>Marie Beispiel</td></tr>
|
||||
<tr><th>{t.accountDash.profileEmail}</th><td>[email protected]</td></tr>
|
||||
<tr><th>{t.accountDash.profileAddress}</th><td>Musterstraße 1<br />10115 Berlin<br />Deutschland</td></tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<a class="btn btn-outline btn-sm" href="#">{t.accountDash.profileEdit}</a>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</Layout>
|
||||
@@ -31,5 +31,8 @@ const t = useTranslations(lang);
|
||||
<a class="btn btn-outline btn-block" href="/ch/shop/">{t.account.guestButton}</a>
|
||||
</div>
|
||||
</div>
|
||||
<div class="container" style="margin-top:1.2rem;">
|
||||
<a class="small" href="/ch/konto/angemeldet/">{t.account.previewLink}</a>
|
||||
</div>
|
||||
</section>
|
||||
</Layout>
|
||||
|
||||
@@ -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;
|
||||
---
|
||||
<Layout title="Über d'Kreativi" description="Lern VanVan kenne – d'Person hinger Van's DIY & Bastelbedarf." lang={lang} path="/ueber-die-kreative/">
|
||||
<section class="section-tight">
|
||||
<div class="container grid grid-2 intro">
|
||||
<div class="container grid grid-2 intro intro-story">
|
||||
<div class="portrait-frame portrait">
|
||||
<img src="/img/vanvan-portrait-holding.webp" alt="VanVan i ihrer Werkstatt mit ihre sälber ghäklete Plushies" width="900" height="1353" loading="lazy" />
|
||||
<span class="portrait-badge">{t.common.handmadeBadge}</span>
|
||||
</div>
|
||||
<div>
|
||||
<div class="intro-story-text">
|
||||
<span class="eyebrow">{t.about.eyebrow}</span>
|
||||
<h1>{t.about.title}</h1>
|
||||
<p class="lead">{t.about.lead}</p>
|
||||
<h2>{ersteStory.title}</h2>
|
||||
{ersteStory.paragraphs.map((p) => <p>{p}</p>)}
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="section-tight">
|
||||
<div class="container story">
|
||||
{t.about.story.map((s) => (
|
||||
<div class="story-section">
|
||||
<h2>{s.title}</h2>
|
||||
{s.paragraphs.map((p) => <p>{p}</p>)}
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</section>
|
||||
{weitereStories.length > 0 && (
|
||||
<section class="section-tight">
|
||||
<div class="container story">
|
||||
{weitereStories.map((s) => (
|
||||
<div class="story-section">
|
||||
<h2>{s.title}</h2>
|
||||
{s.paragraphs.map((p) => <p>{p}</p>)}
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</section>
|
||||
)}
|
||||
|
||||
<section class="section-tight">
|
||||
<div class="container">
|
||||
|
||||
@@ -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;
|
||||
---
|
||||
<Layout title="My account – signed in (preview)" description="Preview of the personal customer area at Van's DIY & Bastelbedarf." lang={lang} path="/konto/angemeldet/">
|
||||
<section class="section-tight">
|
||||
<div class="container">
|
||||
<p class="todo-note">{t.accountDash.previewNote}</p>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="section-tight">
|
||||
<div class="container">
|
||||
<div class="account-head card">
|
||||
<div>
|
||||
<span class="eyebrow">{t.accountDash.eyebrow}</span>
|
||||
<h1>{t.accountDash.greeting}</h1>
|
||||
<p class="lead">{t.accountDash.lead}</p>
|
||||
</div>
|
||||
<a class="btn btn-outline btn-sm" href="/en/konto/">{t.accountDash.logout}</a>
|
||||
</div>
|
||||
|
||||
<div class="account-stats">
|
||||
<div class="card account-stat">
|
||||
<span class="account-stat-value">{beispielBestellungen.length}</span>
|
||||
<span class="small">{t.accountDash.statOrders}</span>
|
||||
</div>
|
||||
<div class="card account-stat">
|
||||
<span class="account-stat-value">0</span>
|
||||
<span class="small">{t.accountDash.statWishlist}</span>
|
||||
</div>
|
||||
<div class="card account-stat">
|
||||
<span class="account-stat-value">{offeneSendungen}</span>
|
||||
<span class="small">{t.accountDash.statOpen}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="section-tight">
|
||||
<div class="container">
|
||||
<h2>{t.accountDash.ordersTitle}</h2>
|
||||
<div class="order-list">
|
||||
{beispielBestellungen.map((b) => (
|
||||
<div class="card order-card">
|
||||
<div class="order-card-head">
|
||||
<div>
|
||||
<span class="order-no">{t.accountDash.orderNo} {b.nummer}</span>
|
||||
<span class="small order-meta">{t.accountDash.orderDate} {b.datum}</span>
|
||||
</div>
|
||||
<span class={`badge order-status order-status-${b.status}`}>{statusLabel[b.status]}</span>
|
||||
</div>
|
||||
|
||||
<ul class="order-items">
|
||||
{b.artikel.map((a) => (
|
||||
<li>
|
||||
<span>{a.menge}× {a.name}</span>
|
||||
<span class="order-item-price">{formatPrice(a.preis * a.menge, lang)}</span>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
|
||||
<div class="order-card-foot">
|
||||
<span class="order-total">{t.accountDash.orderTotal}: <strong>{formatPrice(b.summe, lang)}</strong></span>
|
||||
<div class="order-actions">
|
||||
{b.tracking && <a class="btn btn-outline btn-sm" href={`https://www.dhl.de/de/privatkunden/dhl-sendungsverfolgung.html?piececode=${b.tracking}`} target="_blank" rel="noopener">{t.accountDash.trackingBtn}</a>}
|
||||
<a class="btn btn-outline btn-sm" href="#">{t.accountDash.invoiceBtn}</a>
|
||||
<a class="btn btn-outline btn-sm" href="/en/shop/">{t.accountDash.reorderBtn}</a>
|
||||
</div>
|
||||
</div>
|
||||
{b.tracking && (
|
||||
<p class="small order-tracking-no">{t.accountDash.trackingLabel}: <code>{b.tracking}</code></p>
|
||||
)}
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="section-tight">
|
||||
<div class="container">
|
||||
<h2>{t.accountDash.wishlistTitle}</h2>
|
||||
<div class="card">
|
||||
<p class="small" style="margin:0;">{t.accountDash.wishlistEmpty}</p>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{zuletztAngesehen.length > 0 && (
|
||||
<section class="section-tight">
|
||||
<div class="container">
|
||||
<h2>{t.accountDash.recentTitle}</h2>
|
||||
<div class="grid grid-3">{zuletztAngesehen.map((p) => <ProductCard product={p} lang={lang} />)}</div>
|
||||
</div>
|
||||
</section>
|
||||
)}
|
||||
|
||||
<section class="section-tight">
|
||||
<div class="container">
|
||||
<h2>{t.accountDash.profileTitle}</h2>
|
||||
<div class="card profile-card">
|
||||
<table class="specs">
|
||||
<tbody>
|
||||
<tr><th>{t.accountDash.profileName}</th><td>Marie Beispiel</td></tr>
|
||||
<tr><th>{t.accountDash.profileEmail}</th><td>[email protected]</td></tr>
|
||||
<tr><th>{t.accountDash.profileAddress}</th><td>Musterstraße 1<br />10115 Berlin<br />Deutschland</td></tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<a class="btn btn-outline btn-sm" href="#">{t.accountDash.profileEdit}</a>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</Layout>
|
||||
@@ -31,5 +31,8 @@ const t = useTranslations(lang);
|
||||
<a class="btn btn-outline btn-block" href="/en/shop/">{t.account.guestButton}</a>
|
||||
</div>
|
||||
</div>
|
||||
<div class="container" style="margin-top:1.2rem;">
|
||||
<a class="small" href="/en/konto/angemeldet/">{t.account.previewLink}</a>
|
||||
</div>
|
||||
</section>
|
||||
</Layout>
|
||||
|
||||
@@ -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;
|
||||
---
|
||||
<Layout title="About the Maker" description="Get to know VanVan – the person behind Van's DIY & Bastelbedarf." lang={lang} path="/ueber-die-kreative/">
|
||||
<section class="section-tight">
|
||||
<div class="container grid grid-2 intro">
|
||||
<div class="container grid grid-2 intro intro-story">
|
||||
<div class="portrait-frame portrait">
|
||||
<img src="/img/vanvan-portrait-holding.webp" alt="VanVan in her workshop with her handmade crochet plushies" width="900" height="1353" loading="lazy" />
|
||||
<span class="portrait-badge">{t.common.handmadeBadge}</span>
|
||||
</div>
|
||||
<div>
|
||||
<div class="intro-story-text">
|
||||
<span class="eyebrow">{t.about.eyebrow}</span>
|
||||
<h1>{t.about.title}</h1>
|
||||
<p class="lead">{t.about.lead}</p>
|
||||
<h2>{ersteStory.title}</h2>
|
||||
{ersteStory.paragraphs.map((p) => <p>{p}</p>)}
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="section-tight">
|
||||
<div class="container story">
|
||||
{t.about.story.map((s) => (
|
||||
<div class="story-section">
|
||||
<h2>{s.title}</h2>
|
||||
{s.paragraphs.map((p) => <p>{p}</p>)}
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</section>
|
||||
{weitereStories.length > 0 && (
|
||||
<section class="section-tight">
|
||||
<div class="container story">
|
||||
{weitereStories.map((s) => (
|
||||
<div class="story-section">
|
||||
<h2>{s.title}</h2>
|
||||
{s.paragraphs.map((p) => <p>{p}</p>)}
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</section>
|
||||
)}
|
||||
|
||||
<section class="section-tight">
|
||||
<div class="container">
|
||||
|
||||
@@ -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;
|
||||
---
|
||||
<Layout title="Mon compte – connecté (aperçu)" description="Aperçu de l'espace client personnel de Van's DIY & Bastelbedarf." lang={lang} path="/konto/angemeldet/">
|
||||
<section class="section-tight">
|
||||
<div class="container">
|
||||
<p class="todo-note">{t.accountDash.previewNote}</p>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="section-tight">
|
||||
<div class="container">
|
||||
<div class="account-head card">
|
||||
<div>
|
||||
<span class="eyebrow">{t.accountDash.eyebrow}</span>
|
||||
<h1>{t.accountDash.greeting}</h1>
|
||||
<p class="lead">{t.accountDash.lead}</p>
|
||||
</div>
|
||||
<a class="btn btn-outline btn-sm" href="/fr/konto/">{t.accountDash.logout}</a>
|
||||
</div>
|
||||
|
||||
<div class="account-stats">
|
||||
<div class="card account-stat">
|
||||
<span class="account-stat-value">{beispielBestellungen.length}</span>
|
||||
<span class="small">{t.accountDash.statOrders}</span>
|
||||
</div>
|
||||
<div class="card account-stat">
|
||||
<span class="account-stat-value">0</span>
|
||||
<span class="small">{t.accountDash.statWishlist}</span>
|
||||
</div>
|
||||
<div class="card account-stat">
|
||||
<span class="account-stat-value">{offeneSendungen}</span>
|
||||
<span class="small">{t.accountDash.statOpen}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="section-tight">
|
||||
<div class="container">
|
||||
<h2>{t.accountDash.ordersTitle}</h2>
|
||||
<div class="order-list">
|
||||
{beispielBestellungen.map((b) => (
|
||||
<div class="card order-card">
|
||||
<div class="order-card-head">
|
||||
<div>
|
||||
<span class="order-no">{t.accountDash.orderNo} {b.nummer}</span>
|
||||
<span class="small order-meta">{t.accountDash.orderDate} {b.datum}</span>
|
||||
</div>
|
||||
<span class={`badge order-status order-status-${b.status}`}>{statusLabel[b.status]}</span>
|
||||
</div>
|
||||
|
||||
<ul class="order-items">
|
||||
{b.artikel.map((a) => (
|
||||
<li>
|
||||
<span>{a.menge}× {a.name}</span>
|
||||
<span class="order-item-price">{formatPrice(a.preis * a.menge, lang)}</span>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
|
||||
<div class="order-card-foot">
|
||||
<span class="order-total">{t.accountDash.orderTotal}: <strong>{formatPrice(b.summe, lang)}</strong></span>
|
||||
<div class="order-actions">
|
||||
{b.tracking && <a class="btn btn-outline btn-sm" href={`https://www.dhl.de/de/privatkunden/dhl-sendungsverfolgung.html?piececode=${b.tracking}`} target="_blank" rel="noopener">{t.accountDash.trackingBtn}</a>}
|
||||
<a class="btn btn-outline btn-sm" href="#">{t.accountDash.invoiceBtn}</a>
|
||||
<a class="btn btn-outline btn-sm" href="/fr/shop/">{t.accountDash.reorderBtn}</a>
|
||||
</div>
|
||||
</div>
|
||||
{b.tracking && (
|
||||
<p class="small order-tracking-no">{t.accountDash.trackingLabel}: <code>{b.tracking}</code></p>
|
||||
)}
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="section-tight">
|
||||
<div class="container">
|
||||
<h2>{t.accountDash.wishlistTitle}</h2>
|
||||
<div class="card">
|
||||
<p class="small" style="margin:0;">{t.accountDash.wishlistEmpty}</p>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{zuletztAngesehen.length > 0 && (
|
||||
<section class="section-tight">
|
||||
<div class="container">
|
||||
<h2>{t.accountDash.recentTitle}</h2>
|
||||
<div class="grid grid-3">{zuletztAngesehen.map((p) => <ProductCard product={p} lang={lang} />)}</div>
|
||||
</div>
|
||||
</section>
|
||||
)}
|
||||
|
||||
<section class="section-tight">
|
||||
<div class="container">
|
||||
<h2>{t.accountDash.profileTitle}</h2>
|
||||
<div class="card profile-card">
|
||||
<table class="specs">
|
||||
<tbody>
|
||||
<tr><th>{t.accountDash.profileName}</th><td>Marie Beispiel</td></tr>
|
||||
<tr><th>{t.accountDash.profileEmail}</th><td>[email protected]</td></tr>
|
||||
<tr><th>{t.accountDash.profileAddress}</th><td>Musterstraße 1<br />10115 Berlin<br />Deutschland</td></tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<a class="btn btn-outline btn-sm" href="#">{t.accountDash.profileEdit}</a>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</Layout>
|
||||
@@ -31,5 +31,8 @@ const t = useTranslations(lang);
|
||||
<a class="btn btn-outline btn-block" href="/fr/shop/">{t.account.guestButton}</a>
|
||||
</div>
|
||||
</div>
|
||||
<div class="container" style="margin-top:1.2rem;">
|
||||
<a class="small" href="/fr/konto/angemeldet/">{t.account.previewLink}</a>
|
||||
</div>
|
||||
</section>
|
||||
</Layout>
|
||||
|
||||
@@ -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;
|
||||
---
|
||||
<Layout title="À propos de la créatrice" description="Découvrez VanVan – la personne derrière Van's DIY & Bastelbedarf." lang={lang} path="/ueber-die-kreative/">
|
||||
<section class="section-tight">
|
||||
<div class="container grid grid-2 intro">
|
||||
<div class="container grid grid-2 intro intro-story">
|
||||
<div class="portrait-frame portrait">
|
||||
<img src="/img/vanvan-portrait-holding.webp" alt="VanVan dans son atelier avec ses peluches au crochet faites main" width="900" height="1353" loading="lazy" />
|
||||
<span class="portrait-badge">{t.common.handmadeBadge}</span>
|
||||
</div>
|
||||
<div>
|
||||
<div class="intro-story-text">
|
||||
<span class="eyebrow">{t.about.eyebrow}</span>
|
||||
<h1>{t.about.title}</h1>
|
||||
<p class="lead">{t.about.lead}</p>
|
||||
<h2>{ersteStory.title}</h2>
|
||||
{ersteStory.paragraphs.map((p) => <p>{p}</p>)}
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="section-tight">
|
||||
<div class="container story">
|
||||
{t.about.story.map((s) => (
|
||||
<div class="story-section">
|
||||
<h2>{s.title}</h2>
|
||||
{s.paragraphs.map((p) => <p>{p}</p>)}
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</section>
|
||||
{weitereStories.length > 0 && (
|
||||
<section class="section-tight">
|
||||
<div class="container story">
|
||||
{weitereStories.map((s) => (
|
||||
<div class="story-section">
|
||||
<h2>{s.title}</h2>
|
||||
{s.paragraphs.map((p) => <p>{p}</p>)}
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</section>
|
||||
)}
|
||||
|
||||
<section class="section-tight">
|
||||
<div class="container">
|
||||
|
||||
@@ -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;
|
||||
---
|
||||
<Layout title="Mein Konto – angemeldet (Vorschau)" description="Vorschau des persönlichen Kundenbereichs von Van's DIY & Bastelbedarf." lang={lang} path="/konto/angemeldet/">
|
||||
<section class="section-tight">
|
||||
<div class="container">
|
||||
<p class="todo-note">{t.accountDash.previewNote}</p>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="section-tight">
|
||||
<div class="container">
|
||||
<div class="account-head card">
|
||||
<div>
|
||||
<span class="eyebrow">{t.accountDash.eyebrow}</span>
|
||||
<h1>{t.accountDash.greeting}</h1>
|
||||
<p class="lead">{t.accountDash.lead}</p>
|
||||
</div>
|
||||
<a class="btn btn-outline btn-sm" href="/konto/">{t.accountDash.logout}</a>
|
||||
</div>
|
||||
|
||||
<div class="account-stats">
|
||||
<div class="card account-stat">
|
||||
<span class="account-stat-value">{beispielBestellungen.length}</span>
|
||||
<span class="small">{t.accountDash.statOrders}</span>
|
||||
</div>
|
||||
<div class="card account-stat">
|
||||
<span class="account-stat-value">0</span>
|
||||
<span class="small">{t.accountDash.statWishlist}</span>
|
||||
</div>
|
||||
<div class="card account-stat">
|
||||
<span class="account-stat-value">{offeneSendungen}</span>
|
||||
<span class="small">{t.accountDash.statOpen}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="section-tight">
|
||||
<div class="container">
|
||||
<h2>{t.accountDash.ordersTitle}</h2>
|
||||
<div class="order-list">
|
||||
{beispielBestellungen.map((b) => (
|
||||
<div class="card order-card">
|
||||
<div class="order-card-head">
|
||||
<div>
|
||||
<span class="order-no">{t.accountDash.orderNo} {b.nummer}</span>
|
||||
<span class="small order-meta">{t.accountDash.orderDate} {b.datum}</span>
|
||||
</div>
|
||||
<span class={`badge order-status order-status-${b.status}`}>{statusLabel[b.status]}</span>
|
||||
</div>
|
||||
|
||||
<ul class="order-items">
|
||||
{b.artikel.map((a) => (
|
||||
<li>
|
||||
<span>{a.menge}× {a.name}</span>
|
||||
<span class="order-item-price">{formatPrice(a.preis * a.menge, lang)}</span>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
|
||||
<div class="order-card-foot">
|
||||
<span class="order-total">{t.accountDash.orderTotal}: <strong>{formatPrice(b.summe, lang)}</strong></span>
|
||||
<div class="order-actions">
|
||||
{b.tracking && <a class="btn btn-outline btn-sm" href={`https://www.dhl.de/de/privatkunden/dhl-sendungsverfolgung.html?piececode=${b.tracking}`} target="_blank" rel="noopener">{t.accountDash.trackingBtn}</a>}
|
||||
<a class="btn btn-outline btn-sm" href="#">{t.accountDash.invoiceBtn}</a>
|
||||
<a class="btn btn-outline btn-sm" href="/shop/">{t.accountDash.reorderBtn}</a>
|
||||
</div>
|
||||
</div>
|
||||
{b.tracking && (
|
||||
<p class="small order-tracking-no">{t.accountDash.trackingLabel}: <code>{b.tracking}</code></p>
|
||||
)}
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="section-tight">
|
||||
<div class="container">
|
||||
<h2>{t.accountDash.wishlistTitle}</h2>
|
||||
<div class="card">
|
||||
<p class="small" style="margin:0;">{t.accountDash.wishlistEmpty}</p>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{zuletztAngesehen.length > 0 && (
|
||||
<section class="section-tight">
|
||||
<div class="container">
|
||||
<h2>{t.accountDash.recentTitle}</h2>
|
||||
<div class="grid grid-3">{zuletztAngesehen.map((p) => <ProductCard product={p} lang={lang} />)}</div>
|
||||
</div>
|
||||
</section>
|
||||
)}
|
||||
|
||||
<section class="section-tight">
|
||||
<div class="container">
|
||||
<h2>{t.accountDash.profileTitle}</h2>
|
||||
<div class="card profile-card">
|
||||
<table class="specs">
|
||||
<tbody>
|
||||
<tr><th>{t.accountDash.profileName}</th><td>Marie Beispiel</td></tr>
|
||||
<tr><th>{t.accountDash.profileEmail}</th><td>[email protected]</td></tr>
|
||||
<tr><th>{t.accountDash.profileAddress}</th><td>Musterstraße 1<br />10115 Berlin<br />Deutschland</td></tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<a class="btn btn-outline btn-sm" href="#">{t.accountDash.profileEdit}</a>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</Layout>
|
||||
@@ -31,5 +31,10 @@ const t = useTranslations(lang);
|
||||
<a class="btn btn-outline btn-block" href="/shop/">{t.account.guestButton}</a>
|
||||
</div>
|
||||
</div>
|
||||
{/* Direkter Weg zur Vorschau des eingeloggten Kundenbereichs — solange es noch kein echtes
|
||||
Login gibt (Phase 2), wäre diese Ansicht sonst gar nicht erreichbar. */}
|
||||
<div class="container" style="margin-top:1.2rem;">
|
||||
<a class="small" href="/konto/angemeldet/">{t.account.previewLink}</a>
|
||||
</div>
|
||||
</section>
|
||||
</Layout>
|
||||
|
||||
@@ -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;
|
||||
---
|
||||
<Layout title="Über die Kreative" description="Lerne VanVan kennen – die Person hinter Van's DIY & Bastelbedarf." lang={lang} path="/ueber-die-kreative/">
|
||||
<section class="section-tight">
|
||||
<div class="container grid grid-2 intro">
|
||||
<div class="container grid grid-2 intro intro-story">
|
||||
<div class="portrait-frame portrait">
|
||||
<img src="/img/vanvan-portrait-holding.webp" alt="VanVan in ihrer Werkstatt mit selbstgehäkelten Plushies" width="900" height="1353" loading="lazy" />
|
||||
<span class="portrait-badge">{t.common.handmadeBadge}</span>
|
||||
</div>
|
||||
<div>
|
||||
<div class="intro-story-text">
|
||||
<span class="eyebrow">{t.about.eyebrow}</span>
|
||||
<h1>{t.about.title}</h1>
|
||||
<p class="lead">{t.about.lead}</p>
|
||||
<h2>{ersteStory.title}</h2>
|
||||
{ersteStory.paragraphs.map((p) => <p>{p}</p>)}
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="section-tight">
|
||||
<div class="container story">
|
||||
{t.about.story.map((s) => (
|
||||
<div class="story-section">
|
||||
<h2>{s.title}</h2>
|
||||
{s.paragraphs.map((p) => <p>{p}</p>)}
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</section>
|
||||
{weitereStories.length > 0 && (
|
||||
<section class="section-tight">
|
||||
<div class="container story">
|
||||
{weitereStories.map((s) => (
|
||||
<div class="story-section">
|
||||
<h2>{s.title}</h2>
|
||||
{s.paragraphs.map((p) => <p>{p}</p>)}
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</section>
|
||||
)}
|
||||
|
||||
<section class="section-tight">
|
||||
<div class="container">
|
||||
|
||||
+66
-6
@@ -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. */
|
||||
|
||||
Reference in New Issue
Block a user