diff --git a/CHANGELOG.fr.md b/CHANGELOG.fr.md
index 7066584..c9abc8e 100644
--- a/CHANGELOG.fr.md
+++ b/CHANGELOG.fr.md
@@ -5,6 +5,14 @@
### 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)
+### Corrigé
+- Tableau de bord budget vs réel : les sous-catégories de niveau 3+ s'affichent maintenant sous leur parent au lieu d'en bas de la section (#23)
+
+### Modifié
+- Tableau de bord : la légende du graphique circulaire s'affiche maintenant uniquement au survol, réduisant l'espace et donnant plus de place au tableau budget (#23)
+- Tableau de bord : l'infobulle du graphique circulaire affiche maintenant le pourcentage en plus du montant (#23)
+- Tableau de bord : disposition ajustée pour donner plus d'espace au tableau budget (ratio 2:3) (#23)
+
## [0.6.3]
### Ajouté
diff --git a/CHANGELOG.md b/CHANGELOG.md
index edd44d6..f1935a3 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -5,6 +5,14 @@
### Added
- Budget table: previous year total column displayed as first data column for baseline reference (#16)
+### Fixed
+- Dashboard budget vs actual table: level 3+ subcategories now appear under their parent instead of at the bottom of the section (#23)
+
+### Changed
+- Dashboard: pie chart legend now appears only on hover, reducing space and giving more room to the budget table (#23)
+- Dashboard: pie chart tooltip now shows percentage alongside amount (#23)
+- Dashboard: adjusted grid layout to give more space to the budget table (2:3 ratio) (#23)
+
## [0.6.3]
### Added
diff --git a/src/components/dashboard/CategoryPieChart.tsx b/src/components/dashboard/CategoryPieChart.tsx
index 2fe8195..20f61e0 100644
--- a/src/components/dashboard/CategoryPieChart.tsx
+++ b/src/components/dashboard/CategoryPieChart.tsx
@@ -43,7 +43,7 @@ export default function CategoryPieChart({
}
return (
-
+
{hiddenCategories.size > 0 && (
{t("charts.hiddenCategories")}:
@@ -67,7 +67,7 @@ export default function CategoryPieChart({
)}
-
+
- new Intl.NumberFormat("en-CA", { style: "currency", currency: "CAD" }).format(Number(value))
- }
+ formatter={(value) => {
+ const amount = new Intl.NumberFormat("en-CA", { style: "currency", currency: "CAD" }).format(Number(value));
+ const pct = total > 0 ? ` (${Math.round((Number(value) / total) * 100)}%)` : "";
+ return `${amount}${pct}`;
+ }}
contentStyle={{
backgroundColor: "var(--card)",
border: "1px solid var(--border)",
@@ -110,7 +112,7 @@ export default function CategoryPieChart({
-
+
{data.map((item, index) => {
const isHidden = hiddenCategories.has(item.category_name);
return (
diff --git a/src/pages/DashboardPage.tsx b/src/pages/DashboardPage.tsx
index d484fbf..3349e24 100644
--- a/src/pages/DashboardPage.tsx
+++ b/src/pages/DashboardPage.tsx
@@ -126,7 +126,7 @@ export default function DashboardPage() {
))}
-
+
{t("dashboard.expensesByCategory")}
{
- if (a.category_id === cat.id && !a.is_parent) return -1;
- if (b.category_id === cat.id && !b.is_parent) return 1;
- return a.category_name.localeCompare(b.category_name);
+ // Sort child rows while keeping depth-2 children grouped under their depth-1 parent
+ // Build groups: each depth-1 row optionally followed by its depth-2 children
+ const depth1Groups: { head: BudgetVsActualRow; children: BudgetVsActualRow[] }[] = [];
+ for (const row of allChildRows) {
+ if ((row.depth ?? 0) <= 1) {
+ depth1Groups.push({ head: row, children: [] });
+ }
+ }
+ for (const row of allChildRows) {
+ if ((row.depth ?? 0) === 2) {
+ const parentGroup = depth1Groups.find((g) => g.head.category_id === row.parent_id);
+ if (parentGroup) parentGroup.children.push(row);
+ }
+ }
+ // Sort groups: "(direct)" first, then alphabetical by group name
+ depth1Groups.sort((a, b) => {
+ if (a.head.category_id === cat.id && !a.head.is_parent) return -1;
+ if (b.head.category_id === cat.id && !b.head.is_parent) return 1;
+ return a.head.category_name.localeCompare(b.head.category_name);
});
- rows.push(...allChildRows);
+ // Flatten: each group's head followed by its children
+ for (const group of depth1Groups) {
+ rows.push(group.head, ...group.children);
+ }
}
}
@@ -442,12 +459,8 @@ export async function getBudgetVsActualData(
if (orderA !== orderB) return orderA - orderB;
return (catA?.name ?? "").localeCompare(catB?.name ?? "");
}
- // Within same group: sort by depth, then parent before children
- if (a.is_parent !== b.is_parent && (a.depth ?? 0) === (b.depth ?? 0)) return a.is_parent ? -1 : 1;
- if ((a.depth ?? 0) !== (b.depth ?? 0)) return (a.depth ?? 0) - (b.depth ?? 0);
- if (a.parent_id && a.category_id === a.parent_id) return -1;
- if (b.parent_id && b.category_id === b.parent_id) return 1;
- return a.category_name.localeCompare(b.category_name);
+ // Within same group: preserve insertion order (already correctly structured)
+ return 0;
});
return rows;