/**
* Oveyo PWA — Application principale (style Vivinter)
*
* 5 onglets : Accueil, Carte, Partager, Pharmacie, Moi
* + Vue Espace Commerçant (depuis profil)
*
* Toutes les pages sont branchées sur l'API V4.1+PWA d'Oveyo.
*/
const { useState, useEffect, useRef, useCallback, useMemo } = React;
// Import lucide-react via UMD global
const Lu = window.lucide || {};
const {
Home, MapPin, Camera, Cross, User, ChevronRight, ChevronLeft,
TrendingDown, TrendingUp, AlertTriangle, Bell, Plus, Check, Clock,
Phone, Navigation, Award, Gift, Eye, Sparkles, Store,
Users, ShieldCheck, Edit3, Wifi, WifiOff, Shield, X,
RefreshCw, Search, Filter, Loader2, Send, ArrowRight, Lock,
ChevronDown, ChevronUp, Pill,
} = window.LucideReact || {};
// Components partagés
const { GradientBar, Card, IconBubble, FlashProvider, useFlash, Sheet, EmptyState, Spinner } = window.OveyoUI;
const {
loadState, saveState, defaultState, api,
usePrices, useCatalog, usePharmaciesOnDuty, useShops, usePulseFeed,
useAmbassador, useTrustIndex, useGroupBuys, useGeolocation, useOnlineStatus,
formatRelative, formatPhone, compressImage,
} = window.OveyoState;
// ============================================================
// HEADER GRADIENT (top bar Oveyo)
// ============================================================
const TopBar = ({ state, onBellClick }) => {
const online = useOnlineStatus();
const neighborhood = state.user.neighborhood_slug
? state.user.neighborhood_slug.replace(/-/g, ' ').replace(/\b\w/g, (c) => c.toUpperCase())
: 'Dakar';
return (
Oveyo
{neighborhood}
{online ? : }
{online ? 'Synchro' : 'Hors-ligne'}
);
};
// ============================================================
// PAGE : ACCUEIL
// ============================================================
const HomePage = ({ state, setActiveTab, onOpenSheet }) => {
const { feed, loading: pulseLoading } = usePulseFeed(state.user.neighborhood_slug);
const { data: pricesData } = usePrices(state.user.neighborhood_slug, 7);
const trustIndex = useTrustIndex(state.user.neighborhood_slug);
const groupBuys = useGroupBuys(state.user.neighborhood_slug);
const products = (pricesData?.products || []).slice(0, 4).filter((p) => p.avg_price);
return (
{/* Pulse du quartier */}
{pulseLoading ? (
) : feed.length === 0 ? (
Pas encore d'activité dans ton quartier — sois la première à signaler !
) : (
{feed.slice(0, 3).map((p, i) => (
{p.anonymous ?
: (p.user || '?')[0].toUpperCase()}
{!!p.ambassador && (
)}
{p.anonymous ? (
<>
Anonyme{' '}
· {p.action}{' '}
{p.place}
>
) : (
<>
{p.user}
de {p.area}
{p.action}{' '}
{p.place}
>
)}
{formatRelative(p.created_at) || p.time}
))}
)}
{/* Quick actions */}
{/* Groupe d'achat (si présent) — cliquable */}
{groupBuys.length > 0 && (
)}
{/* Prix du jour */}
Prix du jour
moyenne {state.user.neighborhood_slug ? 'quartier' : 'Dakar'}
{products.length === 0 ? (
Pas encore de prix cette semaine. Sois la première à en signaler un !
) : (
products.map((p, i) => {
const diff = Math.round(p.avg_price - (p.price_official || p.avg_price));
const overpriced = diff > 0;
return (
);
})
)}
{/* CTA Comparer — gros bouton pleine largeur */}
{/* Indice de confiance */}
{trustIndex && (
{Math.round(trustIndex.score * 100)}%
Indice de confiance
{trustIndex.neighborhood_name || 'Dakar'} cette semaine
{trustIndex.honest_shops} boutiques honnêtes · {trustIndex.abuses} abus signalés
)}
);
};
// Export
window.OveyoPages = window.OveyoPages || {};
window.OveyoPages.HomePage = HomePage;
window.OveyoPages.TopBar = TopBar;