Add src/components/AppProvider.tsx

This commit is contained in:
2026-05-02 18:03:43 -04:00
parent 2332ec58d5
commit 1d5da2dd8b

View File

@@ -0,0 +1,143 @@
'use client';
import { createContext, useContext, useState, useCallback, type ReactNode, type Dispatch, type SetStateAction } from 'react';
export type ViewType = 'list' | 'kanban' | 'calendar';
export interface Task {
id: string;
projectId: string | null;
title: string;
description: string;
completed: boolean;
priority: 'low' | 'medium' | 'high' | 'urgent';
dueDate: string | null;
status: 'todo' | 'in_progress' | 'done';
parentTaskId: string | null;
recurrenceRule: 'none' | 'daily' | 'weekly' | 'biweekly' | 'monthly' | 'yearly';
recurrenceInterval: number;
nextOccurrence: string | null;
sortOrder: number;
createdAt: string;
updatedAt: string;
}
export interface Project {
id: string;
name: string;
description: string;
color: string;
sortOrder: number;
createdAt: string;
updatedAt: string;
}
export interface AppState {
view: ViewType;
selectedProject: string | null;
selectedTask: Task | null;
searchQuery: string;
sidebarOpen: boolean;
showNewTaskModal: boolean;
showEditTaskModal: boolean;
showNewProjectModal: boolean;
showRecurrenceModal: boolean;
editingTask: Task | null;
filterStatus: string;
filterPriority: string;
filterDueBefore: string;
filterDueAfter: string;
filterCompleted: string;
tasks: Task[];
projects: Project[];
setView: (view: ViewType) => void;
setSelectedProject: (project: string | null) => void;
setSelectedTask: (task: Task | null) => void;
setSearchQuery: (query: string) => void;
setSidebarOpen: (open: boolean) => void;
setShowNewTaskModal: (show: boolean) => void;
setShowEditTaskModal: (show: boolean) => void;
setShowNewProjectModal: (show: boolean) => void;
setShowRecurrenceModal: (show: boolean) => void;
setEditingTask: (task: Task | null) => void;
setFilterStatus: (status: string) => void;
setFilterPriority: (priority: string) => void;
setFilterDueBefore: (date: string) => void;
setFilterDueAfter: (date: string) => void;
setFilterCompleted: (completed: string) => void;
setTasks: Dispatch<SetStateAction<Task[]>>;
setProjects: Dispatch<SetStateAction<Project[]>>;
refreshTasks: () => Promise<void>;
refreshProjects: () => Promise<void>;
}
const AppContext = createContext<AppState | null>(null);
export function useApp() {
const context = useContext(AppContext);
if (!context) throw new Error('useApp must be used within AppProvider');
return context;
}
export default function AppProvider({ children }: { children: ReactNode }) {
const [view, setView] = useState<ViewType>('list');
const [selectedProject, setSelectedProject] = useState<string | null>(null);
const [selectedTask, setSelectedTask] = useState<Task | null>(null);
const [searchQuery, setSearchQuery] = useState('');
const [sidebarOpen, setSidebarOpen] = useState(true);
const [showNewTaskModal, setShowNewTaskModal] = useState(false);
const [showEditTaskModal, setShowEditTaskModal] = useState(false);
const [showNewProjectModal, setShowNewProjectModal] = useState(false);
const [showRecurrenceModal, setShowRecurrenceModal] = useState(false);
const [editingTask, setEditingTask] = useState<Task | null>(null);
const [filterStatus, setFilterStatus] = useState('');
const [filterPriority, setFilterPriority] = useState('');
const [filterDueBefore, setFilterDueBefore] = useState('');
const [filterDueAfter, setFilterDueAfter] = useState('');
const [filterCompleted, setFilterCompleted] = useState('');
const [tasks, setTasks] = useState<Task[]>([]);
const [projects, setProjects] = useState<Project[]>([]);
const refreshTasks = useCallback(async () => {
try {
const params = new URLSearchParams();
if (selectedProject) params.set('project', selectedProject);
if (searchQuery) params.set('search', searchQuery);
if (filterStatus) params.set('status', filterStatus);
if (filterPriority) params.set('priority', filterPriority);
if (filterDueBefore) params.set('due_before', filterDueBefore);
if (filterDueAfter) params.set('due_after', filterDueAfter);
if (filterCompleted) params.set('completed', filterCompleted);
const res = await fetch(`/api/tasks?${params}`);
const data = await res.json();
setTasks(data);
} catch (error) {
console.error('Failed to refresh tasks:', error);
}
}, [selectedProject, searchQuery, filterStatus, filterPriority, filterDueBefore, filterDueAfter, filterCompleted]);
const refreshProjects = useCallback(async () => {
try {
const res = await fetch('/api/projects');
const data = await res.json();
setProjects(data);
} catch (error) {
console.error('Failed to refresh projects:', error);
}
}, []);
const value: AppState = {
view, selectedProject, selectedTask, searchQuery, sidebarOpen,
showNewTaskModal, showEditTaskModal, showNewProjectModal, showRecurrenceModal,
editingTask, filterStatus, filterPriority, filterDueBefore, filterDueAfter, filterCompleted,
tasks, projects,
setView, setSelectedProject, setSelectedTask, setSearchQuery, setSidebarOpen,
setShowNewTaskModal, setShowEditTaskModal, setShowNewProjectModal, setShowRecurrenceModal,
setEditingTask, setFilterStatus, setFilterPriority, setFilterDueBefore, setFilterDueAfter,
setFilterCompleted, setTasks, setProjects,
refreshTasks, refreshProjects,
};
return <AppContext.Provider value={value}>{children}</AppContext.Provider>;
}