- 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
- Add acknowledge_user: 'Acknowledge user' to ACTION_LABEL
- Add case 'acknowledge_user' to ActionParamsForm mirroring no_action (no-params form)
- Renders as a normal checkbox + Approve action for every company; no automation-gate logic added (D-04) — the auto-approval carve-out lives only in the webhook path (Plan 05)
- Add USER_AWARENESS to ClassificationCardData verdict union
- Add emerald VERDICT_VARIANT_CLASS entry for USER_AWARENESS (distinct from UNWANTED amber and THREAT destructive)
- Add acknowledge_user: 'Acknowledge user' to ACTION_LABEL
classifications.confidence is 0.0-1.0 (1 = max confidence). ClassificationCard
appended "%" directly to the raw value, so confidence=1 rendered as "1%
confidence" -- read as near-zero, the opposite of its true meaning. Found live
while reviewing a Breach Secure Now simulated-phishing classification.
- Add GatedButton: buttons stay in the DOM (D-05) always, wrapped in a
Tooltip explanation when disabled
- Gate all three actions with hasPermission(role, 'phishing', 'approve'|
'remediate') from lib/permissions.ts — the identical check the server
routes enforce, never a bespoke role === 'admin' string check (REVIEW-06)
- Derive resolved = campaignStatus === 'false_positive' OR any completed
remediation_actions row; resolved-state tooltip mirrors the completed
action's approver/date or the campaign's updated_at
- Remediate: AlertDialog confirmation listing approved action count/types,
then POST /remediate (no body), refetch on success
- Mark as false positive: AlertDialog with optional reason Textarea, then
POST /mark-false-positive { reason? }; disabled reason mirrors the
server's 409 guard exactly (approved/completed remediation blocks it)
- Create ActionAreaCard with a null-guard on classification (renders an
informational note, never dereferences recommendedActions, for the
default grouped-but-unclassified state)
- Render one checkbox row per recommended action with an always-visible
params form pre-filled via deriveDefaultParams(actionType, evidence)
- Submit exact ApproveActionInput[] to POST /approve; purge_message
mailboxes is edited as a comma-separated string and normalized to
string[] at submit time
- Refetch via onActionComplete() on success (D-04, no optimistic mutation)
- components/phishing/evidence-card.tsx renders Headers, URLs,
Attachments, Body preview, and Blast Radius tabs for a selected
message
- Body preview renders inside a <pre className="whitespace-pre-wrap">
as plain JSX text only, never via a raw-HTML injection prop
- URLs tab delegates to UrlList (D-09 inert copy-only)
- Blast radius renders explicit unavailable-state copy or a
matched/delivered/held/rejected/clicked stat row + per-recipient
table when ok
- CardTitle explicitly overridden with font-bold per UI-SPEC typography
- Merges reports/classifications/audit-events into one ascending list
(relies on server ordering, no client-side sort)
- Per-kind icon/label/tint: FileText for reports, Sparkles tinted by
verdict for classifications, event_type table for audit rows
(remediation_approved/completed, campaign_marked_false_positive,
campaign_classified, humanized fallback for anything else)
- 8px rail dot + border-l connector per UI-SPEC Timeline Spec
- Renders latest classification verdict/confidence/summary/reasons
- Recommended-action chips (informational, no checkboxes)
- Requires-approval warning Alert when requiresApproval is true
- Reclassify button gated on hasPermission(role, 'phishing', 'analyze')
- Returns null when classification is missing (empty-state handled by plan 06)
- npx shadcn add tooltip generates components/ui/tooltip.tsx (official
registry, no npm dependency added)
- components/phishing/url-list.tsx renders extracted URLs as inert
<code> text with copy-to-clipboard only — no <a>/href, no <Link>,
no navigating onClick per D-09