feat(14-02): integrate wireless tabs into device detail and add wireless links page

- Add Stations tab to StandardConfigSidebar (Monitor section)
- Render WirelessStationTable + RFStatsCard in stations tab
- Create standalone wireless-links route page
- Update Sidebar nav to point Wireless Links to tenant-scoped page

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Jason Staack
2026-03-19 06:47:36 -05:00
parent eec89b802a
commit 3f7fa7d62c
5 changed files with 70 additions and 5 deletions

View File

@@ -120,11 +120,18 @@ export function Sidebar() {
}, },
] ]
: []), : []),
{ ...(!isSuperAdmin(user) && user?.tenant_id
label: 'Wireless', ? [{
href: '/wireless', label: 'Wireless Links',
icon: Wifi, href: `/tenants/${user.tenant_id}/wireless-links`,
}, icon: Wifi,
}]
: [{
label: 'Wireless Links',
href: '/wireless',
icon: Wifi,
}]
),
{ {
label: 'Traffic', label: 'Traffic',
href: '/traffic', href: '/traffic',

View File

@@ -54,6 +54,8 @@ import { SnmpPanel } from '@/components/config/SnmpPanel'
import { ClientsTab } from '@/components/network/ClientsTab' import { ClientsTab } from '@/components/network/ClientsTab'
import { VpnTab } from '@/components/network/VpnTab' import { VpnTab } from '@/components/network/VpnTab'
import { LogsTab } from '@/components/network/LogsTab' import { LogsTab } from '@/components/network/LogsTab'
import { WirelessStationTable } from '@/components/wireless/WirelessStationTable'
import { RFStatsCard } from '@/components/wireless/RFStatsCard'
interface SimpleConfigViewProps { interface SimpleConfigViewProps {
tenantId: string tenantId: string
@@ -107,6 +109,12 @@ export function SimpleConfigView({
{activeTab === 'wireless' && ( {activeTab === 'wireless' && (
<WirelessTab tenantId={tenantId} deviceId={deviceId} active /> <WirelessTab tenantId={tenantId} deviceId={deviceId} active />
)} )}
{activeTab === 'stations' && (
<div className="space-y-4">
<WirelessStationTable tenantId={tenantId} deviceId={deviceId} active />
<RFStatsCard tenantId={tenantId} deviceId={deviceId} active />
</div>
)}
{activeTab === 'interfaces' && ( {activeTab === 'interfaces' && (
<InterfacesPanel tenantId={tenantId} deviceId={deviceId} active /> <InterfacesPanel tenantId={tenantId} deviceId={deviceId} active />
)} )}

View File

@@ -36,6 +36,7 @@ const STANDARD_GROUPS: SidebarGroup[] = [
{ id: 'health', label: 'Health' }, { id: 'health', label: 'Health' },
{ id: 'traffic', label: 'Traffic' }, { id: 'traffic', label: 'Traffic' },
{ id: 'wireless', label: 'Wireless' }, { id: 'wireless', label: 'Wireless' },
{ id: 'stations', label: 'Stations' },
], ],
}, },
{ {

View File

@@ -39,6 +39,7 @@ import { Route as AuthenticatedAboutRouteImport } from './routes/_authenticated/
import { Route as AuthenticatedTenantsIndexRouteImport } from './routes/_authenticated/tenants/index' import { Route as AuthenticatedTenantsIndexRouteImport } from './routes/_authenticated/tenants/index'
import { Route as AuthenticatedSettingsApiKeysRouteImport } from './routes/_authenticated/settings.api-keys' import { Route as AuthenticatedSettingsApiKeysRouteImport } from './routes/_authenticated/settings.api-keys'
import { Route as AuthenticatedTenantsTenantIdIndexRouteImport } from './routes/_authenticated/tenants/$tenantId/index' import { Route as AuthenticatedTenantsTenantIdIndexRouteImport } from './routes/_authenticated/tenants/$tenantId/index'
import { Route as AuthenticatedTenantsTenantIdWirelessLinksRouteImport } from './routes/_authenticated/tenants/$tenantId/wireless-links'
import { Route as AuthenticatedTenantsTenantIdUsersRouteImport } from './routes/_authenticated/tenants/$tenantId/users' import { Route as AuthenticatedTenantsTenantIdUsersRouteImport } from './routes/_authenticated/tenants/$tenantId/users'
import { Route as AuthenticatedTenantsTenantIdSitesIndexRouteImport } from './routes/_authenticated/tenants/$tenantId/sites/index' import { Route as AuthenticatedTenantsTenantIdSitesIndexRouteImport } from './routes/_authenticated/tenants/$tenantId/sites/index'
import { Route as AuthenticatedTenantsTenantIdDevicesIndexRouteImport } from './routes/_authenticated/tenants/$tenantId/devices/index' import { Route as AuthenticatedTenantsTenantIdDevicesIndexRouteImport } from './routes/_authenticated/tenants/$tenantId/devices/index'
@@ -206,6 +207,12 @@ const AuthenticatedTenantsTenantIdIndexRoute =
path: '/tenants/$tenantId/', path: '/tenants/$tenantId/',
getParentRoute: () => AuthenticatedRoute, getParentRoute: () => AuthenticatedRoute,
} as any) } as any)
const AuthenticatedTenantsTenantIdWirelessLinksRoute =
AuthenticatedTenantsTenantIdWirelessLinksRouteImport.update({
id: '/tenants/$tenantId/wireless-links',
path: '/tenants/$tenantId/wireless-links',
getParentRoute: () => AuthenticatedRoute,
} as any)
const AuthenticatedTenantsTenantIdUsersRoute = const AuthenticatedTenantsTenantIdUsersRoute =
AuthenticatedTenantsTenantIdUsersRouteImport.update({ AuthenticatedTenantsTenantIdUsersRouteImport.update({
id: '/tenants/$tenantId/users', id: '/tenants/$tenantId/users',
@@ -285,6 +292,7 @@ export interface FileRoutesByFullPath {
'/settings/api-keys': typeof AuthenticatedSettingsApiKeysRoute '/settings/api-keys': typeof AuthenticatedSettingsApiKeysRoute
'/tenants/': typeof AuthenticatedTenantsIndexRoute '/tenants/': typeof AuthenticatedTenantsIndexRoute
'/tenants/$tenantId/users': typeof AuthenticatedTenantsTenantIdUsersRoute '/tenants/$tenantId/users': typeof AuthenticatedTenantsTenantIdUsersRoute
'/tenants/$tenantId/wireless-links': typeof AuthenticatedTenantsTenantIdWirelessLinksRoute
'/tenants/$tenantId/': typeof AuthenticatedTenantsTenantIdIndexRoute '/tenants/$tenantId/': typeof AuthenticatedTenantsTenantIdIndexRoute
'/tenants/$tenantId/devices/$deviceId': typeof AuthenticatedTenantsTenantIdDevicesDeviceIdRoute '/tenants/$tenantId/devices/$deviceId': typeof AuthenticatedTenantsTenantIdDevicesDeviceIdRoute
'/tenants/$tenantId/devices/add': typeof AuthenticatedTenantsTenantIdDevicesAddRoute '/tenants/$tenantId/devices/add': typeof AuthenticatedTenantsTenantIdDevicesAddRoute
@@ -324,6 +332,7 @@ export interface FileRoutesByTo {
'/settings/api-keys': typeof AuthenticatedSettingsApiKeysRoute '/settings/api-keys': typeof AuthenticatedSettingsApiKeysRoute
'/tenants': typeof AuthenticatedTenantsIndexRoute '/tenants': typeof AuthenticatedTenantsIndexRoute
'/tenants/$tenantId/users': typeof AuthenticatedTenantsTenantIdUsersRoute '/tenants/$tenantId/users': typeof AuthenticatedTenantsTenantIdUsersRoute
'/tenants/$tenantId/wireless-links': typeof AuthenticatedTenantsTenantIdWirelessLinksRoute
'/tenants/$tenantId': typeof AuthenticatedTenantsTenantIdIndexRoute '/tenants/$tenantId': typeof AuthenticatedTenantsTenantIdIndexRoute
'/tenants/$tenantId/devices/$deviceId': typeof AuthenticatedTenantsTenantIdDevicesDeviceIdRoute '/tenants/$tenantId/devices/$deviceId': typeof AuthenticatedTenantsTenantIdDevicesDeviceIdRoute
'/tenants/$tenantId/devices/add': typeof AuthenticatedTenantsTenantIdDevicesAddRoute '/tenants/$tenantId/devices/add': typeof AuthenticatedTenantsTenantIdDevicesAddRoute
@@ -365,6 +374,7 @@ export interface FileRoutesById {
'/_authenticated/settings/api-keys': typeof AuthenticatedSettingsApiKeysRoute '/_authenticated/settings/api-keys': typeof AuthenticatedSettingsApiKeysRoute
'/_authenticated/tenants/': typeof AuthenticatedTenantsIndexRoute '/_authenticated/tenants/': typeof AuthenticatedTenantsIndexRoute
'/_authenticated/tenants/$tenantId/users': typeof AuthenticatedTenantsTenantIdUsersRoute '/_authenticated/tenants/$tenantId/users': typeof AuthenticatedTenantsTenantIdUsersRoute
'/_authenticated/tenants/$tenantId/wireless-links': typeof AuthenticatedTenantsTenantIdWirelessLinksRoute
'/_authenticated/tenants/$tenantId/': typeof AuthenticatedTenantsTenantIdIndexRoute '/_authenticated/tenants/$tenantId/': typeof AuthenticatedTenantsTenantIdIndexRoute
'/_authenticated/tenants/$tenantId/devices/$deviceId': typeof AuthenticatedTenantsTenantIdDevicesDeviceIdRoute '/_authenticated/tenants/$tenantId/devices/$deviceId': typeof AuthenticatedTenantsTenantIdDevicesDeviceIdRoute
'/_authenticated/tenants/$tenantId/devices/add': typeof AuthenticatedTenantsTenantIdDevicesAddRoute '/_authenticated/tenants/$tenantId/devices/add': typeof AuthenticatedTenantsTenantIdDevicesAddRoute
@@ -406,6 +416,7 @@ export interface FileRouteTypes {
| '/settings/api-keys' | '/settings/api-keys'
| '/tenants/' | '/tenants/'
| '/tenants/$tenantId/users' | '/tenants/$tenantId/users'
| '/tenants/$tenantId/wireless-links'
| '/tenants/$tenantId/' | '/tenants/$tenantId/'
| '/tenants/$tenantId/devices/$deviceId' | '/tenants/$tenantId/devices/$deviceId'
| '/tenants/$tenantId/devices/add' | '/tenants/$tenantId/devices/add'
@@ -445,6 +456,7 @@ export interface FileRouteTypes {
| '/settings/api-keys' | '/settings/api-keys'
| '/tenants' | '/tenants'
| '/tenants/$tenantId/users' | '/tenants/$tenantId/users'
| '/tenants/$tenantId/wireless-links'
| '/tenants/$tenantId' | '/tenants/$tenantId'
| '/tenants/$tenantId/devices/$deviceId' | '/tenants/$tenantId/devices/$deviceId'
| '/tenants/$tenantId/devices/add' | '/tenants/$tenantId/devices/add'
@@ -485,6 +497,7 @@ export interface FileRouteTypes {
| '/_authenticated/settings/api-keys' | '/_authenticated/settings/api-keys'
| '/_authenticated/tenants/' | '/_authenticated/tenants/'
| '/_authenticated/tenants/$tenantId/users' | '/_authenticated/tenants/$tenantId/users'
| '/_authenticated/tenants/$tenantId/wireless-links'
| '/_authenticated/tenants/$tenantId/' | '/_authenticated/tenants/$tenantId/'
| '/_authenticated/tenants/$tenantId/devices/$deviceId' | '/_authenticated/tenants/$tenantId/devices/$deviceId'
| '/_authenticated/tenants/$tenantId/devices/add' | '/_authenticated/tenants/$tenantId/devices/add'
@@ -716,6 +729,13 @@ declare module '@tanstack/react-router' {
preLoaderRoute: typeof AuthenticatedTenantsTenantIdIndexRouteImport preLoaderRoute: typeof AuthenticatedTenantsTenantIdIndexRouteImport
parentRoute: typeof AuthenticatedRoute parentRoute: typeof AuthenticatedRoute
} }
'/_authenticated/tenants/$tenantId/wireless-links': {
id: '/_authenticated/tenants/$tenantId/wireless-links'
path: '/tenants/$tenantId/wireless-links'
fullPath: '/tenants/$tenantId/wireless-links'
preLoaderRoute: typeof AuthenticatedTenantsTenantIdWirelessLinksRouteImport
parentRoute: typeof AuthenticatedRoute
}
'/_authenticated/tenants/$tenantId/users': { '/_authenticated/tenants/$tenantId/users': {
id: '/_authenticated/tenants/$tenantId/users' id: '/_authenticated/tenants/$tenantId/users'
path: '/tenants/$tenantId/users' path: '/tenants/$tenantId/users'
@@ -812,6 +832,7 @@ interface AuthenticatedRouteChildren {
AuthenticatedIndexRoute: typeof AuthenticatedIndexRoute AuthenticatedIndexRoute: typeof AuthenticatedIndexRoute
AuthenticatedTenantsIndexRoute: typeof AuthenticatedTenantsIndexRoute AuthenticatedTenantsIndexRoute: typeof AuthenticatedTenantsIndexRoute
AuthenticatedTenantsTenantIdUsersRoute: typeof AuthenticatedTenantsTenantIdUsersRoute AuthenticatedTenantsTenantIdUsersRoute: typeof AuthenticatedTenantsTenantIdUsersRoute
AuthenticatedTenantsTenantIdWirelessLinksRoute: typeof AuthenticatedTenantsTenantIdWirelessLinksRoute
AuthenticatedTenantsTenantIdIndexRoute: typeof AuthenticatedTenantsTenantIdIndexRoute AuthenticatedTenantsTenantIdIndexRoute: typeof AuthenticatedTenantsTenantIdIndexRoute
AuthenticatedTenantsTenantIdDevicesDeviceIdRoute: typeof AuthenticatedTenantsTenantIdDevicesDeviceIdRoute AuthenticatedTenantsTenantIdDevicesDeviceIdRoute: typeof AuthenticatedTenantsTenantIdDevicesDeviceIdRoute
AuthenticatedTenantsTenantIdDevicesAddRoute: typeof AuthenticatedTenantsTenantIdDevicesAddRoute AuthenticatedTenantsTenantIdDevicesAddRoute: typeof AuthenticatedTenantsTenantIdDevicesAddRoute
@@ -847,6 +868,8 @@ const AuthenticatedRouteChildren: AuthenticatedRouteChildren = {
AuthenticatedTenantsIndexRoute: AuthenticatedTenantsIndexRoute, AuthenticatedTenantsIndexRoute: AuthenticatedTenantsIndexRoute,
AuthenticatedTenantsTenantIdUsersRoute: AuthenticatedTenantsTenantIdUsersRoute:
AuthenticatedTenantsTenantIdUsersRoute, AuthenticatedTenantsTenantIdUsersRoute,
AuthenticatedTenantsTenantIdWirelessLinksRoute:
AuthenticatedTenantsTenantIdWirelessLinksRoute,
AuthenticatedTenantsTenantIdIndexRoute: AuthenticatedTenantsTenantIdIndexRoute:
AuthenticatedTenantsTenantIdIndexRoute, AuthenticatedTenantsTenantIdIndexRoute,
AuthenticatedTenantsTenantIdDevicesDeviceIdRoute: AuthenticatedTenantsTenantIdDevicesDeviceIdRoute:

View File

@@ -0,0 +1,26 @@
import { createFileRoute } from '@tanstack/react-router'
import { Wifi } from 'lucide-react'
import { WirelessLinksTable } from '@/components/wireless/WirelessLinksTable'
export const Route = createFileRoute(
'/_authenticated/tenants/$tenantId/wireless-links',
)({
component: WirelessLinksPage,
})
function WirelessLinksPage() {
const { tenantId } = Route.useParams()
return (
<div className="space-y-4">
{/* Header */}
<div className="flex items-center gap-2">
<Wifi className="h-5 w-5 text-text-muted" />
<h1 className="text-lg font-semibold text-text-primary">Wireless Links</h1>
</div>
{/* Links table */}
<WirelessLinksTable tenantId={tenantId} />
</div>
)
}