- {recentInvoices.map((invoice) => (
-
-
-
{invoice.supplierName || "Fournisseur inconnu"}
-
- {invoice.invoiceNumber || "N° inconnu"} • {invoice.invoiceDate ? new Date(invoice.invoiceDate).toLocaleDateString("fr-FR") : "Date inconnue"}
-
+
Qualité d'extraction
+
+
+
+
+
+ Dernière activité
+
+
+
+
+ {stats?.lastActivity
+ ? new Date(stats.lastActivity).toLocaleDateString('fr-FR', { day: '2-digit', month: '2-digit' })
+ : 'N/A'}
+
+
+ {stats?.lastActivity
+ ? new Date(stats.lastActivity).toLocaleTimeString('fr-FR', { hour: '2-digit', minute: '2-digit' })
+ : ''}
+
+
+
+
+
+ {/* Detailed Stats */}
+ {showDetailedStats && (
+ <>
+ {/* Charts Row */}
+
+ {/* Weekly Chart */}
+
+
+ Factures par semaine
+
+ Nombre de factures traitées chaque semaine (30 derniers jours)
+
+
+
+ {stats?.weeklyData && stats.weeklyData.length > 0 ? (
+
+
+
+
+
+
+
+
+
+ ) : (
+
+ Aucune donnée disponible
-
-
- {invoice.totalAmount ? `${parseFloat(invoice.totalAmount).toFixed(2)} €` : "-"}
-
-
- {invoice.status === "completed" && (
- Complété
- )}
- {invoice.status === "processing" && (
- En cours
- )}
- {invoice.status === "error" && (
- Erreur
- )}
-
+ )}
+
+
+
+ {/* Top Suppliers Pie Chart */}
+
+
+ Top 10 fournisseurs
+
+ Répartition des factures par fournisseur (30 derniers jours)
+
+
+
+ {stats?.topSuppliers && stats.topSuppliers.length > 0 ? (
+
+
+ `${entry.name}: ${entry.percentage.toFixed(0)}%`}
+ outerRadius={80}
+ fill="#8884d8"
+ dataKey="count"
+ >
+ {stats.topSuppliers.map((entry, index) => (
+ |
+ ))}
+
+
+
+
+ ) : (
+
+ Aucune donnée disponible
+ )}
+
+
+
+
+ {/* Suppliers List */}
+
+
+ Détail par fournisseur
+ Liste des fournisseurs (30 derniers jours)
+
+
+ {stats?.suppliersList && stats.suppliersList.length > 0 ? (
+
+ {stats.suppliersList.map((supplier, index) => (
+
+
+
+
{supplier.count} factures
+
+ {formatCurrency(supplier.amount)}
+
+
+
+ ))}
- ))}
-
- )}
-
-
+ ) : (
+
+ Aucun fournisseur trouvé
+
+ )}
+
+
+ >
+ )}
);
diff --git a/server/db.ts b/server/db.ts
index 81be172..1ab2bfb 100644
--- a/server/db.ts
+++ b/server/db.ts
@@ -252,14 +252,97 @@ export async function searchInvoices(userId: number, query: string): Promise
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();
+
+ 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();
+ 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
};
}
diff --git a/todo.md b/todo.md
index 223d492..bfb5aa1 100644
--- a/todo.md
+++ b/todo.md
@@ -66,3 +66,11 @@
- [x] Diagnostiquer le problème de connexion locale avec o.pareige@itinova.org
- [x] Corriger l'authentification locale (ajout support cookie auth_token)
+
+## Amélioration du Dashboard
+- [x] Créer route tRPC pour statistiques détaillées (factures par semaine, top fournisseurs)
+- [x] Créer cartes de statistiques (Factures traitées, Montant total, Score moyen, Dernière activité)
+- [x] Créer graphique en barres des factures par semaine (Recharts)
+- [x] Créer graphique circulaire du top 10 fournisseurs (Recharts)
+- [x] Créer liste détaillée des fournisseurs avec nombre de factures
+- [x] Ajouter bouton "Voir les statistiques détaillées"