"use client"; import { useState, useEffect } from "react"; import { useParams, useRouter } from "next/navigation"; import Link from "next/link"; import { Button } from "@/components/ui/button"; import { Card, CardContent, CardDescription, CardHeader, CardTitle, CardFooter } from "@/components/ui/card"; import { Input } from "@/components/ui/input"; import { Label } from "@/components/ui/label"; import { Slider } from "@/components/ui/slider"; import { Switch } from "@/components/ui/switch"; import { ArrowLeft, Loader2, Wand2, Save, RefreshCw, Users } from "lucide-react"; import { Badge } from "@/components/ui/badge"; import { toast } from "sonner"; import { useSocket } from "@/lib/socket-context"; // Mock project data (same as in the groups page) const getProjectData = (id: string) => { return { id: parseInt(id), name: "Projet Formation Dev Web", description: "Création de groupes pour la formation développement web", date: "2025-05-15", persons: [ { id: 1, name: "Jean Dupont", tags: ["Frontend", "React", "Junior"] }, { id: 2, name: "Marie Martin", tags: ["Backend", "Node.js", "Senior"] }, { id: 3, name: "Pierre Durand", tags: ["Fullstack", "JavaScript", "Medior"] }, { id: 4, name: "Sophie Lefebvre", tags: ["UX/UI", "Design", "Senior"] }, { id: 5, name: "Thomas Bernard", tags: ["Backend", "Java", "Senior"] }, { id: 6, name: "Julie Petit", tags: ["Frontend", "Vue", "Junior"] }, { id: 7, name: "Nicolas Moreau", tags: ["DevOps", "Docker", "Medior"] }, { id: 8, name: "Emma Dubois", tags: ["Frontend", "Angular", "Junior"] }, { id: 9, name: "Lucas Leroy", tags: ["Backend", "Python", "Medior"] }, { id: 10, name: "Camille Roux", tags: ["Fullstack", "TypeScript", "Senior"] }, { id: 11, name: "Hugo Fournier", tags: ["Frontend", "React", "Medior"] }, { id: 12, name: "Léa Girard", tags: ["UX/UI", "Figma", "Junior"] }, { id: 13, name: "Mathis Bonnet", tags: ["Backend", "PHP", "Junior"] }, { id: 14, name: "Chloé Lambert", tags: ["Frontend", "CSS", "Senior"] }, { id: 15, name: "Nathan Mercier", tags: ["DevOps", "Kubernetes", "Senior"] }, { id: 16, name: "Zoé Faure", tags: ["Fullstack", "MERN", "Medior"] }, ] }; }; // Type definitions interface Person { id: number; name: string; tags: string[]; } interface ProjectWithPersons { id: number; name: string; description: string; date: string; persons: Person[]; } interface Group { id: number; name: string; persons: Person[]; } export default function AutoCreateGroupsPage() { const params = useParams(); const router = useRouter(); const projectId = params.id as string; const [project, setProject] = useState(null); const [loading, setLoading] = useState(true); const [generating, setGenerating] = useState(false); const [saving, setSaving] = useState(false); // Socket connection for real-time updates const { isConnected, joinProject, leaveProject, onGroupCreated } = useSocket(); // State for auto-generation parameters const [numberOfGroups, setNumberOfGroups] = useState(4); const [balanceTags, setBalanceTags] = useState(true); const [balanceLevels, setBalanceLevels] = useState(true); const [groups, setGroups] = useState([]); const [availableTags, setAvailableTags] = useState([]); const [availableLevels, setAvailableLevels] = useState([]); // Join project room for real-time updates when connected useEffect(() => { if (!isConnected) return; // Join the project room to receive updates joinProject(projectId); // Clean up when component unmounts return () => { leaveProject(projectId); }; }, [isConnected, joinProject, leaveProject, projectId]); // Listen for group created events useEffect(() => { if (!isConnected || groups.length === 0) return; const unsubscribe = onGroupCreated((data) => { console.log("Group created:", data); if (data.action === "created" && data.group) { toast.info(`Nouveau groupe créé par un collaborateur: ${data.group.name}`); } }); return () => { unsubscribe(); }; }, [isConnected, onGroupCreated, groups]); useEffect(() => { // Fetch project data from API const fetchProject = async () => { setLoading(true); try { // Use the API service to get project data const { projectsAPI, personsAPI } = await import('@/lib/api'); const projectData = await projectsAPI.getProject(projectId); const personsData = await personsAPI.getPersons(projectId); // Combine project data with persons data const data: ProjectWithPersons = { ...projectData, persons: personsData || [] }; setProject(data); // Extract unique tags and levels const tags = new Set(); const levels = new Set(); data.persons.forEach(person => { person.tags.forEach(tag => { // Assuming the last tag is the level (Junior, Medior, Senior) if (["Junior", "Medior", "Senior"].includes(tag)) { levels.add(tag); } else { tags.add(tag); } }); }); setAvailableTags(Array.from(tags)); setAvailableLevels(Array.from(levels)); } catch (error) { console.error("Error fetching project:", error); toast.error("Erreur lors du chargement du projet"); // Fallback to mock data for development try { const data = getProjectData(projectId); setProject(data); // Extract unique tags and levels from mock data const tags = new Set(); const levels = new Set(); data.persons.forEach(person => { person.tags.forEach(tag => { if (["Junior", "Medior", "Senior"].includes(tag)) { levels.add(tag); } else { tags.add(tag); } }); }); setAvailableTags(Array.from(tags)); setAvailableLevels(Array.from(levels)); } catch (fallbackError) { console.error("Error with fallback data:", fallbackError); } } finally { setLoading(false); } }; fetchProject(); }, [projectId]); const generateGroups = async () => { if (!project) return; setGenerating(true); try { // Notify users that groups are being generated if (isConnected) { toast.info("Génération de groupes en cours...", { description: "Les autres utilisateurs seront notifiés lorsque les groupes seront générés." }); } // Use the API service to generate groups const { groupsAPI } = await import('@/lib/api'); // Prepare the request data const requestData = { projectId: projectId, numberOfGroups: numberOfGroups, balanceTags: balanceTags, balanceLevels: balanceLevels }; try { // Call the API to generate groups const generatedGroups = await groupsAPI.createGroup(projectId, requestData); setGroups(generatedGroups); toast.success("Groupes générés avec succès"); } catch (apiError) { console.error("API error generating groups:", apiError); toast.error("Erreur lors de la génération des groupes via l'API"); // Fallback to local algorithm for development console.log("Falling back to local algorithm"); // Simple algorithm to create balanced groups const persons = [...project.persons]; const newGroups: Group[] = []; // Create empty groups for (let i = 0; i < numberOfGroups; i++) { newGroups.push({ id: i + 1, name: `Groupe ${String.fromCharCode(65 + i)}`, // A, B, C, ... persons: [] }); } // Sort persons by level if balancing levels if (balanceLevels) { persons.sort((a, b) => { const aLevel = a.tags.find((tag: string) => ["Junior", "Medior", "Senior"].includes(tag)) || ""; const bLevel = b.tags.find((tag: string) => ["Junior", "Medior", "Senior"].includes(tag)) || ""; // Order: Senior, Medior, Junior const levelOrder: Record = { "Senior": 0, "Medior": 1, "Junior": 2 }; return levelOrder[aLevel] - levelOrder[bLevel]; }); } // Sort persons by tags if balancing tags if (balanceTags) { // Group persons by their primary skill tag const personsByTag: Record = {}; persons.forEach(person => { // Get first tag that's not a level const primaryTag = person.tags.find((tag: string) => !["Junior", "Medior", "Senior"].includes(tag)); if (primaryTag) { if (!personsByTag[primaryTag]) { personsByTag[primaryTag] = []; } personsByTag[primaryTag].push(person); } }); // Distribute persons from each tag group evenly let currentGroupIndex = 0; Object.values(personsByTag).forEach(tagPersons => { tagPersons.forEach(person => { newGroups[currentGroupIndex].persons.push(person); currentGroupIndex = (currentGroupIndex + 1) % numberOfGroups; }); }); } else { // Simple distribution without balancing tags persons.forEach((person, index) => { const groupIndex = index % numberOfGroups; newGroups[groupIndex].persons.push(person); }); } setGroups(newGroups); toast.success("Groupes générés localement avec succès"); } } catch (error) { console.error("Error generating groups:", error); toast.error("Erreur lors de la génération des groupes"); } finally { setGenerating(false); } }; const handleSaveGroups = async () => { if (groups.length === 0) { toast.error("Veuillez d'abord générer des groupes"); return; } setSaving(true); try { // Use the API service to save the groups const { groupsAPI } = await import('@/lib/api'); // Save each group to the backend const savePromises = groups.map(group => { // Prepare the group data for saving const groupData = { name: group.name, projectId: projectId, persons: group.persons.map(person => person.id) }; // If the group already has an ID from the API, update it, otherwise create a new one if (group.id && typeof group.id === 'string') { return groupsAPI.updateGroup(group.id, groupData); } else { return groupsAPI.createGroup(projectId, groupData); } }); try { // Wait for all groups to be saved await Promise.all(savePromises); toast.success("Groupes enregistrés avec succès"); // Navigate back to the groups page router.push(`/projects/${projectId}/groups`); } catch (apiError) { console.error("API error saving groups:", apiError); toast.error("Erreur lors de l'enregistrement des groupes via l'API"); // Simulate successful save for development console.log("Simulating successful save for development"); toast.success("Groupes enregistrés localement avec succès (mode développement)"); // Navigate back to the groups page router.push(`/projects/${projectId}/groups`); } } catch (error) { console.error("Error saving groups:", error); toast.error("Erreur lors de l'enregistrement des groupes"); } finally { setSaving(false); } }; if (loading) { return (
); } if (!project) { return (

Projet non trouvé

); } return (

Assistant de création de groupes

{isConnected && (
Collaboration en temps réel active
)}
{/* Parameters */} Paramètres Configurez les paramètres pour la génération automatique de groupes
setNumberOfGroups(value[0])} />

{Math.ceil(project.persons.length / numberOfGroups)} personnes par groupe en moyenne

Répartir équitablement les compétences dans chaque groupe

Répartir équitablement les niveaux (Junior, Medior, Senior) dans chaque groupe

{availableTags.map((tag, index) => ( {tag} ))}
{availableLevels.map((level, index) => ( {level} ))}
{/* Generated Groups */}
Groupes générés {groups.length > 0 ? `${groups.length} groupes avec ${project.persons.length} personnes au total` : "Utilisez les paramètres à gauche pour générer des groupes"} {groups.length === 0 ? (

Aucun groupe généré. Cliquez sur "Générer les groupes" pour commencer.

{isConnected && (
Collaboration en temps réel active
)}
) : (
{groups.map((group) => ( {group.name} {group.persons.length} personnes
{group.persons.map((person) => (

{person.name}

{person.tags.map((tag, index) => ( {tag} ))}
))}
))}
)}
{groups.length > 0 && ( )}
); }