# 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 ID - `taskType: string` - The task type (layout, animation, etc.) - `status: TaskStatus` - Current task status - `taskId?: number | null` - Existing task ID (if task exists) **Events**: - `status-updated` - Emitted after successful status update **API Calls**: 1. `taskService.createShotTask()` - Creates task if it doesn't exist 2. `taskService.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: ```typescript async createShotTask(shotId: number, taskType: string): Promise { 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: ```typescript export interface Shot { // ... existing fields ... task_ids?: Record } ``` 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**: 1. Imported `EditableTaskStatus` component 2. Added `onTaskStatusUpdated` callback to `ShotColumnMeta` interface 3. Updated task status column cells to use `EditableTaskStatus` instead of `TaskStatusBadge` **Column Cell Implementation**: ```typescript 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**: 1. Imported `TaskStatus` enum 2. Added `handleTaskStatusUpdated` method 3. Passed callback to column meta **Handler Implementation**: ```typescript 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**: ```typescript 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 1. User clicks on a task status cell in the shot table 2. Dropdown opens showing all available statuses 3. User selects a new status 4. Component shows loading indicator 5. If task doesn't exist: - API call creates the task - Task ID is returned 6. API call updates the task status 7. Success: - Shot list refreshes with new status - Toast notification appears - Loading indicator disappears 8. 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`: ```python @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): ```json { "task_type": "animation", "status": "not_started", "task_id": 123, "assigned_user_id": null } ``` ### Update Task Status ``` PUT /tasks/{task_id}/status ``` **Request Body**: ```json { "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 1. **No Page Refresh**: Status updates happen instantly via AJAX 2. **Automatic Task Creation**: Tasks are created on-demand when status is first changed 3. **Visual Feedback**: Loading indicators and toast notifications 4. **Error Handling**: Graceful error handling with status revert 5. **Consistent UX**: Matches the asset table behavior 6. **Performance**: Only affected shot data is refreshed ## Technical Details ### Task Creation Flow When a user changes status for a task that doesn't exist: 1. Component checks if `taskId` prop is provided 2. If not, calls `createShotTask(shotId, taskType)` 3. Backend creates task with default values 4. Returns task ID 5. Component then calls `updateTaskStatus(taskId, newStatus)` 6. Status is updated 7. Parent refreshes to show new data ### Status Update Flow When a user changes status for an existing task: 1. Component has `taskId` from props 2. Directly calls `updateTaskStatus(taskId, newStatus)` 3. Status is updated 4. Parent refreshes to show new data ### Data Refresh Strategy After status update: - **Optimistic Update**: Local state is updated immediately - Shot's `task_status` object 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: 1. **Optimistic Updates**: Update UI immediately, revert on error 2. **Batch Updates**: Allow updating multiple tasks at once 3. **Undo/Redo**: Add ability to undo status changes 4. **Keyboard Shortcuts**: Quick status changes via keyboard 5. **Status History**: Track who changed status and when 6. **Validation**: Prevent invalid status transitions 7. **Permissions**: Check user permissions before allowing edits ## Testing To test the implementation: 1. Navigate to a project's Shots tab 2. Switch to table view 3. Find a shot with tasks 4. Click on a task status cell 5. Select a different status 6. Verify: - Loading indicator appears - Status updates in table - Toast notification shows - No page refresh occurs 7. 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 component - `frontend/src/components/shot/columns.ts` - Column definitions - `frontend/src/components/shot/ShotBrowser.vue` - Parent component - `frontend/src/services/task.ts` - Task service with API methods - `frontend/src/services/shot.ts` - Shot service and types - `frontend/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.