Allow updating task status on the task list pagel #20

Merged
drew merged 1 commit from update-task-status into main 2025-10-28 05:01:47 +00:00
2 changed files with 171 additions and 7 deletions
Showing only changes of commit a41611a86f - Show all commits

View file

@ -6,7 +6,6 @@ import {
InputLabel,
Select,
MenuItem,
Chip,
Stack,
Paper,
Table,
@ -46,7 +45,7 @@ interface TaskListProps {
}
export function TaskList({ className, initialTasks }: TaskListProps) {
const { tasks, loading, error, deleteTask } = useTasks({
const { tasks, loading, error, updateTask, deleteTask } = useTasks({
autoFetch: !initialTasks,
initialData: initialTasks,
})
@ -217,6 +216,13 @@ export function TaskList({ className, initialTasks }: TaskListProps) {
}
}
const handleStatusChange = async (taskId: string, newStatus: TaskStatus) => {
const result = await updateTask(taskId, { status: newStatus })
if (!result) {
console.error('Failed to update task status')
}
}
const isAllSelected =
filteredAndSortedTasks.length > 0 &&
filteredAndSortedTasks.every(task => selectedTaskIds.has(task.id))
@ -447,12 +453,139 @@ export function TaskList({ className, initialTasks }: TaskListProps) {
</TableCell>
)}
<TableCell>
<Chip
label={task.status}
color={getStatusColor(task.status)}
<Select
value={task.status}
onChange={e =>
handleStatusChange(
task.id,
e.target.value as TaskStatus
)
}
size="small"
variant="outlined"
/>
MenuProps={{
PaperProps: {
sx: {
padding: '4px',
minWidth: '100px',
'& .MuiList-root': {
padding: 0,
},
},
},
}}
sx={{
minWidth: 90,
height: '24px',
borderRadius: '16px',
backgroundColor:
getStatusColor(task.status) === 'primary'
? 'primary.main'
: getStatusColor(task.status) === 'success'
? 'success.main'
: 'action.disabled',
color:
getStatusColor(task.status) === 'default'
? 'text.primary'
: 'white',
fontSize: '0.75rem',
'& .MuiOutlinedInput-notchedOutline': {
border: 'none',
},
'&:hover .MuiOutlinedInput-notchedOutline': {
border: 'none',
},
'&.Mui-focused .MuiOutlinedInput-notchedOutline': {
border: 'none',
},
'& .MuiSelect-select': {
padding: '2px 12px 2px 12px',
paddingRight: '24px !important',
display: 'flex',
alignItems: 'center',
height: '20px',
lineHeight: '20px',
},
'& .MuiSelect-icon': {
right: '4px',
color:
getStatusColor(task.status) === 'default'
? 'text.primary'
: 'white',
fontSize: '1rem',
},
}}
>
<MenuItem
value={TaskStatus.Todo}
sx={{
backgroundColor: 'primary.main',
color: 'white',
margin: '2px 4px',
borderRadius: '12px',
fontSize: '0.75rem',
minHeight: '24px',
padding: '2px 8px',
'&:hover': {
backgroundColor: 'primary.dark',
},
'&.Mui-selected': {
backgroundColor: 'primary.main',
'&:hover': {
backgroundColor: 'primary.dark',
},
},
}}
>
Todo
</MenuItem>
<MenuItem
value={TaskStatus.Done}
sx={{
backgroundColor: 'success.main',
color: 'white',
margin: '2px 4px',
borderRadius: '12px',
fontSize: '0.75rem',
minHeight: '24px',
padding: '2px 8px',
'&:hover': {
backgroundColor: 'success.dark',
},
'&.Mui-selected': {
backgroundColor: 'success.main',
'&:hover': {
backgroundColor: 'success.dark',
},
},
}}
>
Done
</MenuItem>
<MenuItem
value={TaskStatus.Backlog}
sx={{
backgroundColor: 'action.disabled',
color: 'text.primary',
margin: '2px 4px',
borderRadius: '12px',
fontSize: '0.75rem',
minHeight: '24px',
padding: '2px 8px',
'&:hover': {
backgroundColor: 'action.hover',
},
'&.Mui-selected': {
backgroundColor: 'action.disabled',
'&:hover': {
backgroundColor: 'action.hover',
},
},
}}
>
Backlog
</MenuItem>
</Select>
</TableCell>
<TableCell>
<Tooltip

View file

@ -1,5 +1,10 @@
import { useState, useCallback, useEffect } from 'react'
import type { Task, CreateTaskRequest, ApiError } from '~/types/task'
import type {
Task,
CreateTaskRequest,
UpdateTaskRequest,
ApiError,
} from '~/types/task'
import { TaskStatus } from '~/types/task'
import { apiClient } from '~/services/api'
@ -13,6 +18,7 @@ interface UseTasksState {
interface UseTasksActions {
fetchTasks: () => Promise<void>
createTask: (data: CreateTaskRequest) => Promise<Task | null>
updateTask: (id: string, data: UpdateTaskRequest) => Promise<Task | null>
deleteTask: (id: string) => Promise<boolean>
refreshTasks: () => Promise<void>
clearError: () => void
@ -91,6 +97,30 @@ export function useTasks(
[]
)
const updateTask = useCallback(
async (id: string, data: UpdateTaskRequest): Promise<Task | null> => {
try {
const updatedTask = await apiClient.updateTask(id, data)
// Update the task in the local state immediately
setState(prev => ({
...prev,
tasks: prev.tasks.map(task => (task.id === id ? updatedTask : task)),
}))
return updatedTask
} catch (error) {
const apiError = error as ApiError
setState(prev => ({
...prev,
error: apiError.message,
}))
return null
}
},
[]
)
const deleteTask = useCallback(async (id: string): Promise<boolean> => {
try {
await apiClient.deleteTask(id)
@ -176,6 +206,7 @@ export function useTasks(
...state,
fetchTasks,
createTask,
updateTask,
deleteTask,
refreshTasks,
clearError,