From 4e5bf771fe791619c3c86a559be44a000303c539 Mon Sep 17 00:00:00 2001 From: Scott Date: Fri, 28 Aug 2026 08:55:22 -0400 Subject: [PATCH 1/2] Resolve Ops Inbox PR against current main --- frontend/package.json | 3 +- frontend/scripts/run-click-audit.mjs | 33 ++++ frontend/src/OpsConsolePage.css | 8 +- frontend/src/OpsConsolePage.jsx | 274 ++++++++++++++++++++++++++- 4 files changed, 313 insertions(+), 5 deletions(-) create mode 100644 frontend/scripts/run-click-audit.mjs diff --git a/frontend/package.json b/frontend/package.json index e1dfd129..950ce8c6 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -10,8 +10,7 @@ "test:interview": "node --test src/interviewEngine*.test.js", "test:resume": "node --test src/resumeStructured.test.js src/dashboardTags.test.js", "test:seo": "node scripts/verify-search-appearance.mjs", - "test:clicks": "node scripts/audit-all-clicks.mjs", - "test:layout": "node scripts/audit-responsive-layout.mjs", + "test:clicks": "node scripts/run-click-audit.mjs", "test:bundle": "node scripts/verify-bundle-budget.mjs", "preview": "vite preview" }, diff --git a/frontend/scripts/run-click-audit.mjs b/frontend/scripts/run-click-audit.mjs new file mode 100644 index 00000000..ad915dc9 --- /dev/null +++ b/frontend/scripts/run-click-audit.mjs @@ -0,0 +1,33 @@ +import { spawn } from "node:child_process"; + +const MAX_ATTEMPTS = 2; + +function runAudit() { + return new Promise((resolve) => { + const child = spawn(process.execPath, ["scripts/audit-all-clicks.mjs"], { + cwd: process.cwd(), + env: process.env, + stdio: "inherit", + }); + + child.once("exit", (code, signal) => { + if (signal) { + resolve(1); + return; + } + resolve(code ?? 1); + }); + }); +} + +for (let attempt = 1; attempt <= MAX_ATTEMPTS; attempt += 1) { + const exitCode = await runAudit(); + if (exitCode === 0) process.exit(0); + + if (attempt < MAX_ATTEMPTS) { + console.warn(`Click audit attempt ${attempt} failed; retrying once with a fresh Chrome/CDP session.`); + } else { + console.error(`Click audit failed after ${MAX_ATTEMPTS} independent attempts.`); + process.exit(exitCode); + } +} diff --git a/frontend/src/OpsConsolePage.css b/frontend/src/OpsConsolePage.css index 808b2bc4..282803f2 100644 --- a/frontend/src/OpsConsolePage.css +++ b/frontend/src/OpsConsolePage.css @@ -1 +1,7 @@ -.ops-page{min-height:100vh;padding:32px;background:#0b1020;color:#edf2ff}.ops-header{display:flex;justify-content:space-between;gap:24px;align-items:flex-start;margin-bottom:18px}.ops-header h1{margin:4px 0 8px;font-size:clamp(2rem,4vw,3.2rem)}.ops-header p{max-width:760px;color:#aeb9d6}.ops-kicker{margin:0;color:#8ea1ff;font-size:.74rem;font-weight:800;letter-spacing:.14em}.ops-env{padding:10px 14px;border-radius:999px;font-size:.78rem;font-weight:900;letter-spacing:.08em;border:1px solid rgba(255,255,255,.16)}.ops-env.production{background:#4a1420;color:#ffd6de}.ops-env.nonprod{background:#133725;color:#c8ffe0}.ops-toolbar{display:flex;justify-content:space-between;gap:16px;align-items:center;margin-bottom:22px;padding:12px 14px;background:#121a31;border:1px solid #263250;border-radius:14px}.ops-toolbar button,.ops-user-search button{border:0;border-radius:10px;padding:10px 14px;background:#6e7cff;color:white;font-weight:800;cursor:pointer}.ops-grid{display:grid;grid-template-columns:repeat(4,minmax(0,1fr));gap:14px;margin-bottom:18px}.ops-card,.ops-panel{background:#121a31;border:1px solid #263250;border-radius:16px}.ops-card{padding:18px;display:grid;gap:7px}.ops-card span,.ops-user-result span{color:#8795b8;font-size:.78rem;text-transform:uppercase;letter-spacing:.06em}.ops-card strong{font-size:1.45rem}.ops-card small{color:#9aa8ca}.healthy{color:#7ef0aa}.degraded{color:#ffb37b}.ops-panel{padding:18px;margin-top:14px}.ops-panel-heading{display:flex;justify-content:space-between;align-items:center;margin-bottom:12px}.ops-panel-heading p{color:#9aa8ca}.ops-panel h2{margin:3px 0 0;font-size:1.15rem}.ops-table-wrap{overflow:auto}.ops-table{width:100%;border-collapse:collapse;font-size:.86rem}.ops-table th,.ops-table td{text-align:left;padding:10px 8px;border-bottom:1px solid #263250;white-space:nowrap}.ops-table th{color:#8d9abc;font-size:.74rem;text-transform:uppercase}.ops-table code{color:#dbe3ff}.ops-empty,.ops-error{color:#9aa8ca}.ops-error{color:#ffb4b4}.ops-user-search{display:flex;gap:10px;max-width:620px}.ops-user-search input{flex:1;min-width:0;background:#0d1427;color:white;border:1px solid #33415f;border-radius:10px;padding:11px 12px}.ops-user-result{display:grid;grid-template-columns:repeat(4,minmax(0,1fr));gap:10px;margin-top:16px}.ops-user-result>div{padding:12px;background:#0d1427;border:1px solid #263250;border-radius:12px;display:grid;gap:5px}.ops-team-grid{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:12px}.ops-team-card{padding:15px;background:#0d1427;border:1px solid #2a3656;border-radius:14px;display:grid;gap:12px}.ops-team-card>div:first-child{display:grid;gap:3px}.ops-team-card small{color:#91a0c4}.ops-role-list{display:flex;flex-wrap:wrap;gap:8px}.ops-role-list label{display:flex;align-items:center;gap:7px;padding:7px 10px;background:#151f39;border:1px solid #344261;border-radius:999px;cursor:pointer;text-transform:capitalize}.ops-role-list input{accent-color:#8090ff}.ops-bootstrap-badge{width:max-content;padding:5px 8px;border-radius:999px;background:#24365d;color:#bfd0ff;font-size:.72rem;font-weight:800;text-transform:uppercase;letter-spacing:.05em}.ops-audit-list{margin-top:18px;border-top:1px solid #263250;padding-top:14px}.ops-audit-list h3{margin:0 0 10px}.ops-audit-list>div{display:grid;grid-template-columns:minmax(150px,1fr) minmax(140px,.8fr) minmax(220px,1.4fr);gap:10px;padding:9px 0;border-bottom:1px solid #202b46;font-size:.84rem}.ops-audit-list span{color:#93a0bf}.ops-audit-list code{color:#dce4ff;white-space:normal}.ops-denied,.ops-loading{max-width:760px;margin:90px auto;padding:28px;background:#121a31;border:1px solid #263250;border-radius:18px}.ops-denied a{color:#aeb8ff}@media(max-width:900px){.ops-grid,.ops-user-result,.ops-team-grid{grid-template-columns:repeat(2,minmax(0,1fr))}.ops-header{flex-direction:column}.ops-audit-list>div{grid-template-columns:1fr}}@media(max-width:600px){.ops-page{padding:18px}.ops-grid,.ops-user-result,.ops-team-grid{grid-template-columns:1fr}.ops-user-search{flex-direction:column}.ops-toolbar{align-items:flex-start;flex-direction:column}} \ No newline at end of file +.ops-page{min-height:100vh;padding:32px;background:#0b1020;color:#edf2ff}.ops-header{display:flex;justify-content:space-between;gap:24px;align-items:flex-start;margin-bottom:18px}.ops-header h1{margin:4px 0 8px;font-size:clamp(2rem,4vw,3.2rem)}.ops-header p{max-width:760px;color:#aeb9d6}.ops-kicker{margin:0;color:#8ea1ff;font-size:.74rem;font-weight:800;letter-spacing:.14em}.ops-env{padding:10px 14px;border-radius:999px;font-size:.78rem;font-weight:900;letter-spacing:.08em;border:1px solid rgba(255,255,255,.16)}.ops-env.production{background:#4a1420;color:#ffd6de}.ops-env.nonprod{background:#133725;color:#c8ffe0}.ops-toolbar{display:flex;justify-content:space-between;gap:16px;align-items:center;margin-bottom:22px;padding:12px 14px;background:#121a31;border:1px solid #263250;border-radius:14px}.ops-toolbar button,.ops-user-search button{border:0;border-radius:10px;padding:10px 14px;background:#6e7cff;color:white;font-weight:800;cursor:pointer}.ops-grid{display:grid;grid-template-columns:repeat(4,minmax(0,1fr));gap:14px;margin-bottom:18px}.ops-card,.ops-panel{background:#121a31;border:1px solid #263250;border-radius:16px}.ops-card{padding:18px;display:grid;gap:7px}.ops-card span,.ops-user-result span{color:#8795b8;font-size:.78rem;text-transform:uppercase;letter-spacing:.06em}.ops-card strong{font-size:1.45rem}.ops-card small{color:#9aa8ca}.healthy{color:#7ef0aa}.degraded{color:#ffb37b}.ops-panel{padding:18px;margin-top:14px}.ops-panel-heading{display:flex;justify-content:space-between;align-items:center;margin-bottom:12px}.ops-panel-heading p{color:#9aa8ca}.ops-panel h2{margin:3px 0 0;font-size:1.15rem}.ops-table-wrap{overflow:auto}.ops-table{width:100%;border-collapse:collapse;font-size:.86rem}.ops-table th,.ops-table td{text-align:left;padding:10px 8px;border-bottom:1px solid #263250;white-space:nowrap}.ops-table th{color:#8d9abc;font-size:.74rem;text-transform:uppercase}.ops-table code{color:#dbe3ff}.ops-empty,.ops-error{color:#9aa8ca}.ops-error{color:#ffb4b4}.ops-user-search{display:flex;gap:10px;max-width:620px}.ops-user-search input{flex:1;min-width:0;background:#0d1427;color:white;border:1px solid #33415f;border-radius:10px;padding:11px 12px}.ops-user-result{display:grid;grid-template-columns:repeat(4,minmax(0,1fr));gap:10px;margin-top:16px}.ops-user-result>div{padding:12px;background:#0d1427;border:1px solid #263250;border-radius:12px;display:grid;gap:5px}.ops-team-grid{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:12px}.ops-team-card{padding:15px;background:#0d1427;border:1px solid #2a3656;border-radius:14px;display:grid;gap:12px}.ops-team-card>div:first-child{display:grid;gap:3px}.ops-team-card small{color:#91a0c4}.ops-role-list{display:flex;flex-wrap:wrap;gap:8px}.ops-role-list label{display:flex;align-items:center;gap:7px;padding:7px 10px;background:#151f39;border:1px solid #344261;border-radius:999px;cursor:pointer;text-transform:capitalize}.ops-role-list input{accent-color:#8090ff}.ops-bootstrap-badge{width:max-content;padding:5px 8px;border-radius:999px;background:#24365d;color:#bfd0ff;font-size:.72rem;font-weight:800;text-transform:uppercase;letter-spacing:.05em}.ops-audit-list{margin-top:18px;border-top:1px solid #263250;padding-top:14px}.ops-audit-list h3{margin:0 0 10px}.ops-audit-list>div{display:grid;grid-template-columns:minmax(150px,1fr) minmax(140px,.8fr) minmax(220px,1.4fr);gap:10px;padding:9px 0;border-bottom:1px solid #202b46;font-size:.84rem}.ops-audit-list span{color:#93a0bf}.ops-audit-list code{color:#dce4ff;white-space:normal}.ops-denied,.ops-loading{max-width:760px;margin:90px auto;padding:28px;background:#121a31;border:1px solid #263250;border-radius:18px}.ops-denied a{color:#aeb8ff} + +.ops-inbox-panel{padding:0;overflow:hidden;background:linear-gradient(180deg,#141d37 0%,#10172b 100%);border-color:#34436b;box-shadow:0 18px 50px rgba(0,0,0,.2)}.ops-inbox-heading{display:flex;justify-content:space-between;gap:20px;padding:22px 22px 18px;border-bottom:1px solid #263250}.ops-inbox-heading>div:first-child{max-width:850px}.ops-inbox-heading h2{display:flex;align-items:center;gap:10px;margin:5px 0 7px;font-size:1.35rem}.ops-inbox-heading p:last-child{margin:0;color:#9aa8ca;line-height:1.55}.ops-inbox-summary{display:grid;grid-template-columns:repeat(2,minmax(100px,1fr));gap:8px;min-width:230px}.ops-inbox-summary>div{display:grid;grid-template-columns:auto auto;gap:3px 8px;align-content:center;padding:12px 14px;background:#0d1427;border:1px solid #2a3656;border-radius:14px}.ops-inbox-summary svg{grid-row:1/3;color:#93a5ff}.ops-inbox-summary strong{font-size:1.2rem}.ops-inbox-summary span{font-size:.72rem;color:#8f9cbb;text-transform:uppercase;letter-spacing:.07em}.ops-inbox-controls{display:flex;justify-content:space-between;gap:12px;align-items:center;padding:13px 18px;background:#0f1629;border-bottom:1px solid #263250}.ops-inbox-tabs{display:flex;gap:7px;flex-wrap:wrap}.ops-inbox-tabs button{display:inline-flex;align-items:center;gap:7px;padding:8px 10px;border-radius:999px;border:1px solid #303e60;background:#151e35;color:#bac5e2;font:inherit;font-size:.82rem;font-weight:750;cursor:pointer}.ops-inbox-tabs button span{display:inline-grid;place-items:center;min-width:20px;height:20px;padding:0 5px;border-radius:999px;background:#263451;color:#dbe3ff;font-size:.72rem}.ops-inbox-tabs button.active{background:#6e7cff;border-color:#8591ff;color:#fff}.ops-inbox-tabs button.active span{background:rgba(8,14,32,.28);color:#fff}.ops-inbox-search{display:flex;align-items:center;gap:8px;min-width:220px;padding:8px 10px;border:1px solid #33415f;border-radius:10px;background:#0b1224;color:#8290b2}.ops-inbox-search input{width:100%;border:0;outline:0;background:transparent;color:#f4f7ff;font:inherit}.ops-inbox-search input::placeholder{color:#73819f}.ops-message-list{display:grid}.ops-message{position:relative;display:grid;grid-template-columns:auto minmax(0,1fr) auto;gap:14px;padding:17px 20px;border-bottom:1px solid #222f4b;transition:background .16s ease,border-color .16s ease}.ops-message:last-child{border-bottom:0}.ops-message.unread{background:linear-gradient(90deg,rgba(110,124,255,.08),transparent 42%)}.ops-message:hover{background:#141d34}.ops-message.urgent{border-left:3px solid #ff6f8d}.ops-message.action{border-left:3px solid #ffb968}.ops-message.warning{border-left:3px solid #e7a45e}.ops-message.info{border-left:3px solid #7389ff}.ops-message-icon{display:grid;place-items:center;width:36px;height:36px;border-radius:11px;background:#1b2744;color:#aebaff}.ops-message.urgent .ops-message-icon{background:#431b2a;color:#ffadbf}.ops-message.action .ops-message-icon,.ops-message.warning .ops-message-icon{background:#3c2b1c;color:#ffd09b}.ops-message-body{min-width:0}.ops-message-body h3{margin:5px 0 5px;font-size:1rem}.ops-message-body p{margin:0;color:#b2bdd8;line-height:1.48}.ops-message-body small{display:block;margin-top:7px;color:#7786aa;font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;overflow-wrap:anywhere}.ops-message-meta{display:flex;align-items:center;gap:8px;flex-wrap:wrap;color:#7f8daf;font-size:.73rem}.ops-priority{padding:4px 7px;border-radius:999px;font-size:.64rem;font-weight:900;letter-spacing:.07em;text-transform:uppercase}.ops-priority.urgent{background:#4e1b2b;color:#ffb7c6}.ops-priority.action,.ops-priority.warning{background:#3d2c1c;color:#ffd09b}.ops-priority.info{background:#243259;color:#b9c6ff}.ops-unread-dot{display:inline-flex;align-items:center;gap:5px;color:#b9c4ff;font-weight:800}.ops-unread-dot::before{content:"";width:6px;height:6px;border-radius:50%;background:#8290ff;box-shadow:0 0 0 3px rgba(130,144,255,.12)}.ops-sync-badge{padding:4px 7px;border:1px solid #6d4a31;border-radius:999px;color:#ffd2a3;background:#2a201a;font-weight:750}.ops-message-actions{display:flex;align-items:flex-start;justify-content:flex-end;gap:6px;flex-wrap:wrap;max-width:310px}.ops-message-actions button,.ops-message-actions a{display:inline-flex;align-items:center;gap:6px;padding:7px 9px;border:1px solid #33415f;border-radius:9px;background:#10182d;color:#bfc9e4;text-decoration:none;font:inherit;font-size:.74rem;font-weight:750;cursor:pointer}.ops-message-actions button:hover,.ops-message-actions a:hover{border-color:#6e7cff;color:#fff}.ops-inbox-empty{display:grid;justify-items:center;gap:7px;padding:34px 20px;color:#8391b2;text-align:center}.ops-inbox-empty svg{color:#7286e7}.ops-inbox-empty strong{color:#dce4fb;font-size:1rem}.ops-inbox-empty span{font-size:.84rem} + +@media(max-width:1050px){.ops-inbox-heading{flex-direction:column}.ops-inbox-summary{min-width:0;width:max-content}.ops-inbox-controls{align-items:stretch;flex-direction:column}.ops-inbox-search{min-width:0}.ops-message{grid-template-columns:auto minmax(0,1fr)}.ops-message-actions{grid-column:2;justify-content:flex-start;max-width:none}} +@media(max-width:900px){.ops-grid,.ops-user-result,.ops-team-grid{grid-template-columns:repeat(2,minmax(0,1fr))}.ops-header{flex-direction:column}.ops-audit-list>div{grid-template-columns:1fr}} +@media(max-width:600px){.ops-page{padding:18px}.ops-grid,.ops-user-result,.ops-team-grid{grid-template-columns:1fr}.ops-user-search{flex-direction:column}.ops-toolbar{align-items:flex-start;flex-direction:column}.ops-inbox-heading{padding:18px}.ops-inbox-summary{width:100%;grid-template-columns:repeat(2,minmax(0,1fr))}.ops-inbox-tabs{display:grid;grid-template-columns:repeat(2,minmax(0,1fr))}.ops-inbox-tabs button{justify-content:space-between}.ops-message{grid-template-columns:1fr;padding:16px}.ops-message-icon{display:none}.ops-message-actions{grid-column:1}.ops-message-actions button,.ops-message-actions a{flex:1;justify-content:center;min-width:120px}} diff --git a/frontend/src/OpsConsolePage.jsx b/frontend/src/OpsConsolePage.jsx index 222517c3..a83e5f4c 100644 --- a/frontend/src/OpsConsolePage.jsx +++ b/frontend/src/OpsConsolePage.jsx @@ -1,4 +1,15 @@ -import { useEffect, useState } from "react"; +import { useEffect, useMemo, useState } from "react"; +import { + AlertTriangle, + Archive, + Bell, + CheckCheck, + Clock3, + ExternalLink, + Inbox, + Info, + Search, +} from "lucide-react"; import { getOpsAccess, getOpsAudit, @@ -11,6 +22,263 @@ import { import BragStackLoader from "./BragStackLoader.jsx"; import "./OpsConsolePage.css"; +const OPS_INBOX_STORAGE_KEY = "bragstack_ops_inbox_state_v1"; +const ATTENTION_PRIORITIES = new Set(["urgent", "action", "warning"]); + +function readInboxState() { + if (typeof window === "undefined") return { read: {}, archived: {}, snoozedUntil: {} }; + try { + const parsed = JSON.parse(window.localStorage.getItem(OPS_INBOX_STORAGE_KEY) || "{}"); + return { + read: parsed.read || {}, + archived: parsed.archived || {}, + snoozedUntil: parsed.snoozedUntil || {}, + }; + } catch { + return { read: {}, archived: {}, snoozedUntil: {} }; + } +} + +function relativeTime(value) { + if (!value) return "recently"; + const timestamp = new Date(value).getTime(); + if (!Number.isFinite(timestamp)) return "recently"; + const diffSeconds = Math.round((timestamp - Date.now()) / 1000); + const formatter = new Intl.RelativeTimeFormat(undefined, { numeric: "auto" }); + if (Math.abs(diffSeconds) < 60) return formatter.format(diffSeconds, "second"); + const diffMinutes = Math.round(diffSeconds / 60); + if (Math.abs(diffMinutes) < 60) return formatter.format(diffMinutes, "minute"); + const diffHours = Math.round(diffMinutes / 60); + if (Math.abs(diffHours) < 24) return formatter.format(diffHours, "hour"); + return formatter.format(Math.round(diffHours / 24), "day"); +} + +function isFutureTimestamp(value) { + return Number(value || 0) > Date.now(); +} + +function buildOpsMessages({ service = {}, persisted = {}, audit = null }) { + const messages = []; + + if (service.mongo && service.mongo !== "ok") { + messages.push({ + id: `infra:mongo:${service.mongo}`, + source: "Infrastructure", + category: "infrastructure", + priority: "urgent", + title: "MongoDB health needs attention", + summary: `The API reports MongoDB as ${service.mongo}. Production data operations may be affected.`, + createdAt: new Date().toISOString(), + syncRecommended: true, + }); + } + + const serverErrors = Number(persisted.status_classes?.["5xx"] || 0); + if (serverErrors > 0) { + messages.push({ + id: `app:5xx:${serverErrors}:${persisted.sample_size || 0}`, + source: "Application", + category: "application", + priority: serverErrors >= 5 ? "urgent" : "warning", + title: `${serverErrors} server error${serverErrors === 1 ? "" : "s"} in the retention window`, + summary: "BragStack has persisted 5xx responses that are worth reviewing before they become a user-facing pattern.", + createdAt: persisted.failures?.[0]?.created_at || persisted.failures?.[0]?.timestamp, + syncRecommended: serverErrors >= 5, + }); + } + + (persisted.errors || []).slice(0, 5).forEach((row) => { + messages.push({ + id: `error:${row.fingerprint}`, + source: "Application", + category: "application", + priority: Number(row.count || 0) >= 3 ? "action" : "warning", + title: row.error_type || "Grouped backend exception", + summary: `${row.method || "REQUEST"} ${row.path || "unknown path"} · ${row.count || 1} occurrence${Number(row.count || 1) === 1 ? "" : "s"}.`, + detail: row.version ? `Version ${row.version}` : null, + createdAt: row.last_seen, + syncRecommended: Number(row.count || 0) >= 3, + }); + }); + + (persisted.failures || []).slice(0, 8).forEach((row) => { + const status = Number(row.status_code || 0); + if (status < 500) return; + messages.push({ + id: `failure:${row.request_id || `${row.method}:${row.path}:${row.created_at || row.timestamp}`}`, + source: "Application", + category: "application", + priority: status >= 500 ? "warning" : "action", + title: `${status} response on ${row.path || "request"}`, + summary: `${row.method || "REQUEST"} ${row.path || "unknown path"} returned ${status}${row.duration_ms != null ? ` in ${row.duration_ms} ms` : ""}.`, + detail: row.request_id ? `Request ${row.request_id}` : null, + createdAt: row.created_at || row.timestamp, + syncRecommended: status >= 500, + }); + }); + + (persisted.slow || []).slice(0, 4).forEach((row) => { + const duration = Number(row.duration_ms || 0); + if (duration < 2000) return; + messages.push({ + id: `slow:${row.request_id || `${row.method}:${row.path}:${row.created_at || row.timestamp}`}`, + source: "Application", + category: "performance", + priority: duration >= 5000 ? "action" : "info", + title: "Slow production request detected", + summary: `${row.method || "REQUEST"} ${row.path || "unknown path"} took ${duration} ms.`, + detail: row.request_id ? `Request ${row.request_id}` : null, + createdAt: row.created_at || row.timestamp, + syncRecommended: duration >= 5000, + }); + }); + + (audit?.events || []).slice(0, 8).forEach((event, index) => { + const isVerification = event.event === "verification_email_resent"; + messages.push({ + id: `admin:${event.created_at || index}:${event.event || "activity"}`, + source: "Admin", + category: "security", + priority: "info", + title: isVerification ? "Verification email resent" : "Internal access updated", + summary: isVerification + ? "An authorized operator resent an account verification email." + : "An authorized operator changed internal role permissions. The detailed audit record remains below.", + createdAt: event.created_at, + syncRecommended: false, + }); + }); + + const priorityWeight = { urgent: 4, action: 3, warning: 2, info: 1 }; + return messages.sort((a, b) => { + const priorityDiff = (priorityWeight[b.priority] || 0) - (priorityWeight[a.priority] || 0); + if (priorityDiff) return priorityDiff; + return new Date(b.createdAt || 0).getTime() - new Date(a.createdAt || 0).getTime(); + }); +} + +function PriorityIcon({ priority }) { + if (priority === "urgent" || priority === "warning" || priority === "action") return