SPB Git

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%
2.1 KB · 87 lines tsx
Raw Blame History
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