spb/fabri-ka Public
Agrégateur de produits québécois — www.fabri-ka.com
HTML 57%
Python 19.9%
TypeScript 15.3%
CSS 7.7%
1import { useState } from 'react'23// ---------------------------------------------------------------------------4// StoreLogo — circular store avatar.5// Renders logo_url when available; falls back to a deterministic6// initial-letter avatar (never a broken-image icon).7// ---------------------------------------------------------------------------89export type StoreLogoSize = 'sm' | 'md' | 'lg'1011// Small palette in the Fabri-Ka register (pine / terracotta / earth tones).12const AVATAR_COLORS = [13 '#234438', // pine14 '#C4532E', // terracotta15 '#8A6D3B', // ochre16 '#5B5B8A', // slate violet17 '#A94525', // dark terracotta18 '#3E6253', // sage pine19]2021function hashString(s: string): number {22 let h = 023 for (let i = 0; i < s.length; i++) {24 h = (h * 31 + s.charCodeAt(i)) | 025 }26 return Math.abs(h)27}2829interface StoreLogoProps {30 storeId: string31 name: string32 logoUrl?: string | null33 size?: StoreLogoSize34 /** white ring + soft shadow (hero usage) */35 ring?: boolean36 className?: string37}3839export default function StoreLogo({40 storeId,41 name,42 logoUrl,43 size = 'md',44 ring = false,45 className,46}: StoreLogoProps) {47 // track the failed URL (not a boolean) so navigation between stores48 // that reuses this component instance retries the new logo.49 const [failedUrl, setFailedUrl] = useState<string | null>(null)5051 const cls = [52 'store-logo',53 `store-logo-${size}`,54 ring ? 'store-logo-ring' : '',55 className ?? '',56 ]57 .filter(Boolean)58 .join(' ')5960 if (logoUrl && failedUrl !== logoUrl) {61 return (62 <span className={cls}>63 <img64 src={logoUrl}65 alt={`Logo ${name}`}66 loading="lazy"67 decoding="async"68 onError={() => setFailedUrl(logoUrl)}69 />70 </span>71 )72 }7374 const color = AVATAR_COLORS[hashString(storeId) % AVATAR_COLORS.length]75 const letter = (name.trim().charAt(0) || 'F').toUpperCase()76 return (77 <span78 className={`${cls} store-logo-fallback`}79 style={{ background: color }}80 role="img"81 aria-label={`Logo ${name}`}82 >83 {letter}84 </span>85 )86}87