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:
@@ -1,11 +1,10 @@
|
||||
'use client';
|
||||
|
||||
import { useState, useTransition } from 'react';
|
||||
import { useTransition } from 'react';
|
||||
import { useRouter } from 'next/navigation';
|
||||
import Link from 'next/link';
|
||||
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui';
|
||||
import { Badge } from '@/components/ui';
|
||||
import { Button } from '@/components/ui';
|
||||
import { getGravatarUrl } from '@/lib/gravatar';
|
||||
import type { OKR, KeyResult, OKRStatus, KeyResultStatus } from '@/lib/types';
|
||||
import { OKR_STATUS_LABELS, KEY_RESULT_STATUS_LABELS } from '@/lib/types';
|
||||
@@ -75,9 +74,10 @@ interface OKRCardProps {
|
||||
okr: OKR & { teamMember?: { user: { id: string; email: string; name: string | null } } };
|
||||
teamId: string;
|
||||
isAdmin?: boolean;
|
||||
compact?: boolean;
|
||||
}
|
||||
|
||||
export function OKRCard({ okr, teamId, isAdmin = false }: OKRCardProps) {
|
||||
export function OKRCard({ okr, teamId, isAdmin = false, compact = false }: OKRCardProps) {
|
||||
const router = useRouter();
|
||||
const [isPending, startTransition] = useTransition();
|
||||
const progress = okr.progress || 0;
|
||||
@@ -100,18 +100,128 @@ export function OKRCard({ okr, teamId, isAdmin = false }: OKRCardProps) {
|
||||
|
||||
if (!response.ok) {
|
||||
const error = await response.json();
|
||||
alert(error.error || 'Erreur lors de la suppression de l\'OKR');
|
||||
alert(error.error || "Erreur lors de la suppression de l'OKR");
|
||||
return;
|
||||
}
|
||||
|
||||
router.refresh();
|
||||
} catch (error) {
|
||||
console.error('Error deleting OKR:', error);
|
||||
alert('Erreur lors de la suppression de l\'OKR');
|
||||
alert("Erreur lors de la suppression de l'OKR");
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
if (compact) {
|
||||
return (
|
||||
<Card hover className="relative group">
|
||||
<Link href={`/teams/${teamId}/okrs/${okr.id}`}>
|
||||
<CardHeader className="pb-2">
|
||||
<div className="flex items-start justify-between gap-3">
|
||||
<div className="flex-1 min-w-0 flex items-start gap-3">
|
||||
<span className="text-xl flex-shrink-0">🎯</span>
|
||||
<div className="flex-1 min-w-0">
|
||||
<CardTitle className="text-lg leading-snug mb-1.5 line-clamp-2">
|
||||
{okr.objective}
|
||||
</CardTitle>
|
||||
{okr.teamMember && (
|
||||
<div className="flex items-center gap-1.5">
|
||||
{/* eslint-disable-next-line @next/next/no-img-element */}
|
||||
<img
|
||||
src={getGravatarUrl(okr.teamMember.user.email, 96)}
|
||||
alt={okr.teamMember.user.name || okr.teamMember.user.email}
|
||||
width={16}
|
||||
height={16}
|
||||
className="rounded-full flex-shrink-0"
|
||||
/>
|
||||
<span className="text-xs text-muted line-clamp-1">
|
||||
{okr.teamMember.user.name || okr.teamMember.user.email}
|
||||
</span>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex items-center gap-1.5 flex-shrink-0 relative z-10">
|
||||
{isAdmin && (
|
||||
<button
|
||||
onClick={handleDelete}
|
||||
className="h-5 w-5 p-0 flex items-center justify-center rounded hover:bg-destructive/10 transition-colors flex-shrink-0"
|
||||
style={{
|
||||
color: 'var(--destructive)',
|
||||
border: '1px solid color-mix(in srgb, var(--destructive) 40%, transparent)',
|
||||
backgroundColor: 'color-mix(in srgb, var(--destructive) 5%, transparent)',
|
||||
}}
|
||||
disabled={isPending}
|
||||
title="Supprimer l'OKR"
|
||||
>
|
||||
<svg
|
||||
className="h-3 w-3"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
viewBox="0 0 24 24"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
strokeWidth={2.5}
|
||||
d="M19 7l-.867 12.142A2 2 0 0116.138 21H7.862a2 2 0 01-1.995-1.858L5 7m5 4v6m4-6v6m1-10V4a1 1 0 00-1-1h-4a1 1 0 00-1 1v3M4 7h16"
|
||||
/>
|
||||
</svg>
|
||||
</button>
|
||||
)}
|
||||
<Badge
|
||||
style={{
|
||||
backgroundColor: 'color-mix(in srgb, var(--purple) 15%, transparent)',
|
||||
color: 'var(--purple)',
|
||||
fontSize: '11px',
|
||||
padding: '2px 6px',
|
||||
}}
|
||||
>
|
||||
{okr.period}
|
||||
</Badge>
|
||||
</div>
|
||||
</div>
|
||||
</CardHeader>
|
||||
<CardContent className="pt-0 pb-3">
|
||||
<div className="flex items-center gap-4">
|
||||
{/* Progress Bar */}
|
||||
<div className="flex-1 min-w-0">
|
||||
<div className="mb-1 flex items-center justify-between text-xs">
|
||||
<span className="text-muted">Progression</span>
|
||||
<span className="font-medium" style={{ color: progressColor }}>
|
||||
{progress}%
|
||||
</span>
|
||||
</div>
|
||||
<div className="h-1.5 w-full overflow-hidden rounded-full bg-card-column">
|
||||
<div
|
||||
className="h-full transition-all"
|
||||
style={{
|
||||
width: `${progress}%`,
|
||||
backgroundColor: progressColor,
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Status */}
|
||||
<div className="flex items-center gap-2 flex-shrink-0">
|
||||
<Badge style={getOKRStatusColor(okr.status)} className="text-xs px-2 py-0.5">
|
||||
{OKR_STATUS_LABELS[okr.status]}
|
||||
</Badge>
|
||||
{okr.keyResults && okr.keyResults.length > 0 && (
|
||||
<span className="text-xs text-muted whitespace-nowrap">
|
||||
{okr.keyResults.length} KR{okr.keyResults.length !== 1 ? 's' : ''}
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</CardContent>
|
||||
</Link>
|
||||
</Card>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<Card hover className="h-full relative group">
|
||||
<CardHeader>
|
||||
@@ -141,7 +251,7 @@ export function OKRCard({ okr, teamId, isAdmin = false }: OKRCardProps) {
|
||||
</div>
|
||||
</div>
|
||||
</Link>
|
||||
|
||||
|
||||
{/* Action Zone */}
|
||||
<div className="flex items-center gap-2 flex-shrink-0 relative z-10">
|
||||
{isAdmin && (
|
||||
@@ -208,9 +318,7 @@ export function OKRCard({ okr, teamId, isAdmin = false }: OKRCardProps) {
|
||||
{/* Status */}
|
||||
<div className="flex items-center gap-2">
|
||||
<span className="text-sm text-muted">Statut:</span>
|
||||
<Badge style={getOKRStatusColor(okr.status)}>
|
||||
{OKR_STATUS_LABELS[okr.status]}
|
||||
</Badge>
|
||||
<Badge style={getOKRStatusColor(okr.status)}>{OKR_STATUS_LABELS[okr.status]}</Badge>
|
||||
</div>
|
||||
|
||||
{/* Key Results List */}
|
||||
@@ -223,7 +331,8 @@ export function OKRCard({ okr, teamId, isAdmin = false }: OKRCardProps) {
|
||||
{okr.keyResults
|
||||
.sort((a, b) => a.order - b.order)
|
||||
.map((kr: KeyResult) => {
|
||||
const krProgress = kr.targetValue > 0 ? (kr.currentValue / kr.targetValue) * 100 : 0;
|
||||
const krProgress =
|
||||
kr.targetValue > 0 ? (kr.currentValue / kr.targetValue) * 100 : 0;
|
||||
const krProgressColor =
|
||||
krProgress >= 100
|
||||
? 'var(--success)'
|
||||
@@ -234,7 +343,9 @@ export function OKRCard({ okr, teamId, isAdmin = false }: OKRCardProps) {
|
||||
return (
|
||||
<div key={kr.id} className="space-y-1">
|
||||
<div className="flex items-start justify-between gap-2">
|
||||
<span className="text-sm text-foreground flex-1 line-clamp-2">{kr.title}</span>
|
||||
<span className="text-sm text-foreground flex-1 line-clamp-2">
|
||||
{kr.title}
|
||||
</span>
|
||||
<Badge
|
||||
style={{
|
||||
...getKeyResultStatusColor(kr.status),
|
||||
@@ -276,4 +387,3 @@ export function OKRCard({ okr, teamId, isAdmin = false }: OKRCardProps) {
|
||||
</Card>
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user