# Custom Task Status Update Endpoint ## Overview This document describes the implementation of the PUT endpoint for updating custom task statuses in a project. **Endpoint:** `PUT /projects/{project_id}/task-statuses/{status_id}` **Requirements Implemented:** - 2.1: Support updating name - 2.2: Support updating color - 2.3: Support updating is_default flag - 5.2: If setting as default, unset other default statuses ## Implementation Details ### Endpoint Signature ```python @router.put("/{project_id}/task-statuses/{status_id}") async def update_custom_task_status( project_id: int, status_id: str, status_update: dict, db: Session = Depends(get_db), current_user: User = Depends(require_coordinator_or_admin) ): ``` ### Request Body Schema Uses `CustomTaskStatusUpdate` schema: ```python { "name": "string (optional)", # New status name (1-50 chars) "color": "string (optional)", # Hex color code (e.g., #FF5733) "is_default": "boolean (optional)" # Set as default status } ``` ### Response Schema Returns `CustomTaskStatusResponse`: ```python { "message": "string", "status": { "id": "string", "name": "string", "color": "string", "order": "integer", "is_default": "boolean" }, "all_statuses": { "statuses": [...], # All custom statuses "system_statuses": [...], # System statuses "default_status_id": "string" } } ``` ## Features ### 1. Name Update (Requirement 2.1) - Validates name uniqueness within project - Checks against other custom statuses - Checks against system status names - Returns 409 Conflict if name already exists ```python # Validate name uniqueness if name is being changed if status_update.name is not None and status_update.name != status_to_update.get('name'): # Check against other custom statuses existing_names = [ s.get('name', '').lower() for i, s in enumerate(custom_statuses_data) if isinstance(s, dict) and i != status_index ] # Check against system statuses system_names = [s['name'].lower() for s in SYSTEM_TASK_STATUSES] if status_update.name.lower() in existing_names: raise HTTPException(status_code=409, detail="Name already exists") if status_update.name.lower() in system_names: raise HTTPException(status_code=409, detail="Conflicts with system status") ``` ### 2. Color Update (Requirement 2.2) - Accepts hex color codes (e.g., #FF5733) - Validates color format via schema - Updates color independently of other fields ```python # Update color if provided if status_update.color is not None: status_to_update['color'] = status_update.color ``` ### 3. Default Status Management (Requirement 2.3, 5.2) - When setting a status as default, automatically unsets all other defaults - Ensures only one default status exists at a time - Allows unsetting default status ```python # Handle is_default flag if status_update.is_default is not None: if status_update.is_default: # If setting as default, unset other default statuses for status_data in custom_statuses_data: if isinstance(status_data, dict): status_data['is_default'] = False # Set this status as default status_to_update['is_default'] = True else: # Just unset this status as default status_to_update['is_default'] = False ``` ### 4. JSON Column Updates Uses `flag_modified` to ensure SQLAlchemy detects changes to JSON columns: ```python # Update the status in the list custom_statuses_data[status_index] = status_to_update db_project.custom_task_statuses = custom_statuses_data # Use flag_modified for JSON column updates flag_modified(db_project, 'custom_task_statuses') db.commit() ``` ## Error Handling ### 404 Not Found - Project doesn't exist - Status ID not found in project ### 409 Conflict - Status name already exists in project - Status name conflicts with system status ### 403 Forbidden - User is not coordinator or admin ### 422 Unprocessable Entity - Invalid request body format - Invalid color format - Invalid name length ## Example Usage ### Update Status Name ```bash curl -X PUT "http://localhost:8000/projects/1/task-statuses/custom_abc123" \ -H "Authorization: Bearer " \ -H "Content-Type: application/json" \ -d '{"name": "New Status Name"}' ``` ### Update Status Color ```bash curl -X PUT "http://localhost:8000/projects/1/task-statuses/custom_abc123" \ -H "Authorization: Bearer " \ -H "Content-Type: application/json" \ -d '{"color": "#00FF00"}' ``` ### Update Both Name and Color ```bash curl -X PUT "http://localhost:8000/projects/1/task-statuses/custom_abc123" \ -H "Authorization: Bearer " \ -H "Content-Type: application/json" \ -d '{"name": "Updated Status", "color": "#0000FF"}' ``` ### Set as Default Status ```bash curl -X PUT "http://localhost:8000/projects/1/task-statuses/custom_abc123" \ -H "Authorization: Bearer " \ -H "Content-Type: application/json" \ -d '{"is_default": true}' ``` ## Testing To test this endpoint: 1. Start the backend server: ```bash cd backend uvicorn main:app --reload ``` 2. Create a custom status first (if needed): ```bash curl -X POST "http://localhost:8000/projects/1/task-statuses" \ -H "Authorization: Bearer " \ -H "Content-Type: application/json" \ -d '{"name": "Test Status", "color": "#FF5733"}' ``` 3. Update the status using the examples above 4. Verify changes by getting all statuses: ```bash curl -X GET "http://localhost:8000/projects/1/task-statuses" \ -H "Authorization: Bearer " ``` ## Integration with Frontend The frontend can use this endpoint to: 1. Update status names when users edit them 2. Change status colors via color picker 3. Set/unset default status via toggle or button 4. Update multiple fields at once The response includes the updated status and all statuses, allowing the frontend to update its state in a single request. ## Database Schema The custom task statuses are stored in the `projects` table as a JSON column: ```sql custom_task_statuses JSON -- Array of status objects ``` Each status object has the structure: ```json { "id": "custom_abc123", "name": "Status Name", "color": "#FF5733", "order": 0, "is_default": false } ``` ## Related Endpoints - `GET /projects/{project_id}/task-statuses` - Get all statuses - `POST /projects/{project_id}/task-statuses` - Create new status - `DELETE /projects/{project_id}/task-statuses/{status_id}` - Delete status (to be implemented) - `PATCH /projects/{project_id}/task-statuses/reorder` - Reorder statuses (to be implemented)