SPB Git

spb/fabri-ka Public

Agrégateur de produits québécois — www.fabri-ka.com

HTML 57.9% Python 18.6% TypeScript 15.6% CSS 7.8%
5.2 KB · 174 lines tsx
Raw Blame History
1import { useEffect, useState } from 'react'2import { Link } from 'react-router-dom'3import { Facets, fetchFacets, fetchStats, formatInt, Stats } from '../api'4import CountUp from '../components/CountUp'5import { categoryIcon, IconArrowRight } from '../components/Icons'6import Rail from '../components/Rail'7import SearchBar from '../components/SearchBar'8import Skeleton from '../components/Skeleton'910interface RailDef {11  title: string12  category?: string13  seeAllHref: string14}1516function pickCategory(17  facets: Facets | null,18  keywords: string[]19): string | undefined {20  if (!facets) return undefined21  const found = facets.categories.find((c) =>22    keywords.some(23      (k) =>24        c.key.toLowerCase().includes(k) || c.label.toLowerCase().includes(k)25    )26  )27  return found?.key28}2930export default function Home() {31  const [stats, setStats] = useState<Stats | null>(null)32  const [facets, setFacets] = useState<Facets | null>(null)3334  useEffect(() => {35    const controller = new AbortController()36    fetchStats(controller.signal)37      .then(setStats)38      .catch(() => {})39    fetchFacets(controller.signal)40      .then(setFacets)41      .catch(() => {})42    return () => controller.abort()43  }, [])4445  const rails: RailDef[] = [46    { title: 'Nouveautés', seeAllHref: '/produits?sort=recent' },47  ]48  const erable = pickCategory(facets, ['érable', 'erable', 'miel'])49  if (erable) {50    rails.push({51      title: 'Érable & miel',52      category: erable,53      seeAllHref: `/produits?category=${encodeURIComponent(erable)}`,54    })55  }56  const beaute = pickCategory(facets, ['beauté', 'beaute', 'soins'])57  if (beaute) {58    rails.push({59      title: 'Beauté & soins',60      category: beaute,61      seeAllHref: `/produits?category=${encodeURIComponent(beaute)}`,62    })63  }64  const maison = pickCategory(facets, ['maison', 'déco', 'deco'])65  if (maison) {66    rails.push({67      title: 'Maison & déco',68      category: maison,69      seeAllHref: `/produits?category=${encodeURIComponent(maison)}`,70    })71  }7273  return (74    <div className="page home">75      <section className="hero">76        <p className="hero-eyebrow">Le répertoire des produits d'ici</p>77        <h1 className="hero-title">78          Tous les produits québécois.79          <br />80          <em>Un seul endroit.</em>81        </h1>82        <p className="hero-sub">83          Fabri-Ka rassemble les catalogues des boutiques en ligne d'ici —84          fabriqué, conçu et vendu au Québec.85        </p>8687        <div className="hero-search">88          <SearchBar />89        </div>9091        <div className="hero-stats">92          {stats ? (93            <>94              <div className="stat">95                <span className="stat-number">96                  <CountUp value={stats.totals.products} />97                </span>98                <span className="stat-label">Produits</span>99              </div>100              <div className="stat">101                <span className="stat-number">102                  <CountUp value={stats.totals.stores_live} />103                </span>104                <span className="stat-label">Boutiques</span>105              </div>106              <div className="stat">107                <span className="stat-number">108                  <CountUp value={stats.totals.regions} />109                </span>110                <span className="stat-label">Régions</span>111              </div>112            </>113          ) : (114            <>115              <Skeleton width="90px" height="3rem" />116              <Skeleton width="90px" height="3rem" />117              <Skeleton width="90px" height="3rem" />118            </>119          )}120        </div>121        <Link className="hero-stats-link" to="/stats">122          Statistiques <IconArrowRight size={14} />123        </Link>124      </section>125126      <section className="home-categories" aria-label="Catégories">127        <header className="section-header">128          <h2>Parcourir par catégorie</h2>129          <Link className="section-see-all" to="/produits">130            Tout le catalogue <IconArrowRight size={15} />131          </Link>132        </header>133        <div className="category-chips">134          {facets ? (135            facets.categories.map((c) => (136              <Link137                key={c.key}138                className="chip"139                to={`/produits?category=${encodeURIComponent(c.key)}`}140              >141                <span className="chip-icon" aria-hidden="true">142                  {categoryIcon(c.label)}143                </span>144                {c.label}145                <span className="chip-count">{formatInt(c.n)}</span>146              </Link>147            ))148          ) : (149            <>150              <Skeleton width="110px" height="2.5rem" radius="999px" />151              <Skeleton width="140px" height="2.5rem" radius="999px" />152              <Skeleton width="120px" height="2.5rem" radius="999px" />153              <Skeleton width="100px" height="2.5rem" radius="999px" />154            </>155          )}156        </div>157      </section>158159      {rails.map((r) => (160        <Rail161          key={r.title}162          title={r.title}163          query={164            r.category165              ? { category: r.category, per_page: 12 }166              : { sort: 'recent', per_page: 12 }167          }168          seeAllHref={r.seeAllHref}169        />170      ))}171    </div>172  )173}174