captains-log/frontend/app/hooks/useTask.ts
2025-09-22 20:40:08 -07:00

114 lines
2.9 KiB
TypeScript

import { useState, useCallback } from 'react'
import type { Task, UpdateTaskRequest, ApiError } from '~/types/task'
import { apiClient } from '~/services/api'
interface UseTaskState {
task: Task | null
loading: boolean
error: string | null
}
interface UseTaskActions {
getTask: (id: string) => Promise<void>
updateTask: (id: string, data: UpdateTaskRequest) => Promise<Task | null>
deleteTask: (id: string) => Promise<void>
clearError: () => void
}
export function useTask(): UseTaskState & UseTaskActions {
const [state, setState] = useState<UseTaskState>({
task: null,
loading: false,
error: null,
})
const clearError = useCallback(() => {
setState(prev => ({ ...prev, error: null }))
}, [])
const getTask = useCallback(async (id: string) => {
setState(prev => ({ ...prev, loading: true, error: null }))
try {
const task = await apiClient.getTask(id)
setState(prev => ({ ...prev, task, loading: false }))
} catch (error) {
const apiError = error as ApiError
setState(prev => ({
...prev,
loading: false,
error: apiError.message,
}))
}
}, [])
const updateTask = useCallback(
async (id: string, data: UpdateTaskRequest): Promise<Task | null> => {
setState(prev => ({ ...prev, loading: true, error: null }))
try {
// Optimistic update
if (state.task && state.task.id === id) {
const optimisticTask: Task = {
...state.task,
...data,
updated_at: new Date().toISOString(),
}
setState(prev => ({ ...prev, task: optimisticTask }))
}
const updatedTask = await apiClient.updateTask(id, data)
setState(prev => ({ ...prev, task: updatedTask, loading: false }))
return updatedTask
} catch (error) {
const apiError = error as ApiError
setState(prev => ({
...prev,
loading: false,
error: apiError.message,
}))
// Revert optimistic update on error
if (state.task && state.task.id === id) {
try {
const originalTask = await apiClient.getTask(id)
setState(prev => ({ ...prev, task: originalTask }))
} catch {
// If we can't revert, just keep the optimistic state
}
}
return null
}
},
[state.task]
)
const deleteTask = useCallback(async (id: string) => {
setState(prev => ({ ...prev, loading: true, error: null }))
try {
await apiClient.deleteTask(id)
setState(prev => ({
...prev,
task: prev.task?.id === id ? null : prev.task,
loading: false,
}))
} catch (error) {
const apiError = error as ApiError
setState(prev => ({
...prev,
loading: false,
error: apiError.message,
}))
}
}, [])
return {
...state,
getTask,
updateTask,
deleteTask,
clearError,
}
}