feat: review UI of skill matrix in myteam page
This commit is contained in:
@@ -14,10 +14,8 @@ import { TeamMemberProfile, SkillGap } from "@/lib/team-review-types";
|
|||||||
import { UserCheck, GraduationCap } from "lucide-react";
|
import { UserCheck, GraduationCap } from "lucide-react";
|
||||||
import { TechIcon } from "@/components/icons/tech-icon";
|
import { TechIcon } from "@/components/icons/tech-icon";
|
||||||
import { getImportanceColors } from "@/lib/tech-colors";
|
import { getImportanceColors } from "@/lib/tech-colors";
|
||||||
import {
|
import { isCoverageBelowObjective } from "@/lib/evaluation-utils";
|
||||||
isCoverageBelowObjective,
|
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs";
|
||||||
SKILL_LEVEL_VALUES,
|
|
||||||
} from "@/lib/evaluation-utils";
|
|
||||||
|
|
||||||
interface SkillMatrixProps {
|
interface SkillMatrixProps {
|
||||||
members: TeamMemberProfile[];
|
members: TeamMemberProfile[];
|
||||||
@@ -104,12 +102,167 @@ export function SkillMatrix({ members, skillGaps }: SkillMatrixProps) {
|
|||||||
</CardTitle>
|
</CardTitle>
|
||||||
</CardHeader>
|
</CardHeader>
|
||||||
<CardContent>
|
<CardContent>
|
||||||
<div className="space-y-8">
|
<Tabs defaultValue="all" className="w-full">
|
||||||
{Object.entries(skillsByCategory).map(([category, skills]) => (
|
<TabsList className="mb-4 bg-white/5 border-white/10">
|
||||||
<div key={`category-${category}`}>
|
<TabsTrigger
|
||||||
<h3 className="text-lg font-semibold text-slate-200 mb-4">
|
value="all"
|
||||||
|
className="data-[state=active]:bg-white/10"
|
||||||
|
>
|
||||||
|
Toutes
|
||||||
|
</TabsTrigger>
|
||||||
|
{Object.keys(skillsByCategory).map((category) => (
|
||||||
|
<TabsTrigger
|
||||||
|
key={category}
|
||||||
|
value={category}
|
||||||
|
className="data-[state=active]:bg-white/10"
|
||||||
|
>
|
||||||
{category}
|
{category}
|
||||||
</h3>
|
</TabsTrigger>
|
||||||
|
))}
|
||||||
|
</TabsList>
|
||||||
|
|
||||||
|
<TabsContent value="all">
|
||||||
|
<div className="rounded-md border border-white/10">
|
||||||
|
<Table>
|
||||||
|
<TableHeader>
|
||||||
|
<TableRow className="border-white/10">
|
||||||
|
<TableHead className="w-[200px] text-slate-300">
|
||||||
|
Compétence
|
||||||
|
</TableHead>
|
||||||
|
<TableHead className="w-[120px] text-slate-300">
|
||||||
|
Catégorie
|
||||||
|
</TableHead>
|
||||||
|
{members.map((member) => (
|
||||||
|
<TableHead
|
||||||
|
key={`header-${member.member.uuid}`}
|
||||||
|
className="text-slate-300"
|
||||||
|
>
|
||||||
|
{member.member.firstName} {member.member.lastName}
|
||||||
|
</TableHead>
|
||||||
|
))}
|
||||||
|
<TableHead className="w-[120px] text-slate-300">
|
||||||
|
Couverture
|
||||||
|
</TableHead>
|
||||||
|
</TableRow>
|
||||||
|
</TableHeader>
|
||||||
|
<TableBody>
|
||||||
|
{validSkillGaps.sort(sortByImportance).map((skill) => {
|
||||||
|
const colors = getImportanceColors(skill.importance);
|
||||||
|
return (
|
||||||
|
<TableRow
|
||||||
|
key={`skill-row-${skill.skillId}-${skill.category}`}
|
||||||
|
className="border-white/10"
|
||||||
|
>
|
||||||
|
<TableCell className="font-medium">
|
||||||
|
<div className="flex items-center gap-3">
|
||||||
|
<div
|
||||||
|
className={`w-8 h-8 rounded-lg ${colors.bg} ${colors.border} border flex items-center justify-center`}
|
||||||
|
>
|
||||||
|
<TechIcon
|
||||||
|
iconName={skill.icon || ""}
|
||||||
|
className={`w-4 h-4 ${colors.text}`}
|
||||||
|
fallbackText={skill.skillName}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div className="flex flex-col gap-1">
|
||||||
|
<span className={`${colors.text} font-medium`}>
|
||||||
|
{skill.skillName}
|
||||||
|
</span>
|
||||||
|
{skill.risk === "high" && (
|
||||||
|
<Badge
|
||||||
|
variant="destructive"
|
||||||
|
className="bg-red-500/20 text-red-200 border-red-500/30 w-fit"
|
||||||
|
>
|
||||||
|
Risque
|
||||||
|
</Badge>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</TableCell>
|
||||||
|
<TableCell>
|
||||||
|
<Badge
|
||||||
|
variant="outline"
|
||||||
|
className="bg-white/5 text-slate-300 border-white/10"
|
||||||
|
>
|
||||||
|
{skill.category}
|
||||||
|
</Badge>
|
||||||
|
</TableCell>
|
||||||
|
{members.map((member) => {
|
||||||
|
const memberSkill = member.skills.find(
|
||||||
|
(s) => s.skillId === skill.skillId
|
||||||
|
);
|
||||||
|
return (
|
||||||
|
<TableCell
|
||||||
|
key={`skill-${skill.skillId}-member-${member.member.uuid}`}
|
||||||
|
>
|
||||||
|
<div className="flex items-center gap-2 flex-wrap">
|
||||||
|
{getLevelBadge(memberSkill?.level || null)}
|
||||||
|
{memberSkill?.canMentor && (
|
||||||
|
<Badge
|
||||||
|
variant="outline"
|
||||||
|
className="flex items-center gap-1 bg-green-500/10 text-green-200 border-green-500/30"
|
||||||
|
>
|
||||||
|
<UserCheck className="h-3 w-3" />
|
||||||
|
<span className="text-xs">Mentor</span>
|
||||||
|
</Badge>
|
||||||
|
)}
|
||||||
|
{memberSkill?.wantsToLearn && (
|
||||||
|
<Badge
|
||||||
|
variant="outline"
|
||||||
|
className="flex items-center gap-1 bg-blue-500/10 text-blue-200 border-blue-500/30"
|
||||||
|
>
|
||||||
|
<GraduationCap className="h-3 w-3" />
|
||||||
|
<span className="text-xs">Apprenant</span>
|
||||||
|
</Badge>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</TableCell>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
<TableCell>
|
||||||
|
<div className="flex items-center gap-2">
|
||||||
|
<div className="w-full bg-white/10 rounded-full h-2">
|
||||||
|
<div
|
||||||
|
className={`h-2 rounded-full ${
|
||||||
|
isCoverageBelowObjective(
|
||||||
|
skill.coverage || 0,
|
||||||
|
skill.importance
|
||||||
|
)
|
||||||
|
? "bg-red-500/50"
|
||||||
|
: colors.bg.replace("/20", "/50")
|
||||||
|
}`}
|
||||||
|
style={{
|
||||||
|
width: `${Math.max(
|
||||||
|
0,
|
||||||
|
Math.min(100, skill.coverage || 0)
|
||||||
|
)}%`,
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<span
|
||||||
|
className={`text-sm whitespace-nowrap ${
|
||||||
|
isCoverageBelowObjective(
|
||||||
|
skill.coverage || 0,
|
||||||
|
skill.importance
|
||||||
|
)
|
||||||
|
? "text-red-400"
|
||||||
|
: colors.text
|
||||||
|
}`}
|
||||||
|
>
|
||||||
|
{Math.round(skill.coverage || 0)}%
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</TableCell>
|
||||||
|
</TableRow>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</TableBody>
|
||||||
|
</Table>
|
||||||
|
</div>
|
||||||
|
</TabsContent>
|
||||||
|
|
||||||
|
{Object.entries(skillsByCategory).map(([category, skills]) => (
|
||||||
|
<TabsContent key={category} value={category}>
|
||||||
<div className="rounded-md border border-white/10">
|
<div className="rounded-md border border-white/10">
|
||||||
<Table>
|
<Table>
|
||||||
<TableHeader>
|
<TableHeader>
|
||||||
@@ -236,9 +389,9 @@ export function SkillMatrix({ members, skillGaps }: SkillMatrixProps) {
|
|||||||
</TableBody>
|
</TableBody>
|
||||||
</Table>
|
</Table>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</TabsContent>
|
||||||
))}
|
))}
|
||||||
</div>
|
</Tabs>
|
||||||
</CardContent>
|
</CardContent>
|
||||||
</Card>
|
</Card>
|
||||||
);
|
);
|
||||||
|
|||||||
Reference in New Issue
Block a user