9.5 KiB
Shot Table AJAX Task Status Update Implementation
Overview
This document describes the implementation of AJAX-based task status editing in the shot table view. Previously, task status columns displayed read-only badges. Now they use editable dropdowns that update task status via API calls without page refresh.
Changes Made
1. Created Shot-Specific EditableTaskStatus Component
File: frontend/src/components/shot/EditableTaskStatus.vue
Created a new component specifically for editing shot task statuses, similar to the existing asset version but adapted for shots:
Features:
- Dropdown select with status options
- Loading indicator during API calls
- Automatic task creation if task doesn't exist
- Status update via AJAX
- Error handling with status revert
- Visual feedback during updates
Props:
shotId: number- The shot IDtaskType: string- The task type (layout, animation, etc.)status: TaskStatus- Current task statustaskId?: number | null- Existing task ID (if task exists)
Events:
status-updated- Emitted after successful status update
API Calls:
taskService.createShotTask()- Creates task if it doesn't existtaskService.updateTaskStatus()- Updates the task status
2. Added createShotTask Method to Task Service
File: frontend/src/services/task.ts
Added new method to create shot tasks:
async createShotTask(shotId: number, taskType: string): Promise<TaskStatusInfo> {
const response = await apiClient.post(`/shots/${shotId}/tasks?task_type=${taskType}`)
return response.data
}
This mirrors the existing createAssetTask method but for shots.
3. Updated Shot Interface
File: frontend/src/services/shot.ts
Added task_ids field to Shot interface for easier access to task IDs:
export interface Shot {
// ... existing fields ...
task_ids?: Record<string, number>
}
This allows the editable component to know if a task already exists and its ID.
4. Updated Shot Table Columns
File: frontend/src/components/shot/columns.ts
Changes:
- Imported
EditableTaskStatuscomponent - Added
onTaskStatusUpdatedcallback toShotColumnMetainterface - Updated task status column cells to use
EditableTaskStatusinstead ofTaskStatusBadge
Column Cell Implementation:
cell: ({ row }) => {
const shot = row.original
const status = shot.task_status?.[taskType] || TaskStatus.NOT_STARTED
const taskId = shot.task_ids?.[taskType]
return h(EditableTaskStatus, {
shotId: shot.id,
taskType,
status,
taskId,
onStatusUpdated: (shotId: number, taskType: string, newStatus: TaskStatus) => {
meta.onTaskStatusUpdated(shotId, taskType, newStatus)
},
})
}
5. Updated ShotBrowser Component
File: frontend/src/components/shot/ShotBrowser.vue
Changes:
- Imported
TaskStatusenum - Added
handleTaskStatusUpdatedmethod - Passed callback to column meta
Handler Implementation:
const handleTaskStatusUpdated = (shotId: number, taskType: string, newStatus: TaskStatus) => {
// Update local state instead of reloading all shots (optimistic update)
const shot = shots.value.find(s => s.id === shotId)
if (shot) {
if (!shot.task_status) {
shot.task_status = {}
}
shot.task_status[taskType] = newStatus
}
// Show success toast
toast({
title: 'Task status updated',
description: `${taskType} status updated successfully`,
})
}
Key Improvement: Uses optimistic local state update instead of reloading all shots from the server, matching the asset table behavior for better performance.
Column Meta Update:
const shotColumns = computed(() => {
const meta: ShotColumnMeta = {
episodes: episodes.value,
onEdit: editShot,
onDelete: deleteShot,
onViewTasks: selectShot,
onTaskStatusUpdated: handleTaskStatusUpdated, // Added
}
return createShotColumns(allTaskTypes.value, meta)
})
User Experience Flow
Editing Task Status
- User clicks on a task status cell in the shot table
- Dropdown opens showing all available statuses
- User selects a new status
- Component shows loading indicator
- If task doesn't exist:
- API call creates the task
- Task ID is returned
- API call updates the task status
- Success:
- Shot list refreshes with new status
- Toast notification appears
- Loading indicator disappears
- Error:
- Status reverts to original
- Error is logged to console
- Parent component refreshes data
Visual Feedback
- Loading: Spinner overlay on the dropdown
- Success: Toast notification + table refresh
- Error: Silent revert (status returns to original)
Backend Implementation
Added Endpoint: Create Shot Task
File: backend/routers/shots.py
Added new endpoint after get_shot:
@router.post("/{shot_id}/tasks", response_model=TaskStatusInfo, status_code=status.HTTP_201_CREATED)
async def create_shot_task(
shot_id: int,
task_type: str,
db: Session = Depends(get_db),
current_user: User = Depends(require_coordinator_or_admin)
):
"""Create a new task for a shot"""
# Validates shot exists
# Checks episode access permissions
# Returns existing task if already exists (idempotent)
# Creates new task with default values
# Returns TaskStatusInfo with task_id
Key Features:
- Idempotent: Returns existing task if already created
- Permission check: Requires coordinator or admin role
- Access validation: Checks episode access via shot
- Auto-naming: Creates task name as "{shot_name} - {task_type}"
API Endpoints Used
Create Shot Task
POST /shots/{shot_id}/tasks?task_type={task_type}
Response (201 Created or 200 OK if exists):
{
"task_type": "animation",
"status": "not_started",
"task_id": 123,
"assigned_user_id": null
}
Update Task Status
PUT /tasks/{task_id}/status
Request Body:
{
"status": "in_progress"
}
Response: Updated task object
Get Shots (with task status)
GET /shots/?episode_id={episode_id}
Response: Array of shots with task_status and task_ids populated
Benefits
- No Page Refresh: Status updates happen instantly via AJAX
- Automatic Task Creation: Tasks are created on-demand when status is first changed
- Visual Feedback: Loading indicators and toast notifications
- Error Handling: Graceful error handling with status revert
- Consistent UX: Matches the asset table behavior
- Performance: Only affected shot data is refreshed
Technical Details
Task Creation Flow
When a user changes status for a task that doesn't exist:
- Component checks if
taskIdprop is provided - If not, calls
createShotTask(shotId, taskType) - Backend creates task with default values
- Returns task ID
- Component then calls
updateTaskStatus(taskId, newStatus) - Status is updated
- Parent refreshes to show new data
Status Update Flow
When a user changes status for an existing task:
- Component has
taskIdfrom props - Directly calls
updateTaskStatus(taskId, newStatus) - Status is updated
- Parent refreshes to show new data
Data Refresh Strategy
After status update:
- Optimistic Update: Local state is updated immediately
- Shot's
task_statusobject is modified directly - No server reload required
- Matches asset table behavior for consistent UX
- Much faster than reloading all shots
Comparison with Asset Table
The shot table implementation mirrors the asset table:
| Feature | Asset Table | Shot Table |
|---|---|---|
| Component | EditableTaskStatus.vue (asset) |
EditableTaskStatus.vue (shot) |
| Create Method | createAssetTask() |
createShotTask() |
| Update Method | updateTaskStatus() |
updateTaskStatus() (same) |
| Column Definition | columns.ts (asset) |
columns.ts (shot) |
| Parent Handler | AssetBrowser |
ShotBrowser |
Future Enhancements
Potential improvements:
- Optimistic Updates: Update UI immediately, revert on error
- Batch Updates: Allow updating multiple tasks at once
- Undo/Redo: Add ability to undo status changes
- Keyboard Shortcuts: Quick status changes via keyboard
- Status History: Track who changed status and when
- Validation: Prevent invalid status transitions
- Permissions: Check user permissions before allowing edits
Testing
To test the implementation:
- Navigate to a project's Shots tab
- Switch to table view
- Find a shot with tasks
- Click on a task status cell
- Select a different status
- Verify:
- Loading indicator appears
- Status updates in table
- Toast notification shows
- No page refresh occurs
- Test with a shot that has no tasks:
- Change status
- Verify task is created
- Verify status is set correctly
Related Files
frontend/src/components/shot/EditableTaskStatus.vue- Editable status componentfrontend/src/components/shot/columns.ts- Column definitionsfrontend/src/components/shot/ShotBrowser.vue- Parent componentfrontend/src/services/task.ts- Task service with API methodsfrontend/src/services/shot.ts- Shot service and typesfrontend/src/components/asset/EditableTaskStatus.vue- Asset version (reference)
Conclusion
The shot table now supports AJAX-based task status editing, providing a seamless user experience without page refreshes. The implementation follows the same pattern as the asset table, ensuring consistency across the application.