applicatif 3M, user, chart
This commit is contained in:
@@ -8,6 +8,12 @@ export async function GET() {
|
|||||||
// Récupérer toutes les transactions
|
// Récupérer toutes les transactions
|
||||||
const transactions = await db.collection("transactions").find({}).toArray();
|
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}`);
|
console.log(`Total transactions trouvées: ${transactions.length}`);
|
||||||
|
|
||||||
// Vérifier les champs de date disponibles dans les transactions
|
// Vérifier les champs de date disponibles dans les transactions
|
||||||
@@ -106,14 +112,75 @@ export async function GET() {
|
|||||||
.map(([name, value]) => ({ name, value }))
|
.map(([name, value]) => ({ name, value }))
|
||||||
.sort((a, b) => b.value - a.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:", {
|
console.log("Statistiques calculées:", {
|
||||||
dailyStats,
|
dailyStats,
|
||||||
|
dailyConnections,
|
||||||
totalModels: modelData.length,
|
totalModels: modelData.length,
|
||||||
topModel: modelData[0]
|
topModel: modelData[0]
|
||||||
});
|
});
|
||||||
|
|
||||||
return NextResponse.json({
|
return NextResponse.json({
|
||||||
dailyTokens: dailyStats,
|
dailyTokens: dailyStats,
|
||||||
|
dailyConnections: dailyConnections,
|
||||||
modelDistribution: modelData
|
modelDistribution: modelData
|
||||||
});
|
});
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
|||||||
@@ -158,7 +158,7 @@ export default function AddCredits() {
|
|||||||
⚠️ Action Importante
|
⚠️ Action Importante
|
||||||
</h4>
|
</h4>
|
||||||
<p className="text-yellow-700 text-sm">
|
<p className="text-yellow-700 text-sm">
|
||||||
Cette action va ajouter <strong>5,000,000 crédits</strong> à
|
Cette action va ajouter <strong>3,000,000 crédits</strong> à
|
||||||
chacun des {stats.totalUsers} utilisateurs.
|
chacun des {stats.totalUsers} utilisateurs.
|
||||||
<br />
|
<br />
|
||||||
Total de crédits qui seront ajoutés:{" "}
|
Total de crédits qui seront ajoutés:{" "}
|
||||||
@@ -173,7 +173,7 @@ export default function AddCredits() {
|
|||||||
>
|
>
|
||||||
{loading
|
{loading
|
||||||
? "Ajout en cours..."
|
? "Ajout en cours..."
|
||||||
: `Ajouter 5M crédits à ${stats.totalUsers} utilisateurs`}
|
: `Ajouter 3M crédits à ${stats.totalUsers} utilisateurs`}
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|||||||
80
components/dashboard/charts/user-connections-chart.tsx
Normal file
80
components/dashboard/charts/user-connections-chart.tsx
Normal file
@@ -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 (
|
||||||
|
<Card>
|
||||||
|
<CardHeader className="pb-2">
|
||||||
|
<CardTitle className="text-sm font-medium text-muted-foreground">{title}</CardTitle>
|
||||||
|
</CardHeader>
|
||||||
|
<CardContent className="pt-0">
|
||||||
|
<ResponsiveContainer width="100%" height={200}>
|
||||||
|
<AreaChart data={data}>
|
||||||
|
<defs>
|
||||||
|
<linearGradient id="connectionsGradient" x1="0" y1="0" x2="0" y2="1">
|
||||||
|
<stop offset="5%" stopColor={color} stopOpacity={0.8}/>
|
||||||
|
<stop offset="95%" stopColor={color} stopOpacity={0.2}/>
|
||||||
|
</linearGradient>
|
||||||
|
</defs>
|
||||||
|
<CartesianGrid strokeDasharray="3 3" className="stroke-muted/20" />
|
||||||
|
<XAxis
|
||||||
|
dataKey="name"
|
||||||
|
axisLine={false}
|
||||||
|
tickLine={false}
|
||||||
|
className="text-xs fill-muted-foreground"
|
||||||
|
/>
|
||||||
|
<YAxis
|
||||||
|
axisLine={false}
|
||||||
|
tickLine={false}
|
||||||
|
className="text-xs fill-muted-foreground"
|
||||||
|
tickFormatter={(value) => {
|
||||||
|
return value.toString();
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
<Tooltip
|
||||||
|
contentStyle={{
|
||||||
|
backgroundColor: 'hsl(var(--background))',
|
||||||
|
border: '1px solid hsl(var(--border))',
|
||||||
|
borderRadius: '8px',
|
||||||
|
fontSize: '12px'
|
||||||
|
}}
|
||||||
|
formatter={(value: number) => [
|
||||||
|
`${value} utilisateur${value > 1 ? 's' : ''}`,
|
||||||
|
'Connexions'
|
||||||
|
]}
|
||||||
|
/>
|
||||||
|
<Area
|
||||||
|
type="monotone"
|
||||||
|
dataKey="value"
|
||||||
|
stroke={color}
|
||||||
|
strokeWidth={3}
|
||||||
|
fill="url(#connectionsGradient)"
|
||||||
|
/>
|
||||||
|
</AreaChart>
|
||||||
|
</ResponsiveContainer>
|
||||||
|
</CardContent>
|
||||||
|
</Card>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -52,17 +52,17 @@ export function DashboardUsersList() {
|
|||||||
|
|
||||||
// Récupérer toutes les données nécessaires
|
// Récupérer toutes les données nécessaires
|
||||||
const { data: users, loading: usersLoading } =
|
const { data: users, loading: usersLoading } =
|
||||||
useCollection<LibreChatUser>("users");
|
useCollection<LibreChatUser>("users", { limit: 1000 });
|
||||||
const { data: conversations, loading: conversationsLoading } =
|
const { data: conversations, loading: conversationsLoading } =
|
||||||
useCollection<LibreChatConversation>("conversations");
|
useCollection<LibreChatConversation>("conversations", { limit: 1000 });
|
||||||
const { data: balances, loading: balancesLoading } =
|
const { data: balances, loading: balancesLoading } =
|
||||||
useCollection<LibreChatBalance>("balances");
|
useCollection<LibreChatBalance>("balances", { limit: 1000 });
|
||||||
const { data: messages, loading: messagesLoading } =
|
const { data: messages, loading: messagesLoading } =
|
||||||
useCollection<LibreChatMessage>("messages");
|
useCollection<LibreChatMessage>("messages", { limit: 1000 });
|
||||||
const { data: tokens, loading: tokensLoading } =
|
const { data: tokens, loading: tokensLoading } =
|
||||||
useCollection<TokenDocument>("tokens");
|
useCollection<TokenDocument>("tokens", { limit: 1000 });
|
||||||
const { data: toolcalls, loading: toolcallsLoading } =
|
const { data: toolcalls, loading: toolcallsLoading } =
|
||||||
useCollection<ToolcallDocument>("toolcalls");
|
useCollection<ToolcallDocument>("toolcalls", { limit: 1000 });
|
||||||
|
|
||||||
const processUsers = useCallback(() => {
|
const processUsers = useCallback(() => {
|
||||||
if (
|
if (
|
||||||
@@ -174,20 +174,23 @@ export function DashboardUsersList() {
|
|||||||
const latestBalance = sortedBalances[0];
|
const latestBalance = sortedBalances[0];
|
||||||
const credits = latestBalance ? latestBalance.tokenCredits || 0 : 0;
|
const credits = latestBalance ? latestBalance.tokenCredits || 0 : 0;
|
||||||
|
|
||||||
// Calculer les tokens consommés depuis les crédits
|
// Calculer les tokens réellement consommés depuis les messages (approche principale)
|
||||||
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)
|
|
||||||
const totalTokens = Math.max(
|
const totalTokens = Math.max(
|
||||||
totalTokensFromMessages,
|
totalTokensFromMessages,
|
||||||
totalTokensFromConversations,
|
totalTokensFromConversations,
|
||||||
tokensFromTokensCollection,
|
tokensFromTokensCollection,
|
||||||
tokensFromToolcalls,
|
tokensFromToolcalls
|
||||||
tokensFromCredits
|
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// 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é
|
// Log de débogage très détaillé
|
||||||
console.log(`👤 User ${user.name || user.email}:`, {
|
console.log(`👤 User ${user.name || user.email}:`, {
|
||||||
conversations: userConversations.length,
|
conversations: userConversations.length,
|
||||||
@@ -200,7 +203,8 @@ export function DashboardUsersList() {
|
|||||||
currentCredits: credits,
|
currentCredits: credits,
|
||||||
creditsUsed: creditsUsed,
|
creditsUsed: creditsUsed,
|
||||||
tokensFromCredits: tokensFromCredits,
|
tokensFromCredits: tokensFromCredits,
|
||||||
finalTokens: totalTokens,
|
finalTokens: finalTokens,
|
||||||
|
willBeIncluded: finalTokens > 0,
|
||||||
messagesSample: userMessages.slice(0, 2).map((m) => ({
|
messagesSample: userMessages.slice(0, 2).map((m) => ({
|
||||||
tokenCount: m.tokenCount,
|
tokenCount: m.tokenCount,
|
||||||
model: m.model,
|
model: m.model,
|
||||||
@@ -213,14 +217,14 @@ export function DashboardUsersList() {
|
|||||||
})),
|
})),
|
||||||
});
|
});
|
||||||
|
|
||||||
// Ajouter l'utilisateur seulement s'il a des données significatives
|
// Ajouter l'utilisateur s'il a consommé des tokens (éviter les faux positifs de 3M tokens)
|
||||||
if (userConversations.length > 0 || totalTokens > 0 || credits > 0) {
|
if (finalTokens > 0 && finalTokens < INITIAL_CREDITS) {
|
||||||
processedUsers.push({
|
processedUsers.push({
|
||||||
userId: user._id,
|
userId: user._id,
|
||||||
userName:
|
userName:
|
||||||
user.name || user.username || user.email || "Utilisateur inconnu",
|
user.name || user.username || user.email || "Utilisateur inconnu",
|
||||||
conversations: userConversations.length,
|
conversations: userConversations.length,
|
||||||
tokens: totalTokens,
|
tokens: finalTokens,
|
||||||
credits: credits,
|
credits: credits,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -231,6 +235,17 @@ export function DashboardUsersList() {
|
|||||||
.sort((a, b) => b.tokens - a.tokens)
|
.sort((a, b) => b.tokens - a.tokens)
|
||||||
.slice(0, 5);
|
.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);
|
console.log("✅ Top 5 users processed:", sortedUsers);
|
||||||
setTopUsers(sortedUsers);
|
setTopUsers(sortedUsers);
|
||||||
setIsLoading(false);
|
setIsLoading(false);
|
||||||
|
|||||||
@@ -2,7 +2,15 @@
|
|||||||
|
|
||||||
import { useMetrics } from "@/hooks/useMetrics";
|
import { useMetrics } from "@/hooks/useMetrics";
|
||||||
import { MetricCard } from "@/components/ui/metric-card";
|
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";
|
import { convertCreditsToEuros } from "@/lib/utils/currency";
|
||||||
|
|
||||||
export function OverviewMetrics() {
|
export function OverviewMetrics() {
|
||||||
@@ -30,31 +38,41 @@ export function OverviewMetrics() {
|
|||||||
const creditsInEuros = convertCreditsToEuros(metrics.totalCredits);
|
const creditsInEuros = convertCreditsToEuros(metrics.totalCredits);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="grid gap-4 md:grid-cols-2 lg:grid-cols-4">
|
<div className="space-y-4">
|
||||||
<MetricCard
|
{/* Ligne 1: Utilisateurs actifs, Conversations actives, Tokens consommés */}
|
||||||
title="Utilisateurs totaux"
|
<div className="grid gap-4 md:grid-cols-3">
|
||||||
value={metrics.totalUsers}
|
|
||||||
icon={Users}
|
|
||||||
/>
|
|
||||||
<MetricCard
|
<MetricCard
|
||||||
title="Utilisateurs actifs"
|
title="Utilisateurs actifs"
|
||||||
value={metrics.activeUsers}
|
value={metrics.activeUsers}
|
||||||
icon={UserCheck}
|
icon={UserCheck}
|
||||||
/>
|
/>
|
||||||
<MetricCard
|
<MetricCard
|
||||||
title="Administrateurs"
|
title="Conversations actives"
|
||||||
value={metrics.totalAdmins}
|
value={metrics.activeConversations}
|
||||||
icon={Shield}
|
icon={MessageSquare}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
{/* Nouvelle carte pour les tokens consommés */}
|
|
||||||
<MetricCard
|
<MetricCard
|
||||||
title="Tokens consommés"
|
title="Tokens consommés"
|
||||||
value={metrics.totalTokensConsumed?.toLocaleString() || "0"}
|
value={metrics.totalTokensConsumed?.toLocaleString() || "0"}
|
||||||
icon={Activity}
|
icon={Activity}
|
||||||
description={`${Math.round((metrics.totalTokensConsumed || 0) / (metrics.totalUsers || 1))} par utilisateur`}
|
description={`${Math.round(
|
||||||
|
(metrics.totalTokensConsumed || 0) / (metrics.totalUsers || 1)
|
||||||
|
)} par utilisateur`}
|
||||||
/>
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Ligne 2: Utilisateurs totaux, Messages totaux, Crédits totaux */}
|
||||||
|
<div className="grid gap-4 md:grid-cols-3">
|
||||||
|
<MetricCard
|
||||||
|
title="Utilisateurs totaux"
|
||||||
|
value={metrics.totalUsers}
|
||||||
|
icon={Users}
|
||||||
|
/>
|
||||||
|
<MetricCard
|
||||||
|
title="Messages totaux"
|
||||||
|
value={metrics.totalMessages}
|
||||||
|
icon={FileText}
|
||||||
|
/>
|
||||||
<div className="bg-white rounded-lg border p-6">
|
<div className="bg-white rounded-lg border p-6">
|
||||||
<div className="flex items-center justify-between mb-2">
|
<div className="flex items-center justify-between mb-2">
|
||||||
<h3 className="text-sm font-medium text-gray-600">Crédits totaux</h3>
|
<h3 className="text-sm font-medium text-gray-600">Crédits totaux</h3>
|
||||||
@@ -76,16 +94,7 @@ export function OverviewMetrics() {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<MetricCard
|
</div>
|
||||||
title="Conversations actives"
|
|
||||||
value={metrics.activeConversations}
|
|
||||||
icon={MessageSquare}
|
|
||||||
/>
|
|
||||||
<MetricCard
|
|
||||||
title="Messages totaux"
|
|
||||||
value={metrics.totalMessages}
|
|
||||||
icon={FileText}
|
|
||||||
/>
|
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -1,14 +1,20 @@
|
|||||||
"use client";
|
"use client";
|
||||||
|
|
||||||
import { Card, CardContent } from "@/components/ui/card";
|
import { Card, CardContent } from "@/components/ui/card";
|
||||||
|
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs";
|
||||||
import { useStats } from "@/hooks/useStats";
|
import { useStats } from "@/hooks/useStats";
|
||||||
import { SimpleStatsChart } from "./charts/simple-stats-chart";
|
import { SimpleStatsChart } from "./charts/simple-stats-chart";
|
||||||
|
import { UserConnectionsChart } from "./charts/user-connections-chart";
|
||||||
import { ModelDistributionChart } from "./charts/model-distribution-chart";
|
import { ModelDistributionChart } from "./charts/model-distribution-chart";
|
||||||
import { AlertCircle } from "lucide-react";
|
import { AlertCircle } from "lucide-react";
|
||||||
|
|
||||||
export function RealTimeStats() {
|
export function RealTimeStats() {
|
||||||
const { stats, loading, error } = useStats();
|
const { stats, loading, error } = useStats();
|
||||||
|
|
||||||
|
console.log("RealTimeStats - stats:", stats);
|
||||||
|
console.log("RealTimeStats - loading:", loading);
|
||||||
|
console.log("RealTimeStats - error:", error);
|
||||||
|
|
||||||
if (loading) {
|
if (loading) {
|
||||||
return (
|
return (
|
||||||
<div className="space-y-6">
|
<div className="space-y-6">
|
||||||
@@ -67,7 +73,25 @@ export function RealTimeStats() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="space-y-6">
|
<Tabs defaultValue="connections" className="space-y-6">
|
||||||
|
<TabsList className="grid w-full grid-cols-2">
|
||||||
|
<TabsTrigger value="connections">Nombre de connexions par utilisateur/par jour</TabsTrigger>
|
||||||
|
<TabsTrigger value="tokens">Tokens consommés par jour</TabsTrigger>
|
||||||
|
</TabsList>
|
||||||
|
|
||||||
|
<TabsContent value="connections" className="space-y-6">
|
||||||
|
<UserConnectionsChart
|
||||||
|
title="Nombre de connexions par utilisateur/par jour"
|
||||||
|
data={stats.dailyConnections || []}
|
||||||
|
color="hsl(var(--chart-2))"
|
||||||
|
/>
|
||||||
|
<ModelDistributionChart
|
||||||
|
title="Répartition par modèle"
|
||||||
|
data={stats.modelDistribution}
|
||||||
|
/>
|
||||||
|
</TabsContent>
|
||||||
|
|
||||||
|
<TabsContent value="tokens" className="space-y-6">
|
||||||
<SimpleStatsChart
|
<SimpleStatsChart
|
||||||
title="Tokens consommés par jour"
|
title="Tokens consommés par jour"
|
||||||
data={stats.dailyTokens}
|
data={stats.dailyTokens}
|
||||||
@@ -77,6 +101,7 @@ export function RealTimeStats() {
|
|||||||
title="Répartition par modèle"
|
title="Répartition par modèle"
|
||||||
data={stats.modelDistribution}
|
data={stats.modelDistribution}
|
||||||
/>
|
/>
|
||||||
</div>
|
</TabsContent>
|
||||||
|
</Tabs>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,6 +7,11 @@ interface DailyToken {
|
|||||||
value: number;
|
value: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
interface DailyConnection {
|
||||||
|
name: string;
|
||||||
|
value: number;
|
||||||
|
}
|
||||||
|
|
||||||
interface ModelDistribution {
|
interface ModelDistribution {
|
||||||
name: string;
|
name: string;
|
||||||
value: number;
|
value: number;
|
||||||
@@ -14,6 +19,7 @@ interface ModelDistribution {
|
|||||||
|
|
||||||
interface StatsData {
|
interface StatsData {
|
||||||
dailyTokens: DailyToken[];
|
dailyTokens: DailyToken[];
|
||||||
|
dailyConnections: DailyConnection[];
|
||||||
modelDistribution: ModelDistribution[];
|
modelDistribution: ModelDistribution[];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user