fix: evaluation on empty eval category was KO
This commit is contained in:
@@ -25,7 +25,11 @@ export default async function EvaluationPage() {
|
|||||||
]);
|
]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<EvaluationClientWrapper userEvaluation={userEvaluation} teams={teams}>
|
<EvaluationClientWrapper
|
||||||
|
userEvaluation={userEvaluation}
|
||||||
|
teams={teams}
|
||||||
|
skillCategories={skillCategories}
|
||||||
|
>
|
||||||
<div>
|
<div>
|
||||||
{/* Skill Evaluation */}
|
{/* Skill Evaluation */}
|
||||||
<SkillEvaluation
|
<SkillEvaluation
|
||||||
|
|||||||
@@ -2,7 +2,13 @@
|
|||||||
|
|
||||||
import { useEffect, useState } from "react";
|
import { useEffect, useState } from "react";
|
||||||
import { useUser } from "@/hooks/use-user-context";
|
import { useUser } from "@/hooks/use-user-context";
|
||||||
import { UserEvaluation, Team, SkillLevel } from "@/lib/types";
|
import {
|
||||||
|
UserEvaluation,
|
||||||
|
Team,
|
||||||
|
SkillLevel,
|
||||||
|
UserProfile,
|
||||||
|
CategoryEvaluation,
|
||||||
|
} from "@/lib/types";
|
||||||
import {
|
import {
|
||||||
updateSkillLevel as updateSkillLevelAction,
|
updateSkillLevel as updateSkillLevelAction,
|
||||||
updateSkillMentorStatus as updateSkillMentorStatusAction,
|
updateSkillMentorStatus as updateSkillMentorStatusAction,
|
||||||
@@ -10,19 +16,22 @@ import {
|
|||||||
addSkillToEvaluation as addSkillToEvaluationAction,
|
addSkillToEvaluation as addSkillToEvaluationAction,
|
||||||
removeSkillFromEvaluation as removeSkillFromEvaluationAction,
|
removeSkillFromEvaluation as removeSkillFromEvaluationAction,
|
||||||
} from "@/lib/evaluation-actions";
|
} from "@/lib/evaluation-actions";
|
||||||
|
import { createEmptyEvaluation } from "@/lib/evaluation-utils";
|
||||||
|
|
||||||
interface EvaluationClientWrapperProps {
|
interface EvaluationClientWrapperProps {
|
||||||
userEvaluation: UserEvaluation | null;
|
userEvaluation: UserEvaluation | null;
|
||||||
teams: Team[];
|
teams: Team[];
|
||||||
children: React.ReactNode;
|
children: React.ReactNode;
|
||||||
|
skillCategories?: any[]; // Ajouter les catégories pour créer une évaluation vide
|
||||||
}
|
}
|
||||||
|
|
||||||
export function EvaluationClientWrapper({
|
export function EvaluationClientWrapper({
|
||||||
userEvaluation,
|
userEvaluation,
|
||||||
teams,
|
teams,
|
||||||
children,
|
children,
|
||||||
|
skillCategories = [],
|
||||||
}: EvaluationClientWrapperProps) {
|
}: EvaluationClientWrapperProps) {
|
||||||
const { setUserInfo } = useUser();
|
const { userInfo, setUserInfo } = useUser();
|
||||||
|
|
||||||
// État local pour l'UI optimiste - commence avec les données SSR
|
// État local pour l'UI optimiste - commence avec les données SSR
|
||||||
const [currentEvaluation, setCurrentEvaluation] =
|
const [currentEvaluation, setCurrentEvaluation] =
|
||||||
@@ -30,8 +39,18 @@ export function EvaluationClientWrapper({
|
|||||||
|
|
||||||
// Met à jour l'état local quand les props changent (SSR)
|
// Met à jour l'état local quand les props changent (SSR)
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
if (userEvaluation) {
|
||||||
setCurrentEvaluation(userEvaluation);
|
setCurrentEvaluation(userEvaluation);
|
||||||
}, [userEvaluation]);
|
} else if (skillCategories.length > 0) {
|
||||||
|
// Créer une évaluation vide si aucune n'existe, pour que l'UI puisse fonctionner
|
||||||
|
const emptyEvaluation: UserEvaluation = {
|
||||||
|
profile: { firstName: "", lastName: "", teamId: "" }, // Profile temporaire
|
||||||
|
evaluations: createEmptyEvaluation(skillCategories),
|
||||||
|
lastUpdated: new Date().toISOString(),
|
||||||
|
};
|
||||||
|
setCurrentEvaluation(emptyEvaluation);
|
||||||
|
}
|
||||||
|
}, [userEvaluation, skillCategories]);
|
||||||
|
|
||||||
// Fonctions avec UI optimiste
|
// Fonctions avec UI optimiste
|
||||||
const updateSkillLevel = async (
|
const updateSkillLevel = async (
|
||||||
@@ -173,9 +192,11 @@ export function EvaluationClientWrapper({
|
|||||||
const previousEvaluation = currentEvaluation;
|
const previousEvaluation = currentEvaluation;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const updatedEvaluations = currentEvaluation.evaluations.map(
|
// 1. Mise à jour optimiste de l'UI (immédiate)
|
||||||
(catEval) => {
|
let updatedEvaluations: CategoryEvaluation[] =
|
||||||
|
currentEvaluation.evaluations.map((catEval) => {
|
||||||
if (catEval.category === category) {
|
if (catEval.category === category) {
|
||||||
|
// Si la compétence n'est pas déjà ajoutée
|
||||||
if (!catEval.selectedSkillIds.includes(skillId)) {
|
if (!catEval.selectedSkillIds.includes(skillId)) {
|
||||||
return {
|
return {
|
||||||
...catEval,
|
...catEval,
|
||||||
@@ -184,7 +205,7 @@ export function EvaluationClientWrapper({
|
|||||||
...catEval.skills,
|
...catEval.skills,
|
||||||
{
|
{
|
||||||
skillId,
|
skillId,
|
||||||
level: null,
|
level: "never" as SkillLevel,
|
||||||
canMentor: false,
|
canMentor: false,
|
||||||
wantsToLearn: false,
|
wantsToLearn: false,
|
||||||
},
|
},
|
||||||
@@ -193,8 +214,28 @@ export function EvaluationClientWrapper({
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
return catEval;
|
return catEval;
|
||||||
|
});
|
||||||
|
|
||||||
|
// Si la catégorie n'existe pas encore, la créer
|
||||||
|
if (
|
||||||
|
!updatedEvaluations.find(
|
||||||
|
(evaluation) => evaluation.category === category
|
||||||
|
)
|
||||||
|
) {
|
||||||
|
const newCategoryEvaluation: CategoryEvaluation = {
|
||||||
|
category,
|
||||||
|
selectedSkillIds: [skillId],
|
||||||
|
skills: [
|
||||||
|
{
|
||||||
|
skillId,
|
||||||
|
level: "never",
|
||||||
|
canMentor: false,
|
||||||
|
wantsToLearn: false,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
};
|
||||||
|
updatedEvaluations = [...updatedEvaluations, newCategoryEvaluation];
|
||||||
}
|
}
|
||||||
);
|
|
||||||
|
|
||||||
const newEvaluation: UserEvaluation = {
|
const newEvaluation: UserEvaluation = {
|
||||||
...currentEvaluation,
|
...currentEvaluation,
|
||||||
@@ -202,10 +243,14 @@ export function EvaluationClientWrapper({
|
|||||||
lastUpdated: new Date().toISOString(),
|
lastUpdated: new Date().toISOString(),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// 2. Mettre à jour l'état local immédiatement
|
||||||
setCurrentEvaluation(newEvaluation);
|
setCurrentEvaluation(newEvaluation);
|
||||||
|
|
||||||
|
// 3. Persister côté serveur (en arrière-plan)
|
||||||
await addSkillToEvaluationAction(category, skillId);
|
await addSkillToEvaluationAction(category, skillId);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("Failed to add skill to evaluation:", error);
|
console.error("❌ Failed to add skill:", error);
|
||||||
|
// En cas d'erreur, rollback l'état local
|
||||||
setCurrentEvaluation(previousEvaluation);
|
setCurrentEvaluation(previousEvaluation);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -219,8 +264,9 @@ export function EvaluationClientWrapper({
|
|||||||
const previousEvaluation = currentEvaluation;
|
const previousEvaluation = currentEvaluation;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const updatedEvaluations = currentEvaluation.evaluations.map(
|
// 1. Mise à jour optimiste de l'UI (immédiate)
|
||||||
(catEval) => {
|
const updatedEvaluations: CategoryEvaluation[] =
|
||||||
|
currentEvaluation.evaluations.map((catEval) => {
|
||||||
if (catEval.category === category) {
|
if (catEval.category === category) {
|
||||||
// Filtrer seulement les compétences qui ne sont pas déjà sélectionnées
|
// Filtrer seulement les compétences qui ne sont pas déjà sélectionnées
|
||||||
const newSkillIds = skillIds.filter(
|
const newSkillIds = skillIds.filter(
|
||||||
@@ -235,7 +281,7 @@ export function EvaluationClientWrapper({
|
|||||||
...catEval.skills,
|
...catEval.skills,
|
||||||
...newSkillIds.map((skillId) => ({
|
...newSkillIds.map((skillId) => ({
|
||||||
skillId,
|
skillId,
|
||||||
level: null,
|
level: "never" as SkillLevel,
|
||||||
canMentor: false,
|
canMentor: false,
|
||||||
wantsToLearn: false,
|
wantsToLearn: false,
|
||||||
})),
|
})),
|
||||||
@@ -244,8 +290,7 @@ export function EvaluationClientWrapper({
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
return catEval;
|
return catEval;
|
||||||
}
|
});
|
||||||
);
|
|
||||||
|
|
||||||
const newEvaluation: UserEvaluation = {
|
const newEvaluation: UserEvaluation = {
|
||||||
...currentEvaluation,
|
...currentEvaluation,
|
||||||
@@ -253,14 +298,16 @@ export function EvaluationClientWrapper({
|
|||||||
lastUpdated: new Date().toISOString(),
|
lastUpdated: new Date().toISOString(),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// 2. Mettre à jour l'état local immédiatement
|
||||||
setCurrentEvaluation(newEvaluation);
|
setCurrentEvaluation(newEvaluation);
|
||||||
|
|
||||||
// Ajouter toutes les compétences en parallèle côté API
|
// 3. Persister côté serveur (en arrière-plan)
|
||||||
await Promise.all(
|
await Promise.all(
|
||||||
skillIds.map((skillId) => addSkillToEvaluationAction(category, skillId))
|
skillIds.map((skillId) => addSkillToEvaluationAction(category, skillId))
|
||||||
);
|
);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("Failed to add multiple skills to evaluation:", error);
|
console.error("❌ Failed to add multiple skills:", error);
|
||||||
|
// En cas d'erreur, rollback l'état local
|
||||||
setCurrentEvaluation(previousEvaluation);
|
setCurrentEvaluation(previousEvaluation);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -33,7 +33,8 @@ export function SkillEvaluation({
|
|||||||
} = useEvaluationContext();
|
} = useEvaluationContext();
|
||||||
|
|
||||||
// Utiliser l'évaluation du contexte (avec état optimiste) ou celle des props (SSR)
|
// Utiliser l'évaluation du contexte (avec état optimiste) ou celle des props (SSR)
|
||||||
const activeEvaluations = contextEvaluation?.evaluations || evaluations;
|
const activeEvaluations = contextEvaluation?.evaluations || [];
|
||||||
|
|
||||||
const searchParams = useSearchParams();
|
const searchParams = useSearchParams();
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const categoryParam = searchParams.get("category");
|
const categoryParam = searchParams.get("category");
|
||||||
@@ -90,7 +91,7 @@ export function SkillEvaluation({
|
|||||||
<div className="space-y-8">
|
<div className="space-y-8">
|
||||||
<SkillSelector
|
<SkillSelector
|
||||||
categories={categories}
|
categories={categories}
|
||||||
evaluations={activeEvaluations}
|
evaluations={contextEvaluation?.evaluations || []}
|
||||||
selectedCategory={selectedCategory}
|
selectedCategory={selectedCategory}
|
||||||
onAddSkill={addSkillToEvaluation}
|
onAddSkill={addSkillToEvaluation}
|
||||||
onAddMultipleSkills={addMultipleSkillsToEvaluation}
|
onAddMultipleSkills={addMultipleSkillsToEvaluation}
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
"use client";
|
"use client";
|
||||||
|
|
||||||
import { useState } from "react";
|
import { useState, useEffect } from "react";
|
||||||
import { Button } from "@/components/ui/button";
|
import { Button } from "@/components/ui/button";
|
||||||
import {
|
import {
|
||||||
Dialog,
|
Dialog,
|
||||||
@@ -42,13 +42,22 @@ export function SkillSelector({
|
|||||||
const currentCategory = categories.find(
|
const currentCategory = categories.find(
|
||||||
(cat) => cat.category === selectedCategory
|
(cat) => cat.category === selectedCategory
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// Utiliser les évaluations passées en props (qui viennent du contexte et sont mises à jour)
|
||||||
const currentEvaluation = evaluations.find(
|
const currentEvaluation = evaluations.find(
|
||||||
(evaluation) => evaluation.category === selectedCategory
|
(evaluation) => evaluation.category === selectedCategory
|
||||||
);
|
);
|
||||||
|
|
||||||
if (!currentCategory) return null;
|
if (!currentCategory) return null;
|
||||||
|
|
||||||
const selectedSkillIds = currentEvaluation?.selectedSkillIds || [];
|
// Si la catégorie n'existe pas dans l'évaluation, créer une évaluation vide pour cette catégorie
|
||||||
|
const effectiveEvaluation = currentEvaluation || {
|
||||||
|
category: selectedCategory,
|
||||||
|
skills: [],
|
||||||
|
selectedSkillIds: [],
|
||||||
|
};
|
||||||
|
|
||||||
|
const selectedSkillIds = effectiveEvaluation.selectedSkillIds || [];
|
||||||
|
|
||||||
const filteredSkills = currentCategory.skills.filter(
|
const filteredSkills = currentCategory.skills.filter(
|
||||||
(skill) =>
|
(skill) =>
|
||||||
|
|||||||
@@ -228,12 +228,14 @@ export class EvaluationService {
|
|||||||
// 1. Upsert user
|
// 1. Upsert user
|
||||||
const userId = await this.upsertUser(evaluation.profile);
|
const userId = await this.upsertUser(evaluation.profile);
|
||||||
|
|
||||||
// 2. Upsert user_evaluation
|
// 2. Upsert user_evaluation - d'abord supprimer l'ancienne si elle existe
|
||||||
|
await client.query("DELETE FROM user_evaluations WHERE user_id = $1", [
|
||||||
|
userId,
|
||||||
|
]);
|
||||||
|
|
||||||
const userEvalQuery = `
|
const userEvalQuery = `
|
||||||
INSERT INTO user_evaluations (user_id, last_updated)
|
INSERT INTO user_evaluations (user_id, last_updated)
|
||||||
VALUES ($1, $2)
|
VALUES ($1, $2)
|
||||||
ON CONFLICT (user_id)
|
|
||||||
DO UPDATE SET last_updated = $2
|
|
||||||
RETURNING id
|
RETURNING id
|
||||||
`;
|
`;
|
||||||
|
|
||||||
@@ -244,12 +246,6 @@ export class EvaluationService {
|
|||||||
|
|
||||||
const userEvaluationId = userEvalResult.rows[0].id;
|
const userEvaluationId = userEvalResult.rows[0].id;
|
||||||
|
|
||||||
// 3. Supprimer les anciennes évaluations de skills
|
|
||||||
await client.query(
|
|
||||||
"DELETE FROM skill_evaluations WHERE user_evaluation_id = $1",
|
|
||||||
[userEvaluationId]
|
|
||||||
);
|
|
||||||
|
|
||||||
// 4. Sauvegarder les nouvelles évaluations directement
|
// 4. Sauvegarder les nouvelles évaluations directement
|
||||||
for (const catEval of evaluation.evaluations) {
|
for (const catEval of evaluation.evaluations) {
|
||||||
await this.saveSkillEvaluations(client, userEvaluationId, catEval);
|
await this.saveSkillEvaluations(client, userEvaluationId, catEval);
|
||||||
|
|||||||
Reference in New Issue
Block a user