Update app to fetch and display tasks and labels from the server

Introduce useEffect to fetch tasks and labels on component mount, normalize date data, and implement task editing functionality in the modal.

Replit-Commit-Author: Agent
Replit-Commit-Session-Id: ceced2fc-aa46-458d-ba87-ddd4b7bb1518
Replit-Commit-Checkpoint-Type: intermediate_checkpoint
Replit-Commit-Screenshot-Url: https://storage.googleapis.com/screenshot-production-us-central1/659922a9-0087-461c-90dd-6d9a58b81d4d/ceced2fc-aa46-458d-ba87-ddd4b7bb1518/XZ04DPQ
This commit is contained in:
paul-nothaft
2025-10-23 11:29:42 +00:00
parent bcb43a0ae7
commit 8986785d9a
3 changed files with 255 additions and 25 deletions
+99 -22
View File
@@ -1,4 +1,4 @@
import { useState } from 'react';
import { useState, useEffect } from 'react';
import { queryClient } from "./lib/queryClient";
import { QueryClientProvider } from "@tanstack/react-query";
import { Toaster } from "@/components/ui/toaster";
@@ -15,7 +15,7 @@ import CalendarView from './components/CalendarView';
import KanbanBoard from './components/KanbanBoard';
import ProjectTemplate from './components/ProjectTemplate';
import ThemeToggle from './components/ThemeToggle';
import { Task } from '@shared/schema';
import { Task, Label } from '@shared/schema';
import { addDays, subDays } from 'date-fns';
import { useTimer } from './hooks/useTimer';
@@ -91,33 +91,109 @@ const initialTasks: Task[] = [
function App() {
const [currentTab, setCurrentTab] = useState('tasks');
const [tasks, setTasks] = useState<Task[]>(initialTasks);
const [labels, setLabels] = useState<Label[]>([]);
const [isCreateModalOpen, setIsCreateModalOpen] = useState(false);
const [isTaskDetailsOpen, setIsTaskDetailsOpen] = useState(false);
const [selectedTask, setSelectedTask] = useState<Task | null>(null);
const handleCreateTask = (newTask: Partial<Task>) => {
const task: Task = {
id: Date.now().toString(),
title: newTask.title || '',
description: newTask.description || null,
status: 'todo',
priority: newTask.priority || 'medium',
dueDate: newTask.dueDate || null,
timeTracked: 0,
isTracking: false,
projectId: newTask.projectId || null,
notes: newTask.notes || null,
labelId: newTask.labelId || null
// Fetch tasks and labels from server on mount
useEffect(() => {
const fetchData = async () => {
try {
// Fetch tasks
const tasksResponse = await fetch('/api/tasks');
if (tasksResponse.ok) {
const serverTasks: Task[] = await tasksResponse.json();
// Normalize server tasks - convert date strings to Date objects
const normalizedTasks = serverTasks.map(task => ({
...task,
dueDate: task.dueDate ? new Date(task.dueDate) : null
}));
// Merge server tasks with initial tasks (avoiding duplicates)
const existingIds = new Set(normalizedTasks.map(t => t.id));
const uniqueInitialTasks = initialTasks.filter(t => !existingIds.has(t.id));
setTasks([...normalizedTasks, ...uniqueInitialTasks]);
}
// Fetch labels
const labelsResponse = await fetch('/api/labels');
if (labelsResponse.ok) {
const serverLabels: Label[] = await labelsResponse.json();
setLabels(serverLabels);
}
} catch (error) {
console.error('Error fetching data:', error);
// Keep initial data on error
}
};
setTasks(prev => [...prev, task]);
console.log('Task created:', task);
fetchData();
}, []);
const handleCreateTask = async (newTask: Partial<Task>) => {
try {
const response = await fetch('/api/tasks', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
title: newTask.title || '',
description: newTask.description || null,
status: 'todo',
priority: newTask.priority || 'medium',
dueDate: newTask.dueDate || null,
timeTracked: 0,
isTracking: false,
projectId: newTask.projectId || null,
notes: newTask.notes || null,
labelId: newTask.labelId || null
})
});
if (!response.ok) {
throw new Error('Failed to create task');
}
const createdTask: Task = await response.json();
// Normalize the created task's date
const normalizedTask = {
...createdTask,
dueDate: createdTask.dueDate ? new Date(createdTask.dueDate) : null
};
setTasks(prev => [...prev, normalizedTask]);
console.log('Task created:', normalizedTask);
} catch (error) {
console.error('Error creating task:', error);
}
};
const handleTaskUpdate = (taskId: string, updates: Partial<Task>) => {
setTasks(prev => prev.map(task =>
task.id === taskId ? { ...task, ...updates } : task
));
console.log('Task updated:', taskId, updates);
const handleTaskUpdate = async (taskId: string, updates: Partial<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();
// Normalize the updated task's date
const normalizedTask = {
...updatedTask,
dueDate: updatedTask.dueDate ? new Date(updatedTask.dueDate) : null
};
setTasks(prev => prev.map(task =>
task.id === taskId ? normalizedTask : task
));
console.log('Task updated:', taskId, updates);
} catch (error) {
console.error('Error updating task:', error);
// Optimistic update fallback
setTasks(prev => prev.map(task =>
task.id === taskId ? { ...task, ...updates } : task
));
}
};
// Initialize the timer hook after handleTaskUpdate is defined
@@ -270,6 +346,7 @@ function App() {
onClose={handleTaskDetailsClose}
task={selectedTask}
onSave={handleTaskDetailsSave}
labels={labels}
/>
</div>