Neue HeartRating-Komponente (Herzen statt Sterne, Lila-Babyblau-Verlauf wie der Rest der Markenoptik) + komplettes Rezensions-System: - Produktkarten: Bewertungszeile (Herzen + Anzahl) unter dem Titel, nur wenn Bewertungen vorhanden sind. - Produktseite: volle "Kundenrezensionen"-Sektion mit Durchschnitt und einzelnen Rezensionen (Name, Herzen, Datum, Text) bzw. "Noch keine Bewertungen"-Hinweis. - Shop-Filter: neuer "Mindestbewertung"-Filter im Filter-Sidebar + neue Sortierung "Beste Bewertung". - Startseite: "Was unsere Kund:innen sagen"-Sektion mit Gesamtbewertung und den 3 neuesten Rezensionen - erscheint automatisch, sobald die erste echte Bewertung existiert. - Adminbereich: neues Feld "Bewertungen" pro Produkt (Name/Herzen/Text/Datum), mit deutlichem Hinweis, dass hier nur echte Kundenrueckmeldungen eingetragen werden duerfen - erfundene Bewertungen sind in Deutschland/der EU gesetzlich verboten (UWG). Deshalb bewusst OHNE Beispiel-Bewertungen im Code: alle Bereiche starten leer und blenden sich automatisch ein, sobald VanVan die erste echte Rezension eintraegt. Mit temporaeren Testdaten end-to-end durchgetestet (Herzen-Darstellung, Rezensions-Section, Filter, Startseiten-Sektion) und vor dem Commit wieder aus den echten Produktdaten entfernt. Gilt fuer alle 4 Sprachen. Co-Authored-By: Claude Sonnet 5 <[email protected]>
132 lines
5.8 KiB
Plaintext
132 lines
5.8 KiB
Plaintext
---
|
||
import Layout from "../../layouts/Layout.astro";
|
||
import ProductCard from "../../components/ProductCard.astro";
|
||
import HeartRating from "../../components/HeartRating.astro";
|
||
import { categories } from "../../data/categories";
|
||
import { products, alleBewertungen, gesamtDurchschnitt } from "../../data/products";
|
||
import { useTranslations } from "../../i18n/ui";
|
||
import type { Locale } from "../../i18n/config";
|
||
|
||
const lang: Locale = "fr";
|
||
const t = useTranslations(lang);
|
||
|
||
const neuheiten = products.filter((p) => p.badges.includes("neu")).slice(0, 4);
|
||
const bestseller = products.filter((p) => p.badges.includes("bestseller")).slice(0, 4);
|
||
const sale = products.filter((p) => p.badges.includes("sale") || p.preisAlt).slice(0, 4);
|
||
const topBewertungen = alleBewertungen().slice(0, 3);
|
||
const bewertungSchnittGesamt = gesamtDurchschnitt();
|
||
---
|
||
<Layout title="Accueil" lang={lang} path="/">
|
||
<section class="hero">
|
||
<img src="/logo.png" alt="" aria-hidden="true" class="hero-watermark" />
|
||
<div class="container hero-content">
|
||
<div class="hero-gallery">
|
||
<div class="hero-photo hero-photo-1"><img src="/img/hero-plushie-bunny.webp" alt="Peluche lapin au crochet en salopette bleue, dans l'herbe" loading="eager" width="900" height="1200" /></div>
|
||
<div class="hero-photo hero-photo-2"><img src="/img/hero-plushie-turtle.webp" alt="Peluche tortue au crochet en bleu clair et jaune" loading="eager" width="900" height="1200" /></div>
|
||
<div class="hero-photo hero-photo-3"><img src="/img/hero-plushie-octopus.webp" alt="Peluche poulpe au crochet en rose" loading="eager" width="900" height="1200" /></div>
|
||
<div class="hero-photo hero-photo-4"><img src="/img/hero-plushie-penguin.webp" alt="Peluche pingouin au crochet en bleu clair et blanc" loading="eager" width="900" height="1200" /></div>
|
||
</div>
|
||
<div class="hero-inner">
|
||
<span class="eyebrow">🩵 Van's DIY & Bastelbedarf</span>
|
||
<h1>Fait main avec amour – <br />de la créativité à offrir<br />ou à garder.</h1>
|
||
<p class="lead">Crochet, peluches, bijoux et fournitures créatives – chaque pièce façonnée à la main, avec cœur et patience.</p>
|
||
<div class="btn-row">
|
||
<a class="btn btn-primary" href="/fr/shop/">Découvrir la boutique</a>
|
||
<a class="btn btn-outline" href="/fr/ueber-die-kreative/">À propos de la créatrice</a>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</section>
|
||
|
||
<section class="section-tight">
|
||
<div class="container">
|
||
<h2 class="text-center">Découvrir les catégories</h2>
|
||
<div class="grid grid-3" style="margin-top:1.5rem;">
|
||
{categories.map((c) => (
|
||
<a class="card category-card" href={`/fr/shop/${c.slug}/`}>
|
||
<span class="cat-icon" aria-hidden="true">{c.icon}</span>
|
||
<h3>{c.name[lang]}</h3>
|
||
<p class="small">{c.description[lang]}</p>
|
||
</a>
|
||
))}
|
||
</div>
|
||
</div>
|
||
</section>
|
||
|
||
{neuheiten.length > 0 && (
|
||
<section class="section-tight">
|
||
<div class="container">
|
||
<div class="section-head">
|
||
<h2>Nouveautés</h2>
|
||
<a class="small" href="/fr/shop/">Tout voir →</a>
|
||
</div>
|
||
<div class="grid grid-4">{neuheiten.map((p) => <ProductCard product={p} lang={lang} />)}</div>
|
||
</div>
|
||
</section>
|
||
)}
|
||
|
||
{bestseller.length > 0 && (
|
||
<section class="section-tight">
|
||
<div class="container">
|
||
<div class="section-head">
|
||
<h2>Best-sellers</h2>
|
||
<a class="small" href="/fr/shop/">Tout voir →</a>
|
||
</div>
|
||
<div class="grid grid-4">{bestseller.map((p) => <ProductCard product={p} lang={lang} />)}</div>
|
||
</div>
|
||
</section>
|
||
)}
|
||
|
||
{sale.length > 0 && (
|
||
<section class="section-tight">
|
||
<div class="container">
|
||
<div class="section-head">
|
||
<h2>Soldes %</h2>
|
||
<a class="small" href="/fr/sale/">Toutes les offres →</a>
|
||
</div>
|
||
<div class="grid grid-4">{sale.map((p) => <ProductCard product={p} lang={lang} />)}</div>
|
||
</div>
|
||
</section>
|
||
)}
|
||
|
||
{topBewertungen.length > 0 && bewertungSchnittGesamt !== null && (
|
||
<section class="section-tight">
|
||
<div class="container">
|
||
<span class="eyebrow">🩵 {t.reviews.heading}</span>
|
||
<h2>{t.reviews.heading}</h2>
|
||
<p class="lead">{t.reviews.lead}</p>
|
||
<div class="review-summary-row">
|
||
<HeartRating bewertung={bewertungSchnittGesamt} size={24} showValue={true} anzahlLabel={t.reviews.basedOn(alleBewertungen().length)} />
|
||
</div>
|
||
<div class="grid grid-3">
|
||
{topBewertungen.map((r) => (
|
||
<div class="card review-card">
|
||
<div class="review-card-head">
|
||
<span class="review-name">{r.name}</span>
|
||
<HeartRating bewertung={r.bewertung} size={14} />
|
||
</div>
|
||
{r.text && <p class="small">{r.text[lang]}</p>}
|
||
<a class="small" href={`/fr/produkt/${r.produktSlug}/`}>{t.reviews.toProduct} {r.produktName[lang]}</a>
|
||
</div>
|
||
))}
|
||
</div>
|
||
</div>
|
||
</section>
|
||
)}
|
||
|
||
<section class="section">
|
||
<div class="container grid grid-2 creator-section">
|
||
<div class="portrait-frame creator-photo">
|
||
<img src="/img/vanvan-portrait.png" alt="Photo de VanVan" width="640" height="800" loading="lazy" />
|
||
<span class="portrait-badge">🩵 Fait main</span>
|
||
</div>
|
||
<div>
|
||
<span class="eyebrow">La créatrice derrière la boutique</span>
|
||
<h2>Bonjour, je suis VanVan 🩵</h2>
|
||
<blockquote class="pull-quote">« Chaque maille raconte sa propre histoire – car le véritable artisanat ne se crée pas seulement avec de la laine, mais surtout avec beaucoup de cœur. »</blockquote>
|
||
<a class="btn btn-outline" href="/fr/ueber-die-kreative/">En savoir plus sur moi →</a>
|
||
</div>
|
||
</div>
|
||
</section>
|
||
</Layout>
|