import { useState, useEffect } from 'react'; import { useTranslation } from 'react-i18next'; import { Toaster } from "@/components/ui/toaster"; import { TooltipProvider } from "@/components/ui/tooltip"; import { Button } from "@/components/ui/button"; import { AlertDialog, AlertDialogAction, AlertDialogCancel, AlertDialogContent, AlertDialogDescription, AlertDialogFooter, AlertDialogHeader, AlertDialogTitle, } from "@/components/ui/alert-dialog"; import { Switch, Route, useLocation } from "wouter"; // ADHD Mode Components import { ADHDModeProvider, useADHDMode, BreakReminderProvider, BreakReminderOverlay, EnergyCheckIn, HyperfocusGuard, EncouragementToast } from '@/components/adhd'; // Components import TaskCreationModal from './components/TaskCreationModal'; import TaskDetailsModal from './components/TaskDetailsModal'; import TasksWithCalendar from './components/TasksWithCalendar'; import FocusMode from './components/FocusMode'; import CalendarView from './components/CalendarView'; import KanbanBoard from './components/KanbanBoard'; import ProjectTemplate from './components/ProjectTemplate'; import WeekListView from './components/WeekListView'; import Settings from './pages/settings'; import AchievementsPage from './pages/AchievementsPage'; import UnscheduledTasksPage from './pages/UnscheduledTasksPage'; import { Task, Label } from '@shared/schema'; import { useTimer } from './hooks/useTimer'; import { Plus } from 'lucide-react'; import { SidebarProvider, SidebarInset, SidebarTrigger } from "@/components/ui/sidebar" import { AppSidebar } from "./components/AppSidebar" import { useNotifications } from './hooks/use-notifications'; import { useToast } from "@/hooks/use-toast"; import { CommandPalette } from './components/CommandPalette'; import PomodoroOverlay from './components/PomodoroOverlay'; import AuthPage from "@/pages/AuthPage"; import LeaderboardPage from "@/pages/LeaderboardPage"; import SetupWizard from "@/pages/SetupWizard"; import AdminUserManagement from "@/pages/AdminUserManagement"; import AdminSettings from "@/pages/AdminSettings"; import NotFound from "@/pages/not-found"; // import { AiChat } from "@/components/AiChat"; import ForgotPasswordPage from "@/pages/ForgotPasswordPage"; import ResetPasswordPage from "@/pages/ResetPasswordPage"; import AiChatPage from "@/pages/AiChatPage"; import NotificationsPage from "@/pages/NotificationsPage"; import FocusRoutinePage from "@/pages/FocusRoutinePage"; import { useRoutineBlocker } from './components/RoutineBlocker'; import { useQuery, useQueryClient } from "@tanstack/react-query"; import { User } from "@shared/schema"; import { Loader2 } from "lucide-react"; import TimeTrackingPage from './pages/TimeTrackingPage'; // ADHD Pages import QuickWinsPage from './pages/QuickWinsPage'; import SingleTaskPage from './pages/SingleTaskPage'; import BodyDoublingPage from './pages/BodyDoublingPage'; import ADHDDashboardPage from './pages/ADHDDashboardPage'; // Inner App component that uses ADHD hooks function AppContent() { const { t } = useTranslation(); const { toast } = useToast(); const [, setLocation] = useLocation(); const isBlocked = useRoutineBlocker(); const { isEnabled: adhdEnabled, settings: adhdSettings } = useADHDMode(); const [isCreateModalOpen, setIsCreateModalOpen] = useState(false); const [isTaskDetailsOpen, setIsTaskDetailsOpen] = useState(false); const [selectedTask, setSelectedTask] = useState(null); const [deleteTaskId, setDeleteTaskId] = useState(null); const [showPomodoro, setShowPomodoro] = useState(false); const [activePomodoroTaskId, setActivePomodoroTaskId] = useState(null); // Enable global notifications polling useNotifications({ poll: true }); const queryClient = useQueryClient(); const { data: user, isLoading: isLoadingUser } = useQuery({ queryKey: ["/api/user"], retry: false, }); const { data: setupStatus } = useQuery<{ isSetup: boolean }>({ queryKey: ['/api/setup/status'], }); // Fetch tasks and labels using React Query const { data: tasksData } = useQuery({ queryKey: ['/api/tasks'], enabled: !!user, }); const tasks = (tasksData ?? []).map(task => ({ ...task, dueDate: task.dueDate ? new Date(task.dueDate) : null })); const { data: labelsData } = useQuery({ queryKey: ['/api/labels'], enabled: !!user, }); const labels = labelsData ?? []; const handleCreateTask = async (newTask: Partial) => { try { const response = await fetch('/api/tasks', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ title: newTask.title || '', description: newTask.description || undefined, status: 'todo', priority: newTask.priority || 'medium', dueDate: newTask.dueDate || null, // Explicitly handled as nullable in schema timeTracked: 0, isTracking: false, projectId: newTask.projectId || undefined, notes: newTask.notes || undefined, labelId: newTask.labelId || undefined, energyLevel: newTask.energyLevel || 'medium', estimatedDuration: newTask.estimatedDuration || undefined }) }); if (!response.ok) throw new Error('Failed to create task'); const createdTask: Task = await response.json(); // Update cache optimistically or invalidation queryClient.setQueryData(['/api/tasks'], (old: Task[] = []) => { const normalizedTask = { ...createdTask, dueDate: createdTask.dueDate ? new Date(createdTask.dueDate) : null }; return [...old, normalizedTask]; }); // Invalidate to be sure queryClient.invalidateQueries({ queryKey: ['/api/tasks'] }); toast({ title: t('task.created', 'Task Created'), description: t('task.createdDescription', '"{title}" has been added.', { title: createdTask.title }), }); } catch (error) { console.error('Error creating task:', error); toast({ title: t('error.createTask', 'Error'), description: t('error.createTaskDescription', 'Failed to create task. Please try again.'), variant: "destructive" }); } }; const handleTaskUpdate = async (taskId: string, updates: Partial) => { // Optimistic Update const previousTasks = queryClient.getQueryData(['/api/tasks']); queryClient.setQueryData(['/api/tasks'], (old: Task[] = []) => old.map(task => task.id === taskId ? { ...task, ...updates } : task) ); try { const response = await fetch(`/api/tasks/${taskId}`, { method: 'PATCH', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(updates) }); if (!response.ok) throw new Error('Failed to update task'); const updatedTask: Task = await response.json(); queryClient.setQueryData(['/api/tasks'], (old: Task[] = []) => uniqueTasks(old, updatedTask)); } catch (error) { console.error('Error updating task:', error); // Rollback if (previousTasks) { queryClient.setQueryData(['/api/tasks'], previousTasks); } } }; // Helper to merge updated task into array const uniqueTasks = (tasks: Task[], updated: Task) => { const normalized = { ...updated, dueDate: updated.dueDate ? new Date(updated.dueDate) : null }; return tasks.map(t => t.id === updated.id ? normalized : t); }; const handleCreateFromTemplate = (templateId: string, startDate: Date, projectName: string) => { console.log('Creating project from template:', { templateId, startDate, projectName }); // Assuming this might create tasks, we should invalidate setTimeout(() => queryClient.invalidateQueries({ queryKey: ['/api/tasks'] }), 1000); }; const { startTimer, stopTimer } = useTimer({ tasks, onTaskUpdate: handleTaskUpdate }); const handleStartPomodoro = (taskId: string) => { setActivePomodoroTaskId(taskId); setShowPomodoro(true); startTimer(taskId); }; const handleTaskStatusChange = (taskId: string, newStatus: Task['status']) => { handleTaskUpdate(taskId, { status: newStatus }); }; const handleTaskDrop = (taskId: string, newDate: Date) => { handleTaskUpdate(taskId, { dueDate: newDate }); }; const handleTaskClick = (task: Task) => { setSelectedTask(task); setIsTaskDetailsOpen(true); }; const handleTaskDetailsClose = () => { setIsTaskDetailsOpen(false); setSelectedTask(null); }; const handleTaskDetailsSave = (updatedTask: Task) => { handleTaskUpdate(updatedTask.id, updatedTask); }; const handleTaskDelete = async (taskId: string) => { setDeleteTaskId(taskId); }; const confirmTaskDelete = async () => { if (!deleteTaskId) return; const previousTasks = queryClient.getQueryData(['/api/tasks']); const taskToDelete = tasks.find(task => task.id === deleteTaskId); // Optimistic delete queryClient.setQueryData(['/api/tasks'], (old: Task[] = []) => old.filter(task => task.id !== deleteTaskId) ); if (selectedTask?.id === deleteTaskId) { handleTaskDetailsClose(); } setDeleteTaskId(null); try { const response = await fetch(`/api/tasks/${deleteTaskId}`, { method: 'DELETE' }); if (!response.ok && response.status !== 404) throw new Error('Failed to delete task'); queryClient.invalidateQueries({ queryKey: ['/api/tasks'] }); } catch (error) { console.error('Error deleting task:', error); // Rollback if (previousTasks) { queryClient.setQueryData(['/api/tasks'], previousTasks); } } }; if (isLoadingUser) { return (
); } // Setup Redirect if (setupStatus && !setupStatus.isSetup) { return ( } /> ); } if (isBlocked) { return (
{/* Catch all redirects to nothing, the hook handles the push to routine page */}
} />
); } if (!user) { return (
); } return (
{/* Mobile Header with Sidebar Toggle */}
Logo {t('app.title')}
setLocation('/tasks')} /> setLocation('/tasks')} /> {() => { setLocation('/'); return null; }} { queryClient.setQueryData(['/api/tasks'], (old: Task[] = []) => [...old, ...newTasks]); queryClient.invalidateQueries({ queryKey: ['/api/tasks'] }); }} onNavigateToSettings={() => setLocation('/settings')} /> handleTaskStatusChange(taskId, currentStatus === 'done' ? 'todo' : 'done')} onDelete={(id) => setDeleteTaskId(id)} onUpdate={handleTaskUpdate} onSelect={setSelectedTask} /> setLocation('/templates')} /> {/* Admin Route */} {user?.role === 'admin' && ( <> )} {/* ADHD Mode Routes */}
{/* Floating Action Button */}
setLocation(path.startsWith('/') ? path : `/${path}`)} onCreateTask={() => setIsCreateModalOpen(true)} /> setShowPomodoro(false)} taskId={activePomodoroTaskId} taskTitle={tasks.find(t => t.id === activePomodoroTaskId)?.title || 'Quick Focus'} onCompleteTask={(id) => handleTaskUpdate(id, { status: 'done', isTracking: false })} /> {/* ADHD Mode Overlays */} {adhdEnabled && } {adhdEnabled && adhdSettings.hyperfocusProtection && } {adhdEnabled && adhdSettings.positiveMessagingLevel !== 'off' && } setIsCreateModalOpen(false)} onSave={handleCreateTask} /> { const t = tasks.find(x => x.id === id); if (t) setSelectedTask(t); }} /> !open && setDeleteTaskId(null)}> {t('deleteConfirmation.title')} {t('deleteConfirmation.description')} {t('deleteConfirmation.cancel')} {t('deleteConfirmation.confirm')}
); } // Main App wrapper with ADHD providers function App() { return ( ); } export default App;