refactor: revew all design of services, clients, deadcode, ...

This commit is contained in:
Julien Froidefond
2025-08-24 22:03:15 +02:00
parent f4dcc89c11
commit 6fba622003
63 changed files with 969 additions and 1846 deletions

View File

@@ -1,193 +0,0 @@
export interface Skill {
id: string;
name: string;
description: string;
icon: string;
categoryId: string;
category: string;
usageCount: number;
}
export interface Team {
id: string;
name: string;
direction: string;
memberCount: number;
}
export interface TeamMember {
id: string;
firstName: string;
lastName: string;
fullName: string;
joinedAt: string;
}
export class AdminManagementService {
private static baseUrl = "/api/admin";
// Skills Management
static async getSkills(): Promise<Skill[]> {
const response = await fetch(`${this.baseUrl}/skills`);
if (!response.ok) {
throw new Error("Failed to fetch skills");
}
return response.json();
}
static async createSkill(
skillData: Omit<Skill, "id" | "usageCount">
): Promise<Skill> {
const response = await fetch(`${this.baseUrl}/skills`, {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify(skillData),
});
if (!response.ok) {
const error = await response.json();
throw new Error(error.error || "Failed to create skill");
}
return response.json();
}
static async updateSkill(skillData: Skill): Promise<Skill> {
const response = await fetch(`${this.baseUrl}/skills`, {
method: "PUT",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify(skillData),
});
if (!response.ok) {
const error = await response.json();
throw new Error(error.error || "Failed to update skill");
}
return response.json();
}
static async deleteSkill(skillId: string): Promise<void> {
const response = await fetch(`${this.baseUrl}/skills?id=${skillId}`, {
method: "DELETE",
});
if (!response.ok) {
const error = await response.json();
throw new Error(error.error || "Failed to delete skill");
}
}
// Teams Management
static async getTeams(): Promise<Team[]> {
const response = await fetch(`${this.baseUrl}/teams`);
if (!response.ok) {
throw new Error("Failed to fetch teams");
}
return response.json();
}
static async createTeam(
teamData: Omit<Team, "id" | "memberCount">
): Promise<Team> {
const response = await fetch(`${this.baseUrl}/teams`, {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify(teamData),
});
if (!response.ok) {
const error = await response.json();
throw new Error(error.error || "Failed to create team");
}
return response.json();
}
static async updateTeam(teamData: Team): Promise<Team> {
const response = await fetch(`${this.baseUrl}/teams`, {
method: "PUT",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify(teamData),
});
if (!response.ok) {
const error = await response.json();
throw new Error(error.error || "Failed to update team");
}
return response.json();
}
static async deleteTeam(teamId: string): Promise<void> {
const response = await fetch(`${this.baseUrl}/teams?id=${teamId}`, {
method: "DELETE",
});
if (!response.ok) {
const error = await response.json();
throw new Error(error.error || "Failed to delete team");
}
}
static async deleteDirection(direction: string): Promise<void> {
const response = await fetch(
`${this.baseUrl}/teams?direction=${encodeURIComponent(direction)}`,
{
method: "DELETE",
}
);
if (!response.ok) {
const error = await response.json();
throw new Error(error.error || "Failed to delete direction");
}
}
// Team Members
static async getTeamMembers(teamId: string): Promise<TeamMember[]> {
const response = await fetch(`${this.baseUrl}/teams/${teamId}/members`);
if (!response.ok) {
throw new Error("Failed to fetch team members");
}
return response.json();
}
static async removeTeamMember(
teamId: string,
memberId: string
): Promise<void> {
const response = await fetch(`${this.baseUrl}/teams/${teamId}/members`, {
method: "DELETE",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({ memberId }),
});
if (!response.ok) {
const error = await response.json();
throw new Error(error.error || "Failed to remove team member");
}
}
// User Management
static async deleteUser(userId: string): Promise<void> {
const response = await fetch(`${this.baseUrl}/users/${userId}`, {
method: "DELETE",
});
if (!response.ok) {
const error = await response.json();
throw new Error(error.error || "Failed to delete user");
}
}
}

View File

