feat(23-02): add USER_AWARENESS support to TimelineCard, prevent review-page crash
- Add USER_AWARENESS to classification-entry verdict union and VERDICT_TINT (emerald, matching ClassificationCard)
- Widen campaign_classified audit-case cast to include USER_AWARENESS
- Add defensive fallback on both .split(' ') tint sites so any unrecognized runtime verdict string can never crash the render (T-23-12) — phishing-timeline.ts emits verdict as an unvalidated plain string
This commit is contained in:
parent
547372e57a
commit
9ae834f5e6
1 changed files with 16 additions and 6 deletions
|
|
@ -7,17 +7,23 @@ import { cn } from '@/lib/utils';
|
|||
|
||||
export type TimelineEntry =
|
||||
| { kind: 'report'; at: string; reportId: string; ticketNumber: string | null; companyName: string | null }
|
||||
| { kind: 'classification'; at: string; verdict: 'SPAM' | 'UNWANTED' | 'THREAT'; confidence: string | null }
|
||||
| {
|
||||
kind: 'classification';
|
||||
at: string;
|
||||
verdict: 'SPAM' | 'UNWANTED' | 'THREAT' | 'USER_AWARENESS';
|
||||
confidence: string | null;
|
||||
}
|
||||
| { kind: 'audit'; at: string; eventType: string; actor: string | null; payload: unknown };
|
||||
|
||||
interface TimelineCardProps {
|
||||
timeline: TimelineEntry[];
|
||||
}
|
||||
|
||||
const VERDICT_TINT: Record<'SPAM' | 'UNWANTED' | 'THREAT', string> = {
|
||||
const VERDICT_TINT: Record<'SPAM' | 'UNWANTED' | 'THREAT' | 'USER_AWARENESS', string> = {
|
||||
SPAM: 'bg-slate-500 text-slate-600',
|
||||
UNWANTED: 'bg-amber-500 text-amber-600',
|
||||
THREAT: 'bg-destructive text-destructive',
|
||||
USER_AWARENESS: 'bg-emerald-500 text-emerald-600',
|
||||
};
|
||||
|
||||
function humanizeEventType(eventType: string): string {
|
||||
|
|
@ -46,11 +52,15 @@ function renderEntry(entry: TimelineEntry): {
|
|||
|
||||
if (entry.kind === 'classification') {
|
||||
const confidence = entry.confidence != null ? ` (${entry.confidence}%)` : '';
|
||||
// Defensive fallback: phishing-timeline.ts emits verdict as a plain
|
||||
// unvalidated runtime `string`, so a value outside this component's
|
||||
// narrowed union must never crash the render (T-23-12).
|
||||
const tint = VERDICT_TINT[entry.verdict] ?? 'bg-muted-foreground text-muted-foreground';
|
||||
return {
|
||||
label: `Classified as ${entry.verdict}${confidence}`,
|
||||
icon: Sparkles,
|
||||
dotClass: VERDICT_TINT[entry.verdict].split(' ')[0],
|
||||
textClass: VERDICT_TINT[entry.verdict].split(' ')[1],
|
||||
dotClass: tint.split(' ')[0],
|
||||
textClass: tint.split(' ')[1],
|
||||
};
|
||||
}
|
||||
|
||||
|
|
@ -84,8 +94,8 @@ function renderEntry(entry: TimelineEntry): {
|
|||
textClass: 'text-slate-600',
|
||||
};
|
||||
case 'campaign_classified': {
|
||||
const verdict = payload.verdict as 'SPAM' | 'UNWANTED' | 'THREAT' | undefined;
|
||||
const tint = verdict ? VERDICT_TINT[verdict] : 'bg-muted-foreground text-muted-foreground';
|
||||
const verdict = payload.verdict as 'SPAM' | 'UNWANTED' | 'THREAT' | 'USER_AWARENESS' | undefined;
|
||||
const tint = (verdict && VERDICT_TINT[verdict]) || 'bg-muted-foreground text-muted-foreground';
|
||||
return {
|
||||
label: verdict ? `Classified as ${verdict}` : 'Campaign classified',
|
||||
icon: Sparkles,
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue