Add product image gallery, harden Klaro border reset, remove attribution link

ProductGallery.tsx (main image + thumbnail strip) wired into
TodoKartenHero.tsx, only replacing the static hero photo once a
product actually has extra gallery photos — no visual change
otherwise. Backend field: Products.gallery (see payload repo).

Klaro's hard black border was still showing after the previous
color-only pass — the targeted border:none rule wasn't enough, so
this blanket-resets border/outline on every .klaro descendant and
re-adds only the shadow this theme actually wants. Also disables the
"Realisiert mit Klaro!" attribution link (BSD-3-Clause has no
on-page-attribution requirement, Klaro's own disablePoweredBy flag
covers this cleanly).

Updates the frontend README with the gallery + Klaro fixes.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
Marco
2026-08-01 21:41:23 +00:00
parent f6b001dd11
commit 417bbd430e
7 changed files with 151 additions and 16 deletions
+1
View File
@@ -10,6 +10,7 @@ const product = (overrides: Partial<Product> = {}): Product => ({
price: 12.9,
compareAtPrice: null,
image: "",
gallery: [],
href: null,
active: true,
updatedAt: new Date().toISOString(),
+6
View File
@@ -15,6 +15,7 @@ export type KlaroConfig = {
acceptAll: boolean;
hideDeclineAll: boolean;
noticeAsModal: boolean;
disablePoweredBy: boolean;
translations: Record<string, Record<string, unknown>>;
services: {
name: string;
@@ -96,6 +97,11 @@ export function buildKlaroConfig(codes: TrackingCode[], onAccept: (code: Trackin
acceptAll: true,
hideDeclineAll: false,
noticeAsModal: false,
// Removes the "Realisiert mit Klaro!" attribution link in the modal —
// BSD-3-Clause has no on-page-attribution requirement (only source/
// binary copyright-notice retention), this is just the maintainers'
// own polite ask via a dedicated config flag, not a license term.
disablePoweredBy: true,
translations: {
de: {
consentModal: {
+9
View File
@@ -185,6 +185,11 @@ export type Product = {
price: number;
compareAtPrice: number | null;
image: string;
// Extra photos beyond `image` above, for ProductGallery.tsx's
// thumbnail-strip UI — empty for the vast majority of products (opt-in
// per product, see Products.ts's own field comment). Never includes
// `image` itself; ProductGallery treats `image` as always-slide-zero.
gallery: string[];
href: string | null;
// `active` is opt-in for callers to filter by, not applied inside
// getProducts()/getProductBySlug() themselves — cart, checkout, order
@@ -250,6 +255,7 @@ type PayloadProduct = {
price: number;
compareAtPrice: number | null;
image: { url: string } | number | null;
gallery: ({ url: string } | number)[] | null;
detailHref: string | null;
active: boolean;
updatedAt: string;
@@ -314,6 +320,9 @@ export function mapPayloadProduct(product: PayloadProduct): Product {
price: product.price,
compareAtPrice: product.compareAtPrice ?? null,
image: typeof product.image === "object" && product.image ? product.image.url : "",
gallery: (product.gallery ?? [])
.map((g) => (typeof g === "object" && g ? g.url : null))
.filter((url): url is string => Boolean(url)),
href: product.detailHref || null,
active: product.active,
updatedAt: product.updatedAt,