feat: add Weekly Check-in feature with models, UI components, and session management for enhanced team collaboration
All checks were successful
Deploy with Docker Compose / deploy (push) Successful in 6m24s
All checks were successful
Deploy with Docker Compose / deploy (push) Successful in 6m24s
This commit is contained in:
161
src/components/weekly-checkin/CurrentQuarterOKRs.tsx
Normal file
161
src/components/weekly-checkin/CurrentQuarterOKRs.tsx
Normal file
@@ -0,0 +1,161 @@
|
||||
'use client';
|
||||
|
||||
import { useState } from 'react';
|
||||
import Link from 'next/link';
|
||||
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui';
|
||||
import { Badge } from '@/components/ui';
|
||||
import type { OKR } from '@/lib/types';
|
||||
import { OKR_STATUS_LABELS } from '@/lib/types';
|
||||
|
||||
type OKRWithTeam = OKR & {
|
||||
team?: {
|
||||
id: string;
|
||||
name: string;
|
||||
} | null;
|
||||
};
|
||||
|
||||
interface CurrentQuarterOKRsProps {
|
||||
okrs: OKRWithTeam[];
|
||||
period: string;
|
||||
}
|
||||
|
||||
export function CurrentQuarterOKRs({ okrs, period }: CurrentQuarterOKRsProps) {
|
||||
const [isExpanded, setIsExpanded] = useState(true);
|
||||
|
||||
if (okrs.length === 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<Card className="mb-6">
|
||||
<CardHeader>
|
||||
<CardTitle className="flex items-center justify-between">
|
||||
<button
|
||||
onClick={() => setIsExpanded(!isExpanded)}
|
||||
className="flex items-center gap-2 hover:opacity-80 transition-opacity"
|
||||
>
|
||||
<span>🎯</span>
|
||||
<span>Objectifs du trimestre ({period})</span>
|
||||
<svg
|
||||
className={`w-5 h-5 transition-transform ${isExpanded ? 'rotate-180' : ''}`}
|
||||
fill="none"
|
||||
viewBox="0 0 24 24"
|
||||
stroke="currentColor"
|
||||
>
|
||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M19 9l-7 7-7-7" />
|
||||
</svg>
|
||||
</button>
|
||||
</CardTitle>
|
||||
</CardHeader>
|
||||
{isExpanded && (
|
||||
<CardContent>
|
||||
<div className="space-y-3">
|
||||
{okrs.map((okr) => {
|
||||
const statusColors = getOKRStatusColor(okr.status);
|
||||
return (
|
||||
<div
|
||||
key={okr.id}
|
||||
className="rounded-lg border border-border bg-card p-3 hover:bg-card-hover transition-colors"
|
||||
>
|
||||
<div className="flex items-start justify-between gap-3">
|
||||
<div className="flex-1">
|
||||
<div className="flex items-center gap-2 mb-1">
|
||||
<h4 className="font-medium text-foreground">{okr.objective}</h4>
|
||||
<Badge
|
||||
variant="default"
|
||||
style={{
|
||||
backgroundColor: statusColors.bg,
|
||||
color: statusColors.color,
|
||||
borderColor: statusColors.color + '30',
|
||||
}}
|
||||
>
|
||||
{OKR_STATUS_LABELS[okr.status]}
|
||||
</Badge>
|
||||
{okr.progress !== undefined && (
|
||||
<span className="text-xs text-muted">{okr.progress}%</span>
|
||||
)}
|
||||
</div>
|
||||
{okr.description && (
|
||||
<p className="text-sm text-muted mb-2">{okr.description}</p>
|
||||
)}
|
||||
{okr.keyResults && okr.keyResults.length > 0 && (
|
||||
<ul className="space-y-1 mt-2">
|
||||
{okr.keyResults.slice(0, 3).map((kr) => {
|
||||
const krProgress = kr.targetValue > 0
|
||||
? Math.round((kr.currentValue / kr.targetValue) * 100)
|
||||
: 0;
|
||||
return (
|
||||
<li key={kr.id} className="text-xs text-muted flex items-center gap-2">
|
||||
<span className="w-1.5 h-1.5 rounded-full bg-primary" />
|
||||
<span className="flex-1">{kr.title}</span>
|
||||
<span className="text-muted">
|
||||
{kr.currentValue}/{kr.targetValue} {kr.unit}
|
||||
</span>
|
||||
<span className="text-xs">({krProgress}%)</span>
|
||||
</li>
|
||||
);
|
||||
})}
|
||||
{okr.keyResults.length > 3 && (
|
||||
<li className="text-xs text-muted pl-3.5">
|
||||
+{okr.keyResults.length - 3} autre{okr.keyResults.length - 3 > 1 ? 's' : ''}
|
||||
</li>
|
||||
)}
|
||||
</ul>
|
||||
)}
|
||||
{okr.team && (
|
||||
<div className="mt-2">
|
||||
<span className="text-xs text-muted">Équipe: {okr.team.name}</span>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
<div className="mt-4 pt-4 border-t border-border">
|
||||
<Link
|
||||
href="/objectives"
|
||||
className="text-sm text-primary hover:underline flex items-center gap-1"
|
||||
>
|
||||
Voir tous les objectifs
|
||||
<svg className="w-4 h-4" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M9 5l7 7-7 7" />
|
||||
</svg>
|
||||
</Link>
|
||||
</div>
|
||||
</CardContent>
|
||||
)}
|
||||
</Card>
|
||||
);
|
||||
}
|
||||
|
||||
function getOKRStatusColor(status: OKR['status']): { bg: string; color: string } {
|
||||
switch (status) {
|
||||
case 'NOT_STARTED':
|
||||
return {
|
||||
bg: 'color-mix(in srgb, #6b7280 15%, transparent)',
|
||||
color: '#6b7280',
|
||||
};
|
||||
case 'IN_PROGRESS':
|
||||
return {
|
||||
bg: 'color-mix(in srgb, #3b82f6 15%, transparent)',
|
||||
color: '#3b82f6',
|
||||
};
|
||||
case 'COMPLETED':
|
||||
return {
|
||||
bg: 'color-mix(in srgb, #10b981 15%, transparent)',
|
||||
color: '#10b981',
|
||||
};
|
||||
case 'CANCELLED':
|
||||
return {
|
||||
bg: 'color-mix(in srgb, #ef4444 15%, transparent)',
|
||||
color: '#ef4444',
|
||||
};
|
||||
default:
|
||||
return {
|
||||
bg: 'color-mix(in srgb, #6b7280 15%, transparent)',
|
||||
color: '#6b7280',
|
||||
};
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user