import { useState } from 'react'; import type { Task } from '../services/api'; import { PageParseAPI } from '../services/api'; import { Search, Calendar, AlertTriangle, CheckCircle, Circle, Trash2, Download, Check, X, Edit2, Copy, Database } from 'lucide-react'; interface TaskListProps { tasks: Task[]; onTasksChanged: () => void; variant?: 'full' | 'compact'; } export const TaskList = ({ tasks, onTasksChanged, variant = 'full' }: TaskListProps) => { const [searchQuery, setSearchQuery] = useState(''); const [filterPriority, setFilterPriority] = useState('all'); const [filterStatus, setFilterStatus] = useState('all'); const [editingTaskId, setEditingTaskId] = useState(null); const [copiedMD, setCopiedMD] = useState(false); const [copiedTaskId, setCopiedTaskId] = useState(null); const [editName, setEditName] = useState(''); const [editDueDate, setEditDueDate] = useState(''); const [editPriority, setEditPriority] = useState<'high' | 'medium' | 'low'>('medium'); const [editCategory, setEditCategory] = useState(''); const filteredTasks = tasks.filter(t => { const q = searchQuery.toLowerCase(); const matchesSearch = t.task.toLowerCase().includes(q) || (t.category?.toLowerCase().includes(q) ?? false); const matchesPriority = filterPriority === 'all' || t.priority === filterPriority; const matchesStatus = filterStatus === 'all' || t.status === filterStatus; return matchesSearch && matchesPriority && matchesStatus; }); const handleToggleStatus = async (task: Task) => { const newStatus = task.status === 'todo' ? 'done' : 'todo'; try { await PageParseAPI.updateTask(task.id, { status: newStatus }); onTasksChanged(); } catch (err) { console.error(err); } }; const handleDelete = async (id: number) => { try { await PageParseAPI.deleteTask(id); onTasksChanged(); } catch (err) { console.error(err); } }; const startEditing = (task: Task) => { setEditingTaskId(task.id); setEditName(task.task); setEditDueDate(task.due_date || ''); setEditPriority(task.priority || 'medium'); setEditCategory(task.category || ''); }; const saveEditing = async (id: number) => { try { await PageParseAPI.updateTask(id, { task: editName, due_date: editDueDate || null, priority: editPriority, category: editCategory || null }); setEditingTaskId(null); onTasksChanged(); } catch (err) { console.error(err); } }; const handleCopyMD = async () => { const md = filteredTasks.map(t => `- [${t.status === 'done' ? 'x' : ' '}] ${t.task}${t.due_date ? ` (due: ${t.due_date})` : ''}`).join('\n'); try { await navigator.clipboard.writeText(md); setCopiedMD(true); setTimeout(() => setCopiedMD(false), 2000); } catch (err) { console.error(err); } }; const handleCopyTask = async (task: Task) => { try { await navigator.clipboard.writeText(task.task); setCopiedTaskId(task.id); setTimeout(() => setCopiedTaskId(null), 2000); } catch (err) { console.error(err); } }; const handleExportJSON = () => { const blob = new Blob([JSON.stringify(filteredTasks, null, 2)], { type: 'application/json' }); const url = URL.createObjectURL(blob); const a = document.createElement('a'); a.href = url; a.download = `pageparse_${new Date().toISOString().split('T')[0]}.json`; a.click(); URL.revokeObjectURL(url); }; const handleExportCSV = () => { const headers = ['ID', 'Task', 'Due Date', 'Priority', 'Category', 'Status', 'Confidence']; const rows = filteredTasks.map(t => [t.id, `"${t.task.replace(/"/g, '""')}"`, t.due_date || '', t.priority || '', t.category || '', t.status || '', t.ocr_confidence.toFixed(2)]); const csv = [headers.join(','), ...rows.map(r => r.join(','))].join('\n'); const blob = new Blob([csv], { type: 'text/csv' }); const url = URL.createObjectURL(blob); const a = document.createElement('a'); a.href = url; a.download = `pageparse_${new Date().toISOString().split('T')[0]}.csv`; a.click(); URL.revokeObjectURL(url); }; if (variant === 'compact') { return (

Recent Records ({filteredTasks.length})

setSearchQuery(e.target.value)} />
{filteredTasks.slice(0, 8).map(task => ( ))}
Type Task Due Priority Confidence
{(task.type || 'task').replace('_', ' ')}
{task.task} {(task.ocr_confidence || 0) < 0.6 && Low Conf}
{task.due_date || '—'} {task.priority && {task.priority}} {(task.ocr_confidence * 100).toFixed(0)}%
{!filteredTasks.length &&
No records found.
}
); } return (

All Records ({filteredTasks.length})

setSearchQuery(e.target.value)} />
{filteredTasks.map(task => { const isEditing = editingTaskId === task.id; const isLowConf = (task.ocr_confidence || 0) < 0.6; return ( ); })}
Type Description Due Date Priority Category Confidence Actions
{(task.type || 'task').replace('_', ' ')} {isEditing ? ( setEditName(e.target.value)} style={{ padding: '4px 8px', fontSize: 13 }} /> ) : (
{task.task} {isLowConf && Verify}
)}
{isEditing ? ( setEditDueDate(e.target.value)} style={{ padding: '4px 8px', fontSize: 13 }} /> ) : ( {task.due_date || '—'} )} {isEditing ? ( ) : ( task.priority && {task.priority} )} {isEditing ? ( setEditCategory(e.target.value)} placeholder="e.g. Work" style={{ padding: '4px 8px', fontSize: 13 }} /> ) : ( {task.category || '—'} )}
{(task.ocr_confidence * 100).toFixed(0)}%
{isEditing ? ( <> ) : ( <> )}
{!filteredTasks.length &&
No records found matching the filters.
}
); };