feat: handling SSR on evaluation
This commit is contained in:
@@ -1,28 +1,40 @@
|
|||||||
import { NextRequest, NextResponse } from "next/server";
|
import { NextRequest, NextResponse } from "next/server";
|
||||||
|
import { cookies } from "next/headers";
|
||||||
import { evaluationService } from "@/services/evaluation-service";
|
import { evaluationService } from "@/services/evaluation-service";
|
||||||
import { UserProfile, SkillLevel } from "@/lib/types";
|
import { UserProfile, SkillLevel } from "@/lib/types";
|
||||||
|
|
||||||
|
const COOKIE_NAME = "peakSkills_userId";
|
||||||
|
|
||||||
export async function PUT(request: NextRequest) {
|
export async function PUT(request: NextRequest) {
|
||||||
try {
|
try {
|
||||||
const body = await request.json();
|
// Récupérer l'utilisateur depuis le cookie
|
||||||
const {
|
const cookieStore = await cookies();
|
||||||
profile,
|
const userId = cookieStore.get(COOKIE_NAME)?.value;
|
||||||
category,
|
|
||||||
skillId,
|
|
||||||
level,
|
|
||||||
canMentor,
|
|
||||||
wantsToLearn,
|
|
||||||
action,
|
|
||||||
} = body;
|
|
||||||
|
|
||||||
if (!profile || !category || !skillId) {
|
if (!userId) {
|
||||||
return NextResponse.json(
|
return NextResponse.json(
|
||||||
{ error: "profile, category et skillId sont requis" },
|
{ error: "Utilisateur non authentifié" },
|
||||||
{ status: 400 }
|
{ 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) {
|
switch (action) {
|
||||||
case "updateLevel":
|
case "updateLevel":
|
||||||
|
|||||||
@@ -1,106 +1,49 @@
|
|||||||
"use client";
|
import { redirect } from "next/navigation";
|
||||||
|
|
||||||
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 {
|
import {
|
||||||
Card,
|
isUserAuthenticated,
|
||||||
CardContent,
|
getServerUserEvaluation,
|
||||||
CardDescription,
|
getServerSkillCategories,
|
||||||
CardHeader,
|
getServerTeams,
|
||||||
CardTitle,
|
} from "@/lib/server-auth";
|
||||||
} from "@/components/ui/card";
|
import {
|
||||||
|
EvaluationClientWrapper,
|
||||||
|
WelcomeEvaluationScreen,
|
||||||
|
} from "@/components/evaluation";
|
||||||
|
import { SkillEvaluation } from "@/components/skill-evaluation";
|
||||||
|
|
||||||
export default function EvaluationPage() {
|
export default async function EvaluationPage() {
|
||||||
const {
|
// Vérifier l'authentification
|
||||||
userEvaluation,
|
const isAuthenticated = await isUserAuthenticated();
|
||||||
skillCategories,
|
|
||||||
teams,
|
|
||||||
loading,
|
|
||||||
updateProfile,
|
|
||||||
updateSkillLevel,
|
|
||||||
updateSkillMentorStatus,
|
|
||||||
updateSkillLearningStatus,
|
|
||||||
addSkillToEvaluation,
|
|
||||||
removeSkillFromEvaluation,
|
|
||||||
initializeEmptyEvaluation,
|
|
||||||
} = useEvaluation();
|
|
||||||
|
|
||||||
const { setUserInfo } = useUser();
|
// Si pas de cookie d'authentification, rediriger vers login
|
||||||
|
if (!isAuthenticated) {
|
||||||
// Update user info in navigation when user evaluation is loaded
|
redirect("/login");
|
||||||
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 (
|
|
||||||
<div className="container mx-auto py-8">
|
|
||||||
<div className="flex items-center justify-center min-h-[400px]">
|
|
||||||
<div className="text-center">
|
|
||||||
<div className="animate-spin rounded-full h-8 w-8 border-b-2 border-primary mx-auto mb-4"></div>
|
|
||||||
<p>Chargement...</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 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) {
|
if (!userEvaluation) {
|
||||||
const handleProfileSubmit = (profile: any) => {
|
return <WelcomeEvaluationScreen teams={teams} />;
|
||||||
initializeEmptyEvaluation(profile);
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div className="min-h-screen bg-gradient-to-br from-slate-950 via-slate-900 to-slate-950 relative overflow-hidden">
|
|
||||||
<div className="absolute inset-0 bg-[radial-gradient(ellipse_at_top,_var(--tw-gradient-stops))] from-blue-900/20 via-slate-900 to-slate-950" />
|
|
||||||
<div className="absolute inset-0 bg-grid-white/5 bg-[size:50px_50px]" />
|
|
||||||
|
|
||||||
<div className="relative z-10 container mx-auto py-16 px-6">
|
|
||||||
<div className="text-center mb-12">
|
|
||||||
<h1 className="text-4xl font-bold mb-4 text-white">
|
|
||||||
Commencer l'évaluation
|
|
||||||
</h1>
|
|
||||||
<p className="text-lg text-slate-400 mb-8">
|
|
||||||
Renseignez vos informations pour débuter votre auto-évaluation
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="max-w-2xl mx-auto">
|
|
||||||
<ProfileForm teams={teams} onSubmit={handleProfileSubmit} />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
<EvaluationClientWrapper userEvaluation={userEvaluation} teams={teams}>
|
||||||
<div>
|
<div>
|
||||||
{/* Skill Evaluation */}
|
{/* Skill Evaluation */}
|
||||||
{skillCategories.length > 0 && userEvaluation.evaluations.length > 0 && (
|
{skillCategories.length > 0 &&
|
||||||
|
userEvaluation.evaluations.length > 0 && (
|
||||||
<SkillEvaluation
|
<SkillEvaluation
|
||||||
categories={skillCategories}
|
categories={skillCategories}
|
||||||
evaluations={userEvaluation.evaluations}
|
evaluations={userEvaluation.evaluations}
|
||||||
onUpdateSkill={updateSkillLevel}
|
|
||||||
onUpdateMentorStatus={updateSkillMentorStatus}
|
|
||||||
onUpdateLearningStatus={updateSkillLearningStatus}
|
|
||||||
onAddSkill={addSkillToEvaluation}
|
|
||||||
onRemoveSkill={removeSkillFromEvaluation}
|
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
</EvaluationClientWrapper>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
155
components/evaluation/client-wrapper.tsx
Normal file
155
components/evaluation/client-wrapper.tsx
Normal file
@@ -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 (
|
||||||
|
<EvaluationProvider
|
||||||
|
updateSkillLevel={updateSkillLevel}
|
||||||
|
updateSkillMentorStatus={updateSkillMentorStatus}
|
||||||
|
updateSkillLearningStatus={updateSkillLearningStatus}
|
||||||
|
addSkillToEvaluation={addSkillToEvaluation}
|
||||||
|
removeSkillFromEvaluation={removeSkillFromEvaluation}
|
||||||
|
>
|
||||||
|
{children}
|
||||||
|
</EvaluationProvider>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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<EvaluationContextType | undefined>(
|
||||||
|
undefined
|
||||||
|
);
|
||||||
|
|
||||||
|
function EvaluationProvider({
|
||||||
|
children,
|
||||||
|
updateSkillLevel,
|
||||||
|
updateSkillMentorStatus,
|
||||||
|
updateSkillLearningStatus,
|
||||||
|
addSkillToEvaluation,
|
||||||
|
removeSkillFromEvaluation,
|
||||||
|
}: {
|
||||||
|
children: React.ReactNode;
|
||||||
|
} & EvaluationContextType) {
|
||||||
|
return (
|
||||||
|
<EvaluationContext.Provider
|
||||||
|
value={{
|
||||||
|
updateSkillLevel,
|
||||||
|
updateSkillMentorStatus,
|
||||||
|
updateSkillLearningStatus,
|
||||||
|
addSkillToEvaluation,
|
||||||
|
removeSkillFromEvaluation,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{children}
|
||||||
|
</EvaluationContext.Provider>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function useEvaluationContext() {
|
||||||
|
const context = useContext(EvaluationContext);
|
||||||
|
if (context === undefined) {
|
||||||
|
throw new Error(
|
||||||
|
"useEvaluationContext must be used within an EvaluationProvider"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return context;
|
||||||
|
}
|
||||||
@@ -1,5 +1,9 @@
|
|||||||
|
export {
|
||||||
|
EvaluationClientWrapper,
|
||||||
|
useEvaluationContext,
|
||||||
|
} from "./client-wrapper";
|
||||||
|
export { WelcomeEvaluationScreen } from "./welcome-screen";
|
||||||
export { EvaluationHeader } from "./evaluation-header";
|
export { EvaluationHeader } from "./evaluation-header";
|
||||||
export { SkillLevelLegend } from "./skill-level-legend";
|
|
||||||
export { CategoryTabs } from "./category-tabs";
|
export { CategoryTabs } from "./category-tabs";
|
||||||
export { SkillEvaluationGrid } from "./skill-evaluation-grid";
|
export { SkillEvaluationGrid } from "./skill-evaluation-grid";
|
||||||
export { SkillEvaluationCard } from "./skill-evaluation-card";
|
export { SkillLevelLegend } from "./skill-level-legend";
|
||||||
|
|||||||
73
components/evaluation/welcome-screen.tsx
Normal file
73
components/evaluation/welcome-screen.tsx
Normal file
@@ -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 (
|
||||||
|
<div className="min-h-screen bg-gradient-to-br from-slate-950 via-slate-900 to-slate-950 relative overflow-hidden">
|
||||||
|
<div className="absolute inset-0 bg-[radial-gradient(ellipse_at_top,_var(--tw-gradient-stops))] from-blue-900/20 via-slate-900 to-slate-950" />
|
||||||
|
<div className="absolute inset-0 bg-grid-white/5 bg-[size:50px_50px]" />
|
||||||
|
|
||||||
|
<div className="relative z-10 container mx-auto py-16 px-6">
|
||||||
|
<div className="text-center mb-12">
|
||||||
|
<div className="inline-flex items-center gap-2 px-4 py-2 rounded-full bg-white/5 border border-white/10 backdrop-blur-sm mb-6">
|
||||||
|
<Code2 className="h-4 w-4 text-blue-400" />
|
||||||
|
<span className="text-sm font-medium text-slate-200">
|
||||||
|
PeakSkills - Évaluation
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<h1 className="text-4xl font-bold mb-4 text-white">
|
||||||
|
Commencer l'évaluation
|
||||||
|
</h1>
|
||||||
|
<p className="text-lg text-slate-400 mb-8">
|
||||||
|
Renseignez vos informations pour débuter votre auto-évaluation
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="max-w-2xl mx-auto">
|
||||||
|
<div className="relative">
|
||||||
|
{isSubmitting && (
|
||||||
|
<div className="absolute inset-0 bg-black/20 backdrop-blur-sm z-10 flex items-center justify-center rounded-lg">
|
||||||
|
<div className="text-center">
|
||||||
|
<div className="animate-spin rounded-full h-6 w-6 border-b-2 border-blue-400 mx-auto mb-2"></div>
|
||||||
|
<p className="text-white text-sm">
|
||||||
|
Initialisation de l'évaluation...
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
<ProfileForm teams={teams} onSubmit={handleProfileSubmit} />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -5,6 +5,7 @@ import { useSearchParams, useRouter } from "next/navigation";
|
|||||||
import { TooltipProvider } from "@/components/ui/tooltip";
|
import { TooltipProvider } from "@/components/ui/tooltip";
|
||||||
import { SkillCategory, SkillLevel, CategoryEvaluation } from "@/lib/types";
|
import { SkillCategory, SkillLevel, CategoryEvaluation } from "@/lib/types";
|
||||||
import { SkillSelector } from "./skill-selector";
|
import { SkillSelector } from "./skill-selector";
|
||||||
|
import { useEvaluationContext } from "./evaluation";
|
||||||
import {
|
import {
|
||||||
EvaluationHeader,
|
EvaluationHeader,
|
||||||
SkillLevelLegend,
|
SkillLevelLegend,
|
||||||
@@ -15,30 +16,19 @@ import {
|
|||||||
interface SkillEvaluationProps {
|
interface SkillEvaluationProps {
|
||||||
categories: SkillCategory[];
|
categories: SkillCategory[];
|
||||||
evaluations: CategoryEvaluation[];
|
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({
|
export function SkillEvaluation({
|
||||||
categories,
|
categories,
|
||||||
evaluations,
|
evaluations,
|
||||||
onUpdateSkill,
|
|
||||||
onUpdateMentorStatus,
|
|
||||||
onUpdateLearningStatus,
|
|
||||||
onAddSkill,
|
|
||||||
onRemoveSkill,
|
|
||||||
}: SkillEvaluationProps) {
|
}: SkillEvaluationProps) {
|
||||||
|
const {
|
||||||
|
updateSkillLevel,
|
||||||
|
updateSkillMentorStatus,
|
||||||
|
updateSkillLearningStatus,
|
||||||
|
addSkillToEvaluation,
|
||||||
|
removeSkillFromEvaluation,
|
||||||
|
} = useEvaluationContext();
|
||||||
const searchParams = useSearchParams();
|
const searchParams = useSearchParams();
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const categoryParam = searchParams.get("category");
|
const categoryParam = searchParams.get("category");
|
||||||
@@ -97,18 +87,18 @@ export function SkillEvaluation({
|
|||||||
categories={categories}
|
categories={categories}
|
||||||
evaluations={evaluations}
|
evaluations={evaluations}
|
||||||
selectedCategory={selectedCategory}
|
selectedCategory={selectedCategory}
|
||||||
onAddSkill={onAddSkill}
|
onAddSkill={addSkillToEvaluation}
|
||||||
onRemoveSkill={onRemoveSkill}
|
onRemoveSkill={removeSkillFromEvaluation}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
{currentEvaluation && (
|
{currentEvaluation && (
|
||||||
<SkillEvaluationGrid
|
<SkillEvaluationGrid
|
||||||
currentCategory={currentCategory}
|
currentCategory={currentCategory}
|
||||||
currentEvaluation={currentEvaluation}
|
currentEvaluation={currentEvaluation}
|
||||||
onUpdateSkill={onUpdateSkill}
|
onUpdateSkill={updateSkillLevel}
|
||||||
onUpdateMentorStatus={onUpdateMentorStatus}
|
onUpdateMentorStatus={updateSkillMentorStatus}
|
||||||
onUpdateLearningStatus={onUpdateLearningStatus}
|
onUpdateLearningStatus={updateSkillLearningStatus}
|
||||||
onRemoveSkill={onRemoveSkill}
|
onRemoveSkill={removeSkillFromEvaluation}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
169
lib/evaluation-actions.ts
Normal file
169
lib/evaluation-actions.ts
Normal file
@@ -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<void> {
|
||||||
|
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<void> {
|
||||||
|
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<void> {
|
||||||
|
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<void> {
|
||||||
|
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<void> {
|
||||||
|
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<void> {
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user