feat: refactor ObjectivesPage to utilize ObjectivesList component for improved rendering and simplify OKR status handling in OKRCard with compact view option
Some checks failed
Deploy with Docker Compose / deploy (push) Failing after 4m17s
Some checks failed
Deploy with Docker Compose / deploy (push) Failing after 4m17s
This commit is contained in:
@@ -2,72 +2,8 @@ import { auth } from '@/lib/auth';
|
||||
import { redirect } from 'next/navigation';
|
||||
import Link from 'next/link';
|
||||
import { getUserOKRs } from '@/services/okrs';
|
||||
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui';
|
||||
import { Badge } from '@/components/ui';
|
||||
import { getGravatarUrl } from '@/lib/gravatar';
|
||||
import type { OKRStatus, KeyResultStatus } from '@/lib/types';
|
||||
import { OKR_STATUS_LABELS, KEY_RESULT_STATUS_LABELS } from '@/lib/types';
|
||||
|
||||
// Helper functions for status colors
|
||||
function getOKRStatusColor(status: OKRStatus): { 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',
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
function getKeyResultStatusColor(status: KeyResultStatus): { bg: string; color: string } {
|
||||
switch (status) {
|
||||
case 'NOT_STARTED':
|
||||
return {
|
||||
bg: 'color-mix(in srgb, #6b7280 12%, transparent)',
|
||||
color: '#6b7280',
|
||||
};
|
||||
case 'IN_PROGRESS':
|
||||
return {
|
||||
bg: 'color-mix(in srgb, #3b82f6 12%, transparent)',
|
||||
color: '#3b82f6',
|
||||
};
|
||||
case 'COMPLETED':
|
||||
return {
|
||||
bg: 'color-mix(in srgb, #10b981 12%, transparent)',
|
||||
color: '#10b981',
|
||||
};
|
||||
case 'AT_RISK':
|
||||
return {
|
||||
bg: 'color-mix(in srgb, #f59e0b 12%, transparent)',
|
||||
color: '#f59e0b',
|
||||
};
|
||||
default:
|
||||
return {
|
||||
bg: 'color-mix(in srgb, #6b7280 12%, transparent)',
|
||||
color: '#6b7280',
|
||||
};
|
||||
}
|
||||
}
|
||||
import { Card } from '@/components/ui';
|
||||
import { ObjectivesList } from '@/components/okrs/ObjectivesList';
|
||||
|
||||
export default async function ObjectivesPage() {
|
||||
const session = await auth();
|
||||
@@ -120,8 +56,8 @@ export default async function ObjectivesPage() {
|
||||
<div className="text-5xl mb-4">🎯</div>
|
||||
<h3 className="text-xl font-semibold text-foreground mb-2">Aucun OKR défini</h3>
|
||||
<p className="text-muted mb-6">
|
||||
Vous n'avez pas encore d'OKR défini. Contactez un administrateur d'équipe pour
|
||||
en créer.
|
||||
Vous n'avez pas encore d'OKR défini. Contactez un administrateur d'équipe
|
||||
pour en créer.
|
||||
</p>
|
||||
<Link href="/teams">
|
||||
<span className="inline-block rounded-lg bg-[var(--purple)] px-4 py-2 text-white hover:opacity-90">
|
||||
@@ -130,172 +66,8 @@ export default async function ObjectivesPage() {
|
||||
</Link>
|
||||
</Card>
|
||||
) : (
|
||||
<div className="space-y-8">
|
||||
{periods.map((period) => {
|
||||
const periodOKRs = okrsByPeriod[period];
|
||||
const totalProgress =
|
||||
periodOKRs.reduce((sum, okr) => sum + (okr.progress || 0), 0) / periodOKRs.length;
|
||||
|
||||
return (
|
||||
<div key={period} className="space-y-4">
|
||||
{/* Period Header */}
|
||||
<div className="flex items-center justify-between border-b border-border pb-3">
|
||||
<div className="flex items-center gap-3">
|
||||
<Badge
|
||||
style={{
|
||||
backgroundColor: 'color-mix(in srgb, var(--purple) 15%, transparent)',
|
||||
color: 'var(--purple)',
|
||||
fontSize: '14px',
|
||||
padding: '6px 12px',
|
||||
}}
|
||||
>
|
||||
{period}
|
||||
</Badge>
|
||||
<span className="text-sm text-muted">
|
||||
{periodOKRs.length} OKR{periodOKRs.length !== 1 ? 's' : ''}
|
||||
</span>
|
||||
</div>
|
||||
<div className="text-sm font-medium text-foreground">
|
||||
Progression moyenne: <span style={{ color: 'var(--primary)' }}>{Math.round(totalProgress)}%</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* OKRs Grid */}
|
||||
<div className="grid gap-6 md:grid-cols-2 lg:grid-cols-3">
|
||||
{periodOKRs.map((okr) => {
|
||||
const progress = okr.progress || 0;
|
||||
const progressColor =
|
||||
progress >= 75 ? '#10b981' : progress >= 25 ? '#f59e0b' : '#ef4444';
|
||||
|
||||
return (
|
||||
<Link key={okr.id} href={`/teams/${okr.team.id}/okrs/${okr.id}`}>
|
||||
<Card hover className="h-full flex flex-col">
|
||||
<CardHeader>
|
||||
<div className="flex items-start justify-between mb-2">
|
||||
<CardTitle className="text-lg flex-1 line-clamp-2">{okr.objective}</CardTitle>
|
||||
<Badge style={getOKRStatusColor(okr.status)}>
|
||||
{OKR_STATUS_LABELS[okr.status]}
|
||||
</Badge>
|
||||
</div>
|
||||
{okr.description && (
|
||||
<p className="text-sm text-muted line-clamp-2">{okr.description}</p>
|
||||
)}
|
||||
<div className="mt-2 flex items-center gap-2 text-xs text-muted">
|
||||
<span>👥</span>
|
||||
<span>{okr.team.name}</span>
|
||||
</div>
|
||||
</CardHeader>
|
||||
<CardContent className="flex-1 flex flex-col">
|
||||
{/* Progress Bar */}
|
||||
<div className="mb-4">
|
||||
<div className="mb-1 flex items-center justify-between text-sm">
|
||||
<span className="text-muted">Progression</span>
|
||||
<span className="font-medium" style={{ color: progressColor }}>
|
||||
{progress}%
|
||||
</span>
|
||||
</div>
|
||||
<div className="h-2 w-full overflow-hidden rounded-full bg-card-column">
|
||||
<div
|
||||
className="h-full transition-all"
|
||||
style={{
|
||||
width: `${progress}%`,
|
||||
backgroundColor: progressColor,
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Key Results Preview */}
|
||||
{okr.keyResults && okr.keyResults.length > 0 && (
|
||||
<div className="mt-auto space-y-2">
|
||||
<div className="text-xs font-medium text-muted uppercase tracking-wide">
|
||||
Key Results ({okr.keyResults.length})
|
||||
</div>
|
||||
<div className="space-y-2">
|
||||
{okr.keyResults.slice(0, 3).map((kr) => {
|
||||
const krProgress =
|
||||
kr.targetValue > 0 ? (kr.currentValue / kr.targetValue) * 100 : 0;
|
||||
const krProgressColor =
|
||||
krProgress >= 100
|
||||
? '#10b981'
|
||||
: krProgress >= 50
|
||||
? '#f59e0b'
|
||||
: '#ef4444';
|
||||
|
||||
return (
|
||||
<div key={kr.id} className="space-y-1">
|
||||
<div className="flex items-start justify-between gap-2">
|
||||
<span className="text-xs text-foreground flex-1 line-clamp-1">
|
||||
{kr.title}
|
||||
</span>
|
||||
<Badge
|
||||
style={{
|
||||
...getKeyResultStatusColor(kr.status),
|
||||
fontSize: '9px',
|
||||
padding: '1px 4px',
|
||||
}}
|
||||
>
|
||||
{KEY_RESULT_STATUS_LABELS[kr.status]}
|
||||
</Badge>
|
||||
</div>
|
||||
<div className="flex items-center justify-between text-xs text-muted">
|
||||
<span>
|
||||
{kr.currentValue} / {kr.targetValue} {kr.unit}
|
||||
</span>
|
||||
<span className="font-medium" style={{ color: krProgressColor }}>
|
||||
{Math.round(krProgress)}%
|
||||
</span>
|
||||
</div>
|
||||
<div className="h-1 w-full overflow-hidden rounded-full bg-card-column">
|
||||
<div
|
||||
className="h-full transition-all"
|
||||
style={{
|
||||
width: `${Math.min(krProgress, 100)}%`,
|
||||
backgroundColor: krProgressColor,
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
{okr.keyResults.length > 3 && (
|
||||
<div className="text-xs text-muted text-center pt-1">
|
||||
+{okr.keyResults.length - 3} autre{okr.keyResults.length - 3 !== 1 ? 's' : ''}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Dates */}
|
||||
<div className="mt-4 pt-4 border-t border-border flex items-center justify-between text-xs text-muted">
|
||||
<span>
|
||||
{new Date(okr.startDate).toLocaleDateString('fr-FR', {
|
||||
day: 'numeric',
|
||||
month: 'short',
|
||||
})}
|
||||
</span>
|
||||
<span>→</span>
|
||||
<span>
|
||||
{new Date(okr.endDate).toLocaleDateString('fr-FR', {
|
||||
day: 'numeric',
|
||||
month: 'short',
|
||||
year: 'numeric',
|
||||
})}
|
||||
</span>
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
</Link>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
<ObjectivesList okrsByPeriod={okrsByPeriod} periods={periods} />
|
||||
)}
|
||||
</main>
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user