@@ -1,39 +1,6 @@
import { getPool } from "./database";
import { Team, SkillCategory } from "@/lib/types";
export interface TeamMember {
uuid: string;
firstName: string;
lastName: string;
skills: Array<{
skillId: string;
skillName: string;
category: string;
level: number;
canMentor: boolean;
wantsToLearn: boolean;
}>;
joinDate: string;
}
export interface TeamStats {
teamId: string;
teamName: string;
direction: string;
totalMembers: number;
averageSkillLevel: number;
topSkills: Array<{ skillName: string; averageLevel: number; icon?: string }>;
skillCoverage: number; // Percentage of skills evaluated
members: TeamMember[];
}
export interface DirectionStats {
direction: string;
teams: TeamStats[];
totalMembers: number;
averageSkillLevel: number;
topCategories: Array<{ category: string; averageLevel: number }>;
}
import { TeamMember, TeamStats, DirectionStats } from "@/lib/admin-types";
export class AdminService {
/**

View File

@@ -1,444 +0,0 @@
import {
UserEvaluation,
UserProfile,
SkillLevel,
Team,
SkillCategory,
Skill,
} from "../lib/types";
export class ApiClient {
private baseUrl: string;
constructor() {
this.baseUrl = process.env.NEXT_PUBLIC_API_URL || "";
}
/**
* Charge une évaluation utilisateur depuis l'API
* Si profile est fourni, utilise les paramètres (mode compatibilité)
* Sinon, utilise l'authentification par cookie
*/
async loadUserEvaluation(
profile?: UserProfile
): Promise<UserEvaluation | null> {
try {
let url = `${this.baseUrl}/api/evaluations`;
// Mode compatibilité avec profile en paramètres
if (profile) {
const params = new URLSearchParams({
firstName: profile.firstName,
lastName: profile.lastName,
teamId: profile.teamId,
});
url += `?${params}`;
}
const response = await fetch(url, {
credentials: "same-origin", // Pour inclure les cookies
});
if (!response.ok) {
throw new Error("Erreur lors du chargement de l'évaluation");
}
const data = await response.json();
return data.evaluation;
} catch (error) {
console.error("Erreur lors du chargement de l'évaluation:", error);
return null;
}
}
/**
* Sauvegarde une évaluation utilisateur via l'API
*/
async saveUserEvaluation(evaluation: UserEvaluation): Promise<void> {
try {
const response = await fetch(`${this.baseUrl}/api/evaluations`, {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({ evaluation }),
credentials: "same-origin",
});
if (!response.ok) {
throw new Error("Erreur lors de la sauvegarde de l'évaluation");
}
} catch (error) {
console.error("Erreur lors de la sauvegarde de l'évaluation:", error);
throw error;
}
}
/**
* Met à jour le niveau d'une skill
*/
async updateSkillLevel(
profile: UserProfile,
category: string,
skillId: string,
level: SkillLevel
): Promise<void> {
await this.updateSkill(profile, category, skillId, {
action: "updateLevel",
level,
});
}
/**
* Met à jour le statut de mentorat d'une skill
*/
async updateSkillMentorStatus(
profile: UserProfile,
category: string,
skillId: string,
canMentor: boolean
): Promise<void> {
await this.updateSkill(profile, category, skillId, {
action: "updateMentorStatus",
canMentor,
});
}
/**
* Met à jour le statut d'apprentissage d'une skill
*/
async updateSkillLearningStatus(
profile: UserProfile,
category: string,
skillId: string,
wantsToLearn: boolean
): Promise<void> {
await this.updateSkill(profile, category, skillId, {
action: "updateLearningStatus",
wantsToLearn,
});
}
/**
* Ajoute une skill à l'évaluation
*/
async addSkillToEvaluation(
profile: UserProfile,
category: string,
skillId: string
): Promise<void> {
await this.updateSkill(profile, category, skillId, {
action: "addSkill",
});
}
/**
* Supprime une skill de l'évaluation
*/
async removeSkillFromEvaluation(
profile: UserProfile,
category: string,
skillId: string
): Promise<void> {
await this.updateSkill(profile, category, skillId, {
action: "removeSkill",
});
}
/**
* Méthode utilitaire pour mettre à jour une skill
*/
private async updateSkill(
profile: UserProfile,
category: string,
skillId: string,
options: {
action:
| "updateLevel"
| "updateMentorStatus"
| "updateLearningStatus"
| "addSkill"
| "removeSkill";
level?: SkillLevel;
canMentor?: boolean;
wantsToLearn?: boolean;
}
): Promise<void> {
try {
const response = await fetch(`${this.baseUrl}/api/evaluations/skills`, {
method: "PUT",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({
profile,
category,
skillId,
...options,
}),
credentials: "same-origin",
});
if (!response.ok) {
throw new Error("Erreur lors de la mise à jour de la skill");
}
} catch (error) {
console.error("Erreur lors de la mise à jour de la skill:", error);
throw error;
}
}
/**
* Charge toutes les équipes
*/
async loadTeams(): Promise<Team[]> {
try {
const response = await fetch(`${this.baseUrl}/api/teams`);
if (!response.ok) {
throw new Error("Erreur lors du chargement des équipes");
}
return await response.json();
} catch (error) {
console.error("Erreur lors du chargement des équipes:", error);
return [];
}
}
/**
* Charge une équipe par ID
*/
async loadTeamById(teamId: string): Promise<Team | null> {
try {
const response = await fetch(`${this.baseUrl}/api/teams/${teamId}`);
if (!response.ok) {
if (response.status === 404) {
return null;
}
throw new Error("Erreur lors du chargement de l'équipe");
}
return await response.json();
} catch (error) {
console.error("Erreur lors du chargement de l'équipe:", error);
return null;
}
}
/**
* Charge les équipes par direction
*/
async loadTeamsByDirection(direction: string): Promise<Team[]> {
try {
const response = await fetch(
`${this.baseUrl}/api/teams/direction/${direction}`
);
if (!response.ok) {
throw new Error("Erreur lors du chargement des équipes par direction");
}
return await response.json();
} catch (error) {
console.error(
"Erreur lors du chargement des équipes par direction:",
error
);
return [];
}
}
/**
* Charge toutes les directions
*/
async loadDirections(): Promise<string[]> {
try {
const response = await fetch(`${this.baseUrl}/api/teams/directions`);
if (!response.ok) {
throw new Error("Erreur lors du chargement des directions");
}
return await response.json();
} catch (error) {
console.error("Erreur lors du chargement des directions:", error);
return [];
}
}
/**
* Crée une nouvelle équipe
*/
async createTeam(
team: Omit<Team, "created_at" | "updated_at">
): Promise<Team | null> {
try {
const response = await fetch(`${this.baseUrl}/api/teams`, {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify(team),
});
if (!response.ok) {
throw new Error("Erreur lors de la création de l'équipe");
}
return await response.json();
} catch (error) {
console.error("Erreur lors de la création de l'équipe:", error);
return null;
}
}
/**
* Met à jour une équipe
*/
async updateTeam(
teamId: string,
updates: Partial<Omit<Team, "id">>
): Promise<Team | null> {
try {
const response = await fetch(`${this.baseUrl}/api/teams/${teamId}`, {
method: "PUT",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify(updates),
});
if (!response.ok) {
throw new Error("Erreur lors de la mise à jour de l'équipe");
}
return await response.json();
} catch (error) {
console.error("Erreur lors de la mise à jour de l'équipe:", error);
return null;
}
}
/**
* Supprime une équipe
*/
async deleteTeam(teamId: string): Promise<boolean> {
try {
const response = await fetch(`${this.baseUrl}/api/teams/${teamId}`, {
method: "DELETE",
});
if (!response.ok) {
throw new Error("Erreur lors de la suppression de l'équipe");
}
return true;
} catch (error) {
console.error("Erreur lors de la suppression de l'équipe:", error);
return false;
}
}
/**
* Charge toutes les catégories de skills
*/
async loadSkillCategories(): Promise<SkillCategory[]> {
try {
const response = await fetch(`${this.baseUrl}/api/skills`);
if (!response.ok) {
throw new Error("Erreur lors du chargement des catégories de skills");
}
return await response.json();
} catch (error) {
console.error(
"Erreur lors du chargement des catégories de skills:",
error
);
return [];
}
}
/**
* Charge les skills d'une catégorie
*/
async loadSkillsByCategory(categoryId: string): Promise<Skill[]> {
try {
const response = await fetch(`${this.baseUrl}/api/skills/${categoryId}`);
if (!response.ok) {
throw new Error("Erreur lors du chargement des skills par catégorie");
}
return await response.json();
} catch (error) {
console.error(
"Erreur lors du chargement des skills par catégorie:",
error
);
return [];
}
}
/**
* Crée une nouvelle catégorie de skill
*/
async createSkillCategory(category: {
id: string;
name: string;
icon: string;
}): Promise<boolean> {
try {
const response = await fetch(`${this.baseUrl}/api/skills`, {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify(category),
});
return response.ok;
} catch (error) {
console.error(
"Erreur lors de la création de la catégorie de skill:",
error
);
return false;
}
}
/**
* Crée une nouvelle skill
*/
async createSkill(
categoryId: string,
skill: {
id: string;
name: string;
description: string;
icon?: string;
links: string[];
}
): Promise<boolean> {
try {
const response = await fetch(`${this.baseUrl}/api/skills/${categoryId}`, {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify(skill),
});
return response.ok;
} catch (error) {
console.error("Erreur lors de la création de la skill:", error);
return false;
}
}
}
// Instance singleton
export const apiClient = new ApiClient();

