feat: handling SSR on evaluation

This commit is contained in:
Julien Froidefond
2025-08-21 13:19:46 +02:00
parent 69f23db55d
commit ef16c73625
7 changed files with 481 additions and 135 deletions

View 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;
}

View File

@@ -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";

View 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>
);
}

View File

@@ -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 && (
<SkillEvaluationGrid
currentCategory={currentCategory}
currentEvaluation={currentEvaluation}
onUpdateSkill={onUpdateSkill}
onUpdateMentorStatus={onUpdateMentorStatus}
onUpdateLearningStatus={onUpdateLearningStatus}
onRemoveSkill={onRemoveSkill}
onUpdateSkill={updateSkillLevel}
onUpdateMentorStatus={updateSkillMentorStatus}
onUpdateLearningStatus={updateSkillLearningStatus}
onRemoveSkill={removeSkillFromEvaluation}
/>
)}
</div>