Files
towercontrol/components/kanban/TaskCard.tsx
Julien Froidefond 0b7e0edb2f feat: enhance Kanban functionality and update TODO.md
- Completed the creation and validation forms for tasks in the Kanban board, improving task management capabilities.
- Integrated new task creation and deletion functionalities in the `KanbanBoard` and `KanbanColumn` components.
- Added quick task addition feature in `Column` component for better user experience.
- Updated `TaskCard` to support task deletion with a new button.
- Marked several tasks as completed in `TODO.md` to reflect the progress on Kanban features.
- Updated TypeScript types to include 'manual' as a new task source.
2025-09-14 08:48:39 +02:00

122 lines
4.2 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import { Task } from '@/lib/types';
import { formatDistanceToNow } from 'date-fns';
import { fr } from 'date-fns/locale';
import { Card } from '@/components/ui/Card';
import { Badge } from '@/components/ui/Badge';
interface TaskCardProps {
task: Task;
onDelete?: (taskId: string) => Promise<void>;
}
export function TaskCard({ task, onDelete }: TaskCardProps) {
const handleDelete = async (e: React.MouseEvent) => {
e.preventDefault();
e.stopPropagation();
if (onDelete) {
await onDelete(task.id);
}
};
// Extraire les emojis du titre pour les afficher comme tags visuels
const emojiRegex = /[\u{1F600}-\u{1F64F}]|[\u{1F300}-\u{1F5FF}]|[\u{1F680}-\u{1F6FF}]|[\u{1F1E0}-\u{1F1FF}]|[\u{2600}-\u{26FF}]|[\u{2700}-\u{27BF}]/gu;
const emojis = task.title.match(emojiRegex) || [];
const titleWithoutEmojis = task.title.replace(emojiRegex, '').trim();
return (
<Card className="p-3 hover:border-cyan-500/30 hover:shadow-lg hover:shadow-cyan-500/10 transition-all duration-300 cursor-pointer group">
{/* Header tech avec titre et status */}
<div className="flex items-start gap-2 mb-2">
{emojis.length > 0 && (
<div className="flex gap-1 flex-shrink-0">
{emojis.slice(0, 2).map((emoji, index) => (
<span key={index} className="text-sm opacity-80">
{emoji}
</span>
))}
</div>
)}
<h4 className="font-mono text-sm font-medium text-slate-100 leading-tight line-clamp-2 flex-1">
{titleWithoutEmojis}
</h4>
<div className="flex items-center gap-2 flex-shrink-0">
{/* Bouton de suppression discret */}
{onDelete && (
<button
onClick={handleDelete}
className="opacity-0 group-hover:opacity-100 w-4 h-4 rounded-full bg-red-900/50 hover:bg-red-800/80 border border-red-500/30 hover:border-red-400/50 flex items-center justify-center transition-all duration-200 text-red-400 hover:text-red-300 text-xs"
title="Supprimer la tâche"
>
×
</button>
)}
{/* Indicateur de priorité tech */}
<div className={`w-2 h-2 rounded-full animate-pulse ${
task.priority === 'high' ? 'bg-red-400 shadow-red-400/50 shadow-sm' :
task.priority === 'medium' ? 'bg-yellow-400 shadow-yellow-400/50 shadow-sm' :
'bg-slate-500'
}`} />
</div>
</div>
{/* Description tech */}
{task.description && (
<p className="text-xs text-slate-400 mb-3 line-clamp-1 font-mono">
{task.description}
</p>
)}
{/* Tags avec composant Badge */}
{task.tags && task.tags.length > 0 && (
<div className="flex flex-wrap gap-1 mb-3">
{task.tags.slice(0, 3).map((tag, index) => (
<Badge
key={index}
variant="primary"
size="sm"
className="hover:bg-cyan-950/80 transition-colors"
>
{tag}
</Badge>
))}
{task.tags.length > 3 && (
<Badge variant="outline" size="sm">
+{task.tags.length - 3}
</Badge>
)}
</div>
)}
{/* Footer tech avec séparateur néon */}
<div className="pt-2 border-t border-slate-700/50">
<div className="flex items-center justify-between text-xs">
{task.dueDate ? (
<span className="flex items-center gap-1 text-slate-400 font-mono">
<span className="text-cyan-400"></span>
{formatDistanceToNow(new Date(task.dueDate), {
addSuffix: true,
locale: fr
})}
</span>
) : (
<span className="text-slate-600 font-mono">--:--</span>
)}
{task.source !== 'manual' && task.source && (
<Badge variant="outline" size="sm">
{task.source}
</Badge>
)}
{task.completedAt && (
<span className="text-emerald-400 font-mono font-bold"> DONE</span>
)}
</div>
</div>
</Card>
);
}