From ad575641a1020efb5e86c33b2ec54d8c8fc138ca Mon Sep 17 00:00:00 2001 From: Biqoz Date: Sun, 16 Nov 2025 01:34:01 +0100 Subject: [PATCH] user nouveau onglet --- .claude/settings.local.json | 3 +- app/api/debug-users/route.ts | 29 ++++ app/conversations/page.tsx | 2 +- app/referents/[referent]/[cours]/page.tsx | 194 ++++++++++++++++++++++ app/referents/page.tsx | 183 ++++++++++++++++++++ app/users/page.tsx | 2 +- components/collections/users-table.tsx | 47 ++++-- components/dashboard/create-user.tsx | 28 ++-- components/layout/sidebar.tsx | 6 + package-lock.json | 119 ++++++++++++- package.json | 4 +- public/list_users/Emmanuel_WATHELE.xlsx | Bin 0 -> 10624 bytes public/list_users/IHECS.xlsx | Bin 0 -> 9859 bytes scripts/README.md | 114 +++++++++++++ scripts/import-ihecs.js | 167 +++++++++++++++++++ scripts/import-users.js | 168 +++++++++++++++++++ 16 files changed, 1033 insertions(+), 33 deletions(-) create mode 100644 app/api/debug-users/route.ts create mode 100644 app/referents/[referent]/[cours]/page.tsx create mode 100644 app/referents/page.tsx create mode 100644 public/list_users/Emmanuel_WATHELE.xlsx create mode 100644 public/list_users/IHECS.xlsx create mode 100644 scripts/README.md create mode 100644 scripts/import-ihecs.js create mode 100644 scripts/import-users.js 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 ( +
+ + +
+
+
+

{referent}

+

{cours}

+
+
+ + + +
+ + É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) => ( + +
+
+
+ {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