303 lines
8.8 KiB
Markdown
303 lines
8.8 KiB
Markdown
# 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:
|
|
|
|
```typescript
|
|
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
|
|
|
|
```typescript
|
|
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:
|
|
|
|
```typescript
|
|
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
|
|
|
|
```vue
|
|
<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:
|
|
|
|
```typescript
|
|
// 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:
|
|
|
|
```vue
|
|
<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:
|
|
|
|
```typescript
|
|
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:
|
|
|
|
```typescript
|
|
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
|
|
|
|
## Related Documentation
|
|
|
|
- [Custom Task Status Manager Implementation](./custom-task-status-manager-implementation.md)
|
|
- [Custom Task Status Service Implementation](./custom-task-status-service-implementation.md)
|
|
- [Bulk Actions Implementation](../../backend/docs/bulk-actions-implementation.md)
|