SyedFouzan's picture
{
ffb4c35 verified
Raw History Blame Contribute Delete
5.83 kB
import React, { useState } from 'react';
import { motion } from 'framer-motion';
import {
Calendar,
User,
MessageSquare,
Paperclip,
MoreVertical,
CheckCircle,
Clock,
AlertCircle
} from 'lucide-react';
import { tasksAPI } from '../services/api';
import toast from 'react-hot-toast';
const TaskCard = ({ task, onUpdate, onEdit, onDelete }) => {
const [isMenuOpen, setIsMenuOpen] = useState(false);
const [isUpdating, setIsUpdating] = useState(false);
const getStatusIcon = (status) => {
switch (status) {
case 'completed':
return <CheckCircle className="w-4 h-4 text-green-500" />;
case 'in-progress':
return <Clock className="w-4 h-4 text-blue-500" />;
case 'pending':
return <AlertCircle className="w-4 h-4 text-yellow-500" />;
default:
return null;
}
};
const getPriorityColor = (priority) => {
switch (priority) {
case 'high':
return 'bg-red-100 text-red-800 dark:bg-red-900/20 dark:text-red-400';
case 'medium':
return 'bg-yellow-100 text-yellow-800 dark:bg-yellow-900/20 dark:text-yellow-400';
case 'low':
return 'bg-green-100 text-green-800 dark:bg-green-900/20 dark:text-green-400';
default:
return 'bg-gray-100 text-gray-800 dark:bg-gray-900/20 dark:text-gray-400';
}
};
const handleStatusChange = async (newStatus) => {
setIsUpdating(true);
try {
await tasksAPI.updateStatus(task._id, newStatus);
onUpdate && onUpdate(task._id, { status: newStatus });
toast.success('Task status updated');
} catch (error) {
toast.error('Failed to update status');
} finally {
setIsUpdating(false);
}
};
const formatDate = (date) => {
return new Date(date).toLocaleDateString('en-US', {
month: 'short',
day: 'numeric',
year: 'numeric'
});
};
return (
<motion.div
initial={{ opacity: 0, y: 20 }}
animate={{ opacity: 1, y: 0 }}
className="card hover:shadow-md transition-shadow duration-200"
>
<div className="flex items-start justify-between mb-4">
<div className="flex-1">
<div className="flex items-center gap-2 mb-2">
{getStatusIcon(task.status)}
<h3 className="font-semibold text-gray-900 dark:text-gray-100">
{task.title}
</h3>
</div>
<span className={`inline-flex items-center px-2 py-1 rounded-full text-xs font-medium ${getPriorityColor(task.priority)}`}>
{task.priority}
</span>
</div>
<div className="relative">
<button
onClick={() => setIsMenuOpen(!isMenuOpen)}
className="p-1 rounded-lg hover:bg-gray-100 dark:hover:bg-gray-700 transition-colors"
>
<MoreVertical className="w-4 h-4 text-gray-500" />
</button>
{isMenuOpen && (
<div className="absolute right-0 mt-2 w-48 bg-white dark:bg-gray-800 rounded-lg shadow-lg border border-gray-200 dark:border-gray-700 z-10">
<button
onClick={() => {
onEdit && onEdit(task);
setIsMenuOpen(false);
}}
className="flex items-center gap-2 w-full px-3 py-2 text-sm text-left hover:bg-gray-50 dark:hover:bg-gray-700"
>
Edit
</button>
<select
value={task.status}
onChange={(e) => {
handleStatusChange(e.target.value);
setIsMenuOpen(false);
}}
className="w-full px-3 py-2 text-sm bg-transparent border-0 focus:ring-0 cursor-pointer hover:bg-gray-50 dark:hover:bg-gray-700"
disabled={isUpdating}
>
<option value="pending">Pending</option>
<option value="in-progress">In Progress</option>
<option value="completed">Completed</option>
</select>
<button
onClick={() => {
onDelete && onDelete(task._id);
setIsMenuOpen(false);
}}
className="flex items-center gap-2 w-full px-3 py-2 text-sm text-left text-red-600 hover:bg-red-50 dark:hover:bg-red-900/20"
>
Delete
</button>
</div>
)}
</div>
</div>
<p className="text-sm text-gray-600 dark:text-gray-400 mb-4 line-clamp-2">
{task.description}
</p>
<div className="flex items-center justify-between text-xs text-gray-500 dark:text-gray-400">
<div className="flex items-center gap-4">
{task.assignee && (
<div className="flex items-center gap-1">
<User className="w-3 h-3" />
<span>{task.assignee.name}</span>
</div>
)}
{task.deadline && (
<div className="flex items-center gap-1">
<Calendar className="w-3 h-3" />
<span>{formatDate(task.deadline)}</span>
</div>
)}
</div>
<div className="flex items-center gap-3">
{task.comments && task.comments.length > 0 && (
<div className="flex items-center gap-1">
<MessageSquare className="w-3 h-3" />
<span>{task.comments.length}</span>
</div>
)}
{task.attachments && task.attachments.length > 0 && (
<div className="flex items-center gap-1">
<Paperclip className="w-3 h-3" />
<span>{task.attachments.length}</span>
</div>
)}
</div>
</div>
</motion.div>
);
};
export default TaskCard;