From 9702b90d407dfd98e104e6ee78e8549fc1edcef3 Mon Sep 17 00:00:00 2001
From: Logan Cusano
Date: Sun, 25 Oct 2026 22:07:22 -0400
Subject: [PATCH 1/2] fix(frontend): hide admin-only node and system controls
from non-admins (#222, #283)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
UI gate only — no route auth changes. Node page: override Ack/Force
Revert, Configure/Reassign and Restart OP25 are admin-only; non-admins on
an unconfigured node get a "an admin needs to assign it" note. /nodes
"Needs Configuration" cards open the config modal only for admins, and
link to the node page otherwise. /systems: New/Edit/Duplicate/Delete and
the AI-flags, area-context, talkgroup-pending and vocabulary panels are
admin-only; operators get a read-only list. (Delete's missing catch was
fixed in #290.)
Widening any of these routes to org owners/operators is a separate,
security-reviewed backend change.
Frontend unverified: no Node on the authoring box (#207).
Closes #222
Closes #283
Co-Authored-By: Claude Opus 5.5
---
drb-frontend/app/nodes/[id]/page.tsx | 40 +++++++++++++++++-----------
drb-frontend/app/nodes/page.tsx | 12 ++++++---
drb-frontend/app/systems/page.tsx | 34 ++++++++++++++---------
3 files changed, 56 insertions(+), 30 deletions(-)
diff --git a/drb-frontend/app/nodes/[id]/page.tsx b/drb-frontend/app/nodes/[id]/page.tsx
index 0174722..72aa159 100644
--- a/drb-frontend/app/nodes/[id]/page.tsx
+++ b/drb-frontend/app/nodes/[id]/page.tsx
@@ -160,7 +160,8 @@ export default function NodeDetailPage() {
<> No timeout is currently enforced (permanent override).>
)}
}
)}
@@ -245,20 +246,29 @@ export default function NodeDetailPage() {
)}
{/* Actions */}
+ {/* Configure and Restart are admin-only on the server (server-26#222);
+ non-admins get a note instead of a button that 403s. */}
+ {!isAdmin && !node.configured && (
+
This node isn't configured yet. An admin needs to assign it a radio system.
}
{/* GET /tokens and PUT /tokens/{id}/prefer are platform-admin only
(server-26#204 item 6), so for anyone else this panel could
neither list the pool nor save a choice (server-26#213 item 4). */}
{isAdmin && }
-
-
-
-
+ {/* All admin-only routes (server-26#283), incl. area-context and
+ talkgroup-pending GETs and every vocabulary write. */}
+ {isAdmin && (
+ <>
+
+
+
+
+ >
+ )}
);
})}
--
2.54.0
From e8084357c3d9c1938eb92e553320c75e764a1b75 Mon Sep 17 00:00:00 2001
From: Logan Cusano
Date: Sun, 25 Oct 2026 22:09:57 -0400
Subject: [PATCH 2/2] fix(map): keep muted popup text AA on Leaflet's white
popups
Token text-ink-muted is #8C98AE in dark mode, ~2.9:1 on the always-white
popups (node, incident, path-stop, AIS, 511 camera). Scoped override to
#5B6577 (~6:1). Found in tonight's regression review.
Co-Authored-By: Claude Opus 5.5
---
drb-frontend/app/globals.css | 7 +++++++
1 file changed, 7 insertions(+)
diff --git a/drb-frontend/app/globals.css b/drb-frontend/app/globals.css
index 9dcc7c2..372887b 100644
--- a/drb-frontend/app/globals.css
+++ b/drb-frontend/app/globals.css
@@ -199,6 +199,13 @@ html:not(.dark) .border-indigo-800 { border-color: #a5b4fc !important; }
filter: invert(1) hue-rotate(180deg) brightness(0.9) contrast(0.9) saturate(0.6);
}
+/* Leaflet popups stay white in both themes (their body text is a fixed
+ * gray-900), so the theme's muted ink — light grey in dark mode, ~2.9:1 on
+ * white — is pinned to a grey that holds AA on white (~6:1). */
+.leaflet-popup-content .text-ink-muted {
+ color: #5B6577;
+}
+
/* Pinned 511 camera tiles (MapView DotCameraLayer): strip Leaflet's tooltip
* padding and let the tile's own theme colours show, so the tile is no bigger
* than the video it holds. */
--
2.54.0