diff --git a/.claude/settings.local.json b/.claude/settings.local.json
index a4a68ce..166617c 100644
--- a/.claude/settings.local.json
+++ b/.claude/settings.local.json
@@ -7,7 +7,8 @@
"Bash(npm run build:*)",
"Bash(git add:*)",
"Bash(git commit:*)",
- "Bash(git push)"
+ "Bash(git push)",
+ "Bash(npm install:*)"
],
"deny": [],
"ask": []
diff --git a/app/api/debug-users/route.ts b/app/api/debug-users/route.ts
new file mode 100644
index 0000000..4aa694e
--- /dev/null
+++ b/app/api/debug-users/route.ts
@@ -0,0 +1,29 @@
+import { NextResponse } from "next/server";
+import { getDatabase } from "@/lib/db/mongodb";
+
+export async function GET() {
+ try {
+ const db = await getDatabase();
+
+ // Récupérer 5 users pour debug
+ const users = await db.collection("users")
+ .find({ referent: { $exists: true } })
+ .limit(5)
+ .toArray();
+
+ return NextResponse.json({
+ success: true,
+ count: users.length,
+ users: users.map(u => ({
+ _id: u._id,
+ name: u.name,
+ email: u.email,
+ referent: u.referent,
+ cours: u.cours,
+ })),
+ });
+ } catch (error) {
+ console.error("Debug error:", error);
+ return NextResponse.json({ error: error.message }, { status: 500 });
+ }
+}
diff --git a/app/conversations/page.tsx b/app/conversations/page.tsx
index a7cc329..641d6e3 100644
--- a/app/conversations/page.tsx
+++ b/app/conversations/page.tsx
@@ -6,7 +6,7 @@ export default function ConversationsPage() {
Conversations
- Gestion des conversations Cercle GPTT
+ Gestion des conversations Cercle GPT
diff --git a/app/referents/[referent]/[cours]/page.tsx b/app/referents/[referent]/[cours]/page.tsx
new file mode 100644
index 0000000..d085cc2
--- /dev/null
+++ b/app/referents/[referent]/[cours]/page.tsx
@@ -0,0 +1,194 @@
+"use client";
+
+import { useState, useEffect, useMemo } from "react";
+import { useParams, useRouter } from "next/navigation";
+import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
+import { Button } from "@/components/ui/button";
+import { Badge } from "@/components/ui/badge";
+import {
+ Table,
+ TableBody,
+ TableCell,
+ TableHead,
+ TableHeader,
+ TableRow,
+} from "@/components/ui/table";
+import { ArrowLeft, Mail } from "lucide-react";
+import { formatDate } from "@/lib/utils";
+import { LibreChatUser, LibreChatBalance } from "@/lib/types";
+import { useCollection } from "@/hooks/useCollection";
+
+// Couleurs prédéfinies pour les référents
+const REFERENT_COLORS: Record = {
+ "Emmanuel WATHELE": "#3B82F6",
+ "IHECS": "#10B981",
+};
+
+export default function CoursDetailPage() {
+ const params = useParams();
+ const router = useRouter();
+ const referent = decodeURIComponent(params.referent as string);
+ const cours = decodeURIComponent(params.cours as string);
+
+ const [students, setStudents] = useState([]);
+ const [loading, setLoading] = useState(true);
+
+ // Charger tous les balances
+ const { data: balances = [] } = useCollection("balances", {
+ limit: 1000,
+ });
+
+ // Créer une map des crédits par utilisateur
+ const creditsMap = useMemo(() => {
+ const map = new Map();
+ balances.forEach((balance) => {
+ map.set(balance.user, balance.tokenCredits || 0);
+ });
+ return map;
+ }, [balances]);
+
+ useEffect(() => {
+ const fetchStudents = async () => {
+ try {
+ const response = await fetch(
+ "/api/collections/users?page=1&limit=1000&filter={}"
+ );
+ const result = await response.json();
+
+ if (result.data) {
+ // Filtrer les étudiants pour ce référent et ce cours
+ const filtered = result.data.filter(
+ (user: any) =>
+ user.referent === referent && user.cours === cours
+ );
+ setStudents(filtered);
+ }
+ } catch (error) {
+ console.error("Erreur lors du chargement des étudiants:", error);
+ } finally {
+ setLoading(false);
+ }
+ };
+
+ fetchStudents();
+ }, [referent, cours]);
+
+ const couleur = REFERENT_COLORS[referent] || "#6B7280";
+
+ if (loading) {
+ return (
+
+
+
+
+
+ {Array.from({ length: 5 }).map((_, i) => (
+
+ ))}
+
+
+
+
+ );
+ }
+
+ return (
+
+
+
+
+
+
+
+
+
+ Étudiants ({students.length})
+
+
+
+
+ {students.length === 0 ? (
+
+
+ Aucun étudiant dans ce cours
+
+
+ ) : (
+
+
+
+
+ Nom
+ Email
+ Rôle
+ Crédits
+ Créé le
+
+
+
+ {students.map((student) => (
+
+
+
+
+
+ {student.prenom} {student.nom}
+
+
+
+
+
+
+ {student.email}
+
+
+
+
+ {student.role}
+
+
+
+
+ {(creditsMap.get(student._id) || 0).toLocaleString()} tokens
+
+
+
+ {formatDate(student.createdAt)}
+
+
+ ))}
+
+
+
+ )}
+
+
+
+ );
+}
diff --git a/app/referents/page.tsx b/app/referents/page.tsx
new file mode 100644
index 0000000..645df94
--- /dev/null
+++ b/app/referents/page.tsx
@@ -0,0 +1,183 @@
+"use client";
+
+import { useState, useEffect } from "react";
+import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
+import { Badge } from "@/components/ui/badge";
+import { Button } from "@/components/ui/button";
+import { GraduationCap, Users, ChevronRight } from "lucide-react";
+import Link from "next/link";
+
+// Couleurs prédéfinies pour les référents
+const REFERENT_COLORS: Record = {
+ "Emmanuel WATHELE": "#3B82F6", // Bleu
+ "IHECS": "#10B981", // Vert
+};
+
+interface ReferentData {
+ nom: string;
+ couleur: string;
+ cours: string[];
+ nombreEtudiants: number;
+}
+
+export default function ReferentsPage() {
+ const [referents, setReferents] = useState([]);
+ const [loading, setLoading] = useState(true);
+
+ useEffect(() => {
+ const fetchReferents = async () => {
+ try {
+ // Récupérer tous les users pour compter par référent
+ const response = await fetch(
+ "/api/collections/users?page=1&limit=1000&filter={}"
+ );
+ const result = await response.json();
+
+ if (result.data) {
+ // Grouper par référent
+ const referentsMap = new Map();
+
+ result.data.forEach((user: any) => {
+ if (user.referent) {
+ if (!referentsMap.has(user.referent)) {
+ referentsMap.set(user.referent, {
+ nom: user.referent,
+ couleur: REFERENT_COLORS[user.referent] || "#6B7280", // Gris par défaut
+ cours: [],
+ nombreEtudiants: 0,
+ });
+ }
+
+ const ref = referentsMap.get(user.referent)!;
+ ref.nombreEtudiants++;
+
+ // Ajouter le cours s'il n'existe pas déjà
+ if (user.cours && !ref.cours.includes(user.cours)) {
+ ref.cours.push(user.cours);
+ }
+ }
+ });
+
+ setReferents(Array.from(referentsMap.values()));
+ }
+ } catch (error) {
+ console.error("Erreur lors du chargement des référents:", error);
+ } finally {
+ setLoading(false);
+ }
+ };
+
+ fetchReferents();
+ }, []);
+
+ if (loading) {
+ return (
+
+
+
Référents
+
+ Gestion des référents et leurs cours
+
+
+
+
+
+ {Array.from({ length: 3 }).map((_, i) => (
+
+ ))}
+
+
+
+
+ );
+ }
+
+ if (referents.length === 0) {
+ return (
+
+
+
Référents
+
+ Gestion des référents et leurs cours
+
+
+
+
+
+
+
+ Aucun référent
+
+
+ Importez des utilisateurs avec référents pour commencer.
+
+
+
+
+
+ );
+ }
+
+ return (
+
+
+
Référents
+
+ Gestion des référents et leurs cours
+
+
+
+
+ {referents.map((referent) => (
+
+
+
+
+ {/* Point coloré */}
+
+
+
{referent.nom}
+
+
+
+ {referent.nombreEtudiants} étudiant
+ {referent.nombreEtudiants > 1 ? "s" : ""}
+
+
+
+
+
+
+
+
+
Cours :
+
+ {referent.cours.map((cours) => (
+
+
+
+ ))}
+
+
+
+
+ ))}
+
+
+ );
+}
diff --git a/app/users/page.tsx b/app/users/page.tsx
index 3eb8f82..fd0ae6e 100644
--- a/app/users/page.tsx
+++ b/app/users/page.tsx
@@ -6,7 +6,7 @@ export default function UsersPage() {
Utilisateurs
- Gestion des utilisateurs Cercle GPTT
+ Gestion des utilisateurs Cercle GPT
diff --git a/components/collections/users-table.tsx b/components/collections/users-table.tsx
index 18acd55..57fd305 100644
--- a/components/collections/users-table.tsx
+++ b/components/collections/users-table.tsx
@@ -18,6 +18,12 @@ import { ChevronLeft, ChevronRight, Search } from "lucide-react";
import { formatDate } from "@/lib/utils";
import { LibreChatUser, LibreChatBalance } from "@/lib/types";
+// Couleurs prédéfinies pour les référents
+const REFERENT_COLORS: Record = {
+ "Emmanuel WATHELE": "#3B82F6", // Bleu
+ "IHECS": "#10B981", // Vert
+};
+
export function UsersTable() {
const [page, setPage] = useState(1);
const [searchInput, setSearchInput] = useState(""); // Ce que l'utilisateur tape
@@ -121,22 +127,20 @@ export function UsersTable() {
- ID
+ ID
Nom
Email
- Rôle
- Crédits
- Statut
- Créé le
+ Référent
+ Rôle
+ Crédits
+ Créé le
{users.length > 0 ? (
users.map((user) => {
const userCredits = creditsMap.get(user._id) || 0;
- const isActive =
- new Date(user.updatedAt || user.createdAt) >
- new Date(Date.now() - 30 * 24 * 60 * 60 * 1000);
+ const referentColor = user.referent ? (REFERENT_COLORS[user.referent] || "#6B7280") : null;
return (
@@ -146,11 +150,29 @@ export function UsersTable() {
- {user.name}
+
+ {referentColor && (
+
+ )}
+
{user.name}
+
{user.email}
+
+ {user.referent ? (
+
+ {user.referent}
+
+ ) : (
+ -
+ )}
+
-
-
-
- {isActive ? "Actif" : "Inactif"}
-
-
-
{formatDate(user.createdAt)}
diff --git a/components/dashboard/create-user.tsx b/components/dashboard/create-user.tsx
index 8800492..8a45e4f 100644
--- a/components/dashboard/create-user.tsx
+++ b/components/dashboard/create-user.tsx
@@ -118,7 +118,8 @@ export default function CreateUser() {
} else {
setResult({
success: false,
- message: data.error || "Erreur lors de la création de l'utilisateur",
+ message:
+ data.error || "Erreur lors de la création de l'utilisateur",
});
}
} catch (error) {
@@ -185,7 +186,9 @@ export default function CreateUser() {
-
+
Rôle