feat: review admin overview and popup details fwith importance

This commit is contained in:
Julien Froidefond
2025-08-27 13:16:39 +02:00
parent 94a18b0ca5
commit e9aecca2a5
5 changed files with 415 additions and 82 deletions

View File

@@ -31,6 +31,7 @@ export class AdminService {
tm.uuid_id,
s.id as skill_id,
s.name as skill_name,
s.importance,
sc.name as category_name,
CASE
WHEN se.level = 'never' THEN 0
@@ -53,11 +54,34 @@ export class AdminService {
ss.team_id,
ss.skill_name,
s.icon as skill_icon,
AVG(ss.level_numeric) as avg_level
s.importance,
AVG(ss.level_numeric) as avg_level,
COALESCE(
SUM(CASE
WHEN ss.level_numeric >= 2 THEN 100.0 -- autonomous ou expert
WHEN ss.level_numeric = 1 THEN 50.0 -- not-autonomous
ELSE 0.0 -- never
END) / NULLIF(COUNT(*), 0),
0
) as coverage
FROM skill_stats ss
JOIN skills s ON ss.skill_id = s.id
WHERE ss.skill_name IS NOT NULL
GROUP BY ss.team_id, ss.skill_name, s.icon
GROUP BY ss.team_id, ss.skill_name, s.icon, s.importance
),
critical_skills_coverage AS (
SELECT
team_id,
COALESCE(
AVG(CASE WHEN importance = 'incontournable' THEN coverage ELSE NULL END),
0
) as incontournable_coverage,
COALESCE(
AVG(CASE WHEN importance = 'majeure' THEN coverage ELSE NULL END),
0
) as majeure_coverage
FROM team_skill_averages
GROUP BY team_id
)
SELECT
tm.team_id,
@@ -92,13 +116,26 @@ export class AdminService {
jsonb_build_object(
'skillName', tsa.skill_name,
'averageLevel', tsa.avg_level,
'icon', tsa.skill_icon
) ORDER BY tsa.avg_level DESC
'icon', tsa.skill_icon,
'importance', tsa.importance,
'coverage', tsa.coverage
) ORDER BY
CASE tsa.importance
WHEN 'incontournable' THEN 2
WHEN 'majeure' THEN 1
ELSE 0
END DESC,
tsa.avg_level DESC,
tsa.coverage DESC
)
FROM team_skill_averages tsa
WHERE tsa.team_id = tm.team_id
LIMIT 3
) as top_skills,
jsonb_build_object(
'incontournable', COALESCE(csc.incontournable_coverage, 0),
'majeure', COALESCE(csc.majeure_coverage, 0)
) as critical_skills_coverage,
CASE
WHEN COUNT(DISTINCT ss.skill_id) > 0 THEN
(COUNT(DISTINCT ss.skill_id) * 100.0 / (SELECT COUNT(*) FROM skills))
@@ -106,7 +143,8 @@ export class AdminService {
END as skill_coverage
FROM team_members tm
LEFT JOIN skill_stats ss ON tm.team_id = ss.team_id AND tm.uuid_id = ss.uuid_id
GROUP BY tm.team_id, tm.team_name, tm.direction
LEFT JOIN critical_skills_coverage csc ON tm.team_id = csc.team_id
GROUP BY tm.team_id, tm.team_name, tm.direction, csc.incontournable_coverage, csc.majeure_coverage
ORDER BY tm.direction, tm.team_name
`;
@@ -120,6 +158,10 @@ export class AdminService {
averageSkillLevel: parseFloat(row.avg_skill_level) || 0,
topSkills: row.top_skills || [],
skillCoverage: parseFloat(row.skill_coverage) || 0,
criticalSkillsCoverage: row.critical_skills_coverage || {
incontournable: 0,
majeure: 0,
},
members: (row.members || []).filter(
(member: any) => member.uuid !== null
),
@@ -238,14 +280,15 @@ export class AdminService {
);
if (parseInt(skillsCheck.rows[0].count) > 0) {
throw new Error("Impossible de supprimer une catégorie qui contient des skills");
throw new Error(
"Impossible de supprimer une catégorie qui contient des skills"
);
}
// Supprimer la catégorie
await client.query(
"DELETE FROM skill_categories WHERE id = $1",
[categoryId]
);
await client.query("DELETE FROM skill_categories WHERE id = $1", [
categoryId,
]);
await client.query("COMMIT");
} catch (error) {