"use client"; import { useState, useEffect } from "react"; import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"; import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs"; import { Badge } from "@/components/ui/badge"; import { Progress } from "@/components/ui/progress"; import { Button } from "@/components/ui/button"; import { Users, TrendingUp, BarChart3, Target, Building2, UserCheck, Filter, } from "lucide-react"; import { loadTeams, loadSkillCategories } from "@/lib/data-loader"; import { Team, SkillCategory, UserEvaluation } from "@/lib/types"; import { TeamStatsCard, DirectionOverview, MultiSelectFilter, } from "@/components/admin"; import { TeamDetailModal } from "@/components/admin/team-detail-modal"; interface TeamMember { id: string; firstName: string; lastName: string; skills: Array<{ skillId: string; skillName: string; category: string; level: number; canMentor: boolean; wantsToLearn: boolean; }>; joinDate: string; } interface TeamStats { teamId: string; teamName: string; direction: string; totalMembers: number; averageSkillLevel: number; topSkills: Array<{ skillName: string; averageLevel: number }>; skillCoverage: number; // Percentage of skills evaluated members: TeamMember[]; } interface DirectionStats { direction: string; teams: TeamStats[]; totalMembers: number; averageSkillLevel: number; topCategories: Array<{ category: string; averageLevel: number }>; } // Helper function to generate realistic team members function generateTeamMembers( teamName: string, memberCount: number, skillCategories: SkillCategory[] ) { const firstNames = [ "Alice", "Bob", "Claire", "David", "Emma", "Frank", "Grace", "Hugo", "Iris", "Jack", "Kelly", "Liam", "Maya", "Noah", "Olivia", "Paul", "Quinn", "Rose", "Sam", "Tara", ]; const lastNames = [ "Martin", "Bernard", "Dubois", "Thomas", "Robert", "Richard", "Petit", "Durand", "Leroy", "Moreau", "Simon", "Laurent", "Michel", "Garcia", "David", "Bertrand", "Roux", "Vincent", "Fournier", "Morel", ]; const allSkills: Array<{ skillName: string; category: string; id: string }> = []; skillCategories.forEach((category) => { category.skills.forEach((skill) => { allSkills.push({ skillName: skill.name, category: category.category, id: skill.id, }); }); }); return Array.from({ length: memberCount }, (_, index) => { const firstName = firstNames[Math.floor(Math.random() * firstNames.length)]; const lastName = lastNames[Math.floor(Math.random() * lastNames.length)]; // Each member evaluates 8-15 random skills const skillCount = Math.floor(Math.random() * 8) + 8; const memberSkills = allSkills .sort(() => Math.random() - 0.5) .slice(0, skillCount) .map((skill) => ({ skillId: skill.id, skillName: skill.skillName, category: skill.category, level: Math.floor(Math.random() * 4), // 0-3 canMentor: Math.random() > 0.7, // 30% chance wantsToLearn: Math.random() > 0.6, // 40% chance })); return { id: `${teamName}-member-${index + 1}`, firstName, lastName, skills: memberSkills, joinDate: new Date( Date.now() - Math.random() * 365 * 24 * 60 * 60 * 1000 * 3 ) .toISOString() .split("T")[0], // Random date in last 3 years }; }); } // Helper function to get random skills from categories function getRandomSkills(skillCategories: SkillCategory[], count: number = 3) { const allSkills: Array<{ skillName: string; category: string; icon: string; }> = []; skillCategories.forEach((category) => { category.skills.forEach((skill) => { allSkills.push({ skillName: skill.name, category: category.category, icon: skill.icon || "", }); }); }); // Shuffle and pick random skills const shuffled = allSkills.sort(() => Math.random() - 0.5); const randomSkills = shuffled.slice(0, count).map((skill) => ({ skillName: skill.skillName, averageLevel: Math.random() * 3, icon: skill.icon, })); // Sort by average level (descending - highest first) return randomSkills.sort((a, b) => b.averageLevel - a.averageLevel); } // Mock data generator - à remplacer par de vraies données function generateMockTeamStats( teams: Team[], skillCategories: SkillCategory[] ): TeamStats[] { return teams.map((team) => { const memberCount = Math.floor(Math.random() * 10) + 3; // 3-12 members const members = generateTeamMembers( team.name, memberCount, skillCategories ); return { teamId: team.id, teamName: team.name, direction: team.direction, totalMembers: memberCount, averageSkillLevel: Math.random() * 3, // 0-3 topSkills: getRandomSkills(skillCategories, 3), skillCoverage: Math.random() * 100, // 0-100% members: members, }; }); } function generateDirectionStats(teamStats: TeamStats[]): DirectionStats[] { const directions = Array.from(new Set(teamStats.map((t) => t.direction))); return directions.map((direction) => { const directionTeams = teamStats.filter((t) => t.direction === direction); const totalMembers = directionTeams.reduce( (sum, t) => sum + t.totalMembers, 0 ); const averageSkillLevel = directionTeams.reduce((sum, t) => sum + t.averageSkillLevel, 0) / directionTeams.length; return { direction, teams: directionTeams, totalMembers, averageSkillLevel, topCategories: [ { category: "Frontend", averageLevel: Math.random() * 3 }, { category: "Backend", averageLevel: Math.random() * 3 }, { category: "DevOps", averageLevel: Math.random() * 3 }, ], }; }); } export default function AdminPage() { const [teams, setTeams] = useState([]); const [skillCategories, setSkillCategories] = useState([]); const [teamStats, setTeamStats] = useState([]); const [directionStats, setDirectionStats] = useState([]); const [loading, setLoading] = useState(true); // Filtres const [selectedDirections, setSelectedDirections] = useState([]); const [selectedTeams, setSelectedTeams] = useState([]); // Modale de détails const [selectedTeamForModal, setSelectedTeamForModal] = useState(null); const [isModalOpen, setIsModalOpen] = useState(false); useEffect(() => { async function loadData() { try { const [teamsData, categoriesData] = await Promise.all([ loadTeams(), loadSkillCategories(), ]); setTeams(teamsData); setSkillCategories(categoriesData); // Generate mock stats const mockTeamStats = generateMockTeamStats(teamsData, categoriesData); const mockDirectionStats = generateDirectionStats(mockTeamStats); setTeamStats(mockTeamStats); setDirectionStats(mockDirectionStats); } catch (error) { console.error("Failed to load admin data:", error); } finally { setLoading(false); } } loadData(); }, []); // Fonctions de filtrage const getFilteredTeamStats = () => { let filtered = teamStats; if (selectedDirections.length > 0) { filtered = filtered.filter((team) => selectedDirections.includes(team.direction) ); } if (selectedTeams.length > 0) { filtered = filtered.filter((team) => selectedTeams.includes(team.teamId)); } return filtered; }; const getFilteredDirectionStats = () => { let filtered = directionStats; if (selectedDirections.length > 0) { filtered = filtered.filter((direction) => selectedDirections.includes(direction.direction) ); } // Si des équipes spécifiques sont sélectionnées, filtrer aussi les équipes dans les directions if (selectedTeams.length > 0) { filtered = filtered .map((direction) => ({ ...direction, teams: direction.teams.filter((team) => selectedTeams.includes(team.teamId) ), totalMembers: direction.teams .filter((team) => selectedTeams.includes(team.teamId)) .reduce((sum, team) => sum + team.totalMembers, 0), averageSkillLevel: direction.teams .filter((team) => selectedTeams.includes(team.teamId)) .reduce( (sum, team, _, arr) => sum + team.averageSkillLevel / arr.length, 0 ), })) .filter((direction) => direction.teams.length > 0); } return filtered; }; // Options pour les filtres const directionOptions = Array.from( new Set(teams.map((team) => team.direction)) ).map((direction) => ({ id: direction, label: direction, count: teams.filter((team) => team.direction === direction).length, })); const teamOptions = teams.map((team) => ({ id: team.id, label: `${team.name} (${team.direction})`, count: teamStats.find((stat) => stat.teamId === team.id)?.totalMembers || 0, })); // Fonctions pour les actions des équipes const handleViewTeamDetails = (team: TeamStats) => { setSelectedTeamForModal(team); setIsModalOpen(true); }; const handleExportTeamReport = (team: TeamStats) => { const reportData = { team: team.teamName, direction: team.direction, date: new Date().toLocaleDateString(), stats: { members: team.totalMembers, averageLevel: team.averageSkillLevel, coverage: team.skillCoverage, }, topSkills: team.topSkills, }; const dataStr = JSON.stringify(reportData, null, 2); const dataUri = "data:application/json;charset=utf-8," + encodeURIComponent(dataStr); const exportFileDefaultName = `rapport-${team.teamName.toLowerCase()}-${ new Date().toISOString().split("T")[0] }.json`; const linkElement = document.createElement("a"); linkElement.setAttribute("href", dataUri); linkElement.setAttribute("download", exportFileDefaultName); linkElement.click(); }; if (loading) { return (
Chargement des données d'administration...
); } return (
{/* Background Effects */}
{/* Header */}
Administration

Dashboard Managérial

Vue d'ensemble des compétences par équipe et direction pour pilotage stratégique

{/* Overview Cards */}
{selectedDirections.length > 0 || selectedTeams.length > 0 ? "FILTRÉES" : "TOTAL"}

{selectedDirections.length > 0 || selectedTeams.length > 0 ? getFilteredTeamStats().length : teams.length}

{selectedDirections.length > 0 || selectedTeams.length > 0 ? "Équipes filtrées" : "Équipes"}

{(selectedDirections.length > 0 || selectedTeams.length > 0) && (

sur {teams.length} au total

)}
{selectedDirections.length > 0 || selectedTeams.length > 0 ? "FILTRÉS" : "TOTAL"}

{selectedDirections.length > 0 || selectedTeams.length > 0 ? getFilteredTeamStats().reduce( (sum, t) => sum + t.totalMembers, 0 ) : teamStats.reduce((sum, t) => sum + t.totalMembers, 0)}

{selectedDirections.length > 0 || selectedTeams.length > 0 ? "Membres filtrés" : "Membres"}

{(selectedDirections.length > 0 || selectedTeams.length > 0) && (

sur {teamStats.reduce((sum, t) => sum + t.totalMembers, 0)} au total

)}
{selectedDirections.length > 0 || selectedTeams.length > 0 ? "FILTRÉES" : "TOTAL"}

{selectedDirections.length > 0 || selectedTeams.length > 0 ? getFilteredDirectionStats().length : directionStats.length}

{selectedDirections.length > 0 || selectedTeams.length > 0 ? "Directions filtrées" : "Directions"}

{(selectedDirections.length > 0 || selectedTeams.length > 0) && (

sur {directionStats.length} au total

)}
RÉFÉRENTIEL

{skillCategories.reduce( (sum, cat) => sum + cat.skills.length, 0 )}

Compétences suivies

{skillCategories.length} catégories

{/* Filtres */}

Filtres avancés

} />
} />
{/* Résumé des filtres actifs */} {(selectedDirections.length > 0 || selectedTeams.length > 0) && (
{getFilteredTeamStats().reduce( (sum, t) => sum + t.totalMembers, 0 )}{" "} membres
)}
{/* Main Content Tabs */}
Vue par Direction Vue par Équipe
{getFilteredDirectionStats().length > 0 ? ( getFilteredDirectionStats().map((direction) => ( )) ) : (

Aucune direction trouvée

Aucune direction ne correspond aux filtres sélectionnés.

)}
{getFilteredTeamStats().length > 0 ? ( getFilteredTeamStats().map((team) => ( handleViewTeamDetails(team)} onViewReport={() => handleExportTeamReport(team)} /> )) ) : (

Aucune équipe trouvée

Aucune équipe ne correspond aux filtres sélectionnés.

)}
{/* Modale de détails d'équipe */} setIsModalOpen(false)} team={selectedTeamForModal} />
); }