Sets up API methods and types. Sets up a colorscheme. Sets up a homepage. Removes tailwind in favor of mui for now. Reviewed-on: #7 Co-authored-by: Drew Galbraith <drew@tiramisu.one> Co-committed-by: Drew Galbraith <drew@tiramisu.one>
161 lines
3.7 KiB
TypeScript
161 lines
3.7 KiB
TypeScript
import { useState, useCallback, useEffect } from 'react'
|
|
import type { Task, CreateTaskRequest, ApiError } from '~/types/task'
|
|
import { TaskStatus } from '~/types/task'
|
|
import { apiClient } from '~/services/api'
|
|
|
|
interface UseTasksState {
|
|
tasks: Task[]
|
|
loading: boolean
|
|
error: string | null
|
|
lastFetch: Date | null
|
|
}
|
|
|
|
interface UseTasksActions {
|
|
fetchTasks: () => Promise<void>
|
|
createTask: (data: CreateTaskRequest) => Promise<Task | null>
|
|
refreshTasks: () => Promise<void>
|
|
clearError: () => void
|
|
getTaskById: (id: string) => Task | undefined
|
|
filterTasksByStatus: (status: TaskStatus) => Task[]
|
|
}
|
|
|
|
interface UseTasksOptions {
|
|
autoFetch?: boolean
|
|
refreshInterval?: number
|
|
}
|
|
|
|
export function useTasks(
|
|
options: UseTasksOptions = {}
|
|
): UseTasksState & UseTasksActions {
|
|
const { autoFetch = true, refreshInterval } = options
|
|
|
|
const [state, setState] = useState<UseTasksState>({
|
|
tasks: [],
|
|
loading: false,
|
|
error: null,
|
|
lastFetch: null,
|
|
})
|
|
|
|
const clearError = useCallback(() => {
|
|
setState(prev => ({ ...prev, error: null }))
|
|
}, [])
|
|
|
|
const fetchTasks = useCallback(async () => {
|
|
setState(prev => ({ ...prev, loading: true, error: null }))
|
|
|
|
try {
|
|
const tasks = await apiClient.listTasks()
|
|
setState(prev => ({
|
|
...prev,
|
|
tasks,
|
|
loading: false,
|
|
lastFetch: new Date(),
|
|
}))
|
|
} catch (error) {
|
|
const apiError = error as ApiError
|
|
setState(prev => ({
|
|
...prev,
|
|
loading: false,
|
|
error: apiError.message,
|
|
}))
|
|
}
|
|
}, [])
|
|
|
|
const createTask = useCallback(
|
|
async (data: CreateTaskRequest): Promise<Task | null> => {
|
|
setState(prev => ({ ...prev, loading: true, error: null }))
|
|
|
|
try {
|
|
const newTask = await apiClient.createTask(data)
|
|
|
|
// Add the new task to the beginning of the list (most recent first)
|
|
setState(prev => ({
|
|
...prev,
|
|
tasks: [newTask, ...prev.tasks],
|
|
loading: false,
|
|
}))
|
|
|
|
return newTask
|
|
} catch (error) {
|
|
const apiError = error as ApiError
|
|
setState(prev => ({
|
|
...prev,
|
|
loading: false,
|
|
error: apiError.message,
|
|
}))
|
|
return null
|
|
}
|
|
},
|
|
[]
|
|
)
|
|
|
|
const refreshTasks = useCallback(async () => {
|
|
// Force refresh without showing loading state if tasks already exist
|
|
const showLoading = state.tasks.length === 0
|
|
|
|
if (showLoading) {
|
|
setState(prev => ({ ...prev, loading: true, error: null }))
|
|
} else {
|
|
setState(prev => ({ ...prev, error: null }))
|
|
}
|
|
|
|
try {
|
|
const tasks = await apiClient.listTasks()
|
|
setState(prev => ({
|
|
...prev,
|
|
tasks,
|
|
loading: false,
|
|
lastFetch: new Date(),
|
|
}))
|
|
} catch (error) {
|
|
const apiError = error as ApiError
|
|
setState(prev => ({
|
|
...prev,
|
|
loading: false,
|
|
error: apiError.message,
|
|
}))
|
|
}
|
|
}, [state.tasks])
|
|
|
|
const getTaskById = useCallback(
|
|
(id: string): Task | undefined => {
|
|
return state.tasks.find(task => task.id === id)
|
|
},
|
|
[state.tasks]
|
|
)
|
|
|
|
const filterTasksByStatus = useCallback(
|
|
(status: TaskStatus): Task[] => {
|
|
return state.tasks.filter(task => task.status === status)
|
|
},
|
|
[state.tasks]
|
|
)
|
|
|
|
// Auto-fetch tasks on mount
|
|
useEffect(() => {
|
|
if (autoFetch) {
|
|
fetchTasks()
|
|
}
|
|
}, [autoFetch, fetchTasks])
|
|
|
|
// Set up refresh interval if specified
|
|
useEffect(() => {
|
|
if (!refreshInterval) return
|
|
|
|
const interval = setInterval(() => {
|
|
refreshTasks()
|
|
}, refreshInterval)
|
|
|
|
return () => clearInterval(interval)
|
|
}, [refreshInterval, refreshTasks])
|
|
|
|
return {
|
|
...state,
|
|
fetchTasks,
|
|
createTask,
|
|
refreshTasks,
|
|
clearError,
|
|
getTaskById,
|
|
filterTasksByStatus,
|
|
}
|
|
}
|