Commit graph

739 commits

Author SHA1 Message Date
2beadb0dc7
ux: show sync interval on each integration card
Display "Syncs every Xmin" (or "Xh") alongside the next sync countdown
so users can see the cadence at a glance without opening the configure
panel.
2026-03-12 15:02:30 -05:00
da5cd4054b
fix: clear sync status when integration credentials change
When saving new or updated API credentials, reset last_sync_status to
"never" and clear last_sync_message and last_synced_at so stale error
messages from previous syncs don't persist in the UI.
2026-03-12 14:58:36 -05:00
bbd74e895f
fix: prevent credential inputs resetting on re-render for all integrations
All non-NetBox provider forms (Sonar, Splynx, Gaiia, Preseem, PagerDuty,
VISP) had the same bug as NetBox: credential inputs used value= bindings
reading from saved DB state. Any phx-click event (Test Connection, etc)
triggering a re-render would wipe unsaved typed values.

Fix by adding @integration_credentials assign, initialized from saved
integration when opening config and updated from form params on every
phx-change via validate_integration.
2026-03-12 14:31:31 -05:00
f980e9eb6d
fix: preserve NetBox url/api_token in netbox_config across re-renders
Sync direction button clicks (phx-click) triggered a LiveView re-render
that reset the url and api_token inputs to their saved DB values, wiping
any unsaved typed text. Fix by tracking url and api_token in @netbox_config
so they survive click-triggered re-renders.
2026-03-12 14:25:08 -05:00
6db0213686
remove subtitle from alerts page 2026-03-12 14:09:05 -05:00
5d1d277697
feat: add alert count badge to notification bell
Shows a red badge on the bell icon with the unresolved alert count.
Displays the number when <= 100, blank red dot when > 100 (Apple-style).
Count is loaded at mount and updated live when alerts change via PubSub.
Uses process dictionary pattern (same as cookie consent) so no template
changes needed across the 41 LiveView layouts.
2026-03-12 14:06:18 -05:00
ecc69b4e96
ux: improve navigation, tooltips, and discoverability across pages
- Replace "Back to X" links with proper breadcrumb trails on settings,
  integrations, Gaiia, and Preseem pages
