import React, { useState, useEffect, useCallback, useMemo } from 'react' import { LayoutGrid, List as ListIcon, Plus, Search, Filter, RefreshCw, Folder, UserCheck, Users, CheckCircle2, Clock, Sparkles, AlertCircle, } from 'lucide-react' import { useAuth } from '../contexts/AuthContext' import { useTimer } from '../contexts/TimerContext' import { taskService } from '../services/taskService' import { projectService } from '../services/projectService' import type { Task, TaskStatus, Project } from '../types/task' import { TaskKanbanView } from '../components/tasks/TaskKanbanView' import { TaskListView } from '../components/tasks/TaskListView' import { CreateTaskModal } from '../components/tasks/CreateTaskModal' import { CustomSelect } from '../components/ui/Select' export const TasksPage: React.FC = () => { const { activeOrg } = useAuth() const { activeTask, activeSeconds, startTimer, stopTimer, syncWithTasks } = useTimer() const [tasks, setTasks] = useState([]) const [projects, setProjects] = useState([]) const [loading, setLoading] = useState(true) const [refreshing, setRefreshing] = useState(false) const [viewMode, setViewMode] = useState<'kanban' | 'list'>('kanban') // Filters const [searchQuery, setSearchQuery] = useState('') const [selectedStatus, setSelectedStatus] = useState('ALL') const [selectedProjectId, setSelectedProjectId] = useState('ALL') const [onlyMyTasks, setOnlyMyTasks] = useState(false) // Modal state const [isModalOpen, setIsModalOpen] = useState(false) const [modalInitialStatus, setModalInitialStatus] = useState('NOT_STARTED') const [editingTask, setEditingTask] = useState(null) const [actionError, setActionError] = useState(null) const activeOrgId = activeOrg?.id // Load tasks & projects for current organization const loadData = useCallback(async () => { if (!activeOrgId) return try { setRefreshing(true) const [tasksRes, projsRes] = await Promise.all([ onlyMyTasks ? taskService.listMyTasks({ organizationId: activeOrgId }) : taskService.listTasks({ organizationId: activeOrgId }), projectService.listProjects(activeOrgId).catch(() => []), ]) setTasks(tasksRes || []) setProjects(projsRes || []) syncWithTasks(tasksRes || []) } catch (err) { console.error('Failed to load tasks', err) setActionError('Falha ao carregar tarefas. Tente atualizar a página.') } finally { setLoading(false) setRefreshing(false) } }, [activeOrgId, onlyMyTasks, syncWithTasks]) useEffect(() => { loadData() }, [loadData]) // Filter tasks client-side for ultra-responsive search const filteredTasks = useMemo(() => { return tasks.filter((task) => { // Status filter if (selectedStatus !== 'ALL' && task.status !== selectedStatus) { return false } // Project filter if (selectedProjectId !== 'ALL' && task.project?.id !== selectedProjectId) { return false } // Search text filter if (searchQuery.trim()) { const query = searchQuery.toLowerCase() const matchesTitle = task.title.toLowerCase().includes(query) const matchesDesc = task.description?.toLowerCase().includes(query) const matchesProject = task.project?.name.toLowerCase().includes(query) const matchesCategory = task.category?.name.toLowerCase().includes(query) if (!matchesTitle && !matchesDesc && !matchesProject && !matchesCategory) { return false } } return true }) }, [tasks, selectedStatus, selectedProjectId, searchQuery]) // Timer play/pause handler const handleToggleTimer = async (task: Task) => { setActionError(null) const isCurrentlyRunning = activeTask?.id === task.id try { if (isCurrentlyRunning) { const stopped = await stopTimer(task) if (stopped) { setTasks((prev) => prev.map((t) => (t.id === stopped.id ? stopped : t))) } } else { const started = await startTimer(task) setTasks((prev) => prev.map((t) => (t.id === started.id ? started : t))) } } catch (err: unknown) { console.error('Timer action failed', err) setActionError( err instanceof Error ? err.message : 'Falha ao acionar cronômetro da tarefa.' ) } } // Status change handler const handleStatusChange = async (task: Task, newStatus: TaskStatus) => { setActionError(null) try { // Optimistic update setTasks((prev) => prev.map((t) => (t.id === task.id ? { ...t, status: newStatus } : t)) ) const updated = await taskService.updateStatus(task.id, newStatus) setTasks((prev) => prev.map((t) => (t.id === updated.id ? updated : t))) } catch (err: unknown) { console.error('Failed to update status', err) setActionError('Falha ao atualizar status da tarefa.') loadData() // Revert to server state } } // Create or Update task modal submit const handleModalSubmit = async (taskData: { title: string description: string status: TaskStatus projectId?: number categoryId?: number assigneeUserId?: number }) => { if (!activeOrgId) return setActionError(null) if (editingTask) { const updated = await taskService.updateTask(editingTask.id, { title: taskData.title, description: taskData.description, status: taskData.status, projectId: taskData.projectId, categoryId: taskData.categoryId, assigneeUserId: taskData.assigneeUserId, organizationId: activeOrgId, }) setTasks((prev) => prev.map((t) => (t.id === updated.id ? updated : t))) setEditingTask(null) } else { const created = await taskService.createTask({ title: taskData.title, description: taskData.description, status: taskData.status, projectId: taskData.projectId, categoryId: taskData.categoryId, assigneeUserId: taskData.assigneeUserId, organizationId: activeOrgId, }) setTasks((prev) => [created, ...prev]) } } const handleOpenCreateModal = (initialStatus: TaskStatus = 'NOT_STARTED') => { setEditingTask(null) setModalInitialStatus(initialStatus) setIsModalOpen(true) } const handleOpenEditModal = (task: Task) => { setEditingTask(task) setIsModalOpen(true) } // Summary counts const notStartedCount = tasks.filter((t) => t.status === 'NOT_STARTED').length const inProgressCount = tasks.filter((t) => t.status === 'IN_PROGRESS').length const completedCount = tasks.filter((t) => t.status === 'COMPLETED').length return (
{/* Page Header */}

Quadro de Tarefas

{activeOrg?.name || 'Workspace'}

Planeje, execute e rastreie o tempo investido em cada entrega da equipe.

{/* View Switcher & New Task Button */}
{/* Refresh */} {/* Kanban / List Toggle */}
{/* New Task Button */}
{/* Error alert if any */} {actionError && (
{actionError}
)} {/* Quick KPI Bar */}

Total de Tarefas

{tasks.length}

Em Andamento

{inProgressCount}

A Fazer

{notStartedCount}

Concluídas

{completedCount}

{/* Filter and Search Bar */}
{/* Search Input */}
setSearchQuery(e.target.value)} placeholder="Buscar por título, projeto, categoria..." className="w-full pl-9 pr-3.5 py-2 rounded-xl border border-border-soft bg-surface-raised/80 text-xs text-foreground placeholder-muted focus:outline-none focus:ring-2 focus:ring-brand/40 focus:border-brand transition-all" /> {searchQuery && ( )}
{/* Filters Group */}
{/* Mine vs All toggle */} {/* Project Filter */} {projects.length > 0 && (
setSelectedProjectId(val)} leftIcon={} options={[ { value: 'ALL', label: 'Todos os Projetos' }, ...projects.map((p) => ({ value: p.id, label: p.name, color: p.color || '#0f766e', })), ]} />
)} {/* Status Filter */}
setSelectedStatus(val as TaskStatus | 'ALL')} options={[ { value: 'ALL', label: 'Todos os Status' }, { value: 'NOT_STARTED', label: 'A Fazer', color: '#71717a' }, { value: 'IN_PROGRESS', label: 'Em Andamento', color: '#14b8a6' }, { value: 'COMPLETED', label: 'Concluído', color: '#10b981' }, { value: 'CANCELED', label: 'Cancelado', color: '#f43f5e' }, ]} />
{/* Main Content Area */} {loading ? (
{[1, 2, 3, 4].map((i) => (
))}
) : filteredTasks.length === 0 ? (

Nenhuma tarefa encontrada

{searchQuery || selectedStatus !== 'ALL' || selectedProjectId !== 'ALL' ? 'Nenhum resultado corresponde aos filtros selecionados. Tente limpar os filtros para ver mais.' : 'Você ainda não possui tarefas criadas nesta organização. Comece agora mesmo organizando sua primeira entrega!'}

) : viewMode === 'kanban' ? ( ) : ( )} {/* Task Creation & Edit Modal */} {activeOrgId && ( { setIsModalOpen(false) setEditingTask(null) }} onSubmit={handleModalSubmit} organizationId={activeOrgId} initialStatus={modalInitialStatus} initialTask={editingTask} /> )}
) }