taskflow-frontend / components /TaskItem.tsx
Tahasaif3's picture
'changes'
5e870e6
'use client';
import { useState } from 'react';
import { motion } from 'framer-motion';
import { Check, Trash2, Calendar, Clock, Edit } from 'lucide-react';
import { Task } from '@/lib/types';
import { toggleTaskCompletion, deleteTask } from '@/lib/api';
import { DeleteConfirmModal } from './DeleteConfirmModal';
import { Card } from '@/components/ui/card';
import { useRouter } from 'next/navigation';
import { showToast } from '@/lib/toast';
interface TaskItemProps {
task: Task;
onTaskUpdated: () => void;
onTaskDeleted: () => void;
}
// Custom circular checkbox component with animation
const CircularCheckbox = ({ checked, onChange, disabled }: { checked: boolean; onChange: () => void; disabled?: boolean }) => {
return (
<motion.div
whileHover={{ scale: 1.1 }}
whileTap={{ scale: 0.9 }}
className="relative"
>
<button
type="button"
onClick={onChange}
disabled={disabled}
className={`w-6 h-6 rounded-full border-2 flex items-center justify-center transition-all duration-200 ${checked
? 'bg-gradient-to-r from-green-500 to-emerald-500 border-transparent shadow-lg'
: 'border-gray-600 hover:border-green-500'
} ${disabled ? 'opacity-50 cursor-not-allowed' : 'cursor-pointer'}`}
>
<motion.div
initial={false}
animate={{ scale: checked ? 1 : 0 }}
transition={{ duration: 0.2 }}
>
<Check className="w-4 h-4 text-white" />
</motion.div>
</button>
</motion.div>
);
};
export function TaskItem({ task, onTaskUpdated, onTaskDeleted }: TaskItemProps) {
const router = useRouter();
const [isDeleting, setIsDeleting] = useState(false);
const [showDeleteModal, setShowDeleteModal] = useState(false);
const [localTask, setLocalTask] = useState(task);
// Get user ID from localStorage or context (in a real app, this would come from auth context)
const getUserId = () => {
if (typeof window !== 'undefined') {
const userStr = localStorage.getItem('user');
if (userStr) {
try {
const user = JSON.parse(userStr);
return user.id;
} catch (e) {
console.error('Error parsing user from localStorage:', e);
return null;
}
}
}
return null;
};
const handleToggleCompletion = async () => {
const userId = getUserId();
if (!userId) {
console.error('User ID not found');
return;
}
try {
const updatedTask = await toggleTaskCompletion(userId, localTask.id);
setLocalTask(updatedTask);
onTaskUpdated();
showToast.success(updatedTask.completed ? 'Task completed!' : 'Task reopened');
} catch (error) {
console.error('Error toggling task completion:', error);
showToast.error('Failed to update task status');
}
};
const handleDeleteConfirm = async () => {
const userId = getUserId();
if (!userId) {
console.error('User ID not found');
return;
}
try {
setIsDeleting(true);
await deleteTask(userId, localTask.id);
onTaskDeleted();
showToast.success('Task deleted successfully');
setShowDeleteModal(false);
} catch (error) {
console.error('Error deleting task:', error);
showToast.error('Failed to delete task');
setIsDeleting(false);
}
};
const handleEdit = () => {
router.push(`/tasks/${task.id}`);
};
return (
<>
<Card
className={`bg-gray-900/50 backdrop-blur-xl border-gray-800 p-4 hover:border-gray-700 transition-all duration-300 ${localTask.completed ? 'opacity-70' : 'hover:shadow-lg hover:shadow-indigo-500/10'
}`}
>
<div className="flex items-start justify-between">
<div className="flex items-start space-x-3 flex-1 min-w-0">
<CircularCheckbox
checked={localTask.completed}
onChange={handleToggleCompletion}
/>
<div className="min-w-0 flex-1">
<p
className={`text-base font-medium ${localTask.completed
? 'text-gray-400 line-through'
: 'text-white'
}`}
>
{localTask.title}
{localTask.is_ai_generated && (
<span className="ml-2 inline-flex items-center px-2 py-0.5 rounded-full text-xs font-medium bg-indigo-900 text-indigo-200">
AI
</span>
)}
</p>
{localTask.description && (
<p className="text-sm text-gray-400 mt-1 line-clamp-2">
{localTask.description}
</p>
)}
<div className="flex items-center text-xs text-gray-500 mt-3 space-x-4">
<div className="flex items-center">
<Calendar className="w-3 h-3 mr-1" />
<span>Created: {new Date(localTask.created_at).toLocaleDateString()}</span>
</div>
{localTask.due_date && (
<div className={`flex items-center ${new Date(localTask.due_date) < new Date() && !localTask.completed ? 'text-red-400' : 'text-indigo-400'}`}>
<Clock className="w-3 h-3 mr-1" />
<span>Due: {new Date(localTask.due_date).toLocaleDateString()}</span>
</div>
)}
{localTask.updated_at !== localTask.created_at && (
<div className="flex items-center">
<Clock className="w-3 h-3 mr-1" />
<span>Edited</span>
</div>
)}
</div>
</div>
</div>
<div className="flex space-x-1 ml-4">
<motion.button
whileHover={{ scale: 1.1 }}
whileTap={{ scale: 0.9 }}
onClick={handleEdit}
className="p-2 text-gray-400 hover:text-indigo-400 hover:bg-indigo-900/30 rounded-lg transition-colors"
>
<Edit className="w-4 h-4" />
</motion.button>
<motion.button
whileHover={{ scale: 1.1 }}
whileTap={{ scale: 0.9 }}
onClick={() => setShowDeleteModal(true)}
disabled={isDeleting}
className="p-2 text-gray-400 hover:text-red-400 hover:bg-red-900/30 rounded-lg transition-colors"
>
<Trash2 className="w-4 h-4" />
</motion.button>
</div>
</div>
</Card>
<DeleteConfirmModal
isOpen={showDeleteModal}
onClose={() => setShowDeleteModal(false)}
onConfirm={handleDeleteConfirm}
taskTitle={localTask.title}
loading={isDeleting}
/>
</>
);
}