mirror of
https://github.com/Buriburizaem0n/admin-frontend-domain.git
synced 2026-02-04 20:50:07 +00:00
feat: server table 1/10
This commit is contained in:
@@ -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);
|
||||
}
|
||||
@@ -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>
|
||||
|
||||
28
src/components/ui/checkbox.tsx
Normal file
28
src/components/ui/checkbox.tsx
Normal 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
117
src/components/ui/table.tsx
Normal 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,
|
||||
}
|
||||
@@ -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
116
src/routes/server.tsx
Normal 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>
|
||||
}
|
||||
@@ -1,3 +1,4 @@
|
||||
export * from './server';
|
||||
export * from './user';
|
||||
export * from './mainStore';
|
||||
export * from './authContext';
|
||||
57
src/types/server.tsx
Normal file
57
src/types/server.tsx
Normal 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;
|
||||
}
|
||||
Reference in New Issue
Block a user