feat(11-02): add Sites to sidebar navigation and tenant index page

- MapPin icon and Sites nav link in sidebar Fleet section
- Tenant index shows Sites count card in 3-column grid
- "Manage sites" link added to tenant index bottom links

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Jason Staack
2026-03-18 21:46:53 -05:00
parent 40f2bcd9aa
commit e8c69fb6a6
2 changed files with 39 additions and 4 deletions

View File

@@ -14,6 +14,7 @@ import {
ClipboardList, ClipboardList,
Wifi, Wifi,
BarChart3, BarChart3,
MapPin,
} from 'lucide-react' } from 'lucide-react'
import { cn } from '@/lib/utils' import { cn } from '@/lib/utils'
import { useAuth, isSuperAdmin, isTenantAdmin } from '@/lib/auth' import { useAuth, isSuperAdmin, isTenantAdmin } from '@/lib/auth'
@@ -110,6 +111,15 @@ export function Sidebar() {
}, },
] ]
: []), : []),
...(!isSuperAdmin(user) && user?.tenant_id
? [
{
label: 'Sites',
href: `/tenants/${user.tenant_id}/sites`,
icon: MapPin,
},
]
: []),
{ {
label: 'Wireless', label: 'Wireless',
href: '/wireless', href: '/wireless',

View File

@@ -1,7 +1,7 @@
import { createFileRoute, Link } from '@tanstack/react-router' import { createFileRoute, Link } from '@tanstack/react-router'
import { useQuery } from '@tanstack/react-query' import { useQuery } from '@tanstack/react-query'
import { Users, Monitor, Building2 } from 'lucide-react' import { Users, Monitor, Building2, MapPin } from 'lucide-react'
import { tenantsApi } from '@/lib/api' import { tenantsApi, sitesApi } from '@/lib/api'
import { formatDate } from '@/lib/utils' import { formatDate } from '@/lib/utils'
import { CardGridSkeleton } from '@/components/ui/page-skeleton' import { CardGridSkeleton } from '@/components/ui/page-skeleton'
@@ -17,8 +17,13 @@ function TenantDetailPage() {
queryFn: () => tenantsApi.get(tenantId), queryFn: () => tenantsApi.get(tenantId),
}) })
const { data: sitesData } = useQuery({
queryKey: ['sites', tenantId],
queryFn: () => sitesApi.list(tenantId),
})
if (isLoading) { if (isLoading) {
return <CardGridSkeleton cards={2} /> return <CardGridSkeleton cards={3} />
} }
if (!tenant) { if (!tenant) {
@@ -40,7 +45,7 @@ function TenantDetailPage() {
</div> </div>
</div> </div>
<div className="grid grid-cols-2 gap-4"> <div className="grid grid-cols-3 gap-4">
<Link <Link
to="/tenants/$tenantId/users" to="/tenants/$tenantId/users"
params={{ tenantId }} params={{ tenantId }}
@@ -53,6 +58,18 @@ function TenantDetailPage() {
</div> </div>
</Link> </Link>
<Link
to="/tenants/$tenantId/sites"
params={{ tenantId }}
className="flex items-center gap-3 rounded-lg border border-border bg-surface p-4 hover:bg-elevated/50 transition-colors group"
>
<MapPin className="h-8 w-8 text-text-muted group-hover:text-text-muted transition-colors" />
<div>
<p className="text-2xl font-semibold">{sitesData?.sites.length ?? 0}</p>
<p className="text-xs text-text-secondary">Sites</p>
</div>
</Link>
<Link <Link
to="/tenants/$tenantId/devices" to="/tenants/$tenantId/devices"
params={{ tenantId }} params={{ tenantId }}
@@ -75,6 +92,14 @@ function TenantDetailPage() {
Manage users Manage users
</Link> </Link>
<span className="text-text-muted">·</span> <span className="text-text-muted">·</span>
<Link
to="/tenants/$tenantId/sites"
params={{ tenantId }}
className="text-sm text-text-secondary hover:text-text-primary transition-colors underline-offset-2 hover:underline"
>
Manage sites
</Link>
<span className="text-text-muted">·</span>
<Link <Link
to="/tenants/$tenantId/devices" to="/tenants/$tenantId/devices"
params={{ tenantId }} params={{ tenantId }}