feat: server table 1/10

This commit is contained in:
naiba
2024-11-06 00:09:03 +08:00
parent bfdae2838f
commit 680dc219d3
10 changed files with 427 additions and 2 deletions

View File

@@ -46,3 +46,7 @@ export async function fetcher<T>(method: FetcherMethod, path: string, data?: any
}
return responseData.data;
}
export async function swrFetcher<T>(input: string | URL | globalThis.Request, init?: RequestInit) {
return fetcher<T>(init?.method as FetcherMethod, input.toString(), init?.body);
}

View File

@@ -22,7 +22,7 @@ export default function Header() {
<NavigationMenu className="max-w-full">
<NavigationMenuList>
<Card>
<NavigationMenuLink href="/dashboard/" className={navigationMenuTriggerStyle() + ' !text-foreground'}>
<NavigationMenuLink href="/dashboard" className={navigationMenuTriggerStyle() + ' !text-foreground'}>
<img className="h-7 mr-1" src='/dashboard/logo.svg' />
</NavigationMenuLink>
</Card>
@@ -30,7 +30,7 @@ export default function Header() {
{
profile && <>
<NavigationMenuItem>
<NzNavigationMenuLink href="/dashboard/server" active className={navigationMenuTriggerStyle()}>
<NzNavigationMenuLink href="/dashboard" active className={navigationMenuTriggerStyle()}>
Server
</NzNavigationMenuLink>
</NavigationMenuItem>

View File

@@ -0,0 +1,28 @@
import * as React from "react"
import * as CheckboxPrimitive from "@radix-ui/react-checkbox"
import { Check } from "lucide-react"
import { cn } from "@/lib/utils"
const Checkbox = React.forwardRef<
React.ElementRef<typeof CheckboxPrimitive.Root>,
React.ComponentPropsWithoutRef<typeof CheckboxPrimitive.Root>
>(({ className, ...props }, ref) => (
<CheckboxPrimitive.Root
ref={ref}
className={cn(
"peer h-4 w-4 shrink-0 rounded-sm border border-primary ring-offset-background focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50 data-[state=checked]:bg-primary data-[state=checked]:text-primary-foreground",
className
)}
{...props}
>
<CheckboxPrimitive.Indicator
className={cn("flex items-center justify-center text-current")}
>
<Check className="h-4 w-4" />
</CheckboxPrimitive.Indicator>
</CheckboxPrimitive.Root>
))
Checkbox.displayName = CheckboxPrimitive.Root.displayName
export { Checkbox }

117
src/components/ui/table.tsx Normal file
View File

@@ -0,0 +1,117 @@
import * as React from "react"
import { cn } from "@/lib/utils"
const Table = React.forwardRef<
HTMLTableElement,
React.HTMLAttributes<HTMLTableElement>
>(({ className, ...props }, ref) => (
<div className="relative w-full overflow-auto">
<table
ref={ref}
className={cn("w-full caption-bottom text-sm", className)}
{...props}
/>
</div>
))
Table.displayName = "Table"
const TableHeader = React.forwardRef<
HTMLTableSectionElement,
React.HTMLAttributes<HTMLTableSectionElement>
>(({ className, ...props }, ref) => (
<thead ref={ref} className={cn("[&_tr]:border-b", className)} {...props} />
))
TableHeader.displayName = "TableHeader"
const TableBody = React.forwardRef<
HTMLTableSectionElement,
React.HTMLAttributes<HTMLTableSectionElement>
>(({ className, ...props }, ref) => (
<tbody
ref={ref}
className={cn("[&_tr:last-child]:border-0", className)}
{...props}
/>
))
TableBody.displayName = "TableBody"
const TableFooter = React.forwardRef<
HTMLTableSectionElement,
React.HTMLAttributes<HTMLTableSectionElement>
>(({ className, ...props }, ref) => (
<tfoot
ref={ref}
className={cn(
"border-t bg-muted/50 font-medium [&>tr]:last:border-b-0",
className
)}
{...props}
/>
))
TableFooter.displayName = "TableFooter"
const TableRow = React.forwardRef<
HTMLTableRowElement,
React.HTMLAttributes<HTMLTableRowElement>
>(({ className, ...props }, ref) => (
<tr
ref={ref}
className={cn(
"border-b transition-colors hover:bg-muted/50 data-[state=selected]:bg-muted",
className
)}
{...props}
/>
))
TableRow.displayName = "TableRow"
const TableHead = React.forwardRef<
HTMLTableCellElement,
React.ThHTMLAttributes<HTMLTableCellElement>
>(({ className, ...props }, ref) => (
<th
ref={ref}
className={cn(
"h-12 px-4 text-left align-middle font-medium text-muted-foreground [&:has([role=checkbox])]:pr-0",
className
)}
{...props}
/>
))
TableHead.displayName = "TableHead"
const TableCell = React.forwardRef<
HTMLTableCellElement,
React.TdHTMLAttributes<HTMLTableCellElement>
>(({ className, ...props }, ref) => (
<td
ref={ref}
className={cn("p-4 align-middle [&:has([role=checkbox])]:pr-0", className)}
{...props}
/>
))
TableCell.displayName = "TableCell"
const TableCaption = React.forwardRef<
HTMLTableCaptionElement,
React.HTMLAttributes<HTMLTableCaptionElement>
>(({ className, ...props }, ref) => (
<caption
ref={ref}
className={cn("mt-4 text-sm text-muted-foreground", className)}
{...props}
/>
))
TableCaption.displayName = "TableCaption"
export {
Table,
TableHeader,
TableBody,
TableFooter,
TableHead,
TableRow,
TableCell,
TableCaption,
}

View File

@@ -11,6 +11,7 @@ import Root from "./routes/root";
import ErrorPage from "./error-page";
import ProtectedRoute from './routes/protect';
import LoginPage from './routes/login';
import ServerPage from './routes/server';
import { AuthProvider } from './hooks/useAuth';
const router = createBrowserRouter([
@@ -23,6 +24,10 @@ const router = createBrowserRouter([
path: "/dashboard/login",
element: <LoginPage />,
},
{
path: "/dashboard",
element: <ServerPage />,
},
]
},
]);

116
src/routes/server.tsx Normal file
View File

@@ -0,0 +1,116 @@
import { swrFetcher } from "@/api/api"
import { Checkbox } from "@/components/ui/checkbox"
import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from "@/components/ui/table"
import { Server } from "@/types"
import { ColumnDef, flexRender, getCoreRowModel, useReactTable } from "@tanstack/react-table"
import useSWR from "swr"
export default function ServerPage() {
const columns: ColumnDef<Server>[] = [
{
id: "select",
header: ({ table }) => (
<Checkbox
checked={
table.getIsAllPageRowsSelected() ||
(table.getIsSomePageRowsSelected() && "indeterminate")
}
onCheckedChange={(value) => table.toggleAllPageRowsSelected(!!value)}
aria-label="Select all"
/>
),
cell: ({ row }) => (
<Checkbox
checked={row.getIsSelected()}
onCheckedChange={(value) => row.toggleSelected(!!value)}
aria-label="Select row"
/>
),
enableSorting: false,
enableHiding: false,
},
{
header: "ID",
accessorKey: "id",
accessorFn: (row) => row.id,
},
{
header: "Name",
accessorKey: "name",
accessorFn: (row) => row.name,
},
{
header: "Host",
accessorKey: "host.ip",
accessorFn: (row) => row.host.ip,
},
{
id: "actions",
header: "Actions",
cell: ({ row }) => {
const s = row.original
return (
<>{s.id}</>
)
},
},
]
const { data, error, isLoading } = useSWR<Server[]>('/api/v1/server', swrFetcher)
const table = useReactTable({
data: data ?? [],
columns,
getCoreRowModel: getCoreRowModel(),
})
return <div className="px-9">
<div className="flex space-between mt-4">
<h1 className="text-3xl font-bold tracking-tight">
Server
</h1>
</div>
<Table>
<TableHeader>
{table.getHeaderGroups().map((headerGroup) => (
<TableRow key={headerGroup.id}>
{headerGroup.headers.map((header) => {
return (
<TableHead key={header.id}>
{header.isPlaceholder
? null
: flexRender(
header.column.columnDef.header,
header.getContext()
)}
</TableHead>
)
})}
</TableRow>
))}
</TableHeader>
<TableBody>
{table.getRowModel().rows?.length ? (
table.getRowModel().rows.map((row) => (
<TableRow
key={row.id}
data-state={row.getIsSelected() && "selected"}
>
{row.getVisibleCells().map((cell) => (
<TableCell key={cell.id}>
{flexRender(cell.column.columnDef.cell, cell.getContext())}
</TableCell>
))}
</TableRow>
))
) : (
<TableRow>
<TableCell colSpan={columns.length} className="h-24 text-center">
No results.
</TableCell>
</TableRow>
)}
</TableBody>
</Table>
</div>
}

View File

@@ -1,3 +1,4 @@
export * from './server';
export * from './user';
export * from './mainStore';
export * from './authContext';

57
src/types/server.tsx Normal file
View File

@@ -0,0 +1,57 @@
export interface ServerHost {
arch: string;
bootTime: number;
countryCode: string;
cpu: string[];
diskTotal: number;
gpu: string[];
memTotal: number;
platform: string;
platformVersion: string;
swapTotal: number;
version: string;
virtualization: string;
ip: string;
}
export interface ServerTemperature {
name: string;
temperature: number;
}
export interface ServerState {
cpu: number;
diskUsed: number;
gpu: number[];
load1: number;
load15: number;
load5: number;
memUsed: number;
netInSpeed: number;
netInTransfer: number;
netOutSpeed: number;
netOutTransfer: number;
processCount: number;
swapUsed: number;
tcpConnCount: number;
temperatures: ServerTemperature[];
udpConnCount: number;
uptime: number;
updated_at: string;
uuid: string;
}
export interface Server {
id: number;
name: string;
ddns_profiles: number[];
created_at: string;
display_index: number;
enable_ddns: boolean;
hide_for_guest: boolean;
host: ServerHost;
last_active: string;
note: string;
public_note: string;
state: ServerState;
}