ludops-todo/apps/web/src/App.tsx

139 lines
3.5 KiB
TypeScript

import { useEffect, useState } from 'react'
import './App.css'
import Timeline from './components/Timeline'
import TodaysTasks from './components/TodaysTasks'
import CreateTaskForm from './components/CreateTaskForm'
import type { Task, NewTaskInput } from './types'
import { isTaskDueToday } from './utils/taskUtils'
const APP_NAME = 'TODO'
type ApiStatus = 'loading' | 'ok' | 'error'
function App() {
const [apiStatus, setApiStatus] = useState<ApiStatus>('loading')
const [tasks, setTasks] = useState<Task[]>([])
const [loading, setLoading] = useState(true)
const [error, setError] = useState<string | null>(null)
// Fetch tasks
const fetchTasks = async () => {
try {
const response = await fetch('/api/tasks')
if (!response.ok) throw new Error('Failed to fetch tasks')
const data = await response.json()
setTasks(data)
} catch (error) {
console.error('Error fetching tasks:', error)
} finally {
setLoading(false)
}
}
useEffect(() => {
// Check API status
fetch('/api/status')
.then((r) => (r.ok ? r.json() : Promise.reject()))
.then(() => setApiStatus('ok'))
.catch(() => setApiStatus('error'))
// Fetch tasks
fetchTasks()
}, [])
const handleCreateTask = async (taskInput: NewTaskInput) => {
setError(null)
try {
const response = await fetch('/api/tasks', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(taskInput),
})
if (!response.ok) {
const errorData = await response.json()
setError(errorData.error || 'Failed to create task')
return
}
await fetchTasks()
} catch (error) {
console.error('Error creating task:', error)
setError('Failed to create task')
}
}
const handleCompleteTask = async (taskId: number) => {
setError(null)
try {
const response = await fetch(`/api/tasks/${taskId}/complete`, {
method: 'PATCH',
})
if (!response.ok) {
setError('Failed to complete task')
return
}
await fetchTasks()
} catch (error) {
console.error('Error completing task:', error)
setError('Failed to complete task')
}
}
const handleRenewTask = async (taskId: number) => {
setError(null)
try {
const response = await fetch(`/api/tasks/${taskId}/renew`, {
method: 'PATCH',
})
if (!response.ok) {
setError('Failed to renew task')
return
}
await fetchTasks()
} catch (error) {
console.error('Error renewing task:', error)
setError('Failed to renew task')
}
}
const todaysTasks = tasks.filter(isTaskDueToday)
return (
<main>
<h1>{APP_NAME}</h1>
<div className={`status status--${apiStatus}`}>
{apiStatus === 'loading' && 'Connecting to API…'}
{apiStatus === 'ok' && '✓ API & database connected'}
{apiStatus === 'error' && '⚠️ Could not reach API'}
</div>
{error && (
<div className="error-message">
{error}
<button onClick={() => setError(null)} className="error-dismiss"></button>
</div>
)}
{loading ? (
<p>Loading tasks...</p>
) : (
<>
<TodaysTasks
tasks={todaysTasks}
onComplete={handleCompleteTask}
onRenew={handleRenewTask}
/>
<CreateTaskForm onCreateTask={handleCreateTask} />
<Timeline tasks={tasks} />
</>
)}
</main>
)
}
export default App