feat(design): designauswahl mit klemmbaustein-look als standard
Sechs Designs (Klemmbaustein, Warm, Nachtblau, Bonbon, Bauklotz, Schlicht) als Tokens je data-design, gewaehlt unter Konto und pro Geraet im Cookie gespeichert. Dazu Noppen, Steinkontur, Platzhalter fuer Sets ohne Bild, neues Logo, lokal gebuendelte Schriften und eine Startseite mit Steinreihe und naechstem Termin. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5.5
parent
e9093d0fad
commit
1b825b44c4
@@ -217,7 +217,8 @@ export function CatalogGrid({
|
||||
'relative rounded-xl text-left outline-none transition-opacity',
|
||||
'focus-visible:ring-3 focus-visible:ring-ring/50',
|
||||
owned && 'cursor-not-allowed opacity-50',
|
||||
isSelected && '[&>div]:border-primary [&>div]:ring-2 [&>div]:ring-primary/40',
|
||||
isSelected &&
|
||||
'[&_.brick]:outline-3 [&_.brick]:outline-offset-2 [&_.brick]:outline-primary',
|
||||
)}
|
||||
>
|
||||
<SetCardContent set={item} />
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
import Link from 'next/link';
|
||||
import { Check, Gift, ImageOff } from 'lucide-react';
|
||||
import { Check, Gift } from 'lucide-react';
|
||||
import { cn } from 'cn';
|
||||
import { SetPlaceholder } from '@/components/brick/set-placeholder';
|
||||
import { Studs } from '@/components/brick/studs';
|
||||
import type { CatalogRow } from '@/lib/queries/catalog';
|
||||
import { formatEuro } from '@/lib/money';
|
||||
import { setImageSrc } from '@/lib/images';
|
||||
@@ -17,56 +19,59 @@ export function SetCardContent({ set }: { set: CatalogRow }) {
|
||||
const owned = set.ownedCopies > 0;
|
||||
|
||||
return (
|
||||
<div className="flex h-full flex-col overflow-hidden rounded-xl border border-border bg-card transition-colors">
|
||||
<div className="relative aspect-square bg-muted">
|
||||
{image ? (
|
||||
// Plain img: images are either local files or a URL the user entered,
|
||||
// and the optimizer must never fetch arbitrary remote hosts.
|
||||
// eslint-disable-next-line @next/next/no-img-element
|
||||
<img
|
||||
src={image}
|
||||
alt=""
|
||||
loading="lazy"
|
||||
decoding="async"
|
||||
className="size-full object-contain p-2"
|
||||
/>
|
||||
) : (
|
||||
<div className="flex size-full items-center justify-center text-muted-foreground">
|
||||
<ImageOff className="size-8" aria-hidden />
|
||||
<div className="relative h-full">
|
||||
<Studs count={3} />
|
||||
<div className="brick flex h-full flex-col overflow-hidden rounded-xl bg-card transition-colors">
|
||||
<div className="relative aspect-square border-b-[length:var(--edge-w)] border-edge bg-muted">
|
||||
{image ? (
|
||||
// Plain img: images are either local files or a URL the user entered,
|
||||
// and the optimizer must never fetch arbitrary remote hosts.
|
||||
// eslint-disable-next-line @next/next/no-img-element
|
||||
<img
|
||||
src={image}
|
||||
alt=""
|
||||
loading="lazy"
|
||||
decoding="async"
|
||||
className="size-full object-contain p-2"
|
||||
/>
|
||||
) : (
|
||||
<SetPlaceholder setNumber={set.setNumber} manufacturerName={set.manufacturerName} />
|
||||
)}
|
||||
|
||||
<div className="absolute top-2 left-2 flex flex-col items-start gap-1">
|
||||
{owned ? (
|
||||
<Badge className="gap-1 bg-owned text-owned-foreground">
|
||||
<Check className="size-3" aria-hidden />
|
||||
{set.ownedCopies > 1 ? `${set.ownedCopies}× vorhanden` : 'Vorhanden'}
|
||||
</Badge>
|
||||
) : null}
|
||||
{set.wishlistCount > 0 ? (
|
||||
<Badge className="gap-1">
|
||||
<Gift className="size-3" aria-hidden />
|
||||
Auf Wunschliste
|
||||
</Badge>
|
||||
) : null}
|
||||
</div>
|
||||
)}
|
||||
|
||||
<div className="absolute top-2 left-2 flex flex-col items-start gap-1">
|
||||
{owned ? (
|
||||
<Badge className="gap-1 bg-emerald-600 text-white hover:bg-emerald-600">
|
||||
<Check className="size-3" aria-hidden />
|
||||
{set.ownedCopies > 1 ? `${set.ownedCopies}× vorhanden` : 'Vorhanden'}
|
||||
</Badge>
|
||||
) : null}
|
||||
{set.wishlistCount > 0 ? (
|
||||
<Badge variant="secondary" className="gap-1">
|
||||
<Gift className="size-3" aria-hidden />
|
||||
Auf Wunschliste
|
||||
</Badge>
|
||||
) : null}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="flex flex-1 flex-col gap-1 p-3">
|
||||
<div className="flex items-center gap-2 text-xs text-muted-foreground">
|
||||
<Badge variant="outline" className="font-mono">
|
||||
{set.setNumber}
|
||||
</Badge>
|
||||
<span className="truncate">{set.manufacturerName}</span>
|
||||
</div>
|
||||
<p className="line-clamp-2 text-sm leading-snug font-medium">{set.name}</p>
|
||||
<div className="mt-auto flex items-center justify-between gap-2 pt-1 text-xs text-muted-foreground">
|
||||
<span className="truncate">{[set.themeName, set.year].filter(Boolean).join(' · ')}</span>
|
||||
{set.retailPriceCents !== null ? (
|
||||
<span className="shrink-0 font-medium text-foreground">
|
||||
{formatEuro(set.retailPriceCents)}
|
||||
<div className="flex flex-1 flex-col gap-1 p-3">
|
||||
<div className="flex items-center gap-2 text-xs text-muted-foreground">
|
||||
<Badge variant="outline" className="font-mono">
|
||||
{set.setNumber}
|
||||
</Badge>
|
||||
<span className="truncate">{set.manufacturerName}</span>
|
||||
</div>
|
||||
<p className="line-clamp-2 text-sm leading-snug font-medium">{set.name}</p>
|
||||
<div className="mt-auto flex items-center justify-between gap-2 pt-1 text-xs text-muted-foreground">
|
||||
<span className="truncate">
|
||||
{[set.themeName, set.year].filter(Boolean).join(' · ')}
|
||||
</span>
|
||||
) : null}
|
||||
{set.retailPriceCents !== null ? (
|
||||
<span className="shrink-0 font-medium text-foreground">
|
||||
{formatEuro(set.retailPriceCents)}
|
||||
</span>
|
||||
) : null}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -78,7 +83,7 @@ export function SetCard({ set, className }: { set: CatalogRow; className?: strin
|
||||
<Link
|
||||
href={`/katalog/${set.id}`}
|
||||
className={cn(
|
||||
'rounded-xl outline-none focus-visible:ring-3 focus-visible:ring-ring/50 [&>div]:hover:border-primary/40',
|
||||
'rounded-xl outline-none focus-visible:ring-3 focus-visible:ring-ring/50',
|
||||
className,
|
||||
)}
|
||||
>
|
||||
|
||||
Reference in New Issue
Block a user