187 lines
6.0 KiB
TypeScript
187 lines
6.0 KiB
TypeScript
"use client";
|
|
|
|
import { useState } from "react";
|
|
import { Button } from "@/components/ui/button";
|
|
import { ChevronDown, ChevronRight, ExternalLink } from "lucide-react";
|
|
import { getCategoryIcon } from "@/lib/category-icons";
|
|
import { getScoreColors } from "@/lib/score-utils";
|
|
import { getImportanceColors } from "@/lib/tech-colors";
|
|
import { SkillProgress } from "./skill-progress";
|
|
import Link from "next/link";
|
|
|
|
interface CategoryCardProps {
|
|
category: {
|
|
category: string;
|
|
score: number;
|
|
maxScore: number;
|
|
};
|
|
categoryEval?: {
|
|
category: string;
|
|
selectedSkillIds: string[];
|
|
skills: Array<{
|
|
skillId: string;
|
|
level: string | null;
|
|
}>;
|
|
};
|
|
skillCategory?: {
|
|
category: string;
|
|
icon: string;
|
|
skills: Array<{
|
|
id: string;
|
|
name: string;
|
|
icon?: string;
|
|
importance: "incontournable" | "majeure" | "standard";
|
|
}>;
|
|
};
|
|
}
|
|
|
|
export function CategoryCard({
|
|
category,
|
|
categoryEval,
|
|
skillCategory,
|
|
}: CategoryCardProps) {
|
|
const [isExpanded, setIsExpanded] = useState(false);
|
|
|
|
const skillsCount = categoryEval?.selectedSkillIds?.length || 0;
|
|
const evaluatedCount =
|
|
categoryEval?.skills.filter((s) => s.level !== null).length || 0;
|
|
|
|
const CategoryIcon = skillCategory
|
|
? getCategoryIcon(skillCategory.icon)
|
|
: null;
|
|
|
|
return (
|
|
<div className="bg-white/5 border border-white/10 rounded-lg overflow-hidden transition-colors">
|
|
<div
|
|
className="p-3 hover:bg-white/10 transition-colors cursor-pointer"
|
|
onClick={() => setIsExpanded(!isExpanded)}
|
|
>
|
|
<div className="flex justify-between items-center mb-2">
|
|
<div className="flex items-center gap-2">
|
|
{isExpanded ? (
|
|
<ChevronDown className="h-4 w-4 text-slate-400" />
|
|
) : (
|
|
<ChevronRight className="h-4 w-4 text-slate-400" />
|
|
)}
|
|
{CategoryIcon && <CategoryIcon className="h-4 w-4 text-blue-400" />}
|
|
<h4 className="font-medium text-white text-sm">
|
|
{category.category}
|
|
</h4>
|
|
</div>
|
|
{skillsCount > 0 ? (
|
|
(() => {
|
|
const colors = getScoreColors(category.score);
|
|
return (
|
|
<div
|
|
className={`px-2 py-0.5 rounded-full ${colors.bg} border ${colors.border}`}
|
|
>
|
|
<span className={`text-xs font-medium ${colors.text}`}>
|
|
{Math.round((category.score / 3) * 100)}%
|
|
</span>
|
|
</div>
|
|
);
|
|
})()
|
|
) : (
|
|
<div className="px-2 py-0.5 rounded-full bg-slate-500/20 border border-slate-500/30">
|
|
<span className="text-xs font-medium text-slate-400">Aucune</span>
|
|
</div>
|
|
)}
|
|
</div>
|
|
<div className="text-xs text-slate-400 mb-2">
|
|
{skillsCount > 0
|
|
? `${evaluatedCount}/${skillsCount} compétences sélectionnées évaluées`
|
|
: "Aucune compétence sélectionnée"}
|
|
</div>
|
|
{skillsCount > 0 ? (
|
|
<div className="w-full bg-white/10 rounded-full h-1.5">
|
|
{(() => {
|
|
const colors = getScoreColors(category.score);
|
|
return (
|
|
<div
|
|
className={`bg-gradient-to-r ${colors.gradient} h-1.5 rounded-full transition-all`}
|
|
style={{
|
|
width: `${(category.score / category.maxScore) * 100}%`,
|
|
}}
|
|
></div>
|
|
);
|
|
})()}
|
|
</div>
|
|
) : (
|
|
<div className="w-full bg-slate-500/10 rounded-full h-1.5"></div>
|
|
)}
|
|
</div>
|
|
|
|
{/* Expanded Skills */}
|
|
{isExpanded && (
|
|
<div className="px-3 pb-3 border-t border-white/10 bg-white/5">
|
|
{categoryEval && skillCategory && skillsCount > 0 ? (
|
|
<div className="pt-3 space-y-2 max-h-60 overflow-y-auto">
|
|
{categoryEval.selectedSkillIds
|
|
.map((skillId) => {
|
|
const skill = skillCategory.skills.find(
|
|
(s) => s.id === skillId
|
|
);
|
|
return skill ? { ...skill, id: skillId } : null;
|
|
})
|
|
.filter(
|
|
(skill): skill is NonNullable<typeof skill> => skill !== null
|
|
)
|
|
.sort((a, b) => {
|
|
const importanceOrder = {
|
|
incontournable: 2,
|
|
majeure: 1,
|
|
standard: 0,
|
|
};
|
|
return (
|
|
importanceOrder[b.importance] -
|
|
importanceOrder[a.importance]
|
|
);
|
|
})
|
|
.map((skill) => {
|
|
const skillEval = categoryEval.skills.find(
|
|
(s) => s.skillId === skill.id
|
|
);
|
|
return (
|
|
<SkillProgress
|
|
key={skill.id}
|
|
skill={skill}
|
|
skillEval={skillEval}
|
|
/>
|
|
);
|
|
})}
|
|
</div>
|
|
) : (
|
|
<div className="pt-3 pb-3 text-center">
|
|
<p className="text-slate-400 text-sm mb-3">
|
|
Aucune compétence sélectionnée dans cette catégorie
|
|
</p>
|
|
</div>
|
|
)}
|
|
|
|
<div
|
|
className={`${
|
|
skillsCount > 0 ? "mt-3 pt-3 border-t border-white/10" : ""
|
|
}`}
|
|
>
|
|
<Button
|
|
asChild
|
|
className="w-full bg-blue-500 hover:bg-blue-600 text-white"
|
|
>
|
|
<Link
|
|
href={`/evaluation?category=${encodeURIComponent(
|
|
category.category
|
|
)}`}
|
|
>
|
|
<ExternalLink className="w-4 h-4 mr-2" />
|
|
{skillsCount > 0
|
|
? `Évaluer ${category.category}`
|
|
: `Commencer l'évaluation ${category.category}`}
|
|
</Link>
|
|
</Button>
|
|
</div>
|
|
</div>
|
|
)}
|
|
</div>
|
|
);
|
|
}
|