From 16c6d02e39d4eff466502a496e92ab4123e9681f Mon Sep 17 00:00:00 2001 From: medic-bot Date: Mon, 9 Mar 2026 14:05:47 -0400 Subject: [PATCH] feat: sticky category column, month dropdown selector, default to last completed month (#29) - Add sticky left-0 positioning to all category cells in BudgetVsActualTable - Replace MonthNavigator arrows with inline title + dropdown month selector - Default budget month to previous completed month instead of current - Add i18n keys for new title prefix (FR/EN) Co-Authored-By: Claude Opus 4.6 --- CHANGELOG.fr.md | 5 ++ CHANGELOG.md | 5 ++ .../reports/BudgetVsActualTable.tsx | 10 ++-- src/hooks/useReports.ts | 10 ++-- src/i18n/locales/en.json | 3 +- src/i18n/locales/fr.json | 3 +- src/pages/ReportsPage.tsx | 46 ++++++++++++++----- 7 files changed, 61 insertions(+), 21 deletions(-) diff --git a/CHANGELOG.fr.md b/CHANGELOG.fr.md index 7066584..ac68d08 100644 --- a/CHANGELOG.fr.md +++ b/CHANGELOG.fr.md @@ -5,6 +5,11 @@ ### Ajouté - Tableau de budget : colonne du total de l'année précédente affichée comme première colonne de données pour servir de référence (#16) +### Modifié +- Budget vs Réel : la colonne des catégories reste désormais fixe lors du défilement horizontal (#29) +- Budget vs Réel : titre changé pour « Budget vs Réel pour le mois de [mois] » avec un menu déroulant pour sélectionner le mois (#29) +- Budget vs Réel : le mois par défaut est maintenant le dernier mois complété au lieu du mois courant (#29) + ## [0.6.3] ### Ajouté diff --git a/CHANGELOG.md b/CHANGELOG.md index edd44d6..620c996 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,11 @@ ### Added - Budget table: previous year total column displayed as first data column for baseline reference (#16) +### Changed +- Budget vs Actual: category column now stays fixed when scrolling horizontally (#29) +- Budget vs Actual: title changed to "Budget vs Réel pour le mois de [month]" with a dropdown month selector (#29) +- Budget vs Actual: default month is now the last completed month instead of current month (#29) + ## [0.6.3] ### Added diff --git a/src/components/reports/BudgetVsActualTable.tsx b/src/components/reports/BudgetVsActualTable.tsx index 4a4fda5..d4a09fb 100644 --- a/src/components/reports/BudgetVsActualTable.tsx +++ b/src/components/reports/BudgetVsActualTable.tsx @@ -151,7 +151,7 @@ export default function BudgetVsActualTable({ data }: BudgetVsActualTableProps) - - @@ -224,7 +224,7 @@ export default function BudgetVsActualTable({ data }: BudgetVsActualTableProps) isIntermediateParent ? "bg-[var(--muted)]/15 font-medium" : "" }`} > - + @@ -284,7 +284,7 @@ export default function BudgetVsActualTable({ data }: BudgetVsActualTableProps) })} {/* Grand totals */} - + diff --git a/src/hooks/useReports.ts b/src/hooks/useReports.ts index 16ae339..defbd71 100644 --- a/src/hooks/useReports.ts +++ b/src/hooks/useReports.ts @@ -59,8 +59,8 @@ const initialState: ReportsState = { monthlyTrends: [], categorySpending: [], categoryOverTime: { categories: [], data: [], colors: {}, categoryIds: {} }, - budgetYear: now.getFullYear(), - budgetMonth: now.getMonth() + 1, + budgetYear: now.getMonth() === 0 ? now.getFullYear() - 1 : now.getFullYear(), + budgetMonth: now.getMonth() === 0 ? 12 : now.getMonth(), budgetVsActual: [], pivotConfig: { rows: [], columns: [], filters: {}, values: [] }, pivotResult: { rows: [], columnValues: [], dimensionLabels: {} }, @@ -237,6 +237,10 @@ export function useReports() { dispatch({ type: "SET_BUDGET_MONTH", payload: { year: newYear, month: newMonth } }); }, [state.budgetYear, state.budgetMonth]); + const setBudgetMonth = useCallback((year: number, month: number) => { + dispatch({ type: "SET_BUDGET_MONTH", payload: { year, month } }); + }, []); + const setCustomDates = useCallback((dateFrom: string, dateTo: string) => { dispatch({ type: "SET_CUSTOM_DATES", payload: { dateFrom, dateTo } }); }, []); @@ -249,5 +253,5 @@ export function useReports() { dispatch({ type: "SET_SOURCE_ID", payload: id }); }, []); - return { state, setTab, setPeriod, setCustomDates, navigateBudgetMonth, setPivotConfig, setSourceId }; + return { state, setTab, setPeriod, setCustomDates, navigateBudgetMonth, setBudgetMonth, setPivotConfig, setSourceId }; } diff --git a/src/i18n/locales/en.json b/src/i18n/locales/en.json index b1da85f..7de1e70 100644 --- a/src/i18n/locales/en.json +++ b/src/i18n/locales/en.json @@ -377,7 +377,8 @@ "ytd": "Year-to-Date", "dollarVar": "$ Var", "pctVar": "% Var", - "noData": "No budget or transaction data for this period." + "noData": "No budget or transaction data for this period.", + "titlePrefix": "Budget vs Actual for" }, "dynamic": "Dynamic Report", "export": "Export", diff --git a/src/i18n/locales/fr.json b/src/i18n/locales/fr.json index 28abfd7..0f32f2c 100644 --- a/src/i18n/locales/fr.json +++ b/src/i18n/locales/fr.json @@ -377,7 +377,8 @@ "ytd": "Cumul annuel", "dollarVar": "$ \u00c9cart", "pctVar": "% \u00c9cart", - "noData": "Aucune donn\u00e9e de budget ou de transaction pour cette p\u00e9riode." + "noData": "Aucune donn\u00e9e de budget ou de transaction pour cette p\u00e9riode.", + "titlePrefix": "Budget vs Réel pour le mois de" }, "dynamic": "Rapport dynamique", "export": "Exporter", diff --git a/src/pages/ReportsPage.tsx b/src/pages/ReportsPage.tsx index 906a193..b654fd5 100644 --- a/src/pages/ReportsPage.tsx +++ b/src/pages/ReportsPage.tsx @@ -6,7 +6,6 @@ import { PageHelp } from "../components/shared/PageHelp"; import type { ReportTab, CategoryBreakdownItem, DashboardPeriod, ImportSource } from "../shared/types"; import { getAllSources } from "../services/importSourceService"; import PeriodSelector from "../components/dashboard/PeriodSelector"; -import MonthNavigator from "../components/budget/MonthNavigator"; import MonthlyTrendsChart from "../components/reports/MonthlyTrendsChart"; import MonthlyTrendsTable from "../components/reports/MonthlyTrendsTable"; import CategoryBarChart from "../components/reports/CategoryBarChart"; @@ -48,8 +47,8 @@ function computeDateRange( } export default function ReportsPage() { - const { t } = useTranslation(); - const { state, setTab, setPeriod, setCustomDates, navigateBudgetMonth, setPivotConfig, setSourceId } = useReports(); + const { t, i18n } = useTranslation(); + const { state, setTab, setPeriod, setCustomDates, setBudgetMonth, setPivotConfig, setSourceId } = useReports(); const [sources, setSources] = useState([]); useEffect(() => { @@ -100,16 +99,41 @@ export default function ReportsPage() {
-

{t("reports.title")}

+ {state.tab === "budgetVsActual" ? ( +

+ {t("reports.bva.titlePrefix")} + +

+ ) : ( +

{t("reports.title")}

+ )}
- {state.tab === "budgetVsActual" ? ( - - ) : ( + {state.tab !== "budgetVsActual" && (
+ {t("budget.category")} @@ -207,7 +207,7 @@ export default function BudgetVsActualTable({ data }: BudgetVsActualTableProps) return (
+ {section.label}
+ - {t(typeTotalKeys[section.type])}{t(typeTotalKeys[section.type])} {cadFormatter(sectionTotals.monthActual)}
{t("common.total")}{t("common.total")} {cadFormatter(totals.monthActual)}