114 lines
2.9 KiB
TypeScript
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,
|
|
}
|
|
}
|