diff --git a/app/api/auth/route.ts b/app/api/auth/route.ts index 4165c37..77a58c4 100644 --- a/app/api/auth/route.ts +++ b/app/api/auth/route.ts @@ -53,7 +53,21 @@ export async function POST(request: NextRequest) { } const evaluationService = new EvaluationService(); - const userUuid = await evaluationService.upsertUserUuid(profile); + + // Vérifier s'il y a déjà un cookie d'authentification + const cookieStore = await cookies(); + const existingUserUuid = cookieStore.get(COOKIE_NAME)?.value; + + let userUuid: string; + + if (existingUserUuid) { + // Mettre à jour l'utilisateur existant + await evaluationService.updateUserByUuid(existingUserUuid, profile); + userUuid = existingUserUuid; + } else { + // Créer un nouvel utilisateur + userUuid = await evaluationService.upsertUserUuid(profile); + } // Créer la réponse avec le cookie const response = NextResponse.json( diff --git a/lib/server-auth.ts b/lib/server-auth.ts index 62a6b04..8aad113 100644 --- a/lib/server-auth.ts +++ b/lib/server-auth.ts @@ -48,16 +48,9 @@ export async function getServerUserEvaluation() { try { const evaluationService = new EvaluationService(); - // Récupérer d'abord le profil utilisateur via UUID - const userProfile = await evaluationService.getUserByUuid(userUuid); - - if (!userProfile) { - return null; - } - - // Puis charger l'évaluation avec le profil - const userEvaluation = await evaluationService.loadUserEvaluation( - userProfile + // Charger directement l'évaluation par UUID + const userEvaluation = await evaluationService.loadUserEvaluationByUuid( + userUuid ); return userEvaluation; diff --git a/services/evaluation-service.ts b/services/evaluation-service.ts index 727cc40..0d191f4 100644 --- a/services/evaluation-service.ts +++ b/services/evaluation-service.ts @@ -16,37 +16,89 @@ export class EvaluationService { const client = await pool.connect(); try { - // Vérifier si l'utilisateur existe déjà (par firstName + lastName + teamId) - const existingUserQuery = ` - SELECT uuid_id FROM users - WHERE first_name = $1 AND last_name = $2 AND team_id = $3 + // Créer un nouvel utilisateur avec UUID auto-généré + const insertQuery = ` + INSERT INTO users (first_name, last_name, team_id, uuid_id) + VALUES ($1, $2, $3, uuid_generate_v4()) + RETURNING uuid_id `; - const existingUser = await client.query(existingUserQuery, [ + const result = await client.query(insertQuery, [ profile.firstName, profile.lastName, profile.teamId, ]); - if (existingUser.rows.length > 0) { - // Retourner l'UUID de l'utilisateur existant - return existingUser.rows[0].uuid_id; - } else { - // Créer un nouvel utilisateur avec UUID auto-généré - const insertQuery = ` - INSERT INTO users (first_name, last_name, team_id, uuid_id) - VALUES ($1, $2, $3, uuid_generate_v4()) - RETURNING uuid_id + return result.rows[0].uuid_id; + } catch (error: any) { + // Si erreur de contrainte unique, l'utilisateur existe déjà + if ( + error.code === "23505" && + error.constraint === "users_first_name_last_name_team_id_key" + ) { + // Récupérer l'utilisateur existant + const existingUserQuery = ` + SELECT uuid_id FROM users + WHERE first_name = $1 AND last_name = $2 AND team_id = $3 `; - const result = await client.query(insertQuery, [ + const existingUser = await client.query(existingUserQuery, [ profile.firstName, profile.lastName, profile.teamId, ]); - return result.rows[0].uuid_id; + return existingUser.rows[0].uuid_id; } + throw error; + } finally { + client.release(); + } + } + + /** + * Met à jour un utilisateur existant par son UUID + */ + async updateUserByUuid( + userUuid: string, + profile: UserProfile + ): Promise { + const pool = getPool(); + const client = await pool.connect(); + + try { + // Vérifier s'il existe déjà un utilisateur avec ce nom/prénom dans la nouvelle équipe + const conflictCheckQuery = ` + SELECT uuid_id FROM users + WHERE first_name = $1 AND last_name = $2 AND team_id = $3 AND uuid_id != $4 + `; + + const conflictResult = await client.query(conflictCheckQuery, [ + profile.firstName, + profile.lastName, + profile.teamId, + userUuid, + ]); + + if (conflictResult.rows.length > 0) { + throw new Error( + "Un utilisateur avec ce nom et prénom existe déjà dans cette équipe" + ); + } + + // Mettre à jour l'utilisateur existant + const updateQuery = ` + UPDATE users + SET first_name = $1, last_name = $2, team_id = $3, updated_at = CURRENT_TIMESTAMP + WHERE uuid_id = $4 + `; + + await client.query(updateQuery, [ + profile.firstName, + profile.lastName, + profile.teamId, + userUuid, + ]); } finally { client.release(); } @@ -83,6 +135,104 @@ export class EvaluationService { } } + /** + * Charge une évaluation utilisateur directement par UUID + */ + async loadUserEvaluationByUuid( + userUuid: string + ): Promise { + const pool = getPool(); + const client = await pool.connect(); + + try { + // Trouver l'utilisateur et son évaluation par UUID + const userQuery = ` + SELECT u.*, ue.id as user_evaluation_id, ue.last_updated + FROM users u + LEFT JOIN user_evaluations ue ON u.uuid_id = ue.user_uuid + WHERE u.uuid_id = $1 + `; + + const userResult = await client.query(userQuery, [userUuid]); + + if ( + userResult.rows.length === 0 || + !userResult.rows[0].user_evaluation_id + ) { + return null; + } + + const userData = userResult.rows[0]; + const userEvaluationId = userData.user_evaluation_id; + + // Charger directement les skills évaluées avec leurs catégories + const skillsQuery = ` + SELECT + sc.name as category_name, + se.skill_id, + se.level, + se.can_mentor, + se.wants_to_learn, + se.is_selected + FROM skill_evaluations se + JOIN skills s ON se.skill_id = s.id + JOIN skill_categories sc ON s.category_id = sc.id + WHERE se.user_evaluation_id = $1 + ORDER BY sc.name, s.name + `; + + const skillsResult = await client.query(skillsQuery, [userEvaluationId]); + + // Grouper par catégorie + const categoriesMap = new Map(); + + for (const row of skillsResult.rows) { + const categoryName = row.category_name; + + if (!categoriesMap.has(categoryName)) { + categoriesMap.set(categoryName, { + category: categoryName, + selectedSkillIds: [], + skills: [], + }); + } + + const catEval = categoriesMap.get(categoryName)!; + + // Ajouter aux skills sélectionnées si is_selected = true + if (row.is_selected) { + catEval.selectedSkillIds.push(row.skill_id); + } + + // Ajouter aux évaluations si elle est sélectionnée (donc évaluée) + if (row.is_selected) { + catEval.skills.push({ + skillId: row.skill_id, + level: row.level, + canMentor: row.can_mentor, + wantsToLearn: row.wants_to_learn, + }); + } + } + + const evaluations: CategoryEvaluation[] = Array.from( + categoriesMap.values() + ); + + return { + profile: { + firstName: userData.first_name, + lastName: userData.last_name, + teamId: userData.team_id, + }, + evaluations, + lastUpdated: userData.last_updated.toISOString(), + }; + } finally { + client.release(); + } + } + /** * Crée ou met à jour un utilisateur (legacy - retourne l'ID numérique) */ @@ -318,18 +468,17 @@ export class EvaluationService { const client = await pool.connect(); try { - // Trouver l'utilisateur + // Trouver l'utilisateur par firstName + lastName (équipe peut avoir changé) const userQuery = ` SELECT u.*, ue.id as user_evaluation_id, ue.last_updated FROM users u LEFT JOIN user_evaluations ue ON u.uuid_id = ue.user_uuid - WHERE u.first_name = $1 AND u.last_name = $2 AND u.team_id = $3 + WHERE u.first_name = $1 AND u.last_name = $2 `; const userResult = await client.query(userQuery, [ profile.firstName, profile.lastName, - profile.teamId, ]); if (