feat: review my team page with importance

This commit is contained in:
Julien Froidefond
2025-08-27 12:56:48 +02:00
parent 88dc00a44b
commit 94a18b0ca5
8 changed files with 544 additions and 151 deletions

View File

@@ -522,4 +522,4 @@ export class SkillsService {
client.release();
}
}
}
}

View File

@@ -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`
);
}