Implement complete nested todo tree web app MVP
This commit implements a fully functional self-hosted task decomposition engine with: Backend (FastAPI + SQLite): - RESTful API with full CRUD operations for projects and tasks - Arbitrary-depth hierarchical task structure using self-referencing parent_task_id - JSON import endpoint for seeding projects from LLM-generated breakdowns - SQLAlchemy models with proper relationships and cascade deletes - Status tracking (backlog, in_progress, blocked, done) - Auto-generated OpenAPI documentation Frontend (React + Vite + Tailwind): - Dark cyberpunk theme with orange accents - Project list page with create/import/delete functionality - Dual view modes: * Tree View: Collapsible hierarchical display with inline editing * Kanban Board: Drag-and-drop status management - Real-time CRUD operations for tasks and subtasks - JSON import modal with validation - Responsive design optimized for desktop Infrastructure: - Docker setup with multi-stage builds - docker-compose for orchestration - Nginx reverse proxy for production frontend - Named volume for SQLite persistence - CORS configuration for local development Documentation: - Comprehensive README with setup instructions - Example JSON import file demonstrating nested structure - API endpoint documentation - Data model diagrams
This commit is contained in:
58
frontend/src/utils/api.js
Normal file
58
frontend/src/utils/api.js
Normal file
@@ -0,0 +1,58 @@
|
||||
const API_BASE = '/api';
|
||||
|
||||
async function fetchAPI(endpoint, options = {}) {
|
||||
const response = await fetch(`${API_BASE}${endpoint}`, {
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
...options.headers,
|
||||
},
|
||||
...options,
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
const error = await response.json().catch(() => ({ detail: 'Request failed' }));
|
||||
throw new Error(error.detail || `HTTP ${response.status}`);
|
||||
}
|
||||
|
||||
if (response.status === 204) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return response.json();
|
||||
}
|
||||
|
||||
// Projects
|
||||
export const getProjects = () => fetchAPI('/projects');
|
||||
export const getProject = (id) => fetchAPI(`/projects/${id}`);
|
||||
export const createProject = (data) => fetchAPI('/projects', {
|
||||
method: 'POST',
|
||||
body: JSON.stringify(data),
|
||||
});
|
||||
export const updateProject = (id, data) => fetchAPI(`/projects/${id}`, {
|
||||
method: 'PUT',
|
||||
body: JSON.stringify(data),
|
||||
});
|
||||
export const deleteProject = (id) => fetchAPI(`/projects/${id}`, { method: 'DELETE' });
|
||||
|
||||
// Tasks
|
||||
export const getProjectTasks = (projectId) => fetchAPI(`/projects/${projectId}/tasks`);
|
||||
export const getProjectTaskTree = (projectId) => fetchAPI(`/projects/${projectId}/tasks/tree`);
|
||||
export const getTasksByStatus = (projectId, status) =>
|
||||
fetchAPI(`/projects/${projectId}/tasks/by-status/${status}`);
|
||||
|
||||
export const getTask = (id) => fetchAPI(`/tasks/${id}`);
|
||||
export const createTask = (data) => fetchAPI('/tasks', {
|
||||
method: 'POST',
|
||||
body: JSON.stringify(data),
|
||||
});
|
||||
export const updateTask = (id, data) => fetchAPI(`/tasks/${id}`, {
|
||||
method: 'PUT',
|
||||
body: JSON.stringify(data),
|
||||
});
|
||||
export const deleteTask = (id) => fetchAPI(`/tasks/${id}`, { method: 'DELETE' });
|
||||
|
||||
// JSON Import
|
||||
export const importJSON = (data) => fetchAPI('/import-json', {
|
||||
method: 'POST',
|
||||
body: JSON.stringify(data),
|
||||
});
|
||||
Reference in New Issue
Block a user