import { useEffect, useMemo, useRef, useState } from 'react' import { useSearchParams } from 'react-router-dom' import { Facets, fetchFacets, fetchStores, formatInt, StoresResponse, } from '../api' import EmptyState from '../components/EmptyState' import Skeleton from '../components/Skeleton' import StoreCard from '../components/StoreCard' export default function Stores() { const [searchParams, setSearchParams] = useSearchParams() const [facets, setFacets] = useState(null) const [data, setData] = useState(null) const [loading, setLoading] = useState(true) const [error, setError] = useState(false) const region = searchParams.get('region') ?? '' const q = searchParams.get('q') ?? '' const withProducts = searchParams.get('avec_produits') !== '0' const [search, setSearch] = useState(q) const debounceRef = useRef(undefined) useEffect(() => { setSearch(q) }, [q]) useEffect(() => { const controller = new AbortController() fetchFacets(controller.signal) .then(setFacets) .catch(() => {}) return () => controller.abort() }, []) useEffect(() => { const controller = new AbortController() setLoading(true) setError(false) fetchStores( { region: region || undefined, q: q || undefined, with_products: withProducts, }, controller.signal ) .then((res) => { setData(res) setLoading(false) }) .catch((err: unknown) => { if (err instanceof DOMException && err.name === 'AbortError') return setError(true) setLoading(false) }) return () => controller.abort() }, [region, q, withProducts]) const update = useMemo( () => (patch: { region?: string; q?: string; withProducts?: boolean }) => { setSearchParams( (prev) => { const next = new URLSearchParams(prev) const r = patch.region ?? region const query = patch.q ?? q const wp = patch.withProducts ?? withProducts if (r) next.set('region', r) else next.delete('region') if (query) next.set('q', query) else next.delete('q') if (!wp) next.set('avec_produits', '0') else next.delete('avec_produits') return next }, { replace: true } ) }, [setSearchParams, region, q, withProducts] ) function onSearchInput(value: string) { setSearch(value) window.clearTimeout(debounceRef.current) debounceRef.current = window.setTimeout(() => update({ q: value }), 300) } // Most stocked boutiques first. const sortedStores = useMemo( () => [...(data?.items ?? [])].sort( (a, b) => b.product_count - a.product_count ), [data] ) return (

Boutiques

{data && !loading && (

{formatInt(data.total)} boutique{data.total === 1 ? '' : 's'}

)}
onSearchInput(e.target.value)} placeholder="Rechercher une boutique…" aria-label="Rechercher une boutique" />
{loading ? (
{Array.from({ length: 8 }, (_, i) => (
))}
) : error ? ( ) : sortedStores.length === 0 ? ( ) : (
{sortedStores.map((s) => ( ))}
)}
) }