LinkDesk/frontend/docs/custom-task-status-reorder-...

6.5 KiB

Custom Task Status Drag-and-Drop Reordering Implementation

Overview

This document describes the implementation of drag-and-drop reordering functionality for custom task statuses in the VFX Project Management System.

Feature Description

Users with coordinator or admin roles can reorder custom task statuses by dragging and dropping them within the Custom Task Status Manager. This allows teams to organize their workflow statuses in a logical order that matches their production pipeline.

Implementation Details

Frontend Components

Component: frontend/src/components/settings/CustomTaskStatusManager.vue

Library: vue-draggable-next - A Vue 3 compatible drag-and-drop library based on Sortable.js

Key Features

  1. Drag Handle

    • Visual grip icon (GripVertical from lucide-vue-next)
    • Hover effects for better UX
    • Cursor changes: grab → grabbing during drag
    • Only custom statuses have drag handles (system statuses are not reorderable)
  2. Drag and Drop Behavior

    • 200ms animation duration for smooth transitions
    • Ghost element with reduced opacity during drag
    • Restricted to drag handle only (prevents accidental drags)
    • Disabled state during API calls to prevent conflicts
  3. API Integration

    • Endpoint: PATCH /api/projects/{project_id}/task-statuses/reorder
    • Request body: { "status_ids": ["id1", "id2", "id3", ...] }
    • Optimistic UI updates with error rollback
    • Success/error toast notifications
  4. Error Handling

    • Validates all status IDs are present
    • Prevents duplicate IDs
    • Reverts to original order on API failure
    • User-friendly error messages

Code Structure

// State management
const isDragging = ref(false)
const isReordering = ref(false)

// Computed property with getter/setter for v-model
const customStatuses = computed({
  get: () => allStatuses.value?.statuses || [],
  set: (value) => {
    if (allStatuses.value) {
      allStatuses.value.statuses = value
    }
  }
})

// Drag handlers
const onDragStart = () => {
  isDragging.value = true
}

const onDragEnd = async (event: any) => {
  isDragging.value = false
  
  // Skip if no change
  if (event.oldIndex === event.newIndex) {
    return
  }
  
  // Get new order and call API
  const newOrder = customStatuses.value.map(status => status.id)
  
  try {
    isReordering.value = true
    await customTaskStatusService.reorderStatuses(projectId, {
      status_ids: newOrder
    })
    
    // Show success and reload
    toast({ title: 'Success', description: 'Status order updated' })
    await loadStatuses()
    emit('updated')
  } catch (error) {
    // Revert on error
    await loadStatuses()
    toast({ title: 'Error', description: 'Failed to reorder', variant: 'destructive' })
  } finally {
    isReordering.value = false
  }
}

Template Structure

<VueDraggableNext
  v-model="customStatuses"
  :animation="200"
  handle=".drag-handle"
  ghost-class="opacity-50"
  @start="onDragStart"
  @end="onDragEnd"
  :disabled="isReordering"
  class="divide-y"
>
  <div v-for="customStatus in customStatuses" :key="customStatus.id">
    <!-- Drag Handle -->
    <div class="drag-handle cursor-grab active:cursor-grabbing">
      <GripVertical class="h-5 w-5" />
    </div>
    
    <!-- Status content -->
    <!-- ... -->
  </div>
</VueDraggableNext>

Backend API

Endpoint

PATCH /api/projects/{project_id}/task-statuses/reorder

Request Body

{
  "status_ids": ["custom_status_1", "custom_status_2", "custom_status_3"]
}

Response

{
  "message": "Custom task statuses reordered successfully",
  "status": null,
  "all_statuses": {
    "statuses": [
      {
        "id": "custom_status_1",
        "name": "In Review",
        "color": "#9333EA",
        "order": 0,
        "is_default": false
      },
      // ... more statuses
    ],
    "system_statuses": [...],
    "default_status_id": "not_started"
  }
}

Validation

  • All status IDs must be present (no missing IDs)
  • All status IDs must be valid (no invalid IDs)
  • No duplicate status IDs allowed
  • User must have coordinator or admin role

User Experience

Visual Feedback

  1. Hover State

    • Drag handle changes color on hover
    • Cursor changes to "grab"
  2. Dragging State

    • Cursor changes to "grabbing"
    • Ghost element appears with reduced opacity
    • Other statuses smoothly move to make space
  3. Loading State

    • All status items have reduced opacity
    • Edit and delete buttons are disabled
    • Drag handles show "not-allowed" cursor
  4. Success State

    • Toast notification appears
    • Statuses remain in new order
    • UI returns to normal state
  5. Error State

    • Error toast notification appears
    • Statuses revert to original order
    • UI returns to normal state

Testing

Manual Testing

See frontend/test-drag-drop-reorder.html for comprehensive test cases including:

  • Visual elements verification
  • Basic drag and drop
  • Reorder persistence
  • Multiple reorders
  • Disabled state during reorder
  • No change detection
  • Error handling
  • Animation and transitions
  • Default status reordering
  • Integration with other features

Backend Testing

Run backend/test_reorder_custom_task_status.py to verify:

  • Successful reordering
  • Order field updates
  • Error cases (missing IDs, invalid IDs, duplicates)

Requirements Satisfied

  • 4.1: User can reorder custom task statuses
  • 4.2: Drag-and-drop interface for reordering
  • 4.3: Order persists after page reload
  • 4.4: Visual feedback during drag operation
  • 4.5: Error handling for failed reorder operations

Dependencies

{
  "vue-draggable-next": "^2.2.1"
}

Installation

cd frontend
npm install vue-draggable-next

Future Enhancements

  1. Touch device support for mobile/tablet
  2. Keyboard shortcuts for reordering (Alt+Up/Down)
  3. Undo/redo functionality
  4. Bulk reorder operations
  5. Drag preview with full status card
  6. Animation customization options

Known Limitations

  1. Requires mouse interaction (touch support may vary)
  2. System statuses cannot be reordered
  3. Only works within the same project (no cross-project reordering)
  • frontend/src/components/settings/CustomTaskStatusManager.vue - Main component
  • frontend/src/services/customTaskStatus.ts - API service
  • backend/routers/projects.py - Backend endpoint
  • backend/schemas/custom_task_status.py - Request/response schemas
  • frontend/test-drag-drop-reorder.html - Test documentation
  • backend/test_reorder_custom_task_status.py - Backend test script