From 747b0189a69738872922bc9407ee294667091ed8 Mon Sep 17 00:00:00 2001 From: Julien Froidefond Date: Thu, 21 Aug 2025 14:33:15 +0200 Subject: [PATCH] refactor: modularize AdminClientWrapper by extracting components - Removed unused imports and components from AdminClientWrapper. - Introduced AdminHeader, AdminOverviewCards, AdminFilters, and AdminContentTabs for better code organization and readability. - Streamlined the layout and functionality of the admin dashboard, enhancing maintainability. --- components/admin/admin-client-wrapper.tsx | 336 ++-------------------- components/admin/admin-content-tabs.tsx | 107 +++++++ components/admin/admin-filters.tsx | 108 +++++++ components/admin/admin-header.tsx | 23 ++ components/admin/admin-overview-cards.tsx | 134 +++++++++ components/admin/index.ts | 4 + 6 files changed, 407 insertions(+), 305 deletions(-) create mode 100644 components/admin/admin-content-tabs.tsx create mode 100644 components/admin/admin-filters.tsx create mode 100644 components/admin/admin-header.tsx create mode 100644 components/admin/admin-overview-cards.tsx diff --git a/components/admin/admin-client-wrapper.tsx b/components/admin/admin-client-wrapper.tsx index 4390f60..18d22a1 100644 --- a/components/admin/admin-client-wrapper.tsx +++ b/components/admin/admin-client-wrapper.tsx @@ -1,17 +1,13 @@ "use client"; import { useState } from "react"; -import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs"; -import { Button } from "@/components/ui/button"; -import { Users, Target, Building2, UserCheck, Filter } from "lucide-react"; import { Team, SkillCategory } from "@/lib/types"; -import { - TeamStatsCard, - DirectionOverview, - MultiSelectFilter, -} from "@/components/admin"; -import { TeamDetailModal } from "@/components/admin/team-detail-modal"; import { TeamStats, DirectionStats } from "@/services/admin-service"; +import { TeamDetailModal } from "@/components/admin/team-detail-modal"; +import { AdminHeader } from "./admin-header"; +import { AdminOverviewCards } from "./admin-overview-cards"; +import { AdminFilters } from "./admin-filters"; +import { AdminContentTabs } from "./admin-content-tabs"; interface AdminClientWrapperProps { teams: Team[]; @@ -85,21 +81,6 @@ export function AdminClientWrapper({ return filtered; }; - // Options pour les filtres - const directionOptions = Array.from( - new Set(teams.map((team) => team.direction)) - ).map((direction) => ({ - id: direction, - label: direction, - count: teams.filter((team) => team.direction === direction).length, - })); - - const teamOptions = teams.map((team) => ({ - id: team.id, - label: `${team.name} (${team.direction})`, - count: teamStats.find((stat) => stat.teamId === team.id)?.totalMembers || 0, - })); - // Fonctions pour les actions des équipes const handleViewTeamDetails = (team: TeamStats) => { setSelectedTeamForModal(team); @@ -142,293 +123,38 @@ export function AdminClientWrapper({
{/* Header */} -
-
- - - Administration - -
- -

- Dashboard Managérial -

- -

- Vue d'ensemble des compétences par équipe et direction pour pilotage - stratégique -

-
+ {/* Overview Cards */} -
-
-
-
- -
-
- {selectedDirections.length > 0 || selectedTeams.length > 0 - ? "FILTRÉES" - : "TOTAL"} -
-
-
-

- {selectedDirections.length > 0 || selectedTeams.length > 0 - ? getFilteredTeamStats().length - : teams.length} -

-

- {selectedDirections.length > 0 || selectedTeams.length > 0 - ? "Équipes filtrées" - : "Équipes"} -

- {(selectedDirections.length > 0 || selectedTeams.length > 0) && ( -

- sur {teams.length} au total -

- )} -
-
- -
-
-
- -
-
- {selectedDirections.length > 0 || selectedTeams.length > 0 - ? "FILTRÉS" - : "TOTAL"} -
-
-
-

- {selectedDirections.length > 0 || selectedTeams.length > 0 - ? getFilteredTeamStats().reduce( - (sum, t) => sum + t.totalMembers, - 0 - ) - : teamStats.reduce((sum, t) => sum + t.totalMembers, 0)} -

-

- {selectedDirections.length > 0 || selectedTeams.length > 0 - ? "Membres filtrés" - : "Membres"} -

- {(selectedDirections.length > 0 || selectedTeams.length > 0) && ( -

- sur {teamStats.reduce((sum, t) => sum + t.totalMembers, 0)} au - total -

- )} -
-
- -
-
-
- -
-
- {selectedDirections.length > 0 || selectedTeams.length > 0 - ? "FILTRÉES" - : "TOTAL"} -
-
-
-

