diff --git a/app/api/stats/route.ts b/app/api/stats/route.ts
index 16ffb09..159b333 100644
--- a/app/api/stats/route.ts
+++ b/app/api/stats/route.ts
@@ -8,6 +8,12 @@ export async function GET() {
// Récupérer toutes les transactions
const transactions = await db.collection("transactions").find({}).toArray();
+ // Récupérer les conversations pour analyser les connexions utilisateurs
+ const conversations = await db.collection("conversations").find({}).toArray();
+
+ // Récupérer les messages pour une analyse plus précise de l'activité
+ const messages = await db.collection("messages").find({}).toArray();
+
console.log(`Total transactions trouvées: ${transactions.length}`);
// Vérifier les champs de date disponibles dans les transactions
@@ -106,14 +112,75 @@ export async function GET() {
.map(([name, value]) => ({ name, value }))
.sort((a, b) => b.value - a.value);
+ // Calculer les connexions utilisateurs par jour (7 derniers jours)
+ const dailyConnections = [];
+
+ for (let i = 6; i >= 0; i--) {
+ const date = new Date(today);
+ date.setDate(date.getDate() - i);
+ date.setHours(0, 0, 0, 0);
+
+ const nextDate = new Date(date);
+ nextDate.setDate(nextDate.getDate() + 1);
+
+ // Analyser l'activité des utilisateurs via les messages
+ const activeUsers = new Set();
+
+ messages.forEach(message => {
+ let messageDate = null;
+
+ if (message.createdAt) {
+ messageDate = new Date(message.createdAt);
+ } else if (message.updatedAt) {
+ messageDate = new Date(message.updatedAt);
+ } else if (message._id && message._id.getTimestamp) {
+ messageDate = message._id.getTimestamp();
+ }
+
+ if (messageDate && messageDate >= date && messageDate < nextDate) {
+ if (message.user && message.isCreatedByUser) {
+ activeUsers.add(message.user);
+ }
+ }
+ });
+
+ // Aussi analyser via les conversations créées ce jour-là
+ conversations.forEach(conversation => {
+ let convDate = null;
+
+ if (conversation.createdAt) {
+ convDate = new Date(conversation.createdAt);
+ } else if (conversation.updatedAt) {
+ convDate = new Date(conversation.updatedAt);
+ } else if (conversation._id && conversation._id.getTimestamp) {
+ convDate = conversation._id.getTimestamp();
+ }
+
+ if (convDate && convDate >= date && convDate < nextDate) {
+ if (conversation.user) {
+ activeUsers.add(conversation.user);
+ }
+ }
+ });
+
+ console.log(`${dayNames[date.getDay()]} (${date.toISOString().split('T')[0]}): ${activeUsers.size} utilisateurs actifs`);
+
+ dailyConnections.push({
+ name: dayNames[date.getDay()],
+ value: activeUsers.size
+ });
+ }
+
console.log("Statistiques calculées:", {
dailyStats,
+ dailyConnections,
totalModels: modelData.length,
topModel: modelData[0]
});
return NextResponse.json({
dailyTokens: dailyStats,
+ dailyConnections: dailyConnections,
modelDistribution: modelData
});
} catch (error) {
diff --git a/components/dashboard/add-credits.tsx b/components/dashboard/add-credits.tsx
index 5ad7fc2..7b68510 100644
--- a/components/dashboard/add-credits.tsx
+++ b/components/dashboard/add-credits.tsx
@@ -158,7 +158,7 @@ export default function AddCredits() {
⚠️ Action Importante
- Cette action va ajouter 5,000,000 crédits à
+ Cette action va ajouter 3,000,000 crédits à
chacun des {stats.totalUsers} utilisateurs.
Total de crédits qui seront ajoutés:{" "}
@@ -173,7 +173,7 @@ export default function AddCredits() {
>
{loading
? "Ajout en cours..."
- : `Ajouter 5M crédits à ${stats.totalUsers} utilisateurs`}
+ : `Ajouter 3M crédits à ${stats.totalUsers} utilisateurs`}
)}
diff --git a/components/dashboard/charts/user-connections-chart.tsx b/components/dashboard/charts/user-connections-chart.tsx
new file mode 100644
index 0000000..39dc1c7
--- /dev/null
+++ b/components/dashboard/charts/user-connections-chart.tsx
@@ -0,0 +1,80 @@
+"use client";
+
+import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
+import {
+ AreaChart,
+ Area,
+ XAxis,
+ YAxis,
+ CartesianGrid,
+ Tooltip,
+ ResponsiveContainer
+} from "recharts";
+
+interface UserConnectionsChartProps {
+ title: string;
+ data: Array<{
+ name: string;
+ value: number;
+ }>;
+ color?: string;
+}
+
+export function UserConnectionsChart({ title, data, color = "hsl(var(--chart-2))" }: UserConnectionsChartProps) {
+ console.log("UserConnectionsChart - data:", data);
+ console.log("UserConnectionsChart - title:", title);
+
+ return (
+
+
+ {title}
+
+
+
+
+
+
+
+
+
+
+
+
+ {
+ return value.toString();
+ }}
+ />
+ [
+ `${value} utilisateur${value > 1 ? 's' : ''}`,
+ 'Connexions'
+ ]}
+ />
+
+
+
+
+
+ );
+}
\ No newline at end of file
diff --git a/components/dashboard/dashboard-users-list.tsx b/components/dashboard/dashboard-users-list.tsx
index e4892cb..443fc3a 100644
--- a/components/dashboard/dashboard-users-list.tsx
+++ b/components/dashboard/dashboard-users-list.tsx
@@ -52,17 +52,17 @@ export function DashboardUsersList() {
// Récupérer toutes les données nécessaires
const { data: users, loading: usersLoading } =
- useCollection("users");
+ useCollection("users", { limit: 1000 });
const { data: conversations, loading: conversationsLoading } =
- useCollection("conversations");
+ useCollection("conversations", { limit: 1000 });
const { data: balances, loading: balancesLoading } =
- useCollection("balances");
+ useCollection("balances", { limit: 1000 });
const { data: messages, loading: messagesLoading } =
- useCollection("messages");
+ useCollection("messages", { limit: 1000 });
const { data: tokens, loading: tokensLoading } =
- useCollection("tokens");
+ useCollection("tokens", { limit: 1000 });
const { data: toolcalls, loading: toolcallsLoading } =
- useCollection("toolcalls");
+ useCollection("toolcalls", { limit: 1000 });
const processUsers = useCallback(() => {
if (
@@ -174,20 +174,23 @@ export function DashboardUsersList() {
const latestBalance = sortedBalances[0];
const credits = latestBalance ? latestBalance.tokenCredits || 0 : 0;
- // Calculer les tokens consommés depuis les crédits
- const INITIAL_CREDITS = 3000000;
- const creditsUsed = INITIAL_CREDITS - credits;
- const tokensFromCredits = creditsUsed > 0 ? creditsUsed : 0;
-
- // Prendre la valeur la plus élevée (plus précise)
+ // Calculer les tokens réellement consommés depuis les messages (approche principale)
const totalTokens = Math.max(
totalTokensFromMessages,
totalTokensFromConversations,
tokensFromTokensCollection,
- tokensFromToolcalls,
- tokensFromCredits
+ tokensFromToolcalls
);
+ // Calculer les tokens depuis les crédits seulement si on n'a pas de données de messages
+ const INITIAL_CREDITS = 3000000;
+ const creditsUsed = INITIAL_CREDITS - credits;
+ const tokensFromCredits = creditsUsed > 0 ? creditsUsed : 0;
+
+ // Si on n'a pas de tokens depuis les messages mais qu'on a une consommation de crédits significative
+ const finalTokens = totalTokens > 0 ? totalTokens :
+ (tokensFromCredits > 0 && tokensFromCredits < INITIAL_CREDITS) ? tokensFromCredits : 0;
+
// Log de débogage très détaillé
console.log(`👤 User ${user.name || user.email}:`, {
conversations: userConversations.length,
@@ -200,7 +203,8 @@ export function DashboardUsersList() {
currentCredits: credits,
creditsUsed: creditsUsed,
tokensFromCredits: tokensFromCredits,
- finalTokens: totalTokens,
+ finalTokens: finalTokens,
+ willBeIncluded: finalTokens > 0,
messagesSample: userMessages.slice(0, 2).map((m) => ({
tokenCount: m.tokenCount,
model: m.model,
@@ -213,14 +217,14 @@ export function DashboardUsersList() {
})),
});
- // Ajouter l'utilisateur seulement s'il a des données significatives
- if (userConversations.length > 0 || totalTokens > 0 || credits > 0) {
+ // Ajouter l'utilisateur s'il a consommé des tokens (éviter les faux positifs de 3M tokens)
+ if (finalTokens > 0 && finalTokens < INITIAL_CREDITS) {
processedUsers.push({
userId: user._id,
userName:
user.name || user.username || user.email || "Utilisateur inconnu",
conversations: userConversations.length,
- tokens: totalTokens,
+ tokens: finalTokens,
credits: credits,
});
}
@@ -231,6 +235,17 @@ export function DashboardUsersList() {
.sort((a, b) => b.tokens - a.tokens)
.slice(0, 5);
+ console.log("📊 Processing summary:", {
+ totalUsers: users.length,
+ usersWithActivity: processedUsers.length,
+ top5Users: sortedUsers.length,
+ allProcessedUsers: processedUsers.map(u => ({
+ name: u.userName,
+ conversations: u.conversations,
+ tokens: u.tokens,
+ credits: u.credits
+ }))
+ });
console.log("✅ Top 5 users processed:", sortedUsers);
setTopUsers(sortedUsers);
setIsLoading(false);
diff --git a/components/dashboard/overview-metrics.tsx b/components/dashboard/overview-metrics.tsx
index 77c79fa..1c9e620 100644
--- a/components/dashboard/overview-metrics.tsx
+++ b/components/dashboard/overview-metrics.tsx
@@ -2,7 +2,15 @@
import { useMetrics } from "@/hooks/useMetrics";
import { MetricCard } from "@/components/ui/metric-card";
-import { Users, UserCheck, Shield, Coins, MessageSquare, FileText, Euro, Activity } from "lucide-react";
+import {
+ Users,
+ UserCheck,
+ Coins,
+ MessageSquare,
+ FileText,
+ Euro,
+ Activity,
+} from "lucide-react";
import { convertCreditsToEuros } from "@/lib/utils/currency";
export function OverviewMetrics() {
@@ -30,62 +38,63 @@ export function OverviewMetrics() {
const creditsInEuros = convertCreditsToEuros(metrics.totalCredits);
return (
-
-
-
-
-
- {/* Nouvelle carte pour les tokens consommés */}
-
-
-
-
-
Crédits totaux
-
-
-
+
+ {/* Ligne 1: Utilisateurs actifs, Conversations actives, Tokens consommés */}
+
+
+
+
+
+
+ {/* Ligne 2: Utilisateurs totaux, Messages totaux, Crédits totaux */}
+
+
+
+
-
- {metrics.totalCredits.toLocaleString()}
-
-
crédits disponibles
-
-
- {creditsInEuros.formatted.eur}
+
+ {metrics.totalCredits.toLocaleString()}
-
- {creditsInEuros.formatted.usd} USD
+
crédits disponibles
+
+
+ {creditsInEuros.formatted.eur}
+
+
+ {creditsInEuros.formatted.usd} USD
+
-
-
);
-}
\ No newline at end of file
+}
diff --git a/components/dashboard/real-time-stats.tsx b/components/dashboard/real-time-stats.tsx
index 51bf1bb..4db0d23 100644
--- a/components/dashboard/real-time-stats.tsx
+++ b/components/dashboard/real-time-stats.tsx
@@ -1,13 +1,19 @@
"use client";
import { Card, CardContent } from "@/components/ui/card";
+import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs";
import { useStats } from "@/hooks/useStats";
import { SimpleStatsChart } from "./charts/simple-stats-chart";
+import { UserConnectionsChart } from "./charts/user-connections-chart";
import { ModelDistributionChart } from "./charts/model-distribution-chart";
import { AlertCircle } from "lucide-react";
export function RealTimeStats() {
const { stats, loading, error } = useStats();
+
+ console.log("RealTimeStats - stats:", stats);
+ console.log("RealTimeStats - loading:", loading);
+ console.log("RealTimeStats - error:", error);
if (loading) {
return (
@@ -67,16 +73,35 @@ export function RealTimeStats() {
}
return (
-
-
-
-
+
+
+ Nombre de connexions par utilisateur/par jour
+ Tokens consommés par jour
+
+
+
+
+
+
+
+
+
+
+
+
);
}
diff --git a/hooks/useStats.ts b/hooks/useStats.ts
index 671044e..afa2199 100644
--- a/hooks/useStats.ts
+++ b/hooks/useStats.ts
@@ -7,6 +7,11 @@ interface DailyToken {
value: number;
}
+interface DailyConnection {
+ name: string;
+ value: number;
+}
+
interface ModelDistribution {
name: string;
value: number;
@@ -14,6 +19,7 @@ interface ModelDistribution {
interface StatsData {
dailyTokens: DailyToken[];
+ dailyConnections: DailyConnection[];
modelDistribution: ModelDistribution[];
}