LinkDesk/frontend/docs/bulk-status-custom-support.md

8.8 KiB

Bulk Status Update with Custom Status Support

Overview

This document describes the implementation of custom task status support in the bulk status update feature. The TaskBulkActionsMenu component has been enhanced to fetch and display both system and custom statuses, with proper validation to ensure all selected tasks belong to the same project.

Requirements

  • 10.1: Modify TaskBulkActionsMenu.vue component
  • 10.2: Fetch custom statuses for current project
  • 10.3: Include custom statuses in bulk update dropdown
  • 10.4: Validate all selected tasks are from same project
  • 10.5: Show color indicators in dropdown

Implementation Details

1. Component Props Enhancement

File: frontend/src/components/task/TaskBulkActionsMenu.vue

Added selectedTasks prop to receive task data for validation:

interface Props {
  open: boolean
  position: { x: number; y: number }
  selectedCount: number
  selectedTasks: Task[]  // NEW: For project validation
  projectMembers: ProjectMember[]
  isProcessing?: boolean
}

2. Custom Status Fetching

Implemented status fetching logic that:

  • Fetches both system and custom statuses from the API
  • Triggers when the menu opens
  • Refetches when the project changes
  • Handles loading and error states
const fetchStatuses = async () => {
  if (!currentProjectId.value || hasMultipleProjects.value) {
    systemStatuses.value = []
    customStatuses.value = []
    return
  }

  try {
    isLoadingStatuses.value = true
    const response = await customTaskStatusService.getAllStatuses(currentProjectId.value)
    systemStatuses.value = response.system_statuses
    customStatuses.value = response.statuses
  } catch (error) {
    console.error('Failed to fetch task statuses:', error)
    systemStatuses.value = []
    customStatuses.value = []
  } finally {
    isLoadingStatuses.value = false
  }
}

3. Multi-Project Validation

Implemented validation to ensure all selected tasks belong to the same project:

const hasMultipleProjects = computed(() => {
  if (props.selectedTasks.length === 0) return false
  const projectIds = new Set(props.selectedTasks.map(task => task.project_id))
  return projectIds.size > 1
})

const currentProjectId = computed(() => {
  if (props.selectedTasks.length === 0) return null
  return props.selectedTasks[0].project_id
})

When multiple projects are detected:

  • A warning message is displayed: "Selected tasks are from different projects"
  • The "Set Status" button is disabled
  • The "Assign To" buttons are disabled

4. Status Display with Color Indicators

The dropdown now displays statuses with:

  • Section labels ("System Statuses" and "Custom Statuses")
  • Color indicator dots for each status
  • Proper separation between system and custom statuses
<template>
  <!-- System statuses -->
  <div v-if="systemStatuses.length > 0" class="px-2 py-1 text-xs font-semibold text-muted-foreground">
    System Statuses
  </div>
  <DropdownMenuItem
    v-for="status in systemStatuses"
    :key="status.id"
    @click="handleStatusSelected(status.id)"
    class="flex items-center gap-2"
  >
    <div
      class="w-2 h-2 rounded-full flex-shrink-0"
      :style="{ backgroundColor: status.color }"
    />
    <span>{{ status.name }}</span>
  </DropdownMenuItem>

  <!-- Divider -->
  <div v-if="systemStatuses.length > 0 && customStatuses.length > 0" class="h-px bg-border my-1" />

  <!-- Custom statuses -->
  <div v-if="customStatuses.length > 0" class="px-2 py-1 text-xs font-semibold text-muted-foreground">
    Custom Statuses
  </div>
  <DropdownMenuItem
    v-for="status in customStatuses"
    :key="status.id"
    @click="handleStatusSelected(status.id)"
    class="flex items-center gap-2"
  >
    <div
      class="w-2 h-2 rounded-full flex-shrink-0"
      :style="{ backgroundColor: status.color }"
    />
    <span>{{ status.name }}</span>
  </DropdownMenuItem>
</template>

5. Service Layer Updates

File: frontend/src/services/task.ts

Updated the bulk status update service to accept string status IDs instead of enum values:

// Before
async bulkUpdateStatus(taskIds: number[], status: TaskStatus): Promise<BulkActionResult>

