Create a frontend wireframe. (#7)
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>
This commit is contained in:
parent
7d2b7fc90c
commit
d60d834f38
27 changed files with 3114 additions and 977 deletions
114
frontend/app/hooks/useTask.ts
Normal file
114
frontend/app/hooks/useTask.ts
Normal file
|
|
@ -0,0 +1,114 @@
|
|||
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,
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue