diff --git a/app/api/evaluations/skills/route.ts b/app/api/evaluations/skills/route.ts
index eaf37ff..9c6ab9d 100644
--- a/app/api/evaluations/skills/route.ts
+++ b/app/api/evaluations/skills/route.ts
@@ -1,28 +1,40 @@
import { NextRequest, NextResponse } from "next/server";
+import { cookies } from "next/headers";
import { evaluationService } from "@/services/evaluation-service";
import { UserProfile, SkillLevel } from "@/lib/types";
+const COOKIE_NAME = "peakSkills_userId";
+
export async function PUT(request: NextRequest) {
try {
- const body = await request.json();
- const {
- profile,
- category,
- skillId,
- level,
- canMentor,
- wantsToLearn,
- action,
- } = body;
+ // Récupérer l'utilisateur depuis le cookie
+ const cookieStore = await cookies();
+ const userId = cookieStore.get(COOKIE_NAME)?.value;
- if (!profile || !category || !skillId) {
+ if (!userId) {
return NextResponse.json(
- { error: "profile, category et skillId sont requis" },
- { status: 400 }
+ { error: "Utilisateur non authentifié" },
+ { status: 401 }
);
}
- const userProfile: UserProfile = profile;
+ const userProfile = await evaluationService.getUserById(parseInt(userId));
+ if (!userProfile) {
+ return NextResponse.json(
+ { error: "Utilisateur introuvable" },
+ { status: 404 }
+ );
+ }
+
+ const body = await request.json();
+ const { category, skillId, level, canMentor, wantsToLearn, action } = body;
+
+ if (!category || !skillId) {
+ return NextResponse.json(
+ { error: "category et skillId sont requis" },
+ { status: 400 }
+ );
+ }
switch (action) {
case "updateLevel":
diff --git a/app/evaluation/page.tsx b/app/evaluation/page.tsx
index e886f1b..3eefae4 100644
--- a/app/evaluation/page.tsx
+++ b/app/evaluation/page.tsx
@@ -1,106 +1,49 @@
-"use client";
-
-import { useEffect } from "react";
-import { useEvaluation } from "@/hooks/use-evaluation";
-import { ProfileForm } from "@/components/profile-form";
-import { SkillEvaluation } from "@/components/skill-evaluation";
-import { useUser } from "@/hooks/use-user-context";
+import { redirect } from "next/navigation";
import {
- Card,
- CardContent,
- CardDescription,
- CardHeader,
- CardTitle,
-} from "@/components/ui/card";
+ isUserAuthenticated,
+ getServerUserEvaluation,
+ getServerSkillCategories,
+ getServerTeams,
+} from "@/lib/server-auth";
+import {
+ EvaluationClientWrapper,
+ WelcomeEvaluationScreen,
+} from "@/components/evaluation";
+import { SkillEvaluation } from "@/components/skill-evaluation";
-export default function EvaluationPage() {
- const {
- userEvaluation,
- skillCategories,
- teams,
- loading,
- updateProfile,
- updateSkillLevel,
- updateSkillMentorStatus,
- updateSkillLearningStatus,
- addSkillToEvaluation,
- removeSkillFromEvaluation,
- initializeEmptyEvaluation,
- } = useEvaluation();
+export default async function EvaluationPage() {
+ // Vérifier l'authentification
+ const isAuthenticated = await isUserAuthenticated();
- const { setUserInfo } = useUser();
-
- // Update user info in navigation when user evaluation is loaded
- useEffect(() => {
- if (userEvaluation) {
- const teamName =
- teams.find((t) => t.id === userEvaluation.profile.teamId)?.name || "";
- setUserInfo({
- firstName: userEvaluation.profile.firstName,
- lastName: userEvaluation.profile.lastName,
- teamName,
- });
- } else {
- setUserInfo(null);
- }
- }, [userEvaluation, teams, setUserInfo]);
-
- if (loading) {
- return (
-
- );
+ // Si pas de cookie d'authentification, rediriger vers login
+ if (!isAuthenticated) {
+ redirect("/login");
}
- // If no user evaluation exists, show profile form
+ // Charger les données côté serveur
+ const [userEvaluation, skillCategories, teams] = await Promise.all([
+ getServerUserEvaluation(),
+ getServerSkillCategories(),
+ getServerTeams(),
+ ]);
+
+ // Si pas d'évaluation, afficher l'écran d'accueil évaluation
if (!userEvaluation) {
- const handleProfileSubmit = (profile: any) => {
- initializeEmptyEvaluation(profile);
- };
-
- return (
-
-
-
-
-
-
-
- Commencer l'évaluation
-
-
- Renseignez vos informations pour débuter votre auto-évaluation
-
-
-
-
-
-
- );
+ return ;
}
return (
-
- {/* Skill Evaluation */}
- {skillCategories.length > 0 && userEvaluation.evaluations.length > 0 && (
-
- )}
-
+
+
+ {/* Skill Evaluation */}
+ {skillCategories.length > 0 &&
+ userEvaluation.evaluations.length > 0 && (
+
+ )}
+
+
);
}
diff --git a/components/evaluation/client-wrapper.tsx b/components/evaluation/client-wrapper.tsx
new file mode 100644
index 0000000..c2b0d69
--- /dev/null
+++ b/components/evaluation/client-wrapper.tsx
@@ -0,0 +1,155 @@
+"use client";
+
+import { useEffect } from "react";
+import { useRouter } from "next/navigation";
+import { useUser } from "@/hooks/use-user-context";
+import { UserEvaluation, Team } from "@/lib/types";
+import {
+ updateSkillLevel as updateSkillLevelAction,
+ updateSkillMentorStatus as updateSkillMentorStatusAction,
+ updateSkillLearningStatus as updateSkillLearningStatusAction,
+ addSkillToEvaluation as addSkillToEvaluationAction,
+ removeSkillFromEvaluation as removeSkillFromEvaluationAction,
+} from "@/lib/evaluation-actions";
+
+interface EvaluationClientWrapperProps {
+ userEvaluation: UserEvaluation;
+ teams: Team[];
+ children: React.ReactNode;
+}
+
+export function EvaluationClientWrapper({
+ userEvaluation,
+ teams,
+ children,
+}: EvaluationClientWrapperProps) {
+ const { setUserInfo } = useUser();
+ const router = useRouter();
+
+ // Wrapper functions that refresh the page after API calls
+ const updateSkillLevel = async (
+ category: string,
+ skillId: string,
+ level: any
+ ) => {
+ await updateSkillLevelAction(category, skillId, level);
+ router.refresh();
+ };
+
+ const updateSkillMentorStatus = async (
+ category: string,
+ skillId: string,
+ canMentor: boolean
+ ) => {
+ await updateSkillMentorStatusAction(category, skillId, canMentor);
+ router.refresh();
+ };
+
+ const updateSkillLearningStatus = async (
+ category: string,
+ skillId: string,
+ wantsToLearn: boolean
+ ) => {
+ await updateSkillLearningStatusAction(category, skillId, wantsToLearn);
+ router.refresh();
+ };
+
+ const addSkillToEvaluation = async (category: string, skillId: string) => {
+ await addSkillToEvaluationAction(category, skillId);
+ router.refresh();
+ };
+
+ const removeSkillFromEvaluation = async (
+ category: string,
+ skillId: string
+ ) => {
+ await removeSkillFromEvaluationAction(category, skillId);
+ router.refresh();
+ };
+
+ // Update user info in navigation when user evaluation is loaded
+ useEffect(() => {
+ if (userEvaluation) {
+ const teamName =
+ teams.find((t) => t.id === userEvaluation.profile.teamId)?.name || "";
+ setUserInfo({
+ firstName: userEvaluation.profile.firstName,
+ lastName: userEvaluation.profile.lastName,
+ teamName,
+ });
+ } else {
+ setUserInfo(null);
+ }
+ }, [userEvaluation, teams, setUserInfo]);
+
+ // Provide evaluation functions to children through React context or props
+ return (
+
+ {children}
+
+ );
+}
+
+// Simple context provider for evaluation functions
+import { createContext, useContext } from "react";
+
+interface EvaluationContextType {
+ updateSkillLevel: (categoryId: string, skillId: string, level: any) => void;
+ updateSkillMentorStatus: (
+ categoryId: string,
+ skillId: string,
+ canMentor: boolean
+ ) => void;
+ updateSkillLearningStatus: (
+ categoryId: string,
+ skillId: string,
+ wantsToLearn: boolean
+ ) => void;
+ addSkillToEvaluation: (categoryId: string, skillId: string) => void;
+ removeSkillFromEvaluation: (categoryId: string, skillId: string) => void;
+}
+
+const EvaluationContext = createContext(
+ undefined
+);
+
+function EvaluationProvider({
+ children,
+ updateSkillLevel,
+ updateSkillMentorStatus,
+ updateSkillLearningStatus,
+ addSkillToEvaluation,
+ removeSkillFromEvaluation,
+}: {
+ children: React.ReactNode;
+} & EvaluationContextType) {
+ return (
+
+ {children}
+
+ );
+}
+
+export function useEvaluationContext() {
+ const context = useContext(EvaluationContext);
+ if (context === undefined) {
+ throw new Error(
+ "useEvaluationContext must be used within an EvaluationProvider"
+ );
+ }
+ return context;
+}
diff --git a/components/evaluation/index.ts b/components/evaluation/index.ts
index 3d8026d..f465d0a 100644
--- a/components/evaluation/index.ts
+++ b/components/evaluation/index.ts
@@ -1,5 +1,9 @@
+export {
+ EvaluationClientWrapper,
+ useEvaluationContext,
+} from "./client-wrapper";
+export { WelcomeEvaluationScreen } from "./welcome-screen";
export { EvaluationHeader } from "./evaluation-header";
-export { SkillLevelLegend } from "./skill-level-legend";
export { CategoryTabs } from "./category-tabs";
export { SkillEvaluationGrid } from "./skill-evaluation-grid";
-export { SkillEvaluationCard } from "./skill-evaluation-card";
+export { SkillLevelLegend } from "./skill-level-legend";
diff --git a/components/evaluation/welcome-screen.tsx b/components/evaluation/welcome-screen.tsx
new file mode 100644
index 0000000..2ab1a55
--- /dev/null
+++ b/components/evaluation/welcome-screen.tsx
@@ -0,0 +1,73 @@
+"use client";
+
+import { useState } from "react";
+import { useRouter } from "next/navigation";
+import { ProfileForm } from "@/components/profile-form";
+import { initializeEmptyEvaluation } from "@/lib/evaluation-actions";
+import { Team, UserProfile } from "@/lib/types";
+import { Code2 } from "lucide-react";
+
+interface WelcomeEvaluationScreenProps {
+ teams: Team[];
+}
+
+export function WelcomeEvaluationScreen({
+ teams,
+}: WelcomeEvaluationScreenProps) {
+ const [isSubmitting, setIsSubmitting] = useState(false);
+ const router = useRouter();
+
+ const handleProfileSubmit = async (profile: UserProfile) => {
+ setIsSubmitting(true);
+ try {
+ await initializeEmptyEvaluation(profile);
+ // Rafraîchir la page pour que le SSR prenne en compte la nouvelle évaluation
+ router.refresh();
+ } catch (error) {
+ console.error("Failed to initialize evaluation:", error);
+ } finally {
+ setIsSubmitting(false);
+ }
+ };
+
+ return (
+
+
+
+
+
+
+
+
+
+ PeakSkills - Évaluation
+
+
+
+
+ Commencer l'évaluation
+
+
+ Renseignez vos informations pour débuter votre auto-évaluation
+
+
+
+
+
+ {isSubmitting && (
+
+
+
+
+ Initialisation de l'évaluation...
+
+
+
+ )}
+
+
+
+
+
+ );
+}
diff --git a/components/skill-evaluation.tsx b/components/skill-evaluation.tsx
index 5ee861c..dab8307 100644
--- a/components/skill-evaluation.tsx
+++ b/components/skill-evaluation.tsx
@@ -5,6 +5,7 @@ import { useSearchParams, useRouter } from "next/navigation";
import { TooltipProvider } from "@/components/ui/tooltip";
import { SkillCategory, SkillLevel, CategoryEvaluation } from "@/lib/types";
import { SkillSelector } from "./skill-selector";
+import { useEvaluationContext } from "./evaluation";
import {
EvaluationHeader,
SkillLevelLegend,
@@ -15,30 +16,19 @@ import {
interface SkillEvaluationProps {
categories: SkillCategory[];
evaluations: CategoryEvaluation[];
- onUpdateSkill: (category: string, skillId: string, level: SkillLevel) => void;
- onUpdateMentorStatus: (
- category: string,
- skillId: string,
- canMentor: boolean
- ) => void;
- onUpdateLearningStatus: (
- category: string,
- skillId: string,
- wantsToLearn: boolean
- ) => void;
- onAddSkill: (category: string, skillId: string) => void;
- onRemoveSkill: (category: string, skillId: string) => void;
}
export function SkillEvaluation({
categories,
evaluations,
- onUpdateSkill,
- onUpdateMentorStatus,
- onUpdateLearningStatus,
- onAddSkill,
- onRemoveSkill,
}: SkillEvaluationProps) {
+ const {
+ updateSkillLevel,
+ updateSkillMentorStatus,
+ updateSkillLearningStatus,
+ addSkillToEvaluation,
+ removeSkillFromEvaluation,
+ } = useEvaluationContext();
const searchParams = useSearchParams();
const router = useRouter();
const categoryParam = searchParams.get("category");
@@ -97,18 +87,18 @@ export function SkillEvaluation({
categories={categories}
evaluations={evaluations}
selectedCategory={selectedCategory}
- onAddSkill={onAddSkill}
- onRemoveSkill={onRemoveSkill}
+ onAddSkill={addSkillToEvaluation}
+ onRemoveSkill={removeSkillFromEvaluation}
/>
{currentEvaluation && (
)}
diff --git a/lib/evaluation-actions.ts b/lib/evaluation-actions.ts
new file mode 100644
index 0000000..b5472b6
--- /dev/null
+++ b/lib/evaluation-actions.ts
@@ -0,0 +1,169 @@
+"use client";
+
+import { SkillLevel } from "@/lib/types";
+
+/**
+ * Actions d'évaluation standalone pour SSR
+ * Ces fonctions utilisent directement l'API sans refetch de toutes les données
+ */
+
+export async function updateSkillLevel(
+ category: string,
+ skillId: string,
+ level: SkillLevel
+): Promise {
+ try {
+ const response = await fetch("/api/evaluations/skills", {
+ method: "PUT",
+ headers: {
+ "Content-Type": "application/json",
+ },
+ credentials: "same-origin",
+ body: JSON.stringify({
+ action: "updateLevel",
+ category,
+ skillId,
+ level,
+ }),
+ });
+
+ if (!response.ok) {
+ const errorData = await response.text();
+ console.error("API Error:", response.status, errorData);
+ throw new Error(
+ `Failed to update skill level: ${response.status} - ${errorData}`
+ );
+ }
+
+ // Note: La page se rafraîchira via router.refresh() appelé par le composant
+ } catch (error) {
+ console.error("Failed to update skill level:", error);
+ throw error;
+ }
+}
+
+export async function updateSkillMentorStatus(
+ category: string,
+ skillId: string,
+ canMentor: boolean
+): Promise {
+ try {
+ const response = await fetch("/api/evaluations/skills", {
+ method: "PUT",
+ headers: {
+ "Content-Type": "application/json",
+ },
+ credentials: "same-origin",
+ body: JSON.stringify({
+ action: "updateMentorStatus",
+ category,
+ skillId,
+ canMentor,
+ }),
+ });
+
+ if (!response.ok) {
+ throw new Error("Failed to update skill mentor status");
+ }
+ } catch (error) {
+ console.error("Failed to update skill mentor status:", error);
+ throw error;
+ }
+}
+
+export async function updateSkillLearningStatus(
+ category: string,
+ skillId: string,
+ wantsToLearn: boolean
+): Promise {
+ try {
+ const response = await fetch("/api/evaluations/skills", {
+ method: "PUT",
+ headers: {
+ "Content-Type": "application/json",
+ },
+ credentials: "same-origin",
+ body: JSON.stringify({
+ action: "updateLearningStatus",
+ category,
+ skillId,
+ wantsToLearn,
+ }),
+ });
+
+ if (!response.ok) {
+ throw new Error("Failed to update skill learning status");
+ }
+ } catch (error) {
+ console.error("Failed to update skill learning status:", error);
+ throw error;
+ }
+}
+
+export async function addSkillToEvaluation(
+ category: string,
+ skillId: string
+): Promise {
+ try {
+ const response = await fetch("/api/evaluations/skills", {
+ method: "PUT",
+ headers: {
+ "Content-Type": "application/json",
+ },
+ credentials: "same-origin",
+ body: JSON.stringify({
+ action: "addSkill",
+ category,
+ skillId,
+ }),
+ });
+
+ if (!response.ok) {
+ throw new Error("Failed to add skill to evaluation");
+ }
+ } catch (error) {
+ console.error("Failed to add skill to evaluation:", error);
+ throw error;
+ }
+}
+
+export async function removeSkillFromEvaluation(
+ category: string,
+ skillId: string
+): Promise {
+ try {
+ const response = await fetch("/api/evaluations/skills", {
+ method: "PUT",
+ headers: {
+ "Content-Type": "application/json",
+ },
+ credentials: "same-origin",
+ body: JSON.stringify({
+ action: "removeSkill",
+ category,
+ skillId,
+ }),
+ });
+
+ if (!response.ok) {
+ throw new Error("Failed to remove skill from evaluation");
+ }
+ } catch (error) {
+ console.error("Failed to remove skill from evaluation:", error);
+ throw error;
+ }
+}
+
+export async function initializeEmptyEvaluation(
+ profile: import("@/lib/types").UserProfile
+): Promise {
+ try {
+ // Simplement créer le profil via l'auth, pas besoin de créer une évaluation vide
+ // Le backend créera automatiquement l'évaluation lors du premier accès
+ const { AuthService } = await import("@/lib/auth-utils");
+ await AuthService.login(profile);
+ } catch (error) {
+ console.error("Failed to initialize evaluation:", error);
+ throw error;
+ }
+}