import { useState, useEffect, useCallback } from 'react' import { AuthProvider, useAuth } from './contexts/AuthContext' import { BudgetProvider, useBudget } from './contexts/BudgetContext' import { ThemeProvider } from './contexts/ThemeContext' import { usePullToRefresh } from './hooks/usePullToRefresh' import { api } from './api/client' import LoginPage from './pages/LoginPage' import SetupPage from './pages/SetupPage' import DashboardPage from './pages/DashboardPage' import ExpensesPage from './pages/ExpensesPage' import PointagePage from './pages/PointagePage' import SimulateurPage from './pages/SimulateurPage' import AdminPage from './pages/AdminPage' import SettingsPage from './pages/SettingsPage' import EpargnesPage from './pages/EpargnesPage' // ── Toast ───────────────────────────────────────────────────── function Toast({ msg, type }) { return (
{msg}
) } // ── Budget switcher ─────────────────────────────────────────── function BudgetSwitcher() { const { activeBudgetId, accessibleBudgets, switchBudget } = useBudget() if (accessibleBudgets.length <= 1) return null return (
{accessibleBudgets.map(b => ( ))}
) } // ── Main shell (requires auth) ──────────────────────────────── const DEFAULT_SETTINGS = { nameA: '', nameB: '', incomeA: '', incomeB: '', sliderVal: 63, sliderLocked: false, setupDone: 'false', } const TABS = [ { id: 'budget', label: 'Budget', dot: 'var(--aa)' }, { id: 'depenses', label: 'Dépenses', dot: 'var(--ab)' }, { id: 'pointage', label: 'Pointage', dot: 'var(--aa)' }, { id: 'simu', label: 'Simulateur', dot: '#f9a875' }, { id: 'epargnes', label: 'Épargnes', dot: 'var(--ab)' }, ] function AppShell() { const { user, logout } = useAuth() const { activeBudget, activeBudgetId } = useBudget() const [tab, setTab] = useState('budget') const [accountOpen, setAccountOpen] = useState(false) const [settings, setSettings] = useState(DEFAULT_SETTINGS) const [expenses, setExpenses] = useState([]) const [commonExpenses, setCommonExpenses] = useState([]) const [categories, setCategories] = useState([]) const [loading, setLoading] = useState(true) const [toast, setToast] = useState(null) const [toastTimer, setToastTimer] = useState(null) function showToast(msg, type = 'ok') { setToast({ msg, type }) clearTimeout(toastTimer) const t = setTimeout(() => setToast(null), 2600) setToastTimer(t) } const loadAll = useCallback(async () => { setLoading(true) try { const [s, e, c, ce] = await Promise.all([ api.get('/settings'), api.get('/expenses'), api.get('/categories'), api.get('/expenses?budget_id=1'), ]) setSettings({ ...DEFAULT_SETTINGS, ...s, sliderVal: parseInt(s.sliderVal) || 63, sliderLocked: s.sliderLocked === 'true', }) setExpenses(e) setCategories(c) setCommonExpenses(ce) } catch { showToast('Impossible de joindre le serveur', 'err') } finally { setLoading(false) } }, []) useEffect(() => { if (user) loadAll() }, [user, activeBudgetId, loadAll]) const { pullY, refreshing, triggered } = usePullToRefresh(loadAll) const visibleTabs = TABS if (loading) { return (
) } if (settings.setupDone !== 'true') { return } return (
{/* Pull-to-refresh indicator */} {(pullY > 0 || refreshing) && (
)}
Espace familial

Budget {activeBudget.nom === 'Commun' ? 'Commun' : activeBudget.nom}

{accountOpen && ( <>
setAccountOpen(false)} />
{user?.isAdmin && ( )}
)}
{/* Budget switcher (multi-profil — étape 4) */}
{/* Tab nav */}
{/* Page content */} {tab === 'budget' && ( )} {tab === 'depenses' && ( )} {tab === 'pointage' && ( )} {tab === 'simu' && ( )} {tab === 'epargnes' && } {tab === 'admin' && user?.isAdmin && ( )} {tab === 'compte' && ( )}
Données hébergées sur votre réseau · Budget Commun
{toast && }
) } // ── Root ────────────────────────────────────────────────────── export default function App() { return ( ) } function AppInner() { const { user, loading } = useAuth() if (loading) { return (
) } if (!user) return return }