- {selectedDirections.length > 0 || selectedTeams.length > 0 - ? getFilteredDirectionStats().length - : directionStats.length} -

-

- {selectedDirections.length > 0 || selectedTeams.length > 0 - ? "Directions filtrées" - : "Directions"} -

- {(selectedDirections.length > 0 || selectedTeams.length > 0) && ( -

- sur {directionStats.length} au total -

- )} -
-
- -
-
-
- -
-
- RÉFÉRENTIEL -
-
-
-

- {skillCategories.reduce( - (sum, cat) => sum + (cat.skills?.length || 0), - 0 - )} -

-

Compétences suivies

-

- {skillCategories.length} catégories -

-
-
-
+ {/* Filtres */} -
-
-
- -
-

- Filtres avancés -

-
- -
-
- } - /> -
-
- } - /> -
- - {/* Résumé des filtres actifs */} - {(selectedDirections.length > 0 || selectedTeams.length > 0) && ( -
-
-
- - - {getFilteredTeamStats().reduce( - (sum, t) => sum + t.totalMembers, - 0 - )}{" "} - membres - -
-
- -
- )} -
-
+ {/* Main Content Tabs */} - -
- - - Vue par Direction - - - Vue par Équipe - - -
- - -
- {getFilteredDirectionStats().length > 0 ? ( - getFilteredDirectionStats().map((direction) => ( - - )) - ) : ( -
-
- -
-

- Aucune direction trouvée -

-

- Aucune direction ne correspond aux filtres sélectionnés. -

-
- )} -
-
- - -
- {getFilteredTeamStats().length > 0 ? ( - getFilteredTeamStats().map((team) => ( - handleViewTeamDetails(team)} - onViewReport={() => handleExportTeamReport(team)} - /> - )) - ) : ( -
-
-
- -
-

- Aucune équipe trouvée -

-

- Aucune équipe ne correspond aux filtres sélectionnés. -

-
-
- )} -
-
-
+ {/* Modale de détails d'équipe */} DirectionStats[]; + getFilteredTeamStats: () => TeamStats[]; + onViewTeamDetails: (team: TeamStats) => void; + onExportTeamReport: (team: TeamStats) => void; +} + +export function AdminContentTabs({ + getFilteredDirectionStats, + getFilteredTeamStats, + onViewTeamDetails, + onExportTeamReport, +}: AdminContentTabsProps) { + return ( + +
+ + + Vue par Direction + + + Vue par Équipe + + +
+ + +
+ {getFilteredDirectionStats().length > 0 ? ( + getFilteredDirectionStats().map((direction) => ( + + )) + ) : ( +
+
+ +
+

+ Aucune direction trouvée +

+

+ Aucune direction ne correspond aux filtres sélectionnés. +

+
+ )} +
+
+ + +
+ {getFilteredTeamStats().length > 0 ? ( + getFilteredTeamStats().map((team) => ( + onViewTeamDetails(team)} + onViewReport={() => onExportTeamReport(team)} + /> + )) + ) : ( +
+
+
+ +
+

+ Aucune équipe trouvée +

+

+ Aucune équipe ne correspond aux filtres sélectionnés. +

+
+
+ )} +
+
+
+ ); +} diff --git a/components/admin/admin-filters.tsx b/components/admin/admin-filters.tsx new file mode 100644 index 0000000..9fe5f24 --- /dev/null +++ b/components/admin/admin-filters.tsx @@ -0,0 +1,108 @@ +"use client"; + +import { Button } from "@/components/ui/button"; +import { Users, Target, Building2, Filter } from "lucide-react"; +import { Team } from "@/lib/types"; +import { TeamStats } from "@/services/admin-service"; +import { MultiSelectFilter } from "./multi-select-filter"; + +interface AdminFiltersProps { + teams: Team[]; + teamStats: TeamStats[]; + selectedDirections: string[]; + selectedTeams: string[]; + onDirectionsChange: (directions: string[]) => void; + onTeamsChange: (teams: string[]) => void; + getFilteredTeamStats: () => TeamStats[]; +} + +export function AdminFilters({ + teams, + teamStats, + selectedDirections, + selectedTeams, + onDirectionsChange, + onTeamsChange, + getFilteredTeamStats, +}: AdminFiltersProps) { + const hasFilters = selectedDirections.length > 0 || selectedTeams.length > 0; + + const directionOptions = Array.from( + new Set(teams.map((team) => team.direction)) + ).map((direction) => ({ + id: direction, + label: direction, + count: teams.filter((team) => team.direction === direction).length, + })); + + const teamOptions = teams.map((team) => ({ + id: team.id, + label: `${team.name} (${team.direction})`, + count: teamStats.find((stat) => stat.teamId === team.id)?.totalMembers || 0, + })); + + const handleReset = () => { + onDirectionsChange([]); + onTeamsChange([]); + }; + + return ( +
+
+
+ +
+

Filtres avancés

+
+ +
+
+ } + /> +
+
+ } + /> +
+ + {/* Résumé des filtres actifs */} + {hasFilters && ( +
+
+
+ + + {getFilteredTeamStats().reduce( + (sum, t) => sum + t.totalMembers, + 0 + )}{" "} + membres + +
+
+ +
+ )} +
+
+ ); +} diff --git a/components/admin/admin-header.tsx b/components/admin/admin-header.tsx new file mode 100644 index 0000000..61d89f6 --- /dev/null +++ b/components/admin/admin-header.tsx @@ -0,0 +1,23 @@ +"use client"; + +import { Building2 } from "lucide-react"; + +export function AdminHeader() { + return ( +
+
+ + + Administration + +
+ +

Dashboard Managérial

+ +

+ Vue d'ensemble des compétences par équipe et direction pour pilotage + stratégique +

+
+ ); +} diff --git a/components/admin/admin-overview-cards.tsx b/components/admin/admin-overview-cards.tsx new file mode 100644 index 0000000..58d6fe6 --- /dev/null +++ b/components/admin/admin-overview-cards.tsx @@ -0,0 +1,134 @@ +"use client"; + +import { Users, Target, Building2, UserCheck } from "lucide-react"; +import { Team, SkillCategory } from "@/lib/types"; +import { TeamStats, DirectionStats } from "@/services/admin-service"; + +interface AdminOverviewCardsProps { + teams: Team[]; + skillCategories: SkillCategory[]; + teamStats: TeamStats[]; + directionStats: DirectionStats[]; + selectedDirections: string[]; + selectedTeams: string[]; + getFilteredTeamStats: () => TeamStats[]; + getFilteredDirectionStats: () => DirectionStats[]; +} + +export function AdminOverviewCards({ + teams, + skillCategories, + teamStats, + directionStats, + selectedDirections, + selectedTeams, + getFilteredTeamStats, + getFilteredDirectionStats, +}: AdminOverviewCardsProps) { + const hasFilters = selectedDirections.length > 0 || selectedTeams.length > 0; + + return ( +
+
+
+
+ +
+
+ {hasFilters ? "FILTRÉES" : "TOTAL"} +
+
+
+

+ {hasFilters ? getFilteredTeamStats().length : teams.length} +

+

+ {hasFilters ? "Équipes filtrées" : "Équipes"} +

+ {hasFilters && ( +

+ sur {teams.length} au total +

+ )} +
+
+ +
+
+
+ +
+
+ {hasFilters ? "FILTRÉS" : "TOTAL"} +
+
+
+

+ {hasFilters + ? getFilteredTeamStats().reduce( + (sum, t) => sum + t.totalMembers, + 0 + ) + : teamStats.reduce((sum, t) => sum + t.totalMembers, 0)} +

+

+ {hasFilters ? "Membres filtrés" : "Membres"} +

+ {hasFilters && ( +

+ sur {teamStats.reduce((sum, t) => sum + t.totalMembers, 0)} au + total +

+ )} +
+
+ +
+
+
+ +
+
+ {hasFilters ? "FILTRÉES" : "TOTAL"} +
+
+
+

+ {hasFilters + ? getFilteredDirectionStats().length + : directionStats.length} +

+

+ {hasFilters ? "Directions filtrées" : "Directions"} +

+ {hasFilters && ( +

+ sur {directionStats.length} au total +

+ )} +
+
+ +
+
+
+ +
+
RÉFÉRENTIEL
+
+
+

+ {skillCategories.reduce( + (sum, cat) => sum + (cat.skills?.length || 0), + 0 + )} +

+

Compétences suivies

+

+ {skillCategories.length} catégories +

+
+
+
+ ); +} diff --git a/components/admin/index.ts b/components/admin/index.ts index b86a704..562ca00 100644 --- a/components/admin/index.ts +++ b/components/admin/index.ts @@ -3,3 +3,7 @@ export { DirectionOverview } from "./direction-overview"; export { MultiSelectFilter } from "./multi-select-filter"; export { AdminClientWrapper } from "./admin-client-wrapper"; export { TeamDetailClientWrapper } from "./team-detail-client-wrapper"; +export { AdminHeader } from "./admin-header"; +export { AdminOverviewCards } from "./admin-overview-cards"; +export { AdminFilters } from "./admin-filters"; +export { AdminContentTabs } from "./admin-content-tabs";