From 9a818b72056b9b7e8ca9b48646eed969499771d4 Mon Sep 17 00:00:00 2001 From: Julien Froidefond Date: Mon, 25 Aug 2025 09:20:36 +0200 Subject: [PATCH] refactor: SSR on page teams and split getAdminData --- app/admin/manage/skills/page.tsx | 4 +- app/admin/manage/teams/page.tsx | 12 +- app/admin/manage/users/page.tsx | 4 +- app/admin/page.tsx | 2 +- app/api/admin/users/route.ts | 16 -- clients/domains/admin-client.ts | 4 - .../admin/skills/skills-management-page.tsx | 2 - .../admin/teams/teams-management-page.tsx | 2 - .../admin/users/users-management-page.tsx | 8 +- hooks/use-skills-management.ts | 24 --- hooks/use-teams-management.ts | 21 --- hooks/use-users-management.ts | 32 +--- services/admin-service.ts | 139 +++++++++++++++--- 13 files changed, 136 insertions(+), 134 deletions(-) delete mode 100644 app/api/admin/users/route.ts diff --git a/app/admin/manage/skills/page.tsx b/app/admin/manage/skills/page.tsx index 983b8e8..5e82368 100644 --- a/app/admin/manage/skills/page.tsx +++ b/app/admin/manage/skills/page.tsx @@ -4,13 +4,11 @@ import { SkillsManagementPage } from "@/components/admin/skills"; export default async function SkillsPage() { // Charger les données côté serveur try { - const { skillCategories, teams, skills } = - await AdminService.getAdminData(); + const { skillCategories, skills } = await AdminService.getSkillsPageData(); return ( ); diff --git a/app/admin/manage/teams/page.tsx b/app/admin/manage/teams/page.tsx index 3b7cff8..f4fcde3 100644 --- a/app/admin/manage/teams/page.tsx +++ b/app/admin/manage/teams/page.tsx @@ -4,16 +4,10 @@ import { TeamsManagementPage } from "@/components/admin/teams"; export default async function TeamsPage() { // Charger les données côté serveur try { - const { teams, teamStats, skillCategories } = - await AdminService.getAdminData(); + const { teams, teamStats, directionStats } = + await AdminService.getTeamsPageData(); - return ( - - ); + return ; } catch (error) { console.error("Failed to load admin data:", error); return ( diff --git a/app/admin/manage/users/page.tsx b/app/admin/manage/users/page.tsx index 3a0b931..d03726a 100644 --- a/app/admin/manage/users/page.tsx +++ b/app/admin/manage/users/page.tsx @@ -4,9 +4,9 @@ import { UsersManagementPage } from "@/components/admin/users"; export default async function UsersPage() { // Charger les données côté serveur try { - const { teams } = await AdminService.getAdminData(); + const { teams, users } = await AdminService.getUsersPageData(); - return ; + return ; } catch (error) { console.error("Failed to load admin data:", error); return ( diff --git a/app/admin/page.tsx b/app/admin/page.tsx index 8aef21d..4544751 100644 --- a/app/admin/page.tsx +++ b/app/admin/page.tsx @@ -4,7 +4,7 @@ import { AdminClientWrapper } from "@/components/admin"; export default async function AdminPage() { // Charger les données côté serveur try { - const adminData = await AdminService.getAdminData(); + const adminData = await AdminService.getOverviewPageData(); return ( { - return await this.get(`/admin/users`); - } - async createUser(userData: UserFormData): Promise { return await this.post(`/admin/users`, userData); } diff --git a/components/admin/skills/skills-management-page.tsx b/components/admin/skills/skills-management-page.tsx index 48a7f98..4fcf36b 100644 --- a/components/admin/skills/skills-management-page.tsx +++ b/components/admin/skills/skills-management-page.tsx @@ -14,13 +14,11 @@ import { SkillsList } from "./skills-list"; interface SkillsManagementPageProps { skillCategories: SkillCategory[]; - teams: Team[]; initialSkills: any[]; } export function SkillsManagementPage({ skillCategories, - teams, initialSkills, }: SkillsManagementPageProps) { const [searchTerm, setSearchTerm] = useState(""); diff --git a/components/admin/teams/teams-management-page.tsx b/components/admin/teams/teams-management-page.tsx index 1d19b99..fcd9681 100644 --- a/components/admin/teams/teams-management-page.tsx +++ b/components/admin/teams/teams-management-page.tsx @@ -16,13 +16,11 @@ import { TeamMembersModal } from "../management/team-members-modal"; interface TeamsManagementPageProps { teams: TeamType[]; teamStats: TeamStats[]; - skillCategories: SkillCategory[]; } export function TeamsManagementPage({ teams, teamStats, - skillCategories, }: TeamsManagementPageProps) { const [searchTerm, setSearchTerm] = useState(""); const [isMembersModalOpen, setIsMembersModalOpen] = useState(false); diff --git a/components/admin/users/users-management-page.tsx b/components/admin/users/users-management-page.tsx index 41c4b16..67309c8 100644 --- a/components/admin/users/users-management-page.tsx +++ b/components/admin/users/users-management-page.tsx @@ -13,9 +13,13 @@ import { UsersList } from "./users-list"; interface UsersManagementPageProps { teams: Team[]; + initialUsers: any[]; } -export function UsersManagementPage({ teams }: UsersManagementPageProps) { +export function UsersManagementPage({ + teams, + initialUsers, +}: UsersManagementPageProps) { const [searchTerm, setSearchTerm] = useState(""); const { isCreateDialogOpen, openCreateDialog, closeCreateDialog } = @@ -31,7 +35,7 @@ export function UsersManagementPage({ teams }: UsersManagementPageProps) { resetForm, handleCreateUser, handleDeleteUser, - } = useUsersManagement(teams); + } = useUsersManagement(teams, initialUsers); // Utilisation du hook factorisé pour la vue arborescente const { diff --git a/hooks/use-skills-management.ts b/hooks/use-skills-management.ts index bc65342..2769634 100644 --- a/hooks/use-skills-management.ts +++ b/hooks/use-skills-management.ts @@ -27,36 +27,12 @@ export function useSkillsManagement( const [isSubmitting, setIsSubmitting] = useState(false); const { toast } = useToast(); - // Charger les skills depuis l'API si pas de skills initiales - useEffect(() => { - if (!initialSkills) { - const fetchSkills = async () => { - try { - setIsLoading(true); - const skillsData = await adminClient.getSkills(); - setSkills(skillsData); - } catch (error) { - console.error("Error fetching skills:", error); - toast({ - title: "Erreur", - description: "Impossible de charger les skills", - variant: "destructive", - }); - } finally { - setIsLoading(false); - } - }; - fetchSkills(); - } - }, [initialSkills]); - const resetForm = () => { setSkillFormData({ name: "", categoryId: "", description: "", icon: "" }); setEditingSkill(null); }; const handleCreateSkill = async () => { - console.log("skillFormData", skillFormData); if (!skillFormData.name || !skillFormData.categoryId) { toast({ title: "Erreur", diff --git a/hooks/use-teams-management.ts b/hooks/use-teams-management.ts index 4af7e0b..3c131b1 100644 --- a/hooks/use-teams-management.ts +++ b/hooks/use-teams-management.ts @@ -23,27 +23,6 @@ export function useTeamsManagement( const [isSubmitting, setIsSubmitting] = useState(false); const { toast } = useToast(); - // Charger les teams depuis l'API - const fetchTeams = async () => { - try { - const teamsData = await adminClient.getTeams(); - // Note: on garde les teams existantes pour la compatibilité - // Les nouvelles teams créées via l'API seront visibles après rafraîchissement - } catch (error) { - console.error("Error fetching teams:", error); - toast({ - title: "Erreur", - description: "Impossible de charger les teams", - variant: "destructive", - }); - } - }; - - // Charger les teams au montage du composant - useEffect(() => { - fetchTeams(); - }, []); - const resetForm = () => { setTeamFormData({ name: "", direction: "" }); setEditingTeam(null); diff --git a/hooks/use-users-management.ts b/hooks/use-users-management.ts index 79a8437..3e098ed 100644 --- a/hooks/use-users-management.ts +++ b/hooks/use-users-management.ts @@ -4,9 +4,9 @@ import { Team } from "@/lib/types"; import { adminClient } from "@/clients"; import { User, UserFormData } from "@/clients/domains/admin-client"; -export function useUsersManagement(teams: Team[]) { - const [users, setUsers] = useState([]); - const [isLoading, setIsLoading] = useState(true); +export function useUsersManagement(teams: Team[], initialUsers?: any[]) { + const [users, setUsers] = useState(initialUsers || []); + const [isLoading, setIsLoading] = useState(!initialUsers); const [error, setError] = useState(null); const [deletingUserId, setDeletingUserId] = useState(null); const [userFormData, setUserFormData] = useState({ @@ -17,25 +17,6 @@ export function useUsersManagement(teams: Team[]) { const [isSubmitting, setIsSubmitting] = useState(false); const { toast } = useToast(); - // Charger les utilisateurs depuis l'API - const fetchUsers = async () => { - setIsLoading(true); - setError(null); - try { - const usersData = await adminClient.getUsers(); - setUsers(usersData); - } catch (err: any) { - setError(err.message || "Erreur lors du chargement des utilisateurs"); - } finally { - setIsLoading(false); - } - }; - - // Charger les utilisateurs au montage du composant - useEffect(() => { - fetchUsers(); - }, []); - const resetForm = () => { setUserFormData({ firstName: "", lastName: "", teamId: "" }); }; @@ -52,15 +33,15 @@ export function useUsersManagement(teams: Team[]) { try { setIsSubmitting(true); - await adminClient.createUser(userFormData); + const newUser = await adminClient.createUser(userFormData); toast({ title: "Succès", description: "Utilisateur créé avec succès", }); + // Ajouter le nouvel utilisateur à la liste locale + setUsers([...users, newUser]); resetForm(); - // Rafraîchir la liste - await fetchUsers(); return true; } catch (error: any) { toast({ @@ -128,6 +109,5 @@ export function useUsersManagement(teams: Team[]) { resetForm, handleCreateUser, handleDeleteUser, - fetchUsers, }; } diff --git a/services/admin-service.ts b/services/admin-service.ts index 889d5c0..3fe665b 100644 --- a/services/admin-service.ts +++ b/services/admin-service.ts @@ -191,36 +191,132 @@ export class AdminService { } /** - * Récupère toutes les données nécessaires pour l'admin + * Récupère les données nécessaires pour la page de gestion des skills */ - static async getAdminData(): Promise<{ - teams: Team[]; + static async getSkillsPageData(): Promise<{ skillCategories: SkillCategory[]; - teamStats: TeamStats[]; - directionStats: DirectionStats[]; skills: any[]; }> { const pool = getPool(); try { - // Récupérer toutes les données en parallèle - const [teamsResult, categoriesResult, teamStats, skills] = - await Promise.all([ - pool.query( - "SELECT id, name, direction FROM teams ORDER BY direction, name" - ), - pool.query( - "SELECT id, name, icon FROM skill_categories ORDER BY name" - ), - AdminService.getTeamsStats(), - SkillsService.getAllSkillsWithUsage(), - ]); + const [categoriesResult, skills] = await Promise.all([ + pool.query("SELECT id, name, icon FROM skill_categories ORDER BY name"), + SkillsService.getAllSkillsWithUsage(), + ]); + + const skillCategories = categoriesResult.rows.map((row) => ({ + ...row, + category: row.name, + skills: [], + })); + + return { + skillCategories, + skills, + }; + } catch (error) { + console.error("Error fetching skills page data:", error); + throw new Error("Failed to fetch skills page data"); + } + } + + /** + * Récupère les données nécessaires pour la page de gestion des utilisateurs + */ + static async getUsersPageData(): Promise<{ + teams: Team[]; + users: any[]; + }> { + const pool = getPool(); + + try { + const [teamsResult, usersResult] = await Promise.all([ + pool.query( + "SELECT id, name, direction FROM teams ORDER BY direction, name" + ), + pool.query(` + SELECT + u.uuid_id as uuid, + u.first_name as "firstName", + u.last_name as "lastName", + t.name as "teamName", + EXISTS ( + SELECT 1 FROM user_evaluations ue + WHERE ue.user_uuid = u.uuid_id + ) as "hasEvaluations" + FROM users u + LEFT JOIN teams t ON u.team_id = t.id + ORDER BY u.first_name, u.last_name + `), + ]); + + return { + teams: teamsResult.rows, + users: usersResult.rows, + }; + } catch (error) { + console.error("Error fetching users page data:", error); + throw new Error("Failed to fetch users page data"); + } + } + + /** + * Récupère les données nécessaires pour la page de gestion des équipes + */ + static async getTeamsPageData(): Promise<{ + teams: Team[]; + teamStats: TeamStats[]; + directionStats: DirectionStats[]; + }> { + const pool = getPool(); + + try { + const [teamsResult, teamStats] = await Promise.all([ + pool.query( + "SELECT id, name, direction FROM teams ORDER BY direction, name" + ), + AdminService.getTeamsStats(), + ]); + + const directionStats = AdminService.generateDirectionStats(teamStats); + + return { + teams: teamsResult.rows, + teamStats, + directionStats, + }; + } catch (error) { + console.error("Error fetching teams page data:", error); + throw new Error("Failed to fetch teams page data"); + } + } + + /** + * Récupère les données nécessaires pour la page d'accueil admin + */ + static async getOverviewPageData(): Promise<{ + teams: Team[]; + skillCategories: SkillCategory[]; + teamStats: TeamStats[]; + directionStats: DirectionStats[]; + }> { + const pool = getPool(); + + try { + const [teamsResult, categoriesResult, teamStats] = await Promise.all([ + pool.query( + "SELECT id, name, direction FROM teams ORDER BY direction, name" + ), + pool.query("SELECT id, name, icon FROM skill_categories ORDER BY name"), + AdminService.getTeamsStats(), + ]); const teams = teamsResult.rows; const skillCategories = categoriesResult.rows.map((row) => ({ ...row, - category: row.name, // Adapter le format - skills: [], // Les skills individuelles ne sont pas nécessaires pour l'admin + category: row.name, + skills: [], })); const directionStats = AdminService.generateDirectionStats(teamStats); @@ -230,11 +326,10 @@ export class AdminService { skillCategories, teamStats, directionStats, - skills, }; } catch (error) { - console.error("Error fetching admin data:", error); - throw new Error("Failed to fetch admin data"); + console.error("Error fetching overview page data:", error); + throw new Error("Failed to fetch overview page data"); } } }