From 5c76ec054965e4f9634f37c0637c17e4b49a6f88 Mon Sep 17 00:00:00 2001 From: Julien Froidefond Date: Fri, 22 Aug 2025 13:59:51 +0200 Subject: [PATCH] feat: enhance team overview and skills tabs with improved skill distribution and UI - Updated team overview tab to calculate and display skill distribution by individual skill levels. - Refactored skill tab layout for better responsiveness and readability, including adjustments to grid and text sizes. - Added a new utility function to calculate skill level distribution for cleaner code and reusability. - Improved visual elements for better user interaction and clarity in skill metrics. --- .../admin/team-detail/team-overview-tab.tsx | 141 ++++++++---------- .../admin/team-detail/team-skills-tab.tsx | 42 +++--- lib/score-utils.ts | 17 +++ 3 files changed, 103 insertions(+), 97 deletions(-) diff --git a/components/admin/team-detail/team-overview-tab.tsx b/components/admin/team-detail/team-overview-tab.tsx index 274997a..1a87375 100644 --- a/components/admin/team-detail/team-overview-tab.tsx +++ b/components/admin/team-detail/team-overview-tab.tsx @@ -1,6 +1,6 @@ "use client"; -import { Star, BarChart3, Target } from "lucide-react"; +import { BarChart3, Target, Star } from "lucide-react"; import { TeamStats } from "@/services/admin-service"; import { TechIcon } from "@/components/icons/tech-icon"; @@ -125,75 +125,74 @@ export function TeamOverviewTab({

- Répartition des niveaux + Répartition des niveaux par compétence

- {[ - { - label: "Expert", - count: team.members.filter( - (m) => - m.skills.reduce((avg, s) => avg + s.level, 0) / - m.skills.length >= - 2.5 - ).length, - color: "bg-green-500", - }, - { - label: "Avancé", - count: team.members.filter((m) => { - const avg = - m.skills.reduce((avg, s) => avg + s.level, 0) / - m.skills.length; - return avg >= 2 && avg < 2.5; - }).length, - color: "bg-blue-500", - }, - { - label: "Intermédiaire", - count: team.members.filter((m) => { - const avg = - m.skills.reduce((avg, s) => avg + s.level, 0) / - m.skills.length; - return avg >= 1 && avg < 2; - }).length, - color: "bg-orange-500", - }, - { - label: "Débutant", - count: team.members.filter( - (m) => - m.skills.reduce((avg, s) => avg + s.level, 0) / - m.skills.length < - 1 - ).length, - color: "bg-red-500", - }, - ].map((level, idx) => ( -
-
-
- {level.label} -
-
- {level.count} -
-
+ {(() => { + // Calculer la répartition par compétence individuelle + const allSkills = team.members.flatMap((m) => m.skills); + const totalSkills = allSkills.length; + + const levelDistribution = { + expert: allSkills.filter((s) => s.level === 3).length, + autonomous: allSkills.filter((s) => s.level === 2).length, + notAutonomous: allSkills.filter((s) => s.level === 1).length, + never: allSkills.filter((s) => s.level === 0).length, + }; + + return [ + { + label: "Expert", + count: levelDistribution.expert, + total: totalSkills, + color: "bg-green-500", + }, + { + label: "Autonome", + count: levelDistribution.autonomous, + total: totalSkills, + color: "bg-blue-500", + }, + { + label: "Pas autonome", + count: levelDistribution.notAutonomous, + total: totalSkills, + color: "bg-orange-500", + }, + { + label: "Jamais utilisé", + count: levelDistribution.never, + total: totalSkills, + color: "bg-red-500", + }, + ].map((level, idx) => ( +
+
+
+ + {level.label} + +
+
+ {level.count} +
+
+
+ + {((level.count / level.total) * 100).toFixed(0)}% +
- - {((level.count / team.totalMembers) * 100).toFixed(0)}% -
-
- ))} + )); + })()}
@@ -221,16 +220,6 @@ export function TeamOverviewTab({ {teamInsights.totalLearners}
-
- Mentors disponibles - - {skillAnalysis.reduce( - (sum, skill) => - sum + skill.experts.filter((e) => e.canMentor).length, - 0 - )} - -
diff --git a/components/admin/team-detail/team-skills-tab.tsx b/components/admin/team-detail/team-skills-tab.tsx index 3038aab..a6e74dc 100644 --- a/components/admin/team-detail/team-skills-tab.tsx +++ b/components/admin/team-detail/team-skills-tab.tsx @@ -95,19 +95,21 @@ export function TeamSkillsTab({ skillAnalysis }: TeamSkillsTabProps) {
{/* Liste des compétences détaillée */} -
+
{filteredSkills.map((skill, idx) => (
-
-
-

{skill.skillName}

+
+
+

+ {skill.skillName} +

{skill.category}

@@ -117,17 +119,17 @@ export function TeamSkillsTab({ skillAnalysis }: TeamSkillsTabProps) {
-
+
- Niveau moyen: - + Niveau: + {skill.averageLevel.toFixed(1)}/3
-
+
-
+
{skill.totalEvaluations}
-
Évaluations
+
Éval.
{skill.expertCount}
-
Experts
+
Experts
{skill.learnerCount}
-
Apprenants
+
Appren.
{skill.experts.filter((e) => e.canMentor).length > 0 && (
-
- Mentors disponibles: -
+
Mentors:
{skill.experts .filter((e) => e.canMentor) @@ -171,15 +171,15 @@ export function TeamSkillsTab({ skillAnalysis }: TeamSkillsTabProps) { - {expert.name} + {expert.name.split(" ")[0]} ))} {skill.experts.filter((e) => e.canMentor).length > 2 && ( +{skill.experts.filter((e) => e.canMentor).length - 2} diff --git a/lib/score-utils.ts b/lib/score-utils.ts index e03b424..fcab5d2 100644 --- a/lib/score-utils.ts +++ b/lib/score-utils.ts @@ -49,3 +49,20 @@ export function getSkillLevelLabel(level: string): string { return ""; } } + +/** + * Calcule la répartition des niveaux par compétence individuelle + */ +export function calculateSkillLevelDistribution( + members: Array<{ skills: Array<{ level: number }> }> +) { + const allSkills = members.flatMap((m) => m.skills); + + return { + expert: allSkills.filter((s) => s.level === 3).length, + autonomous: allSkills.filter((s) => s.level === 2).length, + notAutonomous: allSkills.filter((s) => s.level === 1).length, + never: allSkills.filter((s) => s.level === 0).length, + total: allSkills.length, + }; +}