Files
git.stella-ops.org/docs/ui/components/bulk-triage-view.md
StellaOps Bot 17613acf57 feat: add bulk triage view component and related stories
- Exported BulkTriageViewComponent and its related types from findings module.
- Created a new accessibility test suite for score components using axe-core.
- Introduced design tokens for score components to standardize styling.
- Enhanced score breakdown popover for mobile responsiveness with drag handle.
- Added date range selector functionality to score history chart component.
- Implemented unit tests for date range selector in score history chart.
- Created Storybook stories for bulk triage view and score history chart with date range selector.
2025-12-26 01:01:35 +02:00

6.1 KiB

BulkTriageViewComponent

Streamlined interface for triaging multiple findings at once with bucket-based organization.

Overview

The BulkTriageViewComponent provides a triage-focused view with bucket summary cards, one-click bucket selection, and bulk actions.

Selector

<app-bulk-triage-view />

Inputs

Input Type Default Description
findings ScoredFinding[] [] Array of scored findings
selectedIds Set<string> new Set() Currently selected finding IDs
processing boolean false Whether an action is in progress

Outputs

Output Type Description
selectionChange EventEmitter<string[]> Emits when selection changes
actionRequest EventEmitter<BulkActionRequest> Emits when an action is triggered
actionComplete EventEmitter<BulkActionResult> Emits when an action completes

Action Types

type BulkActionType = 'acknowledge' | 'suppress' | 'assign' | 'escalate';

interface BulkActionRequest {
  action: BulkActionType;
  findingIds: string[];
  assignee?: string;      // For 'assign' action
  reason?: string;        // For 'suppress' action
}

interface BulkActionResult {
  action: BulkActionType;
  findingIds: string[];
  success: boolean;
  error?: string;
}

UI Sections

Bucket Summary Cards

Four cards showing findings grouped by priority bucket:

  • Act Now (red): 90-100 score
  • Schedule Next (amber): 70-89 score
  • Investigate (blue): 40-69 score
  • Watchlist (gray): 0-39 score

Each card displays:

  • Bucket name and color
  • Finding count
  • "Select All" button
  • Selection indicator

Action Bar

Appears when findings are selected:

  • Selection count
  • Clear selection button
  • Action buttons: Acknowledge, Suppress, Assign, Escalate
  • Undo button (when history exists)

Progress Overlay

Shown during bulk operations:

  • Action name
  • Progress bar
  • Percentage complete
  • Items processed count

Modals

  • Assign Modal: Email input for assignee
  • Suppress Modal: Text area for suppression reason

Usage Examples

Basic Usage

<app-bulk-triage-view
  [findings]="scoredFindings"
  [selectedIds]="selectedIds"
  (selectionChange)="onSelectionChange($event)"
  (actionRequest)="onActionRequest($event)"
/>

Full Implementation

@Component({
  selector: 'app-triage-page',
  template: `
    <app-bulk-triage-view
      [findings]="findings()"
      [selectedIds]="selectedIds()"
      [processing]="processing()"
      (selectionChange)="updateSelection($event)"
      (actionRequest)="handleAction($event)"
      (actionComplete)="onActionComplete($event)"
    />
  `
})
export class TriagePageComponent {
  findings = signal<ScoredFinding[]>([]);
  selectedIds = signal<Set<string>>(new Set());
  processing = signal(false);

  private triageService = inject(TriageService);

  updateSelection(ids: string[]): void {
    this.selectedIds.set(new Set(ids));
  }

  async handleAction(request: BulkActionRequest): Promise<void> {
    this.processing.set(true);

    try {
      switch (request.action) {
        case 'acknowledge':
          await this.triageService.acknowledge(request.findingIds);
          break;
        case 'suppress':
          await this.triageService.suppress(request.findingIds, request.reason!);
          break;
        case 'assign':
          await this.triageService.assign(request.findingIds, request.assignee!);
          break;
        case 'escalate':
          await this.triageService.escalate(request.findingIds);
          break;
      }

      this.selectedIds.set(new Set());
      await this.refreshFindings();
    } finally {
      this.processing.set(false);
    }
  }
}

With Toast Notifications

<app-bulk-triage-view
  [findings]="findings"
  [selectedIds]="selectedIds"
  (actionComplete)="showToast($event)"
/>
showToast(result: BulkActionResult): void {
  if (result.success) {
    this.toast.success(
      `${result.action} completed for ${result.findingIds.length} findings`
    );
  } else {
    this.toast.error(`Action failed: ${result.error}`);
  }
}

Bucket Selection

Select All in Bucket

Click "Select All" on a bucket card to select all findings in that bucket.

Toggle Bucket

Clicking "Select All" when all bucket items are selected will deselect them.

Partial Selection

When some items in a bucket are selected, the button shows a partial indicator.

Action Descriptions

Action Icon Description
Acknowledge Checkmark Mark findings as reviewed
Suppress Eye-off Suppress with reason (opens modal)
Assign User Assign to team member (opens modal)
Escalate Alert Mark for urgent attention

Undo Capability

The component maintains an undo stack for recent actions:

  • Up to 5 operations stored
  • Undo restores previous selection
  • Toast shows "Undo" button after action

Accessibility

  • Bucket summary has aria-label="Findings by priority"
  • Select All buttons have aria-pressed state
  • Action bar has role="toolbar"
  • Progress overlay announces to screen readers
  • Modals trap focus and support Escape to close
  • Action buttons have descriptive labels

Keyboard Navigation

Key Action
Tab Navigate between elements
Enter/Space Activate buttons
Escape Close modals

Styling

app-bulk-triage-view {
  --bucket-card-padding: 16px;
  --bucket-card-radius: 8px;
  --action-bar-bg: #f9fafb;
  --modal-max-width: 400px;
}

/* Bucket colors */
.bucket-card.act-now { --bucket-color: #DC2626; }
.bucket-card.schedule-next { --bucket-color: #D97706; }
.bucket-card.investigate { --bucket-color: #2563EB; }
.bucket-card.watchlist { --bucket-color: #6B7280; }

Responsive Behavior

Breakpoint Layout
> 640px 4 bucket cards in row
<= 640px 2x2 grid, action labels hidden