import { useState, useEffect } from 'react'; import { apiFetchTasks, apiFetchUsers, apiCreateTask, apiUpdateTask, apiAddActivity, apiAddDependency, apiToggleDependency, apiRemoveDependency, apiCreateUser, apiDeleteUser } from './api'; import type { Task, User, Status } from './data'; import { STATUS_LABELS } from './data'; import { LoginPage } from './Login'; import { Sidebar } from './Sidebar'; import { TopNavbar, BottomToggleBar } from './NavBars'; import { CalendarView, QuickAddPanel } from './Calendar'; import { KanbanBoard } from './Kanban'; import { ListView } from './ListView'; import { TaskDrawer, AddTaskModal } from './TaskDrawer'; import { DashboardPage } from './Dashboard'; import { TeamTasksPage, MembersPage } from './Pages'; import { ReportsPage } from './Reports'; import { NotificationProvider } from './NotificationContext'; import './index.css'; const PAGE_TITLES: Record = { dashboard: 'Dashboard', calendar: 'Calendar', kanban: 'Kanban Board', mytasks: 'My Tasks', teamtasks: 'Team Tasks', reports: 'Reports', members: 'Members', list: 'List View', }; const VIEW_PAGES = ['calendar', 'kanban', 'list']; export default function App() { const now = new Date(); const [currentUser, setCurrentUser] = useState(() => { try { const s = localStorage.getItem('currentUser'); return s ? JSON.parse(s) : null; } catch { return null; } }); const [users, setUsers] = useState([]); const [tasks, setTasks] = useState([]); const [activePage, setActivePage] = useState('calendar'); const [activeView, setActiveView] = useState('calendar'); const [activeTask, setActiveTask] = useState(null); const [showAddModal, setShowAddModal] = useState(false); const [addModalDefaults, setAddModalDefaults] = useState<{ date?: string; status?: Status }>({}); const [calMonth, setCalMonth] = useState({ year: now.getFullYear(), month: now.getMonth() }); const [calView, setCalView] = useState('month'); const [filterUser, setFilterUser] = useState(null); const [searchQuery, setSearchQuery] = useState(''); const [sidebarOpen, setSidebarOpen] = useState(false); const [quickAddDay, setQuickAddDay] = useState<{ date: string; rect: { top: number; left: number } } | null>(null); const [loading, setLoading] = useState(false); // Load data from API when user logs in useEffect(() => { if (!currentUser) return; setLoading(true); Promise.all([apiFetchTasks(), apiFetchUsers()]) .then(([fetchedTasks, fetchedUsers]) => { setTasks(fetchedTasks); setUsers(fetchedUsers); }) .catch(err => { console.error('Failed to load data, using empty state:', err); setTasks([]); // Start empty if backend fails setUsers([currentUser]); }) .finally(() => setLoading(false)); }, [currentUser]); if (!currentUser) return { localStorage.setItem('currentUser', JSON.stringify(u)); setCurrentUser(u); setActivePage('calendar'); setActiveView('calendar'); }} />; const handleNavigate = (page: string) => { setActivePage(page); if (VIEW_PAGES.includes(page)) setActiveView(page); setSidebarOpen(false); }; const handleViewChange = (view: string) => { setActiveView(view); if (VIEW_PAGES.includes(view)) setActivePage(view); }; const handleTaskClick = (t: Task) => setActiveTask(t); const handleDayClick = (date: string, el: HTMLElement) => { const rect = el.getBoundingClientRect(); setQuickAddDay({ date, rect: { top: rect.bottom, left: rect.left } }); }; const handleQuickAdd = async (partial: Partial) => { const tempId = `t${Date.now()}`; const newTask: Task = { id: tempId, title: partial.title || '', description: partial.description || '', status: partial.status || 'todo', priority: partial.priority || 'medium', assignee: partial.assignee || currentUser.id, reporter: currentUser.id, dueDate: partial.dueDate || '', tags: partial.tags || [], subtasks: [], comments: [], activity: [], dependencies: [] }; setTasks(prev => [...prev, newTask]); setQuickAddDay(null); try { const created = await apiCreateTask({ title: newTask.title, description: newTask.description, status: newTask.status, priority: newTask.priority, assignee: newTask.assignee, reporter: newTask.reporter, dueDate: newTask.dueDate, tags: newTask.tags, }); setTasks(prev => prev.map(t => t.id === tempId ? created : t)); } catch (err) { console.error('Failed to quick-add task:', err); } }; const handleAddTask = async (task: Task) => { const tempId = `t${Date.now()}`; const newTask = { ...task, id: tempId }; setTasks(prev => [...prev, newTask]); try { const created = await apiCreateTask({ title: task.title, description: task.description, status: task.status, priority: task.priority, assignee: task.assignee, reporter: currentUser.id, dueDate: task.dueDate, tags: task.tags, dependencies: (task.dependencies || []).map(d => ({ dependsOnUserId: d.dependsOnUserId, description: d.description })), }); setTasks(prev => prev.map(t => t.id === tempId ? created : t)); } catch (err) { console.error('Failed to add task:', err); } }; const handleUpdateTask = async (updated: Task) => { // Optimistic update setTasks(prev => prev.map(t => t.id === updated.id ? updated : t)); setActiveTask(updated); try { const result = await apiUpdateTask(updated.id, { title: updated.title, description: updated.description, status: updated.status, priority: updated.priority, assignee: updated.assignee, reporter: updated.reporter, dueDate: updated.dueDate, tags: updated.tags, subtasks: updated.subtasks, // Ensure subtasks are sent if API supports it (it usually does via full update or we need to check apiUpdateTask) }); // Verification: if result is successful, update state with server result (which might have new IDs etc) setTasks(prev => prev.map(t => t.id === result.id ? result : t)); if (activeTask?.id === result.id) setActiveTask(result); } catch (err) { console.error('Failed to update task:', err); // We might want to revert here, but for now let's keep the optimistic state to resolve the "useless" UI issue visually } }; const handleNewTask = () => { setAddModalDefaults({}); setShowAddModal(true); }; const handleKanbanAdd = (status: Status) => { setAddModalDefaults({ status }); setShowAddModal(true); }; const handleToggleDone = async (taskId: string) => { const task = tasks.find(t => t.id === taskId); if (!task) return; const newStatus = task.status === 'done' ? 'todo' : 'done'; try { const result = await apiUpdateTask(taskId, { status: newStatus }); await apiAddActivity(taskId, `🔄 ${currentUser.name} changed status to ${newStatus}`); setTasks(prev => prev.map(t => t.id === taskId ? result : t)); } catch (err) { console.error('Failed to toggle done:', err); } }; const handleMoveTask = async (taskId: string, newStatus: Status) => { const task = tasks.find(t => t.id === taskId); if (!task || task.status === newStatus) return; // Optimistic update setTasks(prev => prev.map(t => t.id === taskId ? { ...t, status: newStatus } : t)); try { const result = await apiUpdateTask(taskId, { status: newStatus }); await apiAddActivity(taskId, `🔄 ${currentUser.name} moved task to ${STATUS_LABELS[newStatus]}`); setTasks(prev => prev.map(t => t.id === taskId ? result : t)); } catch (err) { console.error('Failed to move task:', err); // Revert on failure setTasks(prev => prev.map(t => t.id === taskId ? task : t)); } }; const handleAddDep = async (taskId: string, dep: { dependsOnUserId: string; description: string }) => { try { const newDep = await apiAddDependency(taskId, dep); setTasks(prev => prev.map(t => t.id === taskId ? { ...t, dependencies: [...(t.dependencies || []), newDep] } : t)); if (activeTask?.id === taskId) setActiveTask(prev => prev ? { ...prev, dependencies: [...(prev.dependencies || []), newDep] } : prev); } catch (err) { console.error('Failed to add dependency:', err); } }; const handleToggleDep = async (taskId: string, depId: string, resolved: boolean) => { try { await apiToggleDependency(taskId, depId, resolved); const updateDeps = (deps: any[]) => deps.map((d: any) => d.id === depId ? { ...d, resolved } : d); setTasks(prev => prev.map(t => t.id === taskId ? { ...t, dependencies: updateDeps(t.dependencies || []) } : t)); if (activeTask?.id === taskId) setActiveTask(prev => prev ? { ...prev, dependencies: updateDeps(prev.dependencies || []) } : prev); } catch (err) { console.error('Failed to toggle dependency:', err); } }; const handleRemoveDep = async (taskId: string, depId: string) => { try { await apiRemoveDependency(taskId, depId); setTasks(prev => prev.map(t => t.id === taskId ? { ...t, dependencies: (t.dependencies || []).filter((d: any) => d.id !== depId) } : t)); if (activeTask?.id === taskId) setActiveTask(prev => prev ? { ...prev, dependencies: (prev.dependencies || []).filter((d: any) => d.id !== depId) } : prev); } catch (err) { console.error('Failed to remove dependency:', err); } }; const handleAddUser = async (data: { name: string; email: string; password: string; role: string; dept: string }) => { const newUser = await apiCreateUser(data); setUsers(prev => [...prev, newUser]); }; const handleDeleteUser = async (id: string) => { await apiDeleteUser(id); setUsers(prev => prev.filter(u => u.id !== id)); // Unassign tasks locally too setTasks(prev => prev.map(t => t.assignee === id ? { ...t, assignee: '' } : t).map(t => t.reporter === id ? { ...t, reporter: '' } : t)); }; const displayPage = VIEW_PAGES.includes(activePage) ? activeView : activePage; const filteredMyTasks = tasks.filter(t => t.assignee === currentUser.id); const pageTitle = PAGE_TITLES[displayPage] || 'Calendar'; if (loading) { return (

Loading...

); } return (
setSidebarOpen(true)} users={users} />
{ localStorage.removeItem('currentUser'); setCurrentUser(null); setActivePage('calendar'); setActiveView('calendar'); setSidebarOpen(false); }} isOpen={sidebarOpen} onClose={() => setSidebarOpen(false)} users={users} />
{displayPage === 'calendar' && ( )} {displayPage === 'kanban' && ( )} {displayPage === 'list' && ( )} {displayPage === 'dashboard' && } {displayPage === 'mytasks' && ( )} {displayPage === 'teamtasks' && } {displayPage === 'reports' && } {displayPage === 'members' && }
{VIEW_PAGES.includes(activePage) && ( )} {activeTask && setActiveTask(null)} onUpdate={handleUpdateTask} onAddDependency={handleAddDep} onToggleDependency={handleToggleDep} onRemoveDependency={handleRemoveDep} users={users} />} {showAddModal && setShowAddModal(false)} onAdd={handleAddTask} defaultDate={addModalDefaults.date} defaultStatus={addModalDefaults.status} users={users} currentUser={currentUser} />} {quickAddDay && (
setQuickAddDay(null)}>
e.stopPropagation()}> { setAddModalDefaults({ date: quickAddDay.date }); setShowAddModal(true); setQuickAddDay(null); }} onClose={() => setQuickAddDay(null)} users={users} />
)}
); }