Tier-Familien-Seiten: nur noch Charaktere mit echtem Admin-Produkt sichtbar
Wichtige Verhaltensänderung: ein Charakter (aus dem Kategorien-Bereich im Admin) ist ab jetzt nur noch eine VORBEREITETE Definition. Er erscheint auf der Seite NUR, wenn ihm im Admin tatsächlich ein Produkt mit genau diesem Charakter-Code zugeordnet wurde (das Feld "Charakter" am Produkt = seine "Rolle"). Ohne zugeordnetes Produkt taucht er nirgends mehr auf — auch nicht mehr als Platzhalter-Karte mit seinem Namen. Konkret: HasiGott und Mama Hase verschwinden von der Hase-Seite, weil für sie (noch) keine Produkte im Admin angelegt wurden. Sobald VanVan ein Produkt mit charakter: hasigott anlegt, erscheint er automatisch. Familien ganz ohne Produkte (Kuh, Wal, Oktopus, Schildkröte, Pinguin) zeigen weiterhin die bereits vorbereiteten, namenlosen "Kommt bald"- Karten (unverändert) — nur die NAMENTLICHE Vorab-Anzeige einzelner Charaktere ohne Produkt entfällt. Zusätzlich: der "Mehr über mich"-Button in der Story-Box hatte keinen erklärenden Text — man wusste nicht, wohin/wozu er führt. Jetzt steht davor "Wenn du mehr über mich erfahren möchtest, dann klicke [hier →]" in allen 4 Sprachen. Co-Authored-By: Claude Sonnet 5 <[email protected]>
This commit is contained in:
co-authored by
Claude Sonnet 5
parent
b28475c26a
commit
8700e2c588
@@ -45,6 +45,7 @@ export const ui = {
|
|||||||
resultCount: (n: number) => `${n} Produkt${n === 1 ? "" : "e"}`, noResults: "Keine Produkte gefunden – bitte Filter anpassen.",
|
resultCount: (n: number) => `${n} Produkt${n === 1 ? "" : "e"}`, noResults: "Keine Produkte gefunden – bitte Filter anpassen.",
|
||||||
breadcrumbShop: "Shop", categoryEmpty: "In dieser Kategorie sind aktuell noch keine Produkte hinterlegt – schau bald wieder vorbei.",
|
breadcrumbShop: "Shop", categoryEmpty: "In dieser Kategorie sind aktuell noch keine Produkte hinterlegt – schau bald wieder vorbei.",
|
||||||
categoryLabel: "Kategorie", productSingular: "Produkt", productPlural: "Produkte", sortLabel: "Sortierung", subcategoriesLabel: "Unterkategorien", mainCategoryLabel: "Hauptkategorie", subcategoryEmpty: "Für diesen Bereich stellt VanVan bald die ersten Produkte ein – schau bald wieder vorbei.", moreAbout: "Mehr über", moreVariants: "Weitere Modelle",
|
categoryLabel: "Kategorie", productSingular: "Produkt", productPlural: "Produkte", sortLabel: "Sortierung", subcategoriesLabel: "Unterkategorien", mainCategoryLabel: "Hauptkategorie", subcategoryEmpty: "Für diesen Bereich stellt VanVan bald die ersten Produkte ein – schau bald wieder vorbei.", moreAbout: "Mehr über", moreVariants: "Weitere Modelle",
|
||||||
|
moreAboutCharacterLead: "Wenn du mehr über mich erfahren möchtest, dann klicke",
|
||||||
},
|
},
|
||||||
sale: {
|
sale: {
|
||||||
eyebrow: "% Sale", title: "Aktuelle Angebote", lead: "Zeitlich begrenzte Rabatte auf ausgewählte handgemachte Stücke.",
|
eyebrow: "% Sale", title: "Aktuelle Angebote", lead: "Zeitlich begrenzte Rabatte auf ausgewählte handgemachte Stücke.",
|
||||||
@@ -233,6 +234,7 @@ export const ui = {
|
|||||||
resultCount: (n: number) => `${n} product${n === 1 ? "" : "s"}`, noResults: "No products found – please adjust your filters.",
|
resultCount: (n: number) => `${n} product${n === 1 ? "" : "s"}`, noResults: "No products found – please adjust your filters.",
|
||||||
breadcrumbShop: "Shop", categoryEmpty: "No products in this category yet – check back soon.",
|
breadcrumbShop: "Shop", categoryEmpty: "No products in this category yet – check back soon.",
|
||||||
categoryLabel: "Category", productSingular: "product", productPlural: "products", sortLabel: "Sort by", subcategoriesLabel: "Subcategories", mainCategoryLabel: "Main category", subcategoryEmpty: "VanVan will be adding the first products here soon – check back shortly.", moreAbout: "More about", moreVariants: "More designs",
|
categoryLabel: "Category", productSingular: "product", productPlural: "products", sortLabel: "Sort by", subcategoriesLabel: "Subcategories", mainCategoryLabel: "Main category", subcategoryEmpty: "VanVan will be adding the first products here soon – check back shortly.", moreAbout: "More about", moreVariants: "More designs",
|
||||||
|
moreAboutCharacterLead: "If you'd like to learn more about me, click",
|
||||||
},
|
},
|
||||||
sale: {
|
sale: {
|
||||||
eyebrow: "% Sale", title: "Current offers", lead: "Time-limited discounts on selected handmade pieces.",
|
eyebrow: "% Sale", title: "Current offers", lead: "Time-limited discounts on selected handmade pieces.",
|
||||||
@@ -421,6 +423,7 @@ export const ui = {
|
|||||||
resultCount: (n: number) => `${n} Produkt`, noResults: "Kei Produkt gfunde – bitte Filter apasse.",
|
resultCount: (n: number) => `${n} Produkt`, noResults: "Kei Produkt gfunde – bitte Filter apasse.",
|
||||||
breadcrumbShop: "Shop", categoryEmpty: "In däre Kategorie sind grad no kei Produkt hinterlegt – lueg spöter nomal verbi.",
|
breadcrumbShop: "Shop", categoryEmpty: "In däre Kategorie sind grad no kei Produkt hinterlegt – lueg spöter nomal verbi.",
|
||||||
categoryLabel: "Kategorie", productSingular: "Produkt", productPlural: "Produkt", sortLabel: "Sortiere", subcategoriesLabel: "Unterkategorie", mainCategoryLabel: "Hauptkategorie", subcategoryEmpty: "Für das da stellt VanVan bald die erschte Produkt i – lueg bald wieder verbi.", moreAbout: "Meh über", moreVariants: "Witeri Modell",
|
categoryLabel: "Kategorie", productSingular: "Produkt", productPlural: "Produkt", sortLabel: "Sortiere", subcategoriesLabel: "Unterkategorie", mainCategoryLabel: "Hauptkategorie", subcategoryEmpty: "Für das da stellt VanVan bald die erschte Produkt i – lueg bald wieder verbi.", moreAbout: "Meh über", moreVariants: "Witeri Modell",
|
||||||
|
moreAboutCharacterLead: "Wenn du meh über mich erfahre witsch, dänn klick",
|
||||||
},
|
},
|
||||||
sale: {
|
sale: {
|
||||||
eyebrow: "% Aktion", title: "Aktuelli Aktion", lead: "Zytlich begrenzti Rabatt uf uswählti, vo Hang gmachti Sächeli.",
|
eyebrow: "% Aktion", title: "Aktuelli Aktion", lead: "Zytlich begrenzti Rabatt uf uswählti, vo Hang gmachti Sächeli.",
|
||||||
@@ -609,6 +612,7 @@ export const ui = {
|
|||||||
resultCount: (n: number) => `${n} produit${n === 1 ? "" : "s"}`, noResults: "Aucun produit trouvé – veuillez ajuster les filtres.",
|
resultCount: (n: number) => `${n} produit${n === 1 ? "" : "s"}`, noResults: "Aucun produit trouvé – veuillez ajuster les filtres.",
|
||||||
breadcrumbShop: "Boutique", categoryEmpty: "Aucun produit dans cette catégorie pour le moment – repassez bientôt.",
|
breadcrumbShop: "Boutique", categoryEmpty: "Aucun produit dans cette catégorie pour le moment – repassez bientôt.",
|
||||||
categoryLabel: "Catégorie", productSingular: "produit", productPlural: "produits", sortLabel: "Trier par", subcategoriesLabel: "Sous-catégories", mainCategoryLabel: "Catégorie principale", subcategoryEmpty: "VanVan ajoutera bientôt les premiers produits ici – repassez bientôt.", moreAbout: "En savoir plus sur", moreVariants: "Autres modèles",
|
categoryLabel: "Catégorie", productSingular: "produit", productPlural: "produits", sortLabel: "Trier par", subcategoriesLabel: "Sous-catégories", mainCategoryLabel: "Catégorie principale", subcategoryEmpty: "VanVan ajoutera bientôt les premiers produits ici – repassez bientôt.", moreAbout: "En savoir plus sur", moreVariants: "Autres modèles",
|
||||||
|
moreAboutCharacterLead: "Si tu veux en savoir plus sur moi, clique",
|
||||||
},
|
},
|
||||||
sale: {
|
sale: {
|
||||||
eyebrow: "% Soldes", title: "Offres actuelles", lead: "Réductions limitées dans le temps sur une sélection de pièces faites main.",
|
eyebrow: "% Soldes", title: "Offres actuelles", lead: "Réductions limitées dans le temps sur une sélection de pièces faites main.",
|
||||||
|
|||||||
@@ -21,19 +21,18 @@ const sub = getSubcategory(kategorie!, unterkategorie!)!;
|
|||||||
// Alle Produkte dieser Unterkategorie (unabhängig davon, welchem Charakter sie zugeordnet sind).
|
// Alle Produkte dieser Unterkategorie (unabhängig davon, welchem Charakter sie zugeordnet sind).
|
||||||
const alleProdukte = productsByUnterkategorie(category.slug, sub.slug);
|
const alleProdukte = productsByUnterkategorie(category.slug, sub.slug);
|
||||||
|
|
||||||
// Jeder Charakter dieser Tier-Familie bekommt seine eigene kompakte Karte (wie die Produktkarten
|
// Ein Charakter (aus dem Kategorien-Bereich im Admin) ist erstmal nur eine VORBEREITETE
|
||||||
// im Shop-Grid), alle nebeneinander in einer Reihe. Sobald VanVan im Adminbereich neue
|
// Definition — er erscheint auf der Seite NUR, wenn ihm im Admin auch wirklich ein Produkt mit
|
||||||
// Charaktere UND passende Produkte ergänzt, wächst diese Reihe automatisch mit. Fehlt einem
|
// genau diesem Charakter-Code zugeordnet wurde (das Feld "Charakter" am Produkt = seine "Rolle").
|
||||||
// Charakter noch ein echtes Produkt, zeigt seine Karte trotzdem schon den Charaktertext (nur ohne
|
// Ohne zugeordnetes Produkt taucht er hier NICHT auf, auch nicht als Platzhalter — nur echte,
|
||||||
// Foto) — die Seite muss nicht auf das erste Foto warten, um zu existieren.
|
// im Admin angelegte Artikel dürfen auf der Seite erscheinen.
|
||||||
const characters = sub.characters ?? [];
|
const characters = sub.characters ?? [];
|
||||||
const characterEntries = characters.map((char) => ({
|
const characterEntries = characters
|
||||||
char,
|
.map((char) => ({ char, product: alleProdukte.find((p) => p.charakter === char.slug) }))
|
||||||
product: alleProdukte.find((p) => p.charakter === char.slug),
|
.filter((e) => e.product);
|
||||||
}));
|
|
||||||
|
|
||||||
// Produkte ohne (bekannte) Charakter-Zuordnung landen als "weitere Modelle" darunter.
|
// Produkte ohne (bekannte) Charakter-Zuordnung landen als "weitere Modelle" darunter.
|
||||||
const zugeordneteSlugs = new Set(characterEntries.map((e) => e.product?.slug).filter(Boolean));
|
const zugeordneteSlugs = new Set(characterEntries.map((e) => e.product!.slug));
|
||||||
const weitere = alleProdukte.filter((p) => !zugeordneteSlugs.has(p.slug));
|
const weitere = alleProdukte.filter((p) => !zugeordneteSlugs.has(p.slug));
|
||||||
const hatCharaktere = characterEntries.length > 0;
|
const hatCharaktere = characterEntries.length > 0;
|
||||||
const hatWeitere = weitere.length > 0;
|
const hatWeitere = weitere.length > 0;
|
||||||
@@ -55,34 +54,23 @@ const komplettLeer = !hatCharaktere && !hatWeitere;
|
|||||||
{hatCharaktere && (
|
{hatCharaktere && (
|
||||||
<section class="section-tight">
|
<section class="section-tight">
|
||||||
<div class="container">
|
<div class="container">
|
||||||
{characterEntries.some(({ product }) => product) && (
|
|
||||||
<div class="character-stories">
|
<div class="character-stories">
|
||||||
{characterEntries.filter(({ product }) => product).map(({ char }) => (
|
{characterEntries.map(({ char }) => (
|
||||||
<div class="card character-story">
|
<div class="card character-story">
|
||||||
{char.vorstellung && <span class="eyebrow">{char.vorstellung[lang].split("\n")[0]}</span>}
|
{char.vorstellung && <span class="eyebrow">{char.vorstellung[lang].split("\n")[0]}</span>}
|
||||||
<p>{char.vorstellung ? char.vorstellung[lang].split("\n")[1] : ""}</p>
|
<p>{char.vorstellung ? char.vorstellung[lang].split("\n")[1] : ""}</p>
|
||||||
{char.mehrLink && (
|
{char.mehrLink && (
|
||||||
|
<p class="character-more-lead">
|
||||||
|
{t.shop.moreAboutCharacterLead}{" "}
|
||||||
<a class="btn btn-outline btn-sm" href={char.mehrLink} target="_blank" rel="noopener">da →</a>
|
<a class="btn btn-outline btn-sm" href={char.mehrLink} target="_blank" rel="noopener">da →</a>
|
||||||
|
</p>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
)}
|
|
||||||
|
|
||||||
<div class="grid grid-3">
|
<div class="grid grid-3">
|
||||||
{characterEntries.map(({ char, product }) =>
|
{characterEntries.map(({ product }) => <ProductCard product={product!} lang={lang} />)}
|
||||||
product ? (
|
|
||||||
<ProductCard product={product} lang={lang} />
|
|
||||||
) : (
|
|
||||||
<div class="card character-card">
|
|
||||||
<div class="product-media">
|
|
||||||
<div class="img-placeholder" role="img" aria-label={char.name[lang]}></div>
|
|
||||||
</div>
|
|
||||||
<h3>{char.name[lang]}</h3>
|
|
||||||
<p class="character-card-desc">{t.shop.subcategoryEmpty}</p>
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
)}
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
|
|||||||
@@ -21,19 +21,18 @@ const sub = getSubcategory(kategorie!, unterkategorie!)!;
|
|||||||
// Alle Produkte dieser Unterkategorie (unabhängig davon, welchem Charakter sie zugeordnet sind).
|
// Alle Produkte dieser Unterkategorie (unabhängig davon, welchem Charakter sie zugeordnet sind).
|
||||||
const alleProdukte = productsByUnterkategorie(category.slug, sub.slug);
|
const alleProdukte = productsByUnterkategorie(category.slug, sub.slug);
|
||||||
|
|
||||||
// Jeder Charakter dieser Tier-Familie bekommt seine eigene kompakte Karte (wie die Produktkarten
|
// Ein Charakter (aus dem Kategorien-Bereich im Admin) ist erstmal nur eine VORBEREITETE
|
||||||
// im Shop-Grid), alle nebeneinander in einer Reihe. Sobald VanVan im Adminbereich neue
|
// Definition — er erscheint auf der Seite NUR, wenn ihm im Admin auch wirklich ein Produkt mit
|
||||||
// Charaktere UND passende Produkte ergänzt, wächst diese Reihe automatisch mit. Fehlt einem
|
// genau diesem Charakter-Code zugeordnet wurde (das Feld "Charakter" am Produkt = seine "Rolle").
|
||||||
// Charakter noch ein echtes Produkt, zeigt seine Karte trotzdem schon den Charaktertext (nur ohne
|
// Ohne zugeordnetes Produkt taucht er hier NICHT auf, auch nicht als Platzhalter — nur echte,
|
||||||
// Foto) — die Seite muss nicht auf das erste Foto warten, um zu existieren.
|
// im Admin angelegte Artikel dürfen auf der Seite erscheinen.
|
||||||
const characters = sub.characters ?? [];
|
const characters = sub.characters ?? [];
|
||||||
const characterEntries = characters.map((char) => ({
|
const characterEntries = characters
|
||||||
char,
|
.map((char) => ({ char, product: alleProdukte.find((p) => p.charakter === char.slug) }))
|
||||||
product: alleProdukte.find((p) => p.charakter === char.slug),
|
.filter((e) => e.product);
|
||||||
}));
|
|
||||||
|
|
||||||
// Produkte ohne (bekannte) Charakter-Zuordnung landen als "weitere Modelle" darunter.
|
// Produkte ohne (bekannte) Charakter-Zuordnung landen als "weitere Modelle" darunter.
|
||||||
const zugeordneteSlugs = new Set(characterEntries.map((e) => e.product?.slug).filter(Boolean));
|
const zugeordneteSlugs = new Set(characterEntries.map((e) => e.product!.slug));
|
||||||
const weitere = alleProdukte.filter((p) => !zugeordneteSlugs.has(p.slug));
|
const weitere = alleProdukte.filter((p) => !zugeordneteSlugs.has(p.slug));
|
||||||
const hatCharaktere = characterEntries.length > 0;
|
const hatCharaktere = characterEntries.length > 0;
|
||||||
const hatWeitere = weitere.length > 0;
|
const hatWeitere = weitere.length > 0;
|
||||||
@@ -55,34 +54,23 @@ const komplettLeer = !hatCharaktere && !hatWeitere;
|
|||||||
{hatCharaktere && (
|
{hatCharaktere && (
|
||||||
<section class="section-tight">
|
<section class="section-tight">
|
||||||
<div class="container">
|
<div class="container">
|
||||||
{characterEntries.some(({ product }) => product) && (
|
|
||||||
<div class="character-stories">
|
<div class="character-stories">
|
||||||
{characterEntries.filter(({ product }) => product).map(({ char }) => (
|
{characterEntries.map(({ char }) => (
|
||||||
<div class="card character-story">
|
<div class="card character-story">
|
||||||
{char.vorstellung && <span class="eyebrow">{char.vorstellung[lang].split("\n")[0]}</span>}
|
{char.vorstellung && <span class="eyebrow">{char.vorstellung[lang].split("\n")[0]}</span>}
|
||||||
<p>{char.vorstellung ? char.vorstellung[lang].split("\n")[1] : ""}</p>
|
<p>{char.vorstellung ? char.vorstellung[lang].split("\n")[1] : ""}</p>
|
||||||
{char.mehrLink && (
|
{char.mehrLink && (
|
||||||
|
<p class="character-more-lead">
|
||||||
|
{t.shop.moreAboutCharacterLead}{" "}
|
||||||
<a class="btn btn-outline btn-sm" href={char.mehrLink} target="_blank" rel="noopener">here →</a>
|
<a class="btn btn-outline btn-sm" href={char.mehrLink} target="_blank" rel="noopener">here →</a>
|
||||||
|
</p>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
)}
|
|
||||||
|
|
||||||
<div class="grid grid-3">
|
<div class="grid grid-3">
|
||||||
{characterEntries.map(({ char, product }) =>
|
{characterEntries.map(({ product }) => <ProductCard product={product!} lang={lang} />)}
|
||||||
product ? (
|
|
||||||
<ProductCard product={product} lang={lang} />
|
|
||||||
) : (
|
|
||||||
<div class="card character-card">
|
|
||||||
<div class="product-media">
|
|
||||||
<div class="img-placeholder" role="img" aria-label={char.name[lang]}></div>
|
|
||||||
</div>
|
|
||||||
<h3>{char.name[lang]}</h3>
|
|
||||||
<p class="character-card-desc">{t.shop.subcategoryEmpty}</p>
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
)}
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
|
|||||||
@@ -21,19 +21,18 @@ const sub = getSubcategory(kategorie!, unterkategorie!)!;
|
|||||||
// Alle Produkte dieser Unterkategorie (unabhängig davon, welchem Charakter sie zugeordnet sind).
|
// Alle Produkte dieser Unterkategorie (unabhängig davon, welchem Charakter sie zugeordnet sind).
|
||||||
const alleProdukte = productsByUnterkategorie(category.slug, sub.slug);
|
const alleProdukte = productsByUnterkategorie(category.slug, sub.slug);
|
||||||
|
|
||||||
// Jeder Charakter dieser Tier-Familie bekommt seine eigene kompakte Karte (wie die Produktkarten
|
// Ein Charakter (aus dem Kategorien-Bereich im Admin) ist erstmal nur eine VORBEREITETE
|
||||||
// im Shop-Grid), alle nebeneinander in einer Reihe. Sobald VanVan im Adminbereich neue
|
// Definition — er erscheint auf der Seite NUR, wenn ihm im Admin auch wirklich ein Produkt mit
|
||||||
// Charaktere UND passende Produkte ergänzt, wächst diese Reihe automatisch mit. Fehlt einem
|
// genau diesem Charakter-Code zugeordnet wurde (das Feld "Charakter" am Produkt = seine "Rolle").
|
||||||
// Charakter noch ein echtes Produkt, zeigt seine Karte trotzdem schon den Charaktertext (nur ohne
|
// Ohne zugeordnetes Produkt taucht er hier NICHT auf, auch nicht als Platzhalter — nur echte,
|
||||||
// Foto) — die Seite muss nicht auf das erste Foto warten, um zu existieren.
|
// im Admin angelegte Artikel dürfen auf der Seite erscheinen.
|
||||||
const characters = sub.characters ?? [];
|
const characters = sub.characters ?? [];
|
||||||
const characterEntries = characters.map((char) => ({
|
const characterEntries = characters
|
||||||
char,
|
.map((char) => ({ char, product: alleProdukte.find((p) => p.charakter === char.slug) }))
|
||||||
product: alleProdukte.find((p) => p.charakter === char.slug),
|
.filter((e) => e.product);
|
||||||
}));
|
|
||||||
|
|
||||||
// Produkte ohne (bekannte) Charakter-Zuordnung landen als "weitere Modelle" darunter.
|
// Produkte ohne (bekannte) Charakter-Zuordnung landen als "weitere Modelle" darunter.
|
||||||
const zugeordneteSlugs = new Set(characterEntries.map((e) => e.product?.slug).filter(Boolean));
|
const zugeordneteSlugs = new Set(characterEntries.map((e) => e.product!.slug));
|
||||||
const weitere = alleProdukte.filter((p) => !zugeordneteSlugs.has(p.slug));
|
const weitere = alleProdukte.filter((p) => !zugeordneteSlugs.has(p.slug));
|
||||||
const hatCharaktere = characterEntries.length > 0;
|
const hatCharaktere = characterEntries.length > 0;
|
||||||
const hatWeitere = weitere.length > 0;
|
const hatWeitere = weitere.length > 0;
|
||||||
@@ -55,34 +54,23 @@ const komplettLeer = !hatCharaktere && !hatWeitere;
|
|||||||
{hatCharaktere && (
|
{hatCharaktere && (
|
||||||
<section class="section-tight">
|
<section class="section-tight">
|
||||||
<div class="container">
|
<div class="container">
|
||||||
{characterEntries.some(({ product }) => product) && (
|
|
||||||
<div class="character-stories">
|
<div class="character-stories">
|
||||||
{characterEntries.filter(({ product }) => product).map(({ char }) => (
|
{characterEntries.map(({ char }) => (
|
||||||
<div class="card character-story">
|
<div class="card character-story">
|
||||||
{char.vorstellung && <span class="eyebrow">{char.vorstellung[lang].split("\n")[0]}</span>}
|
{char.vorstellung && <span class="eyebrow">{char.vorstellung[lang].split("\n")[0]}</span>}
|
||||||
<p>{char.vorstellung ? char.vorstellung[lang].split("\n")[1] : ""}</p>
|
<p>{char.vorstellung ? char.vorstellung[lang].split("\n")[1] : ""}</p>
|
||||||
{char.mehrLink && (
|
{char.mehrLink && (
|
||||||
|
<p class="character-more-lead">
|
||||||
|
{t.shop.moreAboutCharacterLead}{" "}
|
||||||
<a class="btn btn-outline btn-sm" href={char.mehrLink} target="_blank" rel="noopener">ici →</a>
|
<a class="btn btn-outline btn-sm" href={char.mehrLink} target="_blank" rel="noopener">ici →</a>
|
||||||
|
</p>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
)}
|
|
||||||
|
|
||||||
<div class="grid grid-3">
|
<div class="grid grid-3">
|
||||||
{characterEntries.map(({ char, product }) =>
|
{characterEntries.map(({ product }) => <ProductCard product={product!} lang={lang} />)}
|
||||||
product ? (
|
|
||||||
<ProductCard product={product} lang={lang} />
|
|
||||||
) : (
|
|
||||||
<div class="card character-card">
|
|
||||||
<div class="product-media">
|
|
||||||
<div class="img-placeholder" role="img" aria-label={char.name[lang]}></div>
|
|
||||||
</div>
|
|
||||||
<h3>{char.name[lang]}</h3>
|
|
||||||
<p class="character-card-desc">{t.shop.subcategoryEmpty}</p>
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
)}
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
|
|||||||
@@ -21,19 +21,18 @@ const sub = getSubcategory(kategorie!, unterkategorie!)!;
|
|||||||
// Alle Produkte dieser Unterkategorie (unabhängig davon, welchem Charakter sie zugeordnet sind).
|
// Alle Produkte dieser Unterkategorie (unabhängig davon, welchem Charakter sie zugeordnet sind).
|
||||||
const alleProdukte = productsByUnterkategorie(category.slug, sub.slug);
|
const alleProdukte = productsByUnterkategorie(category.slug, sub.slug);
|
||||||
|
|
||||||
// Jeder Charakter dieser Tier-Familie bekommt seine eigene kompakte Karte (wie die Produktkarten
|
// Ein Charakter (aus dem Kategorien-Bereich im Admin) ist erstmal nur eine VORBEREITETE
|
||||||
// im Shop-Grid), alle nebeneinander in einer Reihe. Sobald VanVan im Adminbereich neue
|
// Definition — er erscheint auf der Seite NUR, wenn ihm im Admin auch wirklich ein Produkt mit
|
||||||
// Charaktere UND passende Produkte ergänzt, wächst diese Reihe automatisch mit. Fehlt einem
|
// genau diesem Charakter-Code zugeordnet wurde (das Feld "Charakter" am Produkt = seine "Rolle").
|
||||||
// Charakter noch ein echtes Produkt, zeigt seine Karte trotzdem schon den Charaktertext (nur ohne
|
// Ohne zugeordnetes Produkt taucht er hier NICHT auf, auch nicht als Platzhalter — nur echte,
|
||||||
// Foto) — die Seite muss nicht auf das erste Foto warten, um zu existieren.
|
// im Admin angelegte Artikel dürfen auf der Seite erscheinen.
|
||||||
const characters = sub.characters ?? [];
|
const characters = sub.characters ?? [];
|
||||||
const characterEntries = characters.map((char) => ({
|
const characterEntries = characters
|
||||||
char,
|
.map((char) => ({ char, product: alleProdukte.find((p) => p.charakter === char.slug) }))
|
||||||
product: alleProdukte.find((p) => p.charakter === char.slug),
|
.filter((e) => e.product);
|
||||||
}));
|
|
||||||
|
|
||||||
// Produkte ohne (bekannte) Charakter-Zuordnung landen als "weitere Modelle" darunter.
|
// Produkte ohne (bekannte) Charakter-Zuordnung landen als "weitere Modelle" darunter.
|
||||||
const zugeordneteSlugs = new Set(characterEntries.map((e) => e.product?.slug).filter(Boolean));
|
const zugeordneteSlugs = new Set(characterEntries.map((e) => e.product!.slug));
|
||||||
const weitere = alleProdukte.filter((p) => !zugeordneteSlugs.has(p.slug));
|
const weitere = alleProdukte.filter((p) => !zugeordneteSlugs.has(p.slug));
|
||||||
const hatCharaktere = characterEntries.length > 0;
|
const hatCharaktere = characterEntries.length > 0;
|
||||||
const hatWeitere = weitere.length > 0;
|
const hatWeitere = weitere.length > 0;
|
||||||
@@ -61,35 +60,24 @@ const komplettLeer = !hatCharaktere && !hatWeitere;
|
|||||||
<div class="container">
|
<div class="container">
|
||||||
{/* Die persönlichen Charaktergeschichten stehen als eigene, volle Breite Boxen ÜBER der
|
{/* Die persönlichen Charaktergeschichten stehen als eigene, volle Breite Boxen ÜBER der
|
||||||
Kartenreihe — getrennt von den "Shop"-Karten darunter, die nur Foto/Preis/Infos zeigen. */}
|
Kartenreihe — getrennt von den "Shop"-Karten darunter, die nur Foto/Preis/Infos zeigen. */}
|
||||||
{characterEntries.some(({ product }) => product) && (
|
|
||||||
<div class="character-stories">
|
<div class="character-stories">
|
||||||
{characterEntries.filter(({ product }) => product).map(({ char }) => (
|
{characterEntries.map(({ char }) => (
|
||||||
<div class="card character-story">
|
<div class="card character-story">
|
||||||
{char.vorstellung && <span class="eyebrow">{char.vorstellung[lang].split("\n")[0]}</span>}
|
{char.vorstellung && <span class="eyebrow">{char.vorstellung[lang].split("\n")[0]}</span>}
|
||||||
<p>{char.vorstellung ? char.vorstellung[lang].split("\n")[1] : ""}</p>
|
<p>{char.vorstellung ? char.vorstellung[lang].split("\n")[1] : ""}</p>
|
||||||
{char.mehrLink && (
|
{char.mehrLink && (
|
||||||
|
<p class="character-more-lead">
|
||||||
|
{t.shop.moreAboutCharacterLead}{" "}
|
||||||
<a class="btn btn-outline btn-sm" href={char.mehrLink} target="_blank" rel="noopener">hier →</a>
|
<a class="btn btn-outline btn-sm" href={char.mehrLink} target="_blank" rel="noopener">hier →</a>
|
||||||
|
</p>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
)}
|
|
||||||
|
|
||||||
{/* Kartenreihe wie im Shop-Grid — nur Foto, Preis und Kaufinfos, keine Geschichten. */}
|
{/* Kartenreihe wie im Shop-Grid — nur Foto, Preis und Kaufinfos, keine Geschichten. */}
|
||||||
<div class="grid grid-3">
|
<div class="grid grid-3">
|
||||||
{characterEntries.map(({ char, product }) =>
|
{characterEntries.map(({ product }) => <ProductCard product={product!} lang={lang} />)}
|
||||||
product ? (
|
|
||||||
<ProductCard product={product} lang={lang} />
|
|
||||||
) : (
|
|
||||||
<div class="card character-card">
|
|
||||||
<div class="product-media">
|
|
||||||
<div class="img-placeholder" role="img" aria-label={char.name[lang]}></div>
|
|
||||||
</div>
|
|
||||||
<h3>{char.name[lang]}</h3>
|
|
||||||
<p class="character-card-desc">{t.shop.subcategoryEmpty}</p>
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
)}
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
|
|||||||
@@ -868,6 +868,13 @@ a:focus-visible, button:focus-visible {
|
|||||||
color: transparent;
|
color: transparent;
|
||||||
}
|
}
|
||||||
.character-story p { margin: 0 0 1.1rem; max-width: 62ch; font-size: 1.02rem; color: var(--c-text-muted); }
|
.character-story p { margin: 0 0 1.1rem; max-width: 62ch; font-size: 1.02rem; color: var(--c-text-muted); }
|
||||||
|
.character-story .character-more-lead {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
gap: 0.55em;
|
||||||
|
margin-bottom: 0;
|
||||||
|
}
|
||||||
.character-story .btn {
|
.character-story .btn {
|
||||||
background: linear-gradient(135deg, var(--c-accent), var(--c-purple-glow));
|
background: linear-gradient(135deg, var(--c-accent), var(--c-purple-glow));
|
||||||
color: #08181c;
|
color: #08181c;
|
||||||
|
|||||||
Reference in New Issue
Block a user