// After
async bulkUpdateStatus(taskIds: number[], status: string): Promise<BulkActionResult>

This change allows the service to handle both system status IDs (e.g., "not_started") and custom status IDs (e.g., "custom_status_123").

6. Parent Component Integration

File: frontend/src/components/task/TaskBrowser.vue

Updated to pass the selectedTasks prop:

<TaskBulkActionsMenu
  v-model:open="showContextMenu"
  :position="contextMenuPosition"
  :selected-count="selectedCount"
  :selected-tasks="selectedTasks"  <!-- NEW -->
  :project-members="projectMembers"
  @status-selected="handleBulkStatusUpdate"
  @assignee-selected="handleBulkAssignment"
/>

Updated the status update handler to accept string:

const handleBulkStatusUpdate = async (status: string) => {
  // ... implementation
}

User Experience

Normal Flow (Single Project)

  1. User selects multiple tasks from the same project
  2. User right-clicks to open context menu
  3. User clicks "Set Status" to open submenu
  4. Statuses load (brief loading indicator)
  5. System statuses appear first with label
  6. Custom statuses appear below with label
  7. Each status shows a colored dot indicator
  8. User clicks a status
  9. All selected tasks are updated
  10. Success toast shows count of updated tasks

Multi-Project Detection

  1. User selects tasks from different projects
  2. User right-clicks to open context menu
  3. Warning message appears: "Selected tasks are from different projects"
  4. "Set Status" button is disabled
  5. "Assign To" buttons are disabled
  6. User must adjust selection to single project

Testing

Manual Testing Steps

  1. Setup:

    • Create a project with custom task statuses
    • Create multiple tasks in the project
    • Navigate to the Tasks view
  2. Test Single Project Selection:

    • Select multiple tasks from the same project
    • Right-click to open context menu
    • Click "Set Status" submenu
    • Verify system statuses appear with label
    • Verify custom statuses appear with label
    • Verify color indicators are displayed
    • Click a custom status
    • Verify tasks are updated successfully
  3. Test Multi-Project Validation:

    • Select tasks from different projects
    • Right-click to open context menu
    • Verify warning message appears
    • Verify "Set Status" is disabled
    • Verify "Assign To" is disabled
  4. Test Status Loading:

    • Select tasks and open context menu
    • Verify loading indicator appears briefly
    • Verify statuses load correctly
  5. Test Color Indicators:

    • Verify each status has a colored dot
    • Verify colors match configured status colors

Files Modified

  1. frontend/src/components/task/TaskBulkActionsMenu.vue

    • Added selectedTasks prop
    • Implemented status fetching logic
    • Added multi-project validation
    • Updated UI to show color indicators
    • Added loading and error states
  2. frontend/src/components/task/TaskBrowser.vue

    • Pass selectedTasks to TaskBulkActionsMenu
    • Updated handleBulkStatusUpdate to accept string
  3. frontend/src/services/task.ts

    • Updated bulkUpdateStatus signature to accept string
    • Updated BulkStatusUpdateRequest interface

API Integration

The component uses the customTaskStatusService.getAllStatuses() method which returns:

interface AllTaskStatusesResponse {
  statuses: CustomTaskStatus[]           // Custom statuses
  system_statuses: SystemTaskStatus[]    // System statuses
  default_status_id: string
}

interface CustomTaskStatus {
  id: string
  name: string
  color: string
  order: number
  is_default: boolean
}

interface SystemTaskStatus {
  id: string
  name: string
  color: string
  is_system: boolean
}

Benefits

  1. Flexibility: Users can now use custom statuses in bulk operations
  2. Consistency: Same status options available in bulk and individual updates
  3. Safety: Multi-project validation prevents accidental cross-project updates
  4. Usability: Color indicators help users quickly identify statuses
  5. Organization: Clear separation between system and custom statuses

Future Enhancements

Potential improvements for future iterations:

  1. Add status search/filter for projects with many custom statuses
  2. Show status usage count in dropdown
  3. Add keyboard shortcuts for common status changes
  4. Support status presets for common bulk operations
  5. Add undo functionality for bulk status changes