Ajoute la répartition par type et le top clients par année sur /dashboard
Reprend exactement le pattern des petits multiples déjà en place sur /dashboard/compte (grille figli-year-hbar-grid, variante compacte de h-bar-chart) -- mêmes classes CSS, aucun nouveau style nécessaire. Backend : DashboardStatsController::stats() calcule maintenant aussi total_par_type_par_annee et top_clients_par_annee (top 8, contre 12 en toutes années confondues) à partir des mêmes requêtes SQL déjà en place, sans requête supplémentaire. Frontend : dashboard.js n'a jamais de données ligne par ligne (contraire- ment à dashboard-compte.js qui filtre côté client) -- l'agrégation par année doit donc venir du serveur. Ajout du prop "compact" au HBarChart de dashboard.js (jusqu'ici absent, seule la copie de dashboard-compte.js l'avait).
This commit is contained in:
@@ -54,6 +54,12 @@
|
||||
items: { type: Array, required: true },
|
||||
formatValue: { type: Function, required: true },
|
||||
colorFor: { type: Function, default: null },
|
||||
// Narrower label/value columns, smaller text -- for the per-année
|
||||
// small-multiples grids, where a full-width chart wouldn't fit in
|
||||
// a grid card. Same prop as dashboard-compte.js's own HBarChart
|
||||
// copy (this project duplicates the component rather than sharing
|
||||
// it between dashboard.js/dashboard-compte.js, see dashboard.css).
|
||||
compact: { type: Boolean, default: false },
|
||||
},
|
||||
computed: {
|
||||
hasNegative() {
|
||||
@@ -79,7 +85,7 @@
|
||||
},
|
||||
},
|
||||
template:
|
||||
'<div class="figli-hbar-chart">' +
|
||||
'<div class="figli-hbar-chart" :class="{\'is-compact\': compact}">' +
|
||||
'<div class="figli-hbar-row" v-for="item in items" :key="item.label">' +
|
||||
'<div class="figli-hbar-label" :title="item.label">{{ item.label }}</div>' +
|
||||
'<div class="figli-hbar-track" :class="{\'is-diverging\': hasNegative}">' +
|
||||
@@ -178,6 +184,36 @@
|
||||
if (!this.stats) return [];
|
||||
return this.stats.top_clients.map((c) => ({ label: c.client, value: c.ca }));
|
||||
},
|
||||
// Small multiples, one per year -- same source as typeItems() above
|
||||
// (total_par_type_par_annee is the same node-level SQL query, just
|
||||
// also grouped by année, no extra request). Mirrors
|
||||
// dashboard-compte.js's typeItemsParAnnee, but that one filters a
|
||||
// full row list client-side (it has one, scoped to a single
|
||||
// compte); this page never fetches full rows (see this file's
|
||||
// docblock), so the per-année breakdown has to already be
|
||||
// pre-aggregated server-side.
|
||||
typeItemsParAnnee() {
|
||||
if (!this.stats) return [];
|
||||
return this.stats.annees.map((annee) => {
|
||||
const parType = this.stats.total_par_type_par_annee[annee] || {};
|
||||
const items = Object.entries(parType)
|
||||
.map(([type, value]) => ({ label: TYPE_LABELS[type] || type, value, type }))
|
||||
.sort((a, b) => b.value - a.value);
|
||||
return { annee, items };
|
||||
}).filter((y) => y.items.length);
|
||||
},
|
||||
// Same idea for topClientsItems() -- top_clients_par_annee is
|
||||
// already capped to 8 per year server-side (see
|
||||
// DashboardStatsController::stats()), same reasoning as
|
||||
// dashboard-compte.js capping its own per-année version to 5.
|
||||
topClientsParAnnee() {
|
||||
if (!this.stats) return [];
|
||||
return this.stats.annees.map((annee) => {
|
||||
const items = (this.stats.top_clients_par_annee[annee] || [])
|
||||
.map((c) => ({ label: c.client, value: c.ca }));
|
||||
return { annee, items };
|
||||
}).filter((y) => y.items.length);
|
||||
},
|
||||
// Comptes ordered by all-time solde (richest first) -- same order
|
||||
// as soldeParCompteItems, so the trend grid below reads as a
|
||||
// continuation of the bar chart above it rather than an unrelated
|
||||
|
||||
Reference in New Issue
Block a user