- Fix active_page on config-timeline routes so Devices nav highlights
- Add title tooltip to truncated alert messages (hover to see full text)
- Add tooltips to QoE column header explaining the metric
- Add tooltips to Subs/MRR labels on dashboard status bar
2026-03-12 13:40:10 -05:00
58181923f8
improve Gaiia integration UX
- Make insight messages more descriptive (explain what untracked/ghost/mismatch means)
- Clarify action button labels on insight cards
- Redesign Gaiia mapping page to be TowerOps-centric: devices/sites as primary rows, search Gaiia for what to link them to
- Add IP-based match suggestions and smart sorting (suggestions first, then unlinked, then linked)
- Add search_inventory_items/2 and search_network_sites/2 to Gaiia context
- Make IP addresses clickable links (http://<ip>)
- Add optional Gaiia App URL setting to enable deep links to inventory items and network sites
2026-03-12 13:04:17 -05:00
1b8fb606a5
remove dedicated capacity page from nav and routing
Capacity functionality will be surfaced through insights instead.
2026-03-12 12:20:23 -05:00
904ced77b9
fix: add recurring job dispatch timer for agent-polled devices
The server only sent polling/ping jobs to agents once on WebSocket join
and never re-dispatched them. After the first batch completed (~5-10s),
devices were never polled again until the agent reconnected.

:send_jobs now schedules a follow-up dispatch every 60s (configurable).
Unifies the timer management with :assignments_changed to prevent
accumulation.
2026-03-12 12:07:34 -05:00
80304329bd
improve network map topology visualization
- fix bidirectional link queries to find links where device is source or target
- merge A→B and B→A edges into single link with combined confidence
- include interface speed in edge data for variable edge width
- infer discovered node roles from LLDP/CDP capabilities (router, switch, wireless, phone)
- store remote_capabilities in link metadata during evidence processing
- replace Cytoscape destroy-and-recreate with element diffing to preserve zoom/pan/positions
- add node detail slide-out panel with device info, connections, and confidence
- support ?node= URL param for deep-linking to selected nodes
- add site-based compound node grouping in Cytoscape
2026-03-12 10:59:20 -05:00
72f81c572d
add real-time GraphQL API with time-series queries and subscriptions
- add absinthe_phoenix for WebSocket subscription support
- create GraphQL WebSocket socket with API token auth at /socket/graphql
- add time-series queries: deviceSensors, sensorReadings, interfaceTraffic, checkResults
- add subscriptions: deviceStatusChanged, alertEvent, sensorReadingsUpdated
- wire subscription triggers into agent_channel and alerts contexts
- add org-scoped access control for sensors/interfaces via join queries
- add 17 tests covering queries, org isolation, empty data edge cases, and auth
- update GraphQL API docs page with time-series and subscription sections
2026-03-12 10:24:39 -05:00
89ca5cadb4
add day dividers between date labels on schedule timeline 2026-03-11 14:43:37 -05:00
17fa005781
add maintenance windows REST API and GraphQL support
REST endpoints for CRUD, scoped filtering (active/upcoming/past).
GraphQL queries and mutations for maintenance windows.
Fix agent edit test assertion message.
2026-03-11 14:33:25 -05:00
f6e9063577
feat: add REST API and GraphQL for on-call schedules and escalation policies
REST API v1 endpoints for schedules (14 endpoints) and escalation
policies (10 endpoints) with full CRUD for nested resources (layers,
members, overrides, rules, targets). GraphQL types, resolvers, and
schema mutations for both resource trees.
2026-03-11 14:01:15 -05:00
5026614bf3
fix: nav overflow on small screens and fix bad gettext translations
Bump desktop nav breakpoint from md (768px) to lg (1024px) so the
mobile hamburger menu is used on medium-width screens instead of
nav items overflowing outside the viewport.

Clear all 65 bad fuzzy translations in English .po files (e.g.
"Navigation" was incorrectly translated to "Pagination") and fix
49 fuzzy entries in Spanish .po files with correct translations.
2026-03-11 14:00:31 -05:00
3bba1a6289
fix: display on-call user names instead of emails on schedule index
Update add_member test to use phx-change select instead of form submit.
2026-03-11 13:35:43 -05:00
61368e5c5e
enhance: schedule show with PagerDuty-style timeline and UX improvements
- Add 2-week visual timeline with per-layer and final schedule views
- Color-coded user spans with today marker and prev/next navigation
- Inline user picker when adding new layers (auto-add on select)
- Auto-add members on select for existing layers (no Add button)
- Display user names (first last) instead of emails throughout
- Default handoff time to 8:00 AM and start date to today
2026-03-11 12:59:23 -05:00
0cd2ed3567
feat: add on-call schedules and escalation policies
Built-in PagerDuty-equivalent system for on-call scheduling and alert
escalation. Users can now manage schedules, rotation layers, overrides,
and escalation policies directly in the app alongside PagerDuty.

- On-call schedules with rotation layers (daily/weekly/custom), member
  management, and temporary overrides
- Escalation policies with ordered rules, timeout-based escalation,
  and user/schedule targets
- Automatic escalation via Oban worker with configurable repeat count
- Email notifications via Swoosh for on-call alerts
- Resolver computes who's on-call from layer stacking and overrides
- AlertNotificationWorker integration: starts escalation alongside
  PagerDuty, acknowledges/resolves incidents on alert state changes
- Device and organization schemas support escalation_policy_id
- Escalation policy picker on device form
- Schedules nav item with tabbed index (schedules + escalation policies)
- Full CRUD UI for schedules, layers, members, overrides, rules, targets
- 62 LiveView tests, 56 context/schema/resolver/escalation tests
- 26 E2E Playwright tests for smoke and critical path coverage
2026-03-11 12:32:54 -05:00
41708651a0
refactor: collapse inactive billing providers into expandable disclosure
When a billing provider is active, all other billing providers are
hidden from the main list and collected into a compact <details>
disclosure element showing provider names and descriptions.
2026-03-11 10:09:27 -05:00
031427a247
feat: collapse and dim inactive billing integrations
When a billing provider is active (enabled), the other billing providers
are dimmed and collapsed to a single row showing a lock icon and which
provider is active. This reinforces the mutually exclusive constraint.

Also fixes status emoji accumulating in page title (JS regex needed
unicode flag) and adds missing page_title to 8 LiveViews.
2026-03-11 10:00:38 -05:00
f2608e8f16
fix: status emoji accumulating in page title and add missing page titles
Fix JS regex in StatusTitle hook to use unicode flag so emoji are
properly matched and replaced instead of accumulating. Add page_title
to 8 LiveViews that were missing it, which caused "TowerOps | TowerOps"
duplication from the live_title suffix.
2026-03-11 09:54:18 -05:00
198f2c3cbc
feat: add API key setup help to integration configuration panels
Add setup_help info boxes to each integration provider showing where
to create API keys, step-by-step instructions, required permissions,
and example URL patterns.
2026-03-11 09:46:22 -05:00
91a051f14d
Refactor duplicate site API logic 2026-03-10 16:27:35 -05:00
48270f5e35
chore: remove staged deletions from previous work 2026-03-10 16:20:41 -05:00
532d88ffb9
perf: disable Req retry for faster error tests
- Add retry: false to VISP Client HTTP requests
- Add retry: false to ReleaseChecker GitHub API requests
- Remove unused Plug.Conn import from RemoteIpLogger
- Remove unused default parameter from req_get/2

Results:
- VISP sync test: 7007ms → 113ms (62x faster)
- ReleaseChecker test: 7004ms → 17ms (402x faster)
- UserResetPasswordLive test: 1495ms → 284ms (5x faster)

Req's default retry behavior (1s, 2s, 4s exponential backoff) was
causing 7-second delays for HTTP 500/503 error responses in tests.
For these clients, immediate failure is preferred over retries.
2026-03-10 16:19:31 -05:00
495b1a75b2
Add GeoIP import HTTP tests 2026-03-10 16:02:49 -05:00
e026930f25
logging fix and some other codex fixes 2026-03-10 15:04:28 -05:00
bbb5f886ec
fix: add alert_changed handlers to critical LiveViews
Proactively add catch-all handlers to device index/show pages to
prevent FunctionClauseError when StatusTitleComponent broadcasts
alert changes. These are the most frequently open pages where users
would encounter the crash.
2026-03-10 14:12:29 -05:00
48b6f390a8
fix: handle alert_changed messages in AgentLive.Index
The StatusTitleComponent (rendered in authenticated layout) subscribes
to organization:*:alerts PubSub topic. Since LiveComponents run in the
parent process, both the component and parent LiveView receive these
messages.

Added catch-all handler to prevent FunctionClauseError when alert
changes broadcast. The component already handles the message properly.
2026-03-10 14:10:10 -05:00
bc1ac21f1e
fix: use correct auth check for API organization updates
- API auth uses current_user, not current_scope
- Check membership role directly instead of using owner?/1 helper
- Allow superusers to update organization settings
- Fixes KeyError: key :current_scope not found

Previous code assumed LiveView context with current_scope, but API
requests only have current_user and current_organization_id assigned.
2026-03-10 13:41:38 -05:00
03487b1f55
rename "Admin Dashboard" to "Dashboard" 2026-03-10 13:36:03 -05:00
5fb5fea25e
feat: require org owner permission to update organization settings
- Add authorization check in organization_controller.ex update action
- Only organization owners can modify organization settings via API
- Returns 403 Forbidden if user is not an owner
- Affects all organization settings including use_sites and name
- Organization name can now be updated via API (existing field)

This ensures critical organization settings are only changed by authorized users.
2026-03-10 13:32:38 -05:00
a19155f3de
fix: remove all compact_mode references from device index template 2026-03-10 11:22:40 -05:00
f19fb958cb
refactor: remove non-functional compact view button from devices page
Removed:
- Compact view toggle button from the header
- toggle_compact_mode event handler
- @compact_mode assign

The button was not connected to any actual functionality.
2026-03-10 11:02:12 -05:00
1920c7bc5a
fix: show total discovered device count in tab badge instead of paginated count
The discovered tab badge was showing length(@discovered_devices) which is
the paginated list (20 items per page), not the total count. Now shows
@pagination.total_count to display the actual total (e.g., 876 devices).
2026-03-10 11:00:06 -05:00
6fd03ace16
feat: add comprehensive wireless client tracking and monitoring
Implements real-time wireless client monitoring with historical tracking,
LiveView UI, proactive alerting, and cross-browser e2e tests.

Phase 1: Historical Tracking
- Add TimescaleDB hypertable for wireless_client_readings
- Batch insert client metrics every 60 seconds from DevicePollerWorker
- 90-day retention with compression after 7 days
- Continuous aggregates for hourly (1 year) and daily (5 years) rollups

Phase 2: LiveView UI
- Add wireless tab to device detail page
- Real-time client list with PubSub updates
- Signal strength and SNR badges with 5-level thresholds
- Display MAC, IP, subscriber, TX/RX rates, distance, uptime
- Subscriber matching via device_subscriber_links
- Empty state handling

Phase 3: Proactive Alerting
- WirelessInsightWorker runs every 5 minutes via Oban cron
- 4 insight types with auto-resolution:
  * wireless_signal_weak: < -75 dBm (warning), < -85 dBm (critical)
  * wireless_snr_low: < 15 dB (warning), < 10 dB (critical)
  * wireless_ap_overloaded: > 50 clients (warning), > 75 clients (critical)
  * wireless_client_missing: expected subscribers not connecting
- Hysteresis thresholds prevent alert flapping
- Multi-organization isolation with proper deduplication

Code Quality:
- Refactored reload_current_tab_data to reduce cyclomatic complexity
- Combined double Enum.filter into single pass for efficiency
- Fixed length/1 comparison to use empty list check
- All Credo checks passing

Testing:
- 28 unit tests (ExUnit) - 100% passing
- 15 e2e tests (Playwright) - 100% passing across chromium/firefox/webkit
- Total: 73 tests, all passing

Files changed:
- lib/towerops/workers/wireless_insight_worker.ex (NEW)
- lib/towerops_web/live/device_live/show.ex (wireless tab + refactoring)
- lib/towerops_web/live/device_live/show.html.heex (wireless template)
- lib/towerops/snmp.ex (5 new query functions)
- lib/towerops/gaiia.ex (list_missing_subscribers)
- lib/towerops/preseem/insight.ex (5 new insight types)
- config/runtime.exs (Oban cron schedule)
- test/support/fixtures/snmp_fixtures.ex (NEW)
- test/towerops/workers/wireless_insight_worker_test.exs (NEW)
- test/towerops_web/live/device_live/show_test.exs (9 new tests)
- e2e/tests/wireless-clients.spec.ts (NEW - 15 cross-browser tests)
2026-03-10 09:57:12 -05:00
f09d1b296d feat: add historical wireless client tracking with TimescaleDB
- Create wireless_client_readings hypertable with compression and retention
- Add WirelessClientReading schema and batch insert function
- Update DevicePollerWorker to save historical readings
- Add signal_badge and snr_badge components to CoreComponents
2026-03-09 17:42:17 -05:00
108fb2b7dc
fix: add navigation and footer to changelog page
Wrapped ChangelogLive content in Layouts.authenticated component
to include standard navigation header and footer.
2026-03-09 15:59:13 -05:00
4e3f732f21
ui: comprehensive button styling audit - convert all text-styled actions to proper buttons
Fixed 10 text-styled action buttons across 7 files:

Agent Management:
- Delete Agent buttons (2x) → danger variant
- View Setup links (already fixed) → secondary variant

Organization Settings:
- Remove Member button → danger variant

Admin Pages:
- Edit Overrides button → secondary variant
- Delete Organization button → danger variant
- Remove IP/CIDR button → danger variant
- Delete User button → danger variant

Device Management:
- Delete Backup button → danger variant

All action buttons now use proper .button component with appropriate
variants (danger for destructive actions, secondary for neutral actions).
This provides consistent visual hierarchy and makes destructive actions
more clearly identifiable.
2026-03-09 15:32:42 -05:00
c94ac6e650
ui: convert text-styled action links to proper buttons
- Login page: 'Sign up' link → full-width button
- Agent setup: 'View Setup' text links → secondary buttons

Makes primary actions more visually prominent and consistent
with button component design system.
2026-03-09 15:02:48 -05:00
34e9365520
fix: another Mix.env() call in device_live/index.ex
- Fixed debounce_ms/0 function still using Mix.env()
- Changed to Application.get_env(:towerops, :env)
- This was causing crashes in staging/production
2026-03-09 14:59:41 -05:00
4b11f74e39
fix: replace Mix.env() with Application.get_env() for production compatibility
Mix is not available in production releases. Use Application.get_env/3
to check environment at runtime instead of Mix.env().
2026-03-09 12:47:50 -05:00
fc63a4faaf
fix: properly disable Plug.Telemetry logging for health checks
Set conn.private.log to false to prevent Plug.Telemetry from logging
health check requests in addition to phoenix_log and plug_skip_telemetry.
2026-03-08 16:39:26 -05:00
5a63adacef
fix: align Send Invite button with form inputs
Add invisible label above button to match input field structure,
ensuring proper vertical alignment when using items-end grid alignment.
2026-03-08 16:14:44 -05:00
1c222f8ff5
feat: add capacity insight worker, UI, and reporting views
Add CapacityInsightWorker (every 15 min) that generates critical/warning
insights when backhaul utilization exceeds 90%/75% and auto-resolves
when it drops below 70%.

Add capacity and utilization columns to the device ports tab with
set/clear capacity controls. Add organization-level /capacity page
with summary cards and per-site capacity table. Add capacity summary
card to site show page. Add Capacity link to nav menu.
2026-03-07 14:42:40 -06:00
76545ee31c
fix: handle alert_changed PubSub messages and primary owner ordering
- Add handle_info for {:alert_changed, org_id} in AlertLive.Index and DashboardLive
  to properly handle organization-level alert change broadcasts
- Fix get_primary_owner query to prioritize is_default memberships first,
  ensuring the organization creator is always returned when multiple owners exist

Resolves 5 test failures related to PubSub message handling and
deterministic primary owner selection.
2026-03-07 12:04:30 -06:00
3fa0ea383a
feat: improve organization invitation UX
- Show sender's full name in invitation emails
- Use relative time for "Sent" column (more readable)
- Use date format for "Expires" column (shorter)
- Make invitation table horizontally scrollable

Fixes display cropping issue where long timestamps caused
the pending invitations table to overflow its container.
2026-03-07 11:58:46 -06:00
d5b0f38ed6
feat: add remaining e2e tests and fix 'too many open files' error
- Added e2e tests for auth flows (registration, login, password reset, TOTP)
- Added e2e tests for onboarding and organization creation
- Added e2e tests for team collaboration (org switching, invitations)
- Added e2e tests for activity feed
- Added e2e tests for device graphs (sensors, time ranges, interfaces)
- Added e2e tests for MikroTik backup comparison
- Added e2e tests for sites map (geographic view)
- Added e2e tests for network trace tool

- Fixed 'too many open files' error by configuring Phoenix code reloader
  to ignore deps/, _build/, node_modules/, e2e/, and other non-source directories
- This prevents the file system watcher from monitoring unnecessary files
2026-03-06 17:08:27 -06:00
0832abf33a
feat: comprehensive e2e test coverage for all user-facing features
- Added e2e tests for agents, insights, maintenance windows, maps, help pages
- Added e2e tests for organization settings, config timeline, MikroTik backups
- Added comprehensive search functionality tests
- Added dashboard and sites navigation tests
- Updated existing tests to handle sudo verification redirects
- Fixed navigation tests to be more defensive about missing data
- All 301 tests passing across chromium, firefox, and webkit
2026-03-06 16:58:06 -06:00