32
services/auth-service.ts Normal file
View File

@@ -0,0 +1,32 @@
import { cookies } from "next/headers";
// Constantes pour les cookies (définies ici car auth-service.ts a été supprimé)
export const COOKIE_NAME = "peakSkills_userId";
export const COOKIE_MAX_AGE = 30 * 24 * 60 * 60; // 30 jours
/**
* Service d'authentification côté serveur
* Implémente les méthodes qui nécessitent next/headers
*/
export class AuthService {
/**
* Récupère l'UUID utilisateur depuis le cookie côté serveur
*/
static async getUserUuidFromCookie(): Promise<string | null> {
const cookieStore = await cookies();
const userUuidCookie = cookieStore.get(COOKIE_NAME);
if (!userUuidCookie?.value) {
return null;
}
return userUuidCookie.value;
}
/**
* Vérifie si l'utilisateur est authentifié côté serveur
*/
static async isUserAuthenticated(): Promise<boolean> {
const userUuid = await this.getUserUuidFromCookie();
return !!userUuid;
}
}

View File

@@ -1,4 +0,0 @@
// Client-side services only
// Safe to import in React components and hooks
export { ApiClient, apiClient } from "./api-client";

View File

@@ -15,6 +15,9 @@ export class EvaluationService {
async loadUserEvaluationByUuid(
userUuid: string
): Promise<UserEvaluation | null> {
if (!userUuid) {
return null;
}
const pool = getPool();
const client = await pool.connect();
@@ -677,6 +680,23 @@ export class EvaluationService {
client.release();
}
}
/**
* Récupère l'évaluation complète de l'utilisateur côté serveur
* Combine la récupération du cookie et le chargement de l'évaluation
*/
async getServerUserEvaluation(userUuid: string) {
if (!userUuid) {
return null;
}
try {
return await this.loadUserEvaluationByUuid(userUuid);
} catch (error) {
console.error("Failed to get user evaluation:", error);
return null;
}
}
}
// Instance singleton

View File

@@ -20,8 +20,8 @@ export { SkillsService } from "./skills-service";
// Admin services (server-only)
export { AdminService } from "./admin-service";
// Admin management services (client-side compatible)
export { AdminManagementService } from "./admin-management-service";
// Admin types (can be imported anywhere)
export type { TeamMember, TeamStats, DirectionStats } from "@/lib/admin-types";
// API client (can be used client-side)
export { ApiClient, apiClient } from "./api-client";
// Server auth service (server-side only)
export { AuthService, COOKIE_NAME, COOKIE_MAX_AGE } from "./auth-service";