feat: my team page
This commit is contained in:
166
components/team-review/team-overview.tsx
Normal file
166
components/team-review/team-overview.tsx
Normal file
@@ -0,0 +1,166 @@
|
||||
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
|
||||
import { TeamReviewData } from "@/lib/team-review-types";
|
||||
import { Progress } from "@/components/ui/progress";
|
||||
import { Badge } from "@/components/ui/badge";
|
||||
import { AlertTriangle } from "lucide-react";
|
||||
|
||||
interface TeamOverviewProps {
|
||||
team: TeamReviewData["team"];
|
||||
stats: TeamReviewData["stats"];
|
||||
members: TeamReviewData["members"];
|
||||
categoryCoverage: TeamReviewData["categoryCoverage"];
|
||||
}
|
||||
|
||||
export function TeamOverview({
|
||||
team,
|
||||
stats,
|
||||
members,
|
||||
categoryCoverage,
|
||||
}: TeamOverviewProps) {
|
||||
// Trouver les top contributeurs
|
||||
const topContributors = [...members]
|
||||
.sort((a, b) => b.expertSkills - a.expertSkills)
|
||||
.slice(0, 3);
|
||||
|
||||
// Trouver les catégories les plus fortes
|
||||
const strongestCategories = [...categoryCoverage]
|
||||
.sort((a, b) => b.coverage - a.coverage)
|
||||
.slice(0, 2);
|
||||
|
||||
// Trouver les catégories qui nécessitent de l'attention
|
||||
const categoriesNeedingAttention = [...categoryCoverage]
|
||||
.filter((cat) => {
|
||||
// Une catégorie nécessite de l'attention si :
|
||||
// - Couverture faible (< 40%)
|
||||
// - OU pas assez d'experts (< 2) avec une équipe de taille significative (> 5)
|
||||
// - OU beaucoup d'apprenants (> 30% de l'équipe) avec peu de mentors (< 2)
|
||||
return (
|
||||
cat.coverage < 40 ||
|
||||
(cat.experts < 2 && stats.totalMembers > 5) ||
|
||||
(cat.learners > stats.totalMembers * 0.3 && cat.mentors < 2)
|
||||
);
|
||||
})
|
||||
.sort((a, b) => {
|
||||
// Prioriser les catégories avec le plus de besoins
|
||||
const aScore = a.learners * 2 - a.mentors - a.experts;
|
||||
const bScore = b.learners * 2 - b.mentors - b.experts;
|
||||
return bScore - aScore;
|
||||
})
|
||||
.slice(0, 2);
|
||||
|
||||
return (
|
||||
<Card className="bg-white/5 border-white/10 backdrop-blur">
|
||||
<CardHeader>
|
||||
<CardTitle className="text-slate-200">Vue d'ensemble</CardTitle>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<div className="grid grid-cols-1 md:grid-cols-3 gap-8">
|
||||
{/* Points forts */}
|
||||
<div>
|
||||
<h3 className="text-sm font-medium text-slate-200 mb-4">
|
||||
Points forts
|
||||
</h3>
|
||||
<div className="space-y-4">
|
||||
{strongestCategories.map((cat) => (
|
||||
<div key={cat.category} className="space-y-2">
|
||||
<div className="flex justify-between items-center">
|
||||
<p className="text-sm font-medium text-slate-300">
|
||||
{cat.category}
|
||||
</p>
|
||||
<span className="text-sm text-slate-400">
|
||||
{cat.coverage.toFixed(0)}%
|
||||
</span>
|
||||
</div>
|
||||
<Progress value={cat.coverage} className="bg-white/10" />
|
||||
<p className="text-xs text-slate-400">
|
||||
{cat.experts} experts • {cat.mentors} mentors
|
||||
</p>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Top contributeurs */}
|
||||
<div>
|
||||
<h3 className="text-sm font-medium text-slate-200 mb-4">
|
||||
Top contributeurs
|
||||
</h3>
|
||||
<div className="space-y-4">
|
||||
{topContributors.map((member) => (
|
||||
<div key={member.member.uuid} className="space-y-1">
|
||||
<p className="text-sm font-medium text-slate-300">
|
||||
{member.member.firstName} {member.member.lastName}
|
||||
</p>
|
||||
<div className="flex gap-2 text-xs">
|
||||
<Badge
|
||||
variant="secondary"
|
||||
className="bg-white/10 text-slate-300 border-white/20"
|
||||
>
|
||||
{member.expertSkills} expertises
|
||||
</Badge>
|
||||
{member.mentorSkills > 0 && (
|
||||
<Badge
|
||||
variant="outline"
|
||||
className="text-slate-300 border-white/20"
|
||||
>
|
||||
{member.mentorSkills} mentorats
|
||||
</Badge>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Points d'attention */}
|
||||
<div>
|
||||
<h3 className="text-sm font-medium flex items-center gap-2 text-amber-400 mb-4">
|
||||
<AlertTriangle size={16} />
|
||||
Besoins prioritaires
|
||||
</h3>
|
||||
<div className="space-y-4">
|
||||
{categoriesNeedingAttention.map((cat) => (
|
||||
<div key={cat.category} className="space-y-2">
|
||||
<div className="flex justify-between items-center">
|
||||
<p className="text-sm font-medium text-slate-300">
|
||||
{cat.category}
|
||||
</p>
|
||||
<div className="flex items-center gap-2">
|
||||
<Badge
|
||||
variant="outline"
|
||||
className="text-amber-400 border-amber-400/30"
|
||||
>
|
||||
{cat.learners} apprenants
|
||||
</Badge>
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex items-center gap-4 text-sm text-slate-400">
|
||||
<span>{cat.experts} experts</span>
|
||||
<span>{cat.mentors} mentors</span>
|
||||
</div>
|
||||
<div className="text-xs text-amber-400/80">
|
||||
{cat.coverage < 40 && "Couverture insuffisante • "}
|
||||
{cat.experts < 2 &&
|
||||
stats.totalMembers > 5 &&
|
||||
"Manque d'experts • "}
|
||||
{cat.learners > stats.totalMembers * 0.3 &&
|
||||
cat.mentors < 2 &&
|
||||
"Besoin de mentors"}
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
{stats.learningNeeds > 0 && (
|
||||
<div className="pt-2 border-t border-white/10">
|
||||
<p className="text-sm text-amber-400 flex items-center gap-2">
|
||||
<AlertTriangle size={16} />
|
||||
{stats.learningNeeds} compétences sans expert ni mentor
|
||||
</p>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user