9.2 KiB
Custom Task Status Reorder Endpoint
Overview
This document describes the PATCH endpoint for reordering custom task statuses within a project. The endpoint allows coordinators and administrators to change the display order of custom task statuses.
Endpoint
PATCH /projects/{project_id}/task-statuses/reorder
Authentication
Requires JWT authentication with coordinator or admin role.
Request
Path Parameters
project_id(integer, required): The ID of the project
Request Body
{
"status_ids": ["custom_abc123", "custom_def456", "custom_ghi789"]
}
Fields:
status_ids(array of strings, required): Ordered list of status IDs in the desired sequence- Must contain all existing custom status IDs for the project
- Cannot contain duplicates
- Cannot be empty
Response
Success Response (200 OK)
{
"message": "Custom task statuses reordered successfully",
"status": null,
"all_statuses": {
"statuses": [
{
"id": "custom_abc123",
"name": "Review",
"color": "#9333EA",
"order": 0,
"is_default": false
},
{
"id": "custom_def456",
"name": "Blocked",
"color": "#DC2626",
"order": 1,
"is_default": true
},
{
"id": "custom_ghi789",
"name": "Ready for Delivery",
"color": "#059669",
"order": 2,
"is_default": false
}
],
"system_statuses": [
{
"id": "not_started",
"name": "Not Started",
"color": "#6B7280",
"is_system": true
},
{
"id": "in_progress",
"name": "In Progress",
"color": "#3B82F6",
"is_system": true
},
{
"id": "submitted",
"name": "Submitted",
"color": "#F59E0B",
"is_system": true
},
{
"id": "approved",
"name": "Approved",
"color": "#10B981",
"is_system": true
},
{
"id": "retake",
"name": "Retake",
"color": "#EF4444",
"is_system": true
}
],
"default_status_id": "custom_def456"
}
}
Error Responses
400 Bad Request - Missing Status IDs
{
"detail": "Missing status IDs in reorder request: custom_xyz999"
}
Occurs when the request doesn't include all existing custom status IDs.
400 Bad Request - Invalid Status IDs
{
"detail": "Status IDs not found: invalid_id_12345"
}
Occurs when the request includes status IDs that don't exist in the project.
403 Forbidden
{
"detail": "Insufficient permissions"
}
Occurs when the user doesn't have coordinator or admin role.
404 Not Found
{
"detail": "Project not found"
}
Occurs when the specified project doesn't exist.
422 Unprocessable Entity - Duplicate IDs
{
"detail": "1 validation error for CustomTaskStatusReorder\nstatus_ids\n Value error, Status IDs list contains duplicates"
}
Occurs when the request contains duplicate status IDs.
422 Unprocessable Entity - Empty List
{
"detail": "1 validation error for CustomTaskStatusReorder\nstatus_ids\n Value error, Status IDs list cannot be empty"
}
Occurs when the request contains an empty status_ids array.
Implementation Details
Validation
- Project Existence: Verifies the project exists
- Permission Check: Ensures user has coordinator or admin role
- Complete List: Validates that all existing custom status IDs are included
- No Missing IDs: Ensures no status IDs are omitted
- No Invalid IDs: Ensures all provided IDs exist in the project
- No Duplicates: Validates the list contains no duplicate IDs (handled by Pydantic schema)
Order Update Process
- Parse and validate the reorder request
- Retrieve existing custom statuses from the project
- Create a mapping of status_id to status data
- Reorder statuses according to the provided list
- Update the
orderfield for each status (0-indexed) - Save the reordered list to the database
- Use
flag_modified()to ensure JSON column changes are persisted
Database Changes
- Updates the
custom_task_statusesJSON column in theprojectstable - Each status object's
orderfield is updated to match its position in the new list - Uses SQLAlchemy's
flag_modified()to ensure JSON column changes are detected
Usage Examples
Python (requests)
import requests
# Login and get token
response = requests.post(
"http://localhost:8000/auth/login",
json={"email": "admin@vfx.com", "password": "admin123"}
)
token = response.json()["access_token"]
# Reorder statuses
headers = {"Authorization": f"Bearer {token}"}
data = {
"status_ids": [
"custom_abc123",
"custom_def456",
"custom_ghi789"
]
}
response = requests.patch(
"http://localhost:8000/projects/1/task-statuses/reorder",
headers=headers,
json=data
)
if response.status_code == 200:
result = response.json()
print(f"✅ {result['message']}")
print(f"Reordered {len(result['all_statuses']['statuses'])} statuses")
else:
print(f"❌ Error: {response.json()['detail']}")
JavaScript (fetch)
// Assuming you have a token from login
const token = "your_jwt_token_here";
const reorderStatuses = async (projectId, statusIds) => {
const response = await fetch(
`http://localhost:8000/projects/${projectId}/task-statuses/reorder`,
{
method: 'PATCH',
headers: {
'Authorization': `Bearer ${token}`,
'Content-Type': 'application/json'
},
body: JSON.stringify({
status_ids: statusIds
})
}
);
if (response.ok) {
const result = await response.json();
console.log('✅', result.message);
return result.all_statuses;
} else {
const error = await response.json();
console.error('❌ Error:', error.detail);
throw new Error(error.detail);
}
};
// Usage
const statusIds = [
'custom_abc123',
'custom_def456',
'custom_ghi789'
];
reorderStatuses(1, statusIds)
.then(allStatuses => {
console.log('New order:', allStatuses.statuses);
})
.catch(error => {
console.error('Failed to reorder:', error);
});
Frontend Integration
Drag-and-Drop Implementation
The frontend should implement drag-and-drop functionality using a library like vue-draggable-next:
- Display statuses in their current order
- Allow users to drag statuses to reorder them
- On drop, collect the new order of status IDs
- Call the reorder endpoint with the new order
- Update the UI optimistically or wait for the response
- Handle errors by reverting to the previous order
Example Vue Component
<template>
<draggable
v-model="statuses"
@end="handleReorder"
item-key="id"
>
<template #item="{ element }">
<div class="status-item">
<span class="drag-handle">⋮⋮</span>
<span :style="{ color: element.color }">
{{ element.name }}
</span>
</div>
</template>
</draggable>
</template>
<script setup>
import { ref } from 'vue';
import draggable from 'vuedraggable';
import { reorderTaskStatuses } from '@/services/customTaskStatus';
const props = defineProps({
projectId: Number,
initialStatuses: Array
});
const statuses = ref([...props.initialStatuses]);
const handleReorder = async () => {
const statusIds = statuses.value.map(s => s.id);
try {
const result = await reorderTaskStatuses(props.projectId, statusIds);
// Update with server response
statuses.value = result.all_statuses.statuses;
} catch (error) {
// Revert to original order on error
statuses.value = [...props.initialStatuses];
console.error('Failed to reorder:', error);
}
};
</script>
Testing
A comprehensive test script is available at backend/test_reorder_custom_task_status.py that tests:
- ✅ Successful reordering (reversing order)
- ✅ Order field updates correctly
- ✅ Rejection of incomplete status lists
- ✅ Rejection of invalid status IDs
- ✅ Rejection of duplicate status IDs
Run the test with:
cd backend
python test_reorder_custom_task_status.py
Requirements Validation
This endpoint satisfies the following requirements from the custom task status specification:
- Requirement 4.1: ✅ Displays statuses in their defined order
- Requirement 4.2: ✅ Updates the order when user reorders statuses
- Requirement 4.3: ✅ Updates display order in all dropdowns and filters
- Requirement 4.4: ✅ Validates all status IDs are present
Related Endpoints
GET /projects/{project_id}/task-statuses- Get all task statusesPOST /projects/{project_id}/task-statuses- Create a custom statusPUT /projects/{project_id}/task-statuses/{status_id}- Update a custom statusDELETE /projects/{project_id}/task-statuses/{status_id}- Delete a custom status
Notes
- System statuses (not_started, in_progress, submitted, approved, retake) cannot be reordered
- Only custom statuses can be reordered
- The order field is 0-indexed
- Reordering does not affect the default status designation
- The endpoint uses
flag_modified()to ensure JSON column changes are persisted to the database