- 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.
247 lines
6.1 KiB
Markdown
247 lines
6.1 KiB
Markdown
# 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
|
|
|
|
```html
|
|
<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
|
|
|
|
```typescript
|
|
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
|
|
|
|
```html
|
|
<app-bulk-triage-view
|
|
[findings]="scoredFindings"
|
|
[selectedIds]="selectedIds"
|
|
(selectionChange)="onSelectionChange($event)"
|
|
(actionRequest)="onActionRequest($event)"
|
|
/>
|
|
```
|
|
|
|
### Full Implementation
|
|
|
|
```typescript
|
|
@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
|
|
|
|
```html
|
|
<app-bulk-triage-view
|
|
[findings]="findings"
|
|
[selectedIds]="selectedIds"
|
|
(actionComplete)="showToast($event)"
|
|
/>
|
|
```
|
|
|
|
```typescript
|
|
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
|
|
|
|
```css
|
|
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-list.md) - Findings table view
|
|
- [ScorePill](./score-pill.md) - Score display
|
|
- [ScoreBadge](./score-badge.md) - Evidence flags
|