+
{
+ const label =
+ name === "incontournable"
+ ? "Incontournables (obj. 75%)"
+ : "Majeures (obj. 60%)";
+ return [value, label];
+ }}
+ />
+
diff --git a/lib/team-review-types.ts b/lib/team-review-types.ts
index 6b96677..1e569b5 100644
--- a/lib/team-review-types.ts
+++ b/lib/team-review-types.ts
@@ -12,6 +12,7 @@ export interface TeamMemberSkill {
skillId: string;
skillName: string;
category: string;
+ importance: "incontournable" | "majeure" | "standard";
level: "never" | "not-autonomous" | "autonomous" | "expert";
canMentor: boolean;
wantsToLearn: boolean;
@@ -32,6 +33,7 @@ export interface SkillGap {
skillName: string;
category: string;
icon?: string;
+ importance: "incontournable" | "majeure" | "standard";
teamMembers: number;
experts: number;
mentors: number;
@@ -48,6 +50,10 @@ export interface CategoryCoverage {
experts: number;
mentors: number;
learners: number;
+ criticalSkillsCoverage: {
+ incontournable: number;
+ majeure: number;
+ };
}
export interface TeamReviewData {
@@ -66,6 +72,10 @@ export interface TeamReviewData {
averageTeamLevel: number;
mentorshipOpportunities: number;
learningNeeds: number;
+ criticalSkillsCoverage: {
+ incontournable: number;
+ majeure: number;
+ };
};
}
@@ -74,6 +84,7 @@ export interface MentorOpportunity {
mentee: TeamMember;
skill: string;
category: string;
+ importance: "incontournable" | "majeure" | "standard";
mentorLevel: "autonomous" | "expert";
menteeLevel: "never" | "not-autonomous";
}
diff --git a/services/skills-service.ts b/services/skills-service.ts
index 4f948ac..eb64e7a 100644
--- a/services/skills-service.ts
+++ b/services/skills-service.ts
@@ -522,4 +522,4 @@ export class SkillsService {
client.release();
}
}
-}
\ No newline at end of file
+}
diff --git a/services/team-review-service.ts b/services/team-review-service.ts
index 75aff95..a2e0595 100644
--- a/services/team-review-service.ts
+++ b/services/team-review-service.ts
@@ -32,6 +32,7 @@ export class TeamReviewService {
s.id as skill_id,
s.name as skill_name,
s.icon,
+ s.importance,
sc.name as category
FROM skill_categories sc
JOIN skills s ON s.category_id = sc.id
@@ -54,6 +55,7 @@ export class TeamReviewService {
u.email,
s.id as skill_id,
s.name as skill_name,
+ s.importance,
sc.name as category,
se.level,
se.can_mentor,
@@ -97,6 +99,7 @@ export class TeamReviewService {
skillId: row.skill_id,
skillName: row.skill_name,
category: row.category,
+ importance: row.importance || "standard",
level: row.level as SkillLevel,
canMentor: row.can_mentor || false,
wantsToLearn: row.wants_to_learn || false,
@@ -140,22 +143,28 @@ export class TeamReviewService {
const coverage =
totalTeamMembers > 0 ? (teamMembers / totalTeamMembers) * 100 : 0;
+ // Déterminer le niveau de risque en fonction de l'importance
+ const risk =
+ skill.importance === "incontournable" && experts === 0
+ ? "high"
+ : skill.importance === "majeure" && experts === 0 && mentors === 0
+ ? "high"
+ : experts === 0 && mentors === 0
+ ? "medium"
+ : "low";
+
return {
skillId: skill.skill_id,
skillName: skill.skill_name,
category: skill.category,
icon: skill.icon,
+ importance: skill.importance || "standard",
team_members: teamMembers,
experts,
mentors,
learners,
coverage,
- risk:
- experts === 0 && mentors === 0
- ? "high"
- : experts === 0 || mentors === 0
- ? "medium"
- : "low",
+ risk,
};
});
@@ -172,6 +181,10 @@ export class TeamReviewService {
mentors: 0,
learners: 0,
coverage: 0,
+ criticalSkillsCoverage: {
+ incontournable: 0,
+ majeure: 0,
+ },
});
}
@@ -186,6 +199,17 @@ export class TeamReviewService {
categoryStats.experts += skillGap.experts;
categoryStats.mentors += skillGap.mentors;
categoryStats.learners += skillGap.learners;
+
+ // Calculer la couverture des compétences critiques
+ if (
+ skillGap.importance === "incontournable" &&
+ skillGap.coverage > 50
+ ) {
+ categoryStats.criticalSkillsCoverage.incontournable++;
+ }
+ if (skillGap.importance === "majeure" && skillGap.coverage > 50) {
+ categoryStats.criticalSkillsCoverage.majeure++;
+ }
}
}
@@ -208,6 +232,28 @@ export class TeamReviewService {
);
// 8. Calculer les statistiques globales
+ const criticalSkillsCoverage = {
+ incontournable:
+ (skillGaps
+ .filter((gap) => gap.importance === "incontournable")
+ .reduce((acc, gap) => acc + (gap.coverage > 50 ? 1 : 0), 0) /
+ Math.max(
+ 1,
+ skillGaps.filter((gap) => gap.importance === "incontournable")
+ .length
+ )) *
+ 100,
+ majeure:
+ (skillGaps
+ .filter((gap) => gap.importance === "majeure")
+ .reduce((acc, gap) => acc + (gap.coverage > 50 ? 1 : 0), 0) /
+ Math.max(
+ 1,
+ skillGaps.filter((gap) => gap.importance === "majeure").length
+ )) *
+ 100,
+ };
+
const stats = {
totalMembers: membersMap.size,
totalSkills: skillGaps.length,
@@ -221,6 +267,7 @@ export class TeamReviewService {
0
),
learningNeeds: skillGaps.filter((gap) => gap.risk === "high").length,
+ criticalSkillsCoverage,
};
await client.query("COMMIT");
@@ -249,25 +296,43 @@ export class TeamReviewService {
): string[] {
const recommendations: string[] = [];
- // Analyser les gaps critiques
- const criticalGaps = skillGaps.filter((gap) => gap.risk === "high");
- if (criticalGaps.length > 0) {
+ // Analyser les gaps critiques par importance
+ const uncoveredIncontournables = skillGaps.filter(
+ (gap) => gap.importance === "incontournable" && gap.coverage < 50
+ );
+ if (uncoveredIncontournables.length > 0) {
recommendations.push(
- `Attention : ${
- criticalGaps.length
- } compétences critiques sans expert ni mentor : ${criticalGaps
+ `⚠️ ${
+ uncoveredIncontournables.length
+ } compétences incontournables faiblement couvertes : ${uncoveredIncontournables
.map((gap) => gap.skillName)
.join(", ")}`
);
}
- // Analyser les opportunités de mentorat
- const mentorshipNeeds = skillGaps.filter(
- (gap) => gap.learners > 0 && gap.mentors > 0
- ).length;
- if (mentorshipNeeds > 0) {
+ const uncoveredMajeures = skillGaps.filter(
+ (gap) => gap.importance === "majeure" && gap.coverage < 30
+ );
+ if (uncoveredMajeures.length > 0) {
recommendations.push(
- `${mentorshipNeeds} opportunités de mentorat identifiées`
+ `⚠️ ${
+ uncoveredMajeures.length
+ } compétences majeures faiblement couvertes : ${uncoveredMajeures
+ .map((gap) => gap.skillName)
+ .join(", ")}`
+ );
+ }
+
+ // Analyser les opportunités de mentorat pour les compétences importantes
+ const criticalMentorshipNeeds = skillGaps.filter(
+ (gap) =>
+ gap.learners > 0 &&
+ gap.mentors > 0 &&
+ (gap.importance === "incontournable" || gap.importance === "majeure")
+ ).length;
+ if (criticalMentorshipNeeds > 0) {
+ recommendations.push(
+ `${criticalMentorshipNeeds} opportunités de mentorat sur des compétences critiques identifiées`
);
}
@@ -283,13 +348,19 @@ export class TeamReviewService {
);
}
- // Identifier les experts isolés
- const isolatedExperts = members.filter(
- (member) => member.expertSkills > 0 && member.mentorSkills === 0
+ // Identifier les experts isolés sur des compétences importantes
+ const isolatedExperts = members.filter((member) =>
+ member.skills.some(
+ (skill) =>
+ skill.level === "expert" &&
+ !skill.canMentor &&
+ (skill.importance === "incontournable" ||
+ skill.importance === "majeure")
+ )
);
if (isolatedExperts.length > 0) {
recommendations.push(
- `${isolatedExperts.length} experts pourraient devenir mentors`
+ `${isolatedExperts.length} experts en compétences critiques pourraient devenir mentors`
);
}