Checkpoint: Ajout d'un tableau de bord détaillé avec statistiques avancées et graphiques.
Nouvelles fonctionnalités : - Cartes de statistiques : Factures traitées, Montant total, Score moyen, Dernière activité - Graphique en barres : Évolution des factures par semaine (30 derniers jours) - Graphique circulaire : Top 10 fournisseurs avec pourcentages - Liste détaillée : Tous les fournisseurs avec nombre de factures et montants - Bouton "Voir les statistiques détaillées" pour afficher/masquer les graphiques - Calculs automatiques : montant moyen par facture, score de qualité avec label (Excellent/Bon/Moyen/Faible) Le dashboard utilise Recharts pour les visualisations et offre une vue complète de l'activité de traitement des factures.
This commit is contained in:
87
server/db.ts
87
server/db.ts
@@ -252,14 +252,97 @@ export async function searchInvoices(userId: number, query: string): Promise<Inv
|
||||
|
||||
export async function getInvoiceStats(userId: number) {
|
||||
const db = await getDb();
|
||||
if (!db) return { total: 0, completed: 0, processing: 0, error: 0 };
|
||||
if (!db) return {
|
||||
total: 0,
|
||||
completed: 0,
|
||||
processing: 0,
|
||||
error: 0,
|
||||
totalAmount: 0,
|
||||
averageScore: 0,
|
||||
lastActivity: null,
|
||||
weeklyData: [],
|
||||
topSuppliers: [],
|
||||
suppliersList: []
|
||||
};
|
||||
|
||||
const allInvoices = await getInvoicesByUserId(userId);
|
||||
|
||||
// Calculate basic stats
|
||||
const completed = allInvoices.filter(i => i.status === "completed");
|
||||
const totalAmount = completed.reduce((sum, inv) => sum + (Number(inv.totalAmount) || 0), 0);
|
||||
const averageScore = completed.length > 0
|
||||
? completed.reduce((sum, inv) => sum + (inv.qualityScore || 0), 0) / completed.length
|
||||
: 0;
|
||||
|
||||
// Get last activity
|
||||
const sortedByDate = [...allInvoices].sort((a, b) =>
|
||||
new Date(b.createdAt).getTime() - new Date(a.createdAt).getTime()
|
||||
);
|
||||
const lastActivity = sortedByDate[0]?.createdAt || null;
|
||||
|
||||
// Calculate weekly data (last 30 days)
|
||||
const now = new Date();
|
||||
const thirtyDaysAgo = new Date(now.getTime() - 30 * 24 * 60 * 60 * 1000);
|
||||
const weeklyMap = new Map<string, number>();
|
||||
|
||||
allInvoices.forEach(inv => {
|
||||
const date = new Date(inv.createdAt);
|
||||
if (date >= thirtyDaysAgo) {
|
||||
const weekStart = new Date(date);
|
||||
weekStart.setDate(date.getDate() - date.getDay()); // Start of week
|
||||
const weekKey = `${weekStart.getDate().toString().padStart(2, '0')}/${(weekStart.getMonth() + 1).toString().padStart(2, '0')}`;
|
||||
weeklyMap.set(weekKey, (weeklyMap.get(weekKey) || 0) + 1);
|
||||
}
|
||||
});
|
||||
|
||||
const weeklyData = Array.from(weeklyMap.entries())
|
||||
.map(([week, count]) => ({ week, count }))
|
||||
.sort((a, b) => {
|
||||
const [dayA, monthA] = a.week.split('/').map(Number);
|
||||
const [dayB, monthB] = b.week.split('/').map(Number);
|
||||
return (monthA! * 100 + dayA!) - (monthB! * 100 + dayB!);
|
||||
});
|
||||
|
||||
// Calculate top suppliers
|
||||
const supplierMap = new Map<string, { count: number; amount: number }>();
|
||||
completed.forEach(inv => {
|
||||
const supplier = inv.supplierName || 'Inconnu';
|
||||
const current = supplierMap.get(supplier) || { count: 0, amount: 0 };
|
||||
supplierMap.set(supplier, {
|
||||
count: current.count + 1,
|
||||
amount: current.amount + (Number(inv.totalAmount) || 0)
|
||||
});
|
||||
});
|
||||
|
||||
const topSuppliers = Array.from(supplierMap.entries())
|
||||
.map(([name, data]) => ({
|
||||
name,
|
||||
count: data.count,
|
||||
amount: data.amount,
|
||||
percentage: completed.length > 0 ? (data.count / completed.length * 100) : 0
|
||||
}))
|
||||
.sort((a, b) => b.count - a.count)
|
||||
.slice(0, 10);
|
||||
|
||||
const suppliersList = Array.from(supplierMap.entries())
|
||||
.map(([name, data]) => ({
|
||||
name,
|
||||
count: data.count,
|
||||
amount: data.amount
|
||||
}))
|
||||
.sort((a, b) => b.count - a.count);
|
||||
|
||||
return {
|
||||
total: allInvoices.length,
|
||||
completed: allInvoices.filter(i => i.status === "completed").length,
|
||||
completed: completed.length,
|
||||
processing: allInvoices.filter(i => i.status === "processing").length,
|
||||
error: allInvoices.filter(i => i.status === "error").length,
|
||||
totalAmount,
|
||||
averageScore: Math.round(averageScore),
|
||||
lastActivity,
|
||||
weeklyData,
|
||||
topSuppliers,
|
||||
suppliersList
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user