// --------------------------------------------------------------------------- // Fabri-Ka — favoris « Mon univers Ka » (magasin central du hub Groupe KA) // Aucun stockage local : l'état vient du hub (GET /api/favorites) et chaque ♥ // y est poussé (POST /api/favorites/toggle), avec mise à jour optimiste et // retour arrière si le hub refuse. Non connecté -> le clic ♥ envoie vers la // connexion KA ID puis ramène sur la page en cours. // --------------------------------------------------------------------------- import { createContext, useCallback, useContext, useEffect, useState, type MouseEvent, type ReactNode, } from 'react' import { useLocation } from 'react-router-dom' import { fetchFavorites, productFavItem, toggleFavorite, type FavoriteItem, type Product, } from './api' import { IconHeart } from './components/Icons' interface FavoritesState { /** premier chargement effectué (connecté ou non) */ ready: boolean /** session KA ID active (sinon, toggle => redirection connexion) */ loggedIn: boolean /** item_id (uid produit) des favoris, tels que lus au hub */ ids: ReadonlySet toggle: (item: FavoriteItem) => void } const FavoritesContext = createContext({ ready: false, loggedIn: false, ids: new Set(), toggle: () => {}, }) export function useFavorites(): FavoritesState { return useContext(FavoritesContext) } export function FavoritesProvider({ children }: { children: ReactNode }) { const location = useLocation() const [ready, setReady] = useState(false) const [loggedIn, setLoggedIn] = useState(false) const [ids, setIds] = useState>(new Set()) // Un seul chargement au montage : après la connexion SSO, la page se // recharge entièrement, donc l'état revient toujours frais du hub. useEffect(() => { const controller = new AbortController() fetchFavorites(controller.signal) .then((r) => { if (r === null) { setLoggedIn(false) setIds(new Set()) } else { setLoggedIn(true) setIds(new Set(r.ids)) } setReady(true) }) .catch(() => {}) return () => controller.abort() }, []) const toggle = useCallback( (item: FavoriteItem) => { if (!loggedIn) { const next = location.pathname + location.search window.location.href = `/api/auth/ka/login?next=${encodeURIComponent(next)}` return } const on = !ids.has(item.item_id) const optimistic = new Set(ids) if (on) optimistic.add(item.item_id) else optimistic.delete(item.item_id) setIds(optimistic) toggleFavorite(on, item) .then((ok) => { if (ok) return // le hub n'a pas pris le ♥ : on rétablit l'état précédent setIds((cur) => { const revert = new Set(cur) if (on) revert.delete(item.item_id) else revert.add(item.item_id) return revert }) }) .catch(() => {}) }, [ids, loggedIn, location.pathname, location.search] ) return ( {children} ) } /** Bouton ♥ d'un produit (cartes et fiche) — état lu/poussé au hub KA. */ export function FavButton({ product, size = 17, className, }: { product: Product size?: number className?: string }) { const { ids, toggle } = useFavorites() const on = ids.has(product.uid) const label = on ? 'Retirer des favoris' : 'Ajouter aux favoris' function onClick(e: MouseEvent) { // les cartes produit sont des : le ♥ ne doit pas naviguer e.preventDefault() e.stopPropagation() toggle(productFavItem(product)) } return ( ) }