fix: display subcategories under their parent in dashboard budget table (#23)
- Fix group-aware sorting for depth-2 children in budget vs actual data - Reduce pie chart height and show legend only on hover - Add percentage to pie chart tooltip - Adjust dashboard grid layout to give more space to the budget table Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
8742c25945
commit
518bbedba8
5 changed files with 50 additions and 19 deletions
|
|
@ -5,6 +5,14 @@
|
||||||
### Ajouté
|
### 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)
|
- 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]
|
## [0.6.3]
|
||||||
|
|
||||||
### Ajouté
|
### Ajouté
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,14 @@
|
||||||
### Added
|
### Added
|
||||||
- Budget table: previous year total column displayed as first data column for baseline reference (#16)
|
- 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]
|
## [0.6.3]
|
||||||
|
|
||||||
### Added
|
### Added
|
||||||
|
|
|
||||||
|
|
@ -43,7 +43,7 @@ export default function CategoryPieChart({
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="bg-[var(--card)] rounded-xl p-6 border border-[var(--border)]">
|
<div className="group bg-[var(--card)] rounded-xl p-6 border border-[var(--border)]">
|
||||||
{hiddenCategories.size > 0 && (
|
{hiddenCategories.size > 0 && (
|
||||||
<div className="flex flex-wrap items-center gap-2 mb-3">
|
<div className="flex flex-wrap items-center gap-2 mb-3">
|
||||||
<span className="text-xs text-[var(--muted-foreground)]">{t("charts.hiddenCategories")}:</span>
|
<span className="text-xs text-[var(--muted-foreground)]">{t("charts.hiddenCategories")}:</span>
|
||||||
|
|
@ -67,7 +67,7 @@ export default function CategoryPieChart({
|
||||||
)}
|
)}
|
||||||
|
|
||||||
<div onContextMenu={handleContextMenu}>
|
<div onContextMenu={handleContextMenu}>
|
||||||
<ResponsiveContainer width="100%" height={280}>
|
<ResponsiveContainer width="100%" height={200}>
|
||||||
<PieChart>
|
<PieChart>
|
||||||
<ChartPatternDefs
|
<ChartPatternDefs
|
||||||
prefix="cat-pie"
|
prefix="cat-pie"
|
||||||
|
|
@ -94,9 +94,11 @@ export default function CategoryPieChart({
|
||||||
))}
|
))}
|
||||||
</Pie>
|
</Pie>
|
||||||
<Tooltip
|
<Tooltip
|
||||||
formatter={(value) =>
|
formatter={(value) => {
|
||||||
new Intl.NumberFormat("en-CA", { style: "currency", currency: "CAD" }).format(Number(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={{
|
contentStyle={{
|
||||||
backgroundColor: "var(--card)",
|
backgroundColor: "var(--card)",
|
||||||
border: "1px solid var(--border)",
|
border: "1px solid var(--border)",
|
||||||
|
|
@ -110,7 +112,7 @@ export default function CategoryPieChart({
|
||||||
</ResponsiveContainer>
|
</ResponsiveContainer>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="flex flex-wrap gap-x-4 gap-y-1 mt-2">
|
<div className="flex flex-wrap gap-x-4 gap-y-1 mt-2 max-h-0 overflow-hidden opacity-0 group-hover:max-h-96 group-hover:opacity-100 transition-all duration-300">
|
||||||
{data.map((item, index) => {
|
{data.map((item, index) => {
|
||||||
const isHidden = hiddenCategories.has(item.category_name);
|
const isHidden = hiddenCategories.has(item.category_name);
|
||||||
return (
|
return (
|
||||||
|
|
|
||||||
|
|
@ -126,7 +126,7 @@ export default function DashboardPage() {
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="grid grid-cols-1 lg:grid-cols-2 gap-4 mb-6">
|
<div className="grid grid-cols-1 lg:grid-cols-[2fr_3fr] gap-4 mb-6">
|
||||||
<div>
|
<div>
|
||||||
<h2 className="text-lg font-semibold mb-3">{t("dashboard.expensesByCategory")}</h2>
|
<h2 className="text-lg font-semibold mb-3">{t("dashboard.expensesByCategory")}</h2>
|
||||||
<CategoryPieChart
|
<CategoryPieChart
|
||||||
|
|
|
||||||
|
|
@ -404,13 +404,30 @@ export async function getBudgetVsActualData(
|
||||||
|
|
||||||
rows.push(parent);
|
rows.push(parent);
|
||||||
|
|
||||||
// Sort: "(direct)" first, then subtotals with their children, then alphabetical leaves
|
// Sort child rows while keeping depth-2 children grouped under their depth-1 parent
|
||||||
allChildRows.sort((a, b) => {
|
// Build groups: each depth-1 row optionally followed by its depth-2 children
|
||||||
if (a.category_id === cat.id && !a.is_parent) return -1;
|
const depth1Groups: { head: BudgetVsActualRow; children: BudgetVsActualRow[] }[] = [];
|
||||||
if (b.category_id === cat.id && !b.is_parent) return 1;
|
for (const row of allChildRows) {
|
||||||
return a.category_name.localeCompare(b.category_name);
|
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;
|
if (orderA !== orderB) return orderA - orderB;
|
||||||
return (catA?.name ?? "").localeCompare(catB?.name ?? "");
|
return (catA?.name ?? "").localeCompare(catB?.name ?? "");
|
||||||
}
|
}
|
||||||
// Within same group: sort by depth, then parent before children
|
// Within same group: preserve insertion order (already correctly structured)
|
||||||
if (a.is_parent !== b.is_parent && (a.depth ?? 0) === (b.depth ?? 0)) return a.is_parent ? -1 : 1;
|
return 0;
|
||||||
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);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
return rows;
|
return rows;
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue