Checkpoint: Budget réel confirmé en ordre décroissant de montant total ; graphique en barres annuel comparant les montants BAP et abonnements ajouté au tableau de bord.
All checks were successful
Validation applicative / TypeScript, tests et build (push) Successful in 3m19s

This commit is contained in:
Manus
2026-08-30 08:56:27 +00:00
parent 5128322a6d
commit 9997e383cc
3 changed files with 34 additions and 5 deletions

View File

@@ -155,12 +155,27 @@ export default function Dashboard() {
</CardHeader> </CardHeader>
<CardContent> <CardContent>
{stats?.annualSummary?.length ? ( {stats?.annualSummary?.length ? (
<div className="space-y-6">
<div className="h-[300px] rounded-lg border bg-white p-4">
<ResponsiveContainer width="100%" height="100%">
<BarChart data={[...stats.annualSummary].reverse()} margin={{ top: 8, right: 16, left: 12, bottom: 4 }}>
<CartesianGrid strokeDasharray="3 3" vertical={false} />
<XAxis dataKey="year" />
<YAxis tickFormatter={(value) => new Intl.NumberFormat("fr-FR", { notation: "compact", maximumFractionDigits: 1 }).format(value)} />
<Tooltip formatter={(value: number) => formatCurrency(Number(value))} labelFormatter={(year) => `Année ${year}`} />
<Legend />
<Bar dataKey="bapAmount" name="Montant BAP" fill="#2563eb" radius={[4, 4, 0, 0]} />
<Bar dataKey="subscriptionAmount" name="Montant abonnements" fill="#7c3aed" radius={[4, 4, 0, 0]} />
</BarChart>
</ResponsiveContainer>
</div>
<div className="overflow-x-auto rounded-lg border"> <div className="overflow-x-auto rounded-lg border">
<table className="w-full min-w-[780px] text-sm"> <table className="w-full min-w-[780px] text-sm">
<thead className="bg-muted/60 text-left text-muted-foreground"><tr><th className="px-4 py-3 font-medium">Année</th><th className="px-4 py-3 text-right font-medium">Factures BAP</th><th className="px-4 py-3 text-right font-medium">Montant BAP</th><th className="px-4 py-3 text-right font-medium">Abonnements</th><th className="px-4 py-3 text-right font-medium">Montant abonnements</th><th className="px-4 py-3 text-right font-medium">Total annuel</th></tr></thead> <thead className="bg-muted/60 text-left text-muted-foreground"><tr><th className="px-4 py-3 font-medium">Année</th><th className="px-4 py-3 text-right font-medium">Factures BAP</th><th className="px-4 py-3 text-right font-medium">Montant BAP</th><th className="px-4 py-3 text-right font-medium">Abonnements</th><th className="px-4 py-3 text-right font-medium">Montant abonnements</th><th className="px-4 py-3 text-right font-medium">Total annuel</th></tr></thead>
<tbody>{stats.annualSummary.map((row) => <tr key={row.year} className="border-t hover:bg-muted/30"><td className="px-4 py-3 font-semibold">{row.year}</td><td className="px-4 py-3 text-right">{row.bapCount}</td><td className="px-4 py-3 text-right text-blue-700">{formatCurrency(row.bapAmount)}</td><td className="px-4 py-3 text-right">{row.subscriptionCount}</td><td className="px-4 py-3 text-right text-violet-700">{formatCurrency(row.subscriptionAmount)}</td><td className="px-4 py-3 text-right font-semibold text-emerald-700">{formatCurrency(row.totalAmount)}</td></tr>)}</tbody> <tbody>{stats.annualSummary.map((row) => <tr key={row.year} className="border-t hover:bg-muted/30"><td className="px-4 py-3 font-semibold">{row.year}</td><td className="px-4 py-3 text-right">{row.bapCount}</td><td className="px-4 py-3 text-right text-blue-700">{formatCurrency(row.bapAmount)}</td><td className="px-4 py-3 text-right">{row.subscriptionCount}</td><td className="px-4 py-3 text-right text-violet-700">{formatCurrency(row.subscriptionAmount)}</td><td className="px-4 py-3 text-right font-semibold text-emerald-700">{formatCurrency(row.totalAmount)}</td></tr>)}</tbody>
</table> </table>
</div> </div>
</div>
) : <div className="py-8 text-center text-muted-foreground">Aucune facture finalisée avec une date disponible.</div>} ) : <div className="py-8 text-center text-muted-foreground">Aucune facture finalisée avec une date disponible.</div>}
</CardContent> </CardContent>
</Card> </Card>

View File

@@ -21,4 +21,13 @@ describe("agrégats de facturation", () => {
{ supplierName: "SFR", subscriptionCount: 1, subscriptionAmount: 20, nonSubscriptionCount: 1, nonSubscriptionAmount: 100, totalAmount: 120 }, { supplierName: "SFR", subscriptionCount: 1, subscriptionAmount: 20, nonSubscriptionCount: 1, nonSubscriptionAmount: 100, totalAmount: 120 },
]); ]);
}); });
it("classe le budget du fournisseur le plus facturé au moins facturé", () => {
const rows = buildSupplierBudgetSummary([
...invoices,
{ invoiceDate: "2026-05-22", createdAt: "2026-05-22", status: "completed", isSubscription: 0, supplierName: "Orange", totalAmount: "300" },
], "2026", "05");
expect(rows.map((row) => row.supplierName)).toEqual(["Orange", "SFR"]);
});
}); });

View File

@@ -820,3 +820,8 @@
- [x] Créer lécran Budget réel à la fin du menu Facturation - [x] Créer lécran Budget réel à la fin du menu Facturation
- [x] Afficher par fournisseur les montants Abonnement, Hors abonnement et le total - [x] Afficher par fournisseur les montants Abonnement, Hors abonnement et le total
- [x] Ajouter les tests et valider TypeScript, build et affichage en sandbox - [x] Ajouter les tests et valider TypeScript, build et affichage en sandbox
## Améliorations Budget réel et tableau de bord
- [x] Confirmer le tri décroissant par montant total dans le budget
- [x] Ajouter un graphique annuel BAP versus abonnements au tableau de bord
- [x] Ajouter les tests et valider TypeScript, build et rendu sandbox