captains-log/frontend/app/hooks/useTasks.ts

160 lines
3.7 KiB
TypeScript

import { useState, useCallback, useEffect } from 'react'
import { Task, CreateTaskRequest, TaskStatus, ApiError } 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,
}
}