chore: initial project scaffold with admin web, backend, desktop client, and deployment setup
Add monorepo structure for NexaVPN WireGuard control plane including: - .gitignore for node_modules, build artifacts, and environment files - README with project overview, monorepo layout, and quick start guide - Admin web UI with React, Vite, TypeScript, and nginx reverse proxy - API client with type definitions for users, devices, policies, gateways, and audit logs - Admin pages for dashboard, users, devices, policies, g
This commit is contained in:
39
admin-web/src/features/devices/DevicesPage.tsx
Normal file
39
admin-web/src/features/devices/DevicesPage.tsx
Normal file
@@ -0,0 +1,39 @@
|
||||
import { useQuery } from "@tanstack/react-query";
|
||||
|
||||
import { api } from "../../api/client";
|
||||
import { Page } from "../../components/Page";
|
||||
import { Table } from "../../components/Table";
|
||||
|
||||
const columns = [
|
||||
{ key: "name", label: "Device" },
|
||||
{ key: "owner", label: "Owner" },
|
||||
{ key: "platform", label: "Platform" },
|
||||
{ key: "ip", label: "VPN IP" },
|
||||
{ key: "status", label: "Status" }
|
||||
];
|
||||
|
||||
export function DevicesPage() {
|
||||
const query = useQuery({
|
||||
queryKey: ["devices"],
|
||||
queryFn: api.devices
|
||||
});
|
||||
|
||||
const rows = query.data?.map((device) => ({
|
||||
name: device.name,
|
||||
owner: device.user_id ?? "assigned user",
|
||||
platform: device.platform,
|
||||
ip: device.assigned_ip ?? "-",
|
||||
status: device.status
|
||||
})) ?? [];
|
||||
|
||||
return (
|
||||
<Page title="Devices" subtitle="Inspect enrolled clients, assignments, and profile lifecycle.">
|
||||
{query.isError ? <p className="notice">Unable to load devices from the API.</p> : null}
|
||||
<Table
|
||||
columns={columns}
|
||||
rows={rows}
|
||||
renderCell={(row, column) => <span>{row[column.key as keyof (typeof rows)[number]]}</span>}
|
||||
/>
|
||||
</Page>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user