Skip to content

feat(ui): PrimeVue 4 migration + remove @featherds/styles SCSS build dependency#8

Open
cnewkirk wants to merge 698 commits into
developfrom
feat/ui-refactor-omnibus
Open

feat(ui): PrimeVue 4 migration + remove @featherds/styles SCSS build dependency#8
cnewkirk wants to merge 698 commits into
developfrom
feat/ui-refactor-omnibus

Conversation

@cnewkirk

@cnewkirk cnewkirk commented May 1, 2026

Copy link
Copy Markdown
Owner

Summary

  • Topology node icons — SVG network device icons (router, switch, firewall, server, load-balancer, console, PDU, etc.) rendered on topology nodes with alarm-severity-colored strokes. Three-tier resolution: user regex patterns → OpenNMS node categories → built-in label patterns → server fallback. Async category enrichment upgrades icons after node data loads. New "Name Pattern → Icon" settings section for user-defined overrides. INDETERMINATE severity color fixed to pass WCAG AA contrast (3:1) against the dark node background.
  • Admin JSP retirement — categories.jsp, sendevent.jsp, applications.jsp, newInterface.jsp retired with Vue SPA redirects. Vue Add Interface page added.
  • Style / dependency cleanup — `@featherds/styles` removed from direct dependencies; `~@featherds` alias removed from vite config; local `_utils.scss` replaces upstream imports throughout.
  • Topology lab — RRD history preserved across lab churns; overlay image bakes in default password hash at build time.

New files (icons feature)

  • `ui/src/composables/useNodeIconResolver.ts` — icon resolution composable (19 tests)
  • `ui/src/assets/topology-icons/console.svg`
  • `ui/src/assets/topology-icons/pdu.svg`
  • `ui/tests/components/Topology/iconRegistry.test.ts`
  • `ui/tests/composables/useNodeIconResolver.test.ts`

Test plan

  • Open topology view — all nodes render with dark circle + device icon
  • spine-01/02 → router icon, leaf-01/02/03 → switch icon, host-02 → server icon, IP prefix nodes → cloud icon
  • Trigger an alarm — icon stroke and border ring change color to match severity
  • Select a node — dark background preserved (not overwritten by selection color)
  • Verify a down node — red border ring, dark background, icon still visible
  • Open icon settings → Name Pattern → Icon section appears, add a rule, verify it takes effect on graph
  • Existing icon category and OID tabs unchanged

🤖 Generated with Claude Code

Chance Newkirk added 30 commits April 12, 2026 21:08
…migration shim

Replaces flat WidgetConfig with per-type discriminated union interfaces
(summary, table, graph, node-status, availability), bumps CONFIG_VERSION
to 3, adds DashboardTimeRange + resolveTimeRange, and adds a v2→v3
migration shim in loadConfig/loadFromServer.
…protocol

- build-dark-mode-overlay.sh: sed-patch connect-src to allow tiles.opennms.org,
  openstreetmap.org, and opentopomap.org, plus worker-src 'self' blob: so
  MapLibre's Web Workers load. The overlay script is the only path that
  actually reaches the running container; editing webapp/WEB-INF/web.xml alone
  is a dead letter (48e44da, 47f013b confirmed this the hard way).
- CLAUDE.md: add container-freshness check to the session-start protocol and
  spell out that deploy-to-container.sh only covers ui/src/ — anything touching
  JSPs, jars, features, or config XML needs a full overlay rebuild.
Dark-mode outlined severity styling was unreadable on the softer light-mode
surfaces (warning/minor amber on light gray = invisible). Move the light-mode
overrides into the global opennms-feather-styles.scss so they apply to both
SeverityBadge and the Alarms filter chips without fighting Vue's scoped-CSS
transformer, which silently drops descendant selectors inside :global(X) .child
blocks.

Also deepen the light-mode surface palette (--feather-surface plus the full
--feather-elevation-background-* stack that Feather DS hardcodes to #fff) and
boost border/shade opacity so card edges and row dividers remain visible
against the darker gray-blue background. SummaryWidget's warning KPI value
gets a dedicated dark-amber color since #fbe947 is unreadable on light.
…tence

Create the structural shell of SideNav.vue with:
- Collapse state managed locally with localStorage persistence
- Toggle button with PrimeIcons (pi-angle-left/pi-angle-right)
- CSS Grid integration via sidenav-collapsed class on .app-shell
- Accessible aria-expanded and aria-label attributes

Also add placeholder TopBar.vue to satisfy App.vue imports.

Task 3 of 11: Shell redesign — collapse mechanics only, nav items
added in Task 4.
…etConfigDialog to thin shell

Creates ui/src/components/Dashboard/config/ with SummaryWidgetConfigForm, TableWidgetConfigForm,
GraphWidgetConfigForm (stub), NodeStatusWidgetConfigForm, and AvailabilityWidgetConfigForm using
PrimeVue 4 inputs. WidgetConfigDialog is now a thin shell that dispatches to the correct form
based on widget type, using typed computed refs to avoid Vue template TS cast limitations.
Replaces collapse-only skeleton with full nav implementation: three
collapsible sections (Monitor, Network, Administration), plugin items
from pluginStore, auto-open on active route, and per-section accordion.
Applies Task 3 review fixes: no-op width transition removed, corrected
aria-expanded placement on toggle button, rgba hover backgrounds, and
focus-visible outlines on all interactive elements.
…nt boundary

- autoOpenSection: use exact match or startsWith(item.to + '/') to avoid
  false prefix matches (e.g. /nodes hitting /node-details)
- Admin section subheaders now carry visible guards matching their children:
  System/MonitoringConfig/Notifications → adminRole, Tools → adminRole||dcbRole
  Provisioning left ungated (has two ungated child items)
- Remove unnecessary dynamic binding :title="'Plugins'" → title="Plugins"
…n routes

- Replace FeatherIcon/SearchIcon in Search.vue with inline SVG
- Overwrite TopBar.vue stub with full PrimeVue implementation (logo, search, add-node button, datetime, notifications, user menu, theme toggle)
- Wire expanded prop for UserNotificationsMenuItem and UserSelfServiceMenuItem
- Add /provision/quick-add and /provision/requisitions stub routes to router
…ingle dropdown

Co-Authored-By: Chance Newkirk <chance.newkirk+github@pm.me>
…PrimeVue

Wire menu/App.vue to Shell/TopBar.vue and Shell/SideNav.vue; remove dead
Feather DS FeatherAppBar/FeatherSidenav components and their imports.
Chance Newkirk added 30 commits May 2, 2026 13:04
Admin hub: remove page-level surface override so cards get real
elevation contrast against the background; add border-light border and
slightly rounded corners to AdminCard.

UserAccount: new Vue page at /account with change-password form using
the existing REST service. UserSelfServiceMenuItem now routes to /account
via Vue router instead of navigating to the legacy selfService JSP.

Assisted-By: Claude <noreply@anthropic.com>
The lab containers persist across test-opennms rebuilds, but the test
container's /opt/opennms/etc/imports/Topology-Lab.xml is wiped every
podman rm/run cycle — leaving the lab orphaned (containers up, OpenNMS
unaware) and the UI with a near-empty state until reattached.

- .topology-lab/Topology-Lab.xml: single-source requisition XML
- provision-topology-lab.sh: idempotent reattach script — copies the
  XML into test-opennms, triggers import, patches snmpifspeed back to
  1 Gbps after provisiond's racy initial scan, cleans up EnLinkd
  auto-discovered duplicates, reloads EnLinkd
- start-topology-lab.sh phase 7 now calls provision-topology-lab.sh
  instead of duplicating ~130 lines of inline XML and provisioning
  logic — single source of truth
- CLAUDE.md: both checks (lab containers up + Topology-Lab requisition
  present) are now default session-start steps for any UI work, not
  gated on "is this a topology task?" Includes the rebuild-wipe gotcha
- EnlinkdLinksTab.vue: PanelLoader straggler missed from the earlier
  ProgressSpinner sweep
…+ JSP redirects

Replaces 6 legacy JSPs with Vue SPA pages: Help (doc/community/support links),
Support (commercial + community cards), SystemStatus (daemon status from /rest/info),
About (system info from /api/v2/system/about), and RtcCategory (nodes-in-category
table with active outage cross-reference). Adds systemInfoService.ts, 5 new routes,
legacy route map entries, and JSP redirect scriptlets for all 6 paths.
… + JSP redirects

- Domain D: KscReports list + KscReportDetail with graph image rendering
- Domain F: Reports list with filter, links to legacy report runner
- Domain C: Notifications list + NotificationDetail with acknowledge support
- Domain B: Assets node browser + AssetEdit form + Hardware inventory viewer
- 4 service files: kscReportService, reportService, notificationInboxService, assetService
- 8 router entries added before /account
- 5 JSP redirects: KSC/index, report/index, notification/index, asset/index, hardware/list

Assisted-By: Claude Sonnet 4.6
Add global PrimeVue (unstyled) to tests/setup.ts so all component tests
that mount PrimeVue widgets (InputText, Button, Menu, Paginator) get the
required $primevue.config injection without style-sheet DOM errors.

EventConfigEventTable.vue: add data-property attrs to sortable th
elements and defineExpose({ sortChanged, sort, columns, expandedRows })
so test assertions can drive sort state directly.

EventConfigEventTable.test.ts: align all 64 assertions to the current
PrimeVue-based component API — th selectors replace SortHeader finders,
Menu model prop replaces MenuItem DOM stub traversal, Paginator @page
event replaces update:modelValue, and icon names use pi pi-* prefix.

EmptyList.vue: add data-test="empty-list" to root div for test
findability.

Assisted-By: Claude Sonnet 4.6
Includes help, about, status, support, rtc/index, rtc/category,
KSC/index, report/index, notification/index, and hardware/list redirects
in the staging and Dockerfile COPY sections so they survive container
rebuilds.

All new JSPs now return HTTP 302 to their respective /ui/* routes.
rtc/category.jsp preserves the ?category= query parameter.

Assisted-By: Claude Sonnet 4.6
Browser default body { margin: 8px } was never overridden,
producing a visible white gap on left, right, and top in all themes.

Assisted-By: Claude Sonnet 4.6
PrimeVue Aura's {list.option.focus.background} resolves to {slate.100}
(#f1f5f9) which is near-white and invisible on our surface, and under
certain specificity conditions can render as a dark/black bar instead.
Replace with a theme-neutral rgba(0,0,0,0.08) overlay under
html:not(.open-dark) to guarantee a visible tint on any light surface.

Assisted-By: Claude Sonnet 4.6
All six dashboard widgets (Alarms, Outages, Nodes, NodeStatus,
Availability, Summary) previously rendered empty/default states
immediately on mount before their AJAX calls returned. This made the
dashboard flash "All services up", "100% availability", or "No nodes
found" briefly before real data arrived.

Each widget now initialises isLoading=true, renders PanelLoader while
the fetch is in-flight, and reveals content (or the real empty-state)
only in the finally block. WidgetFrame still owns the manual-refresh
spinner, so the two concerns don't overlap.

Assisted-By: Claude Sonnet 4.6
46 JSPs that still rendered legacy content now issue a sendRedirect
to the corresponding Vue route on first request, so bookmarks and
server-provided links land in the correct Vue page.

Simple redirects: account/selfService/*, roles/*, application/index.jsp,
report/database/index.jsp, admin/snmpmanage.jsp, admin/nodelabel.jsp,
all admin/discovery/add-*.jsp and config-done/scan-done, all
admin/notification/wizard sub-pages, admin/nodemanagement/* pages,
admin/sched-outages/editoutage.jsp, admin/product-update-enrollment,
alarm|event/severity.jsp, asset/nodelist.jsp, and action-result
confirmation pages (deleteNodesFinish, manageNodesFinish,
interfaceAdded, interfaceDeleted, serviceDeleted, postevent).

Parameterised redirects (preserve ID from request param):
  notification/detail.jsp → /ui/notification/:id
  asset/modify.jsp        → /ui/asset/:nodeId/edit
  admin/nodelabel.jsp     → /ui/node/:nodeId

JSP changes require overlay image rebuild to take effect.

Assisted-By: Claude Sonnet 4.6
Previously had 22 entries covering only the most common menu paths.
Now maps all 65+ known server menu URLs to their Vue equivalents,
covering monitoring, inventory, admin, account, and tools sections.

The file is reference documentation and is also used as a lookup
table by any future caller of resolveVueRoute(). The SideNav uses
static router-link entries directly, so this file is not on the
hot navigation path.

Assisted-By: Claude Sonnet 4.6
…hout

Dashboard widgets: add position:relative to WidgetFrame.widget-body and
switch widget PanelLoaders to overlay mode so the spinner centers inside
the card instead of appearing in the upper-left corner.

Full-page loaders: replace "Loading…" text in ResourceGraphsPanel,
NetworkTab, NodeDetails, InterfaceDetail, OutageDetail, NotificationDetail,
AssetEdit, ScheduledOutages, GeocoderConfig, SystemConfig, ThresholdConfig
with centered PanelLoader wrappers.

Assisted-By: Claude Sonnet 4.6
Replace the manually-maintained list of individual cp/COPY commands with
a grep-based loop that automatically discovers every JSP/HTM file in the
source tree that contains sendRedirect. The Dockerfile COPY lines are
generated dynamically from the staged overlay directory using a find()
command substitution inside the heredoc.

Before this change, only ~30 JSPs were in the overlay; ~80 committed
sendRedirect files (admin/*, element/*, account/*, alarm/*, asset/*, etc.)
were never staged and therefore never activated in the test container.
This closes the gap without requiring manual maintenance whenever new
redirect JSPs are added.

Assisted-By: Claude Sonnet 4.6
28 tests covering simple path redirects, parameterized detail-page
redirects, POST pass-through, AJAX pass-through, and REST/Vue
fast-path pass-through. Class under test does not exist yet.

Assisted-By: Claude Sonnet 4.6
Replaces per-JSP sendRedirect stubs and fills the Spring MVC list-route gap
(alarm/list.htm, event/list.htm, outage/list.htm, notification/browse,
element/service.jsp) with a single Filter that fires before any JSP or
DispatcherServlet execution. Covers 80+ simple paths and 16 parameterized
patterns (node detail, interface, alarm/event/outage/notification detail, asset edit).

Assisted-By: Claude Sonnet 4.6
…rity

Filter runs after auth check, before StoreRequestProperties — avoids
unnecessary processing for redirected requests.

Assisted-By: Claude Sonnet 4.6
…tFilter

Per-JSP sendRedirect calls are now dead code — the filter intercepts GET
requests before any JSP executes. Removed all 126 pure-redirect stubs,
including both top-level JSPs (about, admin, alarm, element, etc.) and
WEB-INF/jsp view templates that contained nothing but redirect logic.

Files retained: login.jsp, logoff.jsp, accessDenied.jsp, errors/, geomap/,
heatmap/, trend/, rss.jsp, and any JSP with substantive non-redirect content.

Assisted-By: Claude Sonnet 4.6
The overlay build patches web.xml in-place via sed/python rather than
copying the source version (which would break CXF servlet mappings).
Add a python3 RUN step that inserts the <filter> declaration before
the first <filter-mapping> block and the <filter-mapping> after the
springSecurityFilterChain mapping so auth runs before redirect logic.

Assisted-By: Claude Sonnet 4.6
Both tests assumed list() returned a ModelAndView with query results.
list() now redirects to Vue and returns null. Replace with redirect
verification.

Assisted-By: Claude Sonnet 4.6
…ntrollerTest

Three private helpers (getOnmsEvents, getAlarms, getEvents) and their
associated imports were left behind when the old tests were replaced.
No test references them.

Assisted-By: Claude Sonnet 4.6
…ering JSP

createFavorite and deleteFavorite on AlarmFilterController and
EventController called a private list() helper that rendered the legacy
alarm/list and event/list JSP templates. Now they redirect to /ui/alarms
and /ui/events, completing the Vue migration for these endpoints.

Assisted-By: Claude Sonnet 4.6
OutageFilterController and NotificationFilterController rendered
outage/list and notification/list JSP views. Replace handleRequestInternal
with a direct redirect to /ui/outages and /ui/notifications. The
LegacyRedirectFilter already intercepts GETs; this closes the POST path.

Assisted-By: Claude Sonnet 4.6
Add 20 SIMPLE_REDIRECTS entries for ad-hoc graph wizard steps
(adhoc2-4, chooseresource, forecast, nrtg, grafana, results),
KSC report workflow (customReport, customView, customGraphEditDetails,
formProc*), and trend views — all redirecting to /resource-graphs
or /ksc-reports. 6 new tests verify representative redirects all pass.

Assisted-By: Claude Sonnet 4.6
…ctFilter

- 7 graph ad-hoc wizard JSPs (GET paths redirect, no forms remain)
- 1 KSC customGraphChooseResource wizard JSP
- 9 orphaned widget JSPs (geomap, heatmap-box, rtc, status-box, trend)
- 2 orphaned JSON AJAX endpoints (sched-outages autocomplete)
- 5 WEB-INF/jsp view templates whose controllers now redirect to Vue

Assisted-By: Claude Sonnet 4.6
…ollers

All four Spring MVC controllers now redirect to Vue SPA routes; the
private list() overloads, query-parms builders, filter-callback helpers,
backing fields, and unused imports that were only reachable from those
dead paths have been removed.  Spring XML property wiring for the
dropped fields in OutageFilterController and NotificationFilterController
has been stripped from dispatcher-servlet.xml.  EventControllerTest
setUp() no longer calls the removed setWebAlarmRepository() setter.

Assisted-By: Claude Sonnet 4.6
…rTest

Covers two previously untested filter branches: HEAD requests to a
known legacy path should redirect (not pass through), and a mixed
Accept header that includes both text/html and application/json should
also redirect rather than being treated as an AJAX/API call.

Assisted-By: Claude Sonnet 4.6
…ty removal

NotificationFilterController and OutageFilterController had their successView/
defaultShortLimit/etc setters removed, but dispatcher-servlet.xml lives in the
base image WEB-INF — not in the webapp jar. Without copying it, Jetty tries to
inject the old properties into the stripped controllers and fails to start.

Assisted-By: Claude Sonnet 4.6
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant