- 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.
6.1 KiB
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-pressedstate - 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 |
Related Components
- FindingsList - Findings table view
- ScorePill - Score display
- ScoreBadge - Evidence flags