import React, { useState, useMemo } from 'react' import { Box, Typography, FormControl, InputLabel, Select, MenuItem, Chip, Stack, Paper, Table, TableBody, TableCell, TableContainer, TableHead, TableRow, useMediaQuery, useTheme, Checkbox, Toolbar, Button, Dialog, DialogTitle, DialogContent, DialogContentText, DialogActions, } from '@mui/material' import { Delete as DeleteIcon } from '@mui/icons-material' import { useTasks } from '~/hooks/useTasks' import { TaskStatus, type Task } from '~/types/task' import LoadingSpinner from './LoadingSpinner' type SortOption = | 'created_desc' | 'created_asc' | 'title_asc' | 'title_desc' | 'status' interface TaskListProps { className?: string initialTasks?: Task[] } export function TaskList({ className, initialTasks }: TaskListProps) { const { tasks, loading, error, deleteTask } = useTasks({ autoFetch: !initialTasks, initialData: initialTasks, }) const [statusFilter, setStatusFilter] = useState('all') const [sortBy, setSortBy] = useState('created_desc') const [selectedTaskIds, setSelectedTaskIds] = useState>(new Set()) const [deleteDialogOpen, setDeleteDialogOpen] = useState(false) const theme = useTheme() const isMobile = useMediaQuery(theme.breakpoints.down('md')) const filteredAndSortedTasks = useMemo(() => { let filteredTasks = tasks // Apply status filter if (statusFilter !== 'all') { filteredTasks = tasks.filter(task => task.status === statusFilter) } // Apply sorting const sorted = [...filteredTasks].sort((a, b) => { switch (sortBy) { case 'created_desc': return ( new Date(b.created_at).getTime() - new Date(a.created_at).getTime() ) case 'created_asc': return ( new Date(a.created_at).getTime() - new Date(b.created_at).getTime() ) case 'title_asc': return a.title.localeCompare(b.title) case 'title_desc': return b.title.localeCompare(a.title) case 'status': { const statusOrder = { [TaskStatus.Todo]: 0, [TaskStatus.Backlog]: 1, [TaskStatus.Done]: 2, } return statusOrder[a.status] - statusOrder[b.status] } default: return 0 } }) return sorted }, [tasks, statusFilter, sortBy]) const getStatusColor = (status: TaskStatus) => { switch (status) { case TaskStatus.Todo: return 'primary' case TaskStatus.Done: return 'success' case TaskStatus.Backlog: return 'default' default: return 'default' } } const formatCompactDate = (dateString: string) => { const date = new Date(dateString) const now = new Date() const diffInDays = Math.floor( (now.getTime() - date.getTime()) / (1000 * 60 * 60 * 24) ) if (diffInDays === 0) return 'Today' if (diffInDays === 1) return 'Yesterday' if (diffInDays < 7) return `${diffInDays}d ago` if (diffInDays < 30) return `${Math.floor(diffInDays / 7)}w ago` if (diffInDays < 365) return `${Math.floor(diffInDays / 30)}mo ago` return `${Math.floor(diffInDays / 365)}y ago` } const handleSelectAll = (checked: boolean) => { if (checked) { setSelectedTaskIds(new Set(filteredAndSortedTasks.map(task => task.id))) } else { setSelectedTaskIds(new Set()) } } const handleSelectTask = (taskId: string, checked: boolean) => { const newSelected = new Set(selectedTaskIds) if (checked) { newSelected.add(taskId) } else { newSelected.delete(taskId) } setSelectedTaskIds(newSelected) } const handleBulkDelete = async () => { try { const results = await Promise.all( Array.from(selectedTaskIds).map(taskId => deleteTask(taskId)) ) // Check if all deletions were successful const allSuccessful = results.every(result => result === true) if (allSuccessful) { setSelectedTaskIds(new Set()) setDeleteDialogOpen(false) } else { console.error('Some tasks failed to delete') // Clear selection of successfully deleted tasks const failedTaskIds = Array.from(selectedTaskIds).filter( (taskId, index) => !results[index] ) setSelectedTaskIds(new Set(failedTaskIds)) } } catch (error) { console.error('Error deleting tasks:', error) } } const isAllSelected = filteredAndSortedTasks.length > 0 && filteredAndSortedTasks.every(task => selectedTaskIds.has(task.id)) const isIndeterminate = selectedTaskIds.size > 0 && !isAllSelected if (loading) { return } if (error) { return ( Error loading tasks {error} ) } return ( {/* Filter and Sort Controls */} Status Sort by {filteredAndSortedTasks.length} task {filteredAndSortedTasks.length !== 1 ? 's' : ''} {/* Bulk Actions Toolbar */} {selectedTaskIds.size > 0 && ( {selectedTaskIds.size} task{selectedTaskIds.size > 1 ? 's' : ''}{' '} selected )} {/* Task Table */} {filteredAndSortedTasks.length === 0 ? ( No tasks found {statusFilter === 'all' ? 'Create your first task to get started!' : `No ${statusFilter} tasks found.`} ) : ( handleSelectAll(e.target.checked)} inputProps={{ 'aria-label': 'select all tasks', }} /> Title {!isMobile && ( Description )} Status Created {!isMobile && ( Completed )} {filteredAndSortedTasks.map(task => ( handleSelectTask(task.id, e.target.checked) } inputProps={{ 'aria-labelledby': `task-${task.id}`, }} /> {task.title} {isMobile && task.description && ( {task.description} )} {!isMobile && ( {task.description || '—'} )} {formatCompactDate(task.created_at)} {!isMobile && ( {task.completed_at ? formatCompactDate(task.completed_at) : '—'} )} ))}
)} {/* Delete Confirmation Dialog */} setDeleteDialogOpen(false)} aria-labelledby="delete-dialog-title" aria-describedby="delete-dialog-description" > Delete {selectedTaskIds.size} task {selectedTaskIds.size > 1 ? 's' : ''}? This action cannot be undone. Are you sure you want to delete the selected task{selectedTaskIds.size > 1 ? 's' : ''}?
) }