- Added `date-fns` as a dependency in `package.json` and `package-lock.json`. - Refactored `Home` component to `HomePage`, implementing server-side rendering for tasks and stats retrieval. - Integrated `Header` and `KanbanBoard` components for improved UI structure. - Marked Kanban components as completed in `TODO.md`.
113 lines
3.7 KiB
TypeScript
113 lines
3.7 KiB
TypeScript
import { Task } from '@/lib/types';
|
|
import { formatDistanceToNow } from 'date-fns';
|
|
import { fr } from 'date-fns/locale';
|
|
|
|
interface TaskCardProps {
|
|
task: Task;
|
|
}
|
|
|
|
export function TaskCard({ task }: TaskCardProps) {
|
|
// 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();
|
|
|
|
// Couleur de priorité
|
|
const priorityColors = {
|
|
low: 'bg-gray-100 text-gray-600 dark:bg-gray-700 dark:text-gray-300',
|
|
medium: 'bg-yellow-100 text-yellow-700 dark:bg-yellow-900/30 dark:text-yellow-300',
|
|
high: 'bg-orange-100 text-orange-700 dark:bg-orange-900/30 dark:text-orange-300',
|
|
urgent: 'bg-red-100 text-red-700 dark:bg-red-900/30 dark:text-red-300'
|
|
};
|
|
|
|
// Couleur de source
|
|
const sourceColors = {
|
|
reminders: 'bg-blue-100 text-blue-700 dark:bg-blue-900/30 dark:text-blue-300',
|
|
jira: 'bg-purple-100 text-purple-700 dark:bg-purple-900/30 dark:text-purple-300'
|
|
};
|
|
|
|
return (
|
|
<div className="bg-white dark:bg-gray-700 rounded-lg shadow-sm border border-gray-200 dark:border-gray-600 p-4 hover:shadow-md transition-shadow cursor-pointer">
|
|
{/* En-tête avec emojis */}
|
|
{emojis.length > 0 && (
|
|
<div className="flex gap-1 mb-2">
|
|
{emojis.map((emoji, index) => (
|
|
<span key={index} className="text-lg">
|
|
{emoji}
|
|
</span>
|
|
))}
|
|
</div>
|
|
)}
|
|
|
|
{/* Titre */}
|
|
<h4 className="font-medium text-gray-900 dark:text-white mb-2 line-clamp-2">
|
|
{titleWithoutEmojis}
|
|
</h4>
|
|
|
|
{/* Description si présente */}
|
|
{task.description && (
|
|
<p className="text-sm text-gray-600 dark:text-gray-300 mb-3 line-clamp-2">
|
|
{task.description}
|
|
</p>
|
|
)}
|
|
|
|
{/* Tags */}
|
|
<div className="flex flex-wrap gap-1 mb-3">
|
|
{/* Priorité */}
|
|
<span className={`px-2 py-1 rounded-full text-xs font-medium ${priorityColors[task.priority]}`}>
|
|
{task.priority}
|
|
</span>
|
|
|
|
{/* Source */}
|
|
<span className={`px-2 py-1 rounded-full text-xs font-medium ${sourceColors[task.source as keyof typeof sourceColors]}`}>
|
|
{task.source}
|
|
</span>
|
|
|
|
{/* Tags personnalisés */}
|
|
{task.tags && task.tags.length > 0 && (
|
|
task.tags.slice(0, 2).map((tag, index) => (
|
|
<span
|
|
key={index}
|
|
className="px-2 py-1 rounded-full text-xs font-medium bg-indigo-100 text-indigo-700 dark:bg-indigo-900/30 dark:text-indigo-300"
|
|
>
|
|
#{tag}
|
|
</span>
|
|
))
|
|
)}
|
|
</div>
|
|
|
|
{/* Footer avec dates */}
|
|
<div className="flex items-center justify-between text-xs text-gray-500 dark:text-gray-400">
|
|
<div>
|
|
{task.dueDate && (
|
|
<span className="flex items-center gap-1">
|
|
📅 {formatDistanceToNow(new Date(task.dueDate), {
|
|
addSuffix: true,
|
|
locale: fr
|
|
})}
|
|
</span>
|
|
)}
|
|
</div>
|
|
|
|
<div>
|
|
{task.completedAt ? (
|
|
<span className="flex items-center gap-1 text-green-600 dark:text-green-400">
|
|
✅ {formatDistanceToNow(new Date(task.completedAt), {
|
|
addSuffix: true,
|
|
locale: fr
|
|
})}
|
|
</span>
|
|
) : (
|
|
<span>
|
|
Créé {formatDistanceToNow(new Date(task.createdAt), {
|
|
addSuffix: true,
|
|
locale: fr
|
|
})}
|
|
</span>
|
|
)}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|