ci: add GitHub Pages deployment workflow for docs site Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
90 lines
2.9 KiB
TypeScript
90 lines
2.9 KiB
TypeScript
import { createFileRoute, Link } from '@tanstack/react-router'
|
|
import { useState } from 'react'
|
|
import { Plus, Scan, ChevronRight } from 'lucide-react'
|
|
import { useQuery } from '@tanstack/react-query'
|
|
import { z } from 'zod'
|
|
import { tenantsApi } from '@/lib/api'
|
|
import { useAuth, canWrite } from '@/lib/auth'
|
|
import { Button } from '@/components/ui/button'
|
|
import { FleetTable } from '@/components/fleet/FleetTable'
|
|
import { DeviceFilters } from '@/components/fleet/DeviceFilters'
|
|
import { AddDeviceForm } from '@/components/fleet/AddDeviceForm'
|
|
|
|
const searchSchema = z.object({
|
|
search: z.string().optional(),
|
|
status: z.string().optional(),
|
|
sort_by: z.string().optional(),
|
|
sort_dir: z.enum(['asc', 'desc']).optional(),
|
|
page: z.number().int().positive().optional(),
|
|
page_size: z.number().int().positive().optional(),
|
|
})
|
|
|
|
export const Route = createFileRoute('/_authenticated/tenants/$tenantId/devices/')({
|
|
validateSearch: searchSchema,
|
|
component: DevicesPage,
|
|
})
|
|
|
|
function DevicesPage() {
|
|
const { tenantId } = Route.useParams()
|
|
const search = Route.useSearch()
|
|
const { user } = useAuth()
|
|
const [addOpen, setAddOpen] = useState(false)
|
|
|
|
const { data: tenant } = useQuery({
|
|
queryKey: ['tenants', tenantId],
|
|
queryFn: () => tenantsApi.get(tenantId),
|
|
})
|
|
|
|
return (
|
|
<div className="space-y-3">
|
|
{/* Breadcrumb */}
|
|
<div className="flex items-center gap-1 text-xs text-text-muted">
|
|
<Link to="/tenants" className="hover:text-text-secondary transition-colors">
|
|
Tenants
|
|
</Link>
|
|
<ChevronRight className="h-3 w-3" />
|
|
<Link
|
|
to="/tenants/$tenantId"
|
|
params={{ tenantId }}
|
|
className="hover:text-text-secondary transition-colors"
|
|
>
|
|
{tenant?.name ?? tenantId}
|
|
</Link>
|
|
<ChevronRight className="h-3 w-3" />
|
|
<span className="text-text-secondary">Devices</span>
|
|
</div>
|
|
|
|
{/* Header */}
|
|
<div className="flex items-center justify-between gap-4">
|
|
<DeviceFilters tenantId={tenantId} />
|
|
{canWrite(user) && (
|
|
<div className="flex items-center gap-2 flex-shrink-0">
|
|
<Link to="/tenants/$tenantId/devices/scan" params={{ tenantId }}>
|
|
<Button variant="outline" size="sm">
|
|
<Scan className="h-3.5 w-3.5" />
|
|
Scan Subnet
|
|
</Button>
|
|
</Link>
|
|
<Button size="sm" onClick={() => setAddOpen(true)}>
|
|
<Plus className="h-3.5 w-3.5" />
|
|
Add Device
|
|
</Button>
|
|
</div>
|
|
)}
|
|
</div>
|
|
|
|
<FleetTable
|
|
tenantId={tenantId}
|
|
search={search.search}
|
|
status={search.status}
|
|
sortBy={search.sort_by}
|
|
sortDir={search.sort_dir}
|
|
page={search.page}
|
|
pageSize={search.page_size}
|
|
/>
|
|
|
|
<AddDeviceForm tenantId={tenantId} open={addOpen} onClose={() => setAddOpen(false)} />
|
|
</div>
|
|
)
|
|
}
|