// DashboardPage — home page at `/`. // // Converged onto the `/reports/cartes` model (Issue #279, epic #260): the KPI // row, top movers and budget adherence are the exact Cartes presentational // components, sourced from the same `getCartesSnapshot` reference-month // snapshot. Two Dashboard-specific widgets remain: the top-expense-categories // ranked bar chart (replacing the former expense-only pie) and the net-worth // tile (from the Bilan — a different metric from every transactional widget // here, see NetWorthTile's own header comment). // // Onboarding (#352): the page branches on the getting-started checklist // `status`, never on a bare counter. While it loads, only the title and the // categories banner are rendered (no flash of 0 $ widgets nor of the empty state). A // profile without any transaction gets `DashboardEmptyState` instead of the // widgets, outside the `isLoading` veil so its link stays clickable. With // transactions, the page shows no list of its own: the getting-started banner // that `AppShell` puts on top of every page replaced the compact bar (#397, // D7). A first load that fails falls back to the v0.15.0 page without the // list; the provider logs it. // // Since #396 the checklist comes from `OnboardingProvider` (AppShell), shared // by every page and re-read live, instead of a load of its own at mount. import { useState, useCallback, useMemo } from "react"; import { useTranslation } from "react-i18next"; import { useDashboard } from "../hooks/useDashboard"; import { useOnboarding } from "../contexts/OnboardingContext"; import { PageHelp } from "../components/shared/PageHelp"; import PeriodSelector from "../components/dashboard/PeriodSelector"; import CategoryBarChart from "../components/reports/CategoryBarChart"; import CategoriesV1DiscoveryBanner from "../components/dashboard/CategoriesV1DiscoveryBanner"; import DashboardEmptyState from "../components/dashboard/DashboardEmptyState"; import NetWorthTile from "../components/dashboard/NetWorthTile"; import FilterPanel from "../components/reports/FilterPanel"; import KpiCard from "../components/reports/cards/KpiCard"; import IncomeExpenseOverlayChart from "../components/reports/cards/IncomeExpenseOverlayChart"; import TopMoversList from "../components/reports/cards/TopMoversList"; import BudgetAdherenceCard from "../components/reports/cards/BudgetAdherenceCard"; import CategoryOverTimeChart from "../components/reports/CategoryOverTimeChart"; import TransactionDetailModal from "../components/shared/TransactionDetailModal"; import type { CategoryBreakdownItem } from "../shared/types"; import { computeDateRange, buildMonthOptions } from "../utils/dateRange"; export default function DashboardPage() { const { t, i18n } = useTranslation(); const { state, setPeriod, setCustomDates, setReferencePeriod, setAccountIds, } = useDashboard(); const { categoryBreakdown, categoryOverTime, cartesSnapshot, period, isLoading } = state; const onboarding = useOnboarding(); const [hiddenCategories, setHiddenCategories] = useState>(new Set()); const [detailModal, setDetailModal] = useState(null); const toggleHidden = useCallback((name: string) => { setHiddenCategories((prev) => { const next = new Set(prev); if (next.has(name)) next.delete(name); else next.add(name); return next; }); }, []); const showAll = useCallback(() => setHiddenCategories(new Set()), []); const viewDetails = useCallback((item: CategoryBreakdownItem) => { setDetailModal(item); }, []); const monthOptions = useMemo(() => buildMonthOptions(i18n.language), [i18n.language]); const { dateFrom, dateTo } = computeDateRange(period, state.customDateFrom, state.customDateTo); const header = ( <>

{t("dashboard.title")}

); if (onboarding.status === "loading") { return
{header}
; } const { checklist } = onboarding; const checklistReady = onboarding.status === "ready" && checklist !== null; if (checklistReady && onboarding.counters?.transactions === 0) { return (
{header}
); } return (
{header} {state.error && (
{state.error}
)} } accountIds={state.accountIds} onAccountIdsChange={setAccountIds} accounts={state.accounts} />

{t("dashboard.kpiSectionTitle")}

{cartesSnapshot && ( <>
)}
{cartesSnapshot && ( <>
)}

{t("dashboard.expensesByCategory")}

{t("dashboard.expensesOverTime")}

{detailModal && ( setDetailModal(null)} /> )}
); }