diff --git a/CHANGELOG.fr.md b/CHANGELOG.fr.md index 7066584..ef44c28 100644 --- a/CHANGELOG.fr.md +++ b/CHANGELOG.fr.md @@ -5,6 +5,13 @@ ### 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 : les catégories de niveau 4 apparaissent maintenant directement sous leur parent dans le tableau budget vs réel au lieu d'en bas de la section (#23) + +### Modifié +- Tableau de bord : graphique circulaire réduit en taille et étiquettes de la légende affichées seulement au survol pour donner plus d'espace au tableau budget vs réel (#23) +- Tableau de bord : disposition du graphique circulaire et du tableau budget passée d'un ratio 1:1 à 1:2 pour une meilleure lisibilité du tableau (#23) + ## [0.6.3] ### Ajouté diff --git a/CHANGELOG.md b/CHANGELOG.md index edd44d6..4ac6cd5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,13 @@ ### Added - Budget table: previous year total column displayed as first data column for baseline reference (#16) +### Fixed +- Dashboard: level-4 categories now appear directly under their parent in the budget vs actual table instead of at the bottom of the section (#23) + +### Changed +- Dashboard: pie chart reduced in size and legend labels only shown on hover to give more space to the budget vs actual table (#23) +- Dashboard: pie chart and budget table layout changed from equal 1:1 to 1:2 ratio for better table readability (#23) + ## [0.6.3] ### Added diff --git a/src/components/dashboard/CategoryPieChart.tsx b/src/components/dashboard/CategoryPieChart.tsx index 2fe8195..76377a9 100644 --- a/src/components/dashboard/CategoryPieChart.tsx +++ b/src/components/dashboard/CategoryPieChart.tsx @@ -24,6 +24,7 @@ export default function CategoryPieChart({ const { t } = useTranslation(); const hoveredRef = useRef(null); const [contextMenu, setContextMenu] = useState<{ x: number; y: number; item: CategoryBreakdownItem } | null>(null); + const [showLegend, setShowLegend] = useState(false); const visibleData = data.filter((d) => !hiddenCategories.has(d.category_name)); const total = visibleData.reduce((sum, d) => sum + d.total, 0); @@ -66,8 +67,12 @@ export default function CategoryPieChart({ )} -
- +
setShowLegend(true)} + onMouseLeave={() => setShowLegend(false)} + > + {visibleData.map((item, index) => ( @@ -110,7 +115,11 @@ 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..4bf8340 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); - }); - rows.push(...allChildRows); + // Sort preserving parent-child grouping: + // 1. "(direct)" first + // 2. Sub-groups (depth-1 parent + its depth-2 children) stay together, sorted by parent name + // 3. Standalone depth-1 leaves sorted alphabetically + const directRow = allChildRows.find((r) => r.category_id === cat.id && !r.is_parent); + const subGroups: { parent: BudgetVsActualRow; children: BudgetVsActualRow[] }[] = []; + const standaloneLeaves: BudgetVsActualRow[] = []; + + for (const row of allChildRows) { + if (row.category_id === cat.id && !row.is_parent) continue; // skip direct row + if (row.is_parent && row.depth === 1) { + subGroups.push({ parent: row, children: [] }); + } else if (row.depth === 2 && subGroups.length > 0) { + // Find the matching sub-group for this child + const matchingGroup = subGroups.find((g) => g.parent.category_id === row.parent_id); + if (matchingGroup) { + matchingGroup.children.push(row); + } else { + standaloneLeaves.push(row); + } + } else { + standaloneLeaves.push(row); + } + } + + // Sort sub-groups by parent name, children within each group alphabetically + subGroups.sort((a, b) => a.parent.category_name.localeCompare(b.parent.category_name)); + for (const group of subGroups) { + group.children.sort((a, b) => { + // "(direct)" entries first within group + if (a.category_id === group.parent.category_id) return -1; + if (b.category_id === group.parent.category_id) return 1; + return a.category_name.localeCompare(b.category_name); + }); + } + standaloneLeaves.sort((a, b) => a.category_name.localeCompare(b.category_name)); + + const sortedChildren: BudgetVsActualRow[] = []; + if (directRow) sortedChildren.push(directRow); + for (const group of subGroups) { + sortedChildren.push(group.parent, ...group.children); + } + sortedChildren.push(...standaloneLeaves); + rows.push(...sortedChildren); } }