Add host setup functions and client components
This commit is contained in:
@@ -21,7 +21,7 @@ export default function AdminHero({ title, description, buttons }: Props) {
|
||||
<Section>
|
||||
<Stack className="w-full items-stretch">
|
||||
<Row className="w-full justify-between items-start">
|
||||
<Stack>
|
||||
<Stack className="gap-3">
|
||||
<H1>{title}</H1>
|
||||
{description ? (
|
||||
typeof description == "string" ? (
|
||||
@@ -45,7 +45,6 @@ export default function AdminHero({ title, description, buttons }: Props) {
|
||||
</Stack>
|
||||
<Row>{buttons}</Row>
|
||||
</Row>
|
||||
<Divider className="my-4" />
|
||||
</Stack>
|
||||
</Section>
|
||||
);
|
||||
|
||||
@@ -0,0 +1,35 @@
|
||||
import Span from "@/src/components/twui/layout/Span";
|
||||
import formatUnixTimestamp from "@/src/utils/format-unix-timestamp";
|
||||
import type { BUN_SQLITE_WGUI_CLIENTS } from "@/db/types/db";
|
||||
|
||||
type Props = {
|
||||
client: BUN_SQLITE_WGUI_CLIENTS;
|
||||
};
|
||||
|
||||
export default function ClientRow({ client }: Props) {
|
||||
return (
|
||||
<tr className="border-b border-slate-200/60 dark:border-white/5 last:border-b-0 hover:bg-foreground-light/[0.02] dark:hover:bg-foreground-dark/[0.02] transition-colors">
|
||||
<td className="px-4 py-[9px]">
|
||||
<Span className="text-[13.5px] font-medium text-foreground-light dark:text-foreground-dark">
|
||||
{client.name || "—"}
|
||||
</Span>
|
||||
</td>
|
||||
<td className="px-4 py-[9px] tabular text-[13px] text-foreground-light/60 dark:text-foreground-dark/60 whitespace-nowrap">
|
||||
{client.wg_ip_address || "—"}
|
||||
</td>
|
||||
<td className="px-4 py-[9px] tabular text-[13px] text-foreground-light/45 dark:text-foreground-dark/45 whitespace-nowrap">
|
||||
{client.allowed_ips || "—"}
|
||||
</td>
|
||||
<td className="px-4 py-[9px]">
|
||||
<Span className="font-mono text-[12px] text-foreground-light/45 dark:text-foreground-dark/45 whitespace-nowrap">
|
||||
{client.public_key
|
||||
? `${client.public_key.slice(0, 16)}…`
|
||||
: "—"}
|
||||
</Span>
|
||||
</td>
|
||||
<td className="px-4 py-[9px] tabular text-[12px] text-foreground-light/40 dark:text-foreground-dark/40 text-right whitespace-nowrap">
|
||||
{formatUnixTimestamp({ timestamp: client.created_at })}
|
||||
</td>
|
||||
</tr>
|
||||
);
|
||||
}
|
||||
+77
@@ -0,0 +1,77 @@
|
||||
import { useEffect, useState, type ComponentProps } from "react";
|
||||
import Button from "../twui/layout/Button";
|
||||
import useStatus from "../twui/hooks/useStatus";
|
||||
import fetchApi from "../twui/utils/fetch/fetchApi";
|
||||
import type { ApiReqParams } from "@/src/types";
|
||||
import Row from "../twui/layout/Row";
|
||||
import Input from "../twui/form/Input";
|
||||
import Stack from "../twui/layout/Stack";
|
||||
import { Network } from "lucide-react";
|
||||
import Span from "../twui/layout/Span";
|
||||
import { AppData } from "@/src/data/app-data";
|
||||
|
||||
type Props = {
|
||||
button_props?: Omit<ComponentProps<typeof Button>, "title">;
|
||||
};
|
||||
|
||||
export default function SetupMainHostButton({ button_props }: Props) {
|
||||
const { loading, setLoading, ready, setReady } = useStatus();
|
||||
|
||||
const [wgIP, setWgIP] = useState<string>(AppData["DefaultPrivateIP"]);
|
||||
|
||||
useEffect(() => {
|
||||
fetchApi<ApiReqParams>(
|
||||
`/api/admin/check-private-ip-address-availability`,
|
||||
{
|
||||
method: "POST",
|
||||
body: { ip_address: wgIP },
|
||||
},
|
||||
).then((res) => {
|
||||
console.log(`res`, res);
|
||||
});
|
||||
}, [wgIP]);
|
||||
|
||||
return (
|
||||
<Stack className="w-full items-stretch">
|
||||
<Input
|
||||
defaultValue={wgIP}
|
||||
changeHandler={(v) => {
|
||||
setWgIP(v);
|
||||
}}
|
||||
prefix={<Network size={17} opacity={0.5} />}
|
||||
suffix={
|
||||
<Span className="text-sm opacity-50 whitespace-nowrap">
|
||||
Selected Wireguard IP Address
|
||||
</Span>
|
||||
}
|
||||
autoFocus
|
||||
/>
|
||||
|
||||
<Button
|
||||
title="Setup Main Host"
|
||||
{...button_props}
|
||||
loading={loading}
|
||||
onClick={() => {
|
||||
setLoading(true);
|
||||
|
||||
fetchApi<ApiReqParams>(`/api/admin/setup-main-host`, {
|
||||
method: "POST",
|
||||
body: {
|
||||
ip_address: ``,
|
||||
},
|
||||
})
|
||||
.then((res) => {
|
||||
console.log(`res`, res);
|
||||
})
|
||||
.finally(() => {
|
||||
setTimeout(() => {
|
||||
setLoading(false);
|
||||
}, 4000);
|
||||
});
|
||||
}}
|
||||
>
|
||||
Setup Main Host
|
||||
</Button>
|
||||
</Stack>
|
||||
);
|
||||
}
|
||||
@@ -1,52 +0,0 @@
|
||||
import { twMerge } from "tailwind-merge";
|
||||
|
||||
export type WgStatus = "connected" | "idle" | "error";
|
||||
|
||||
type Props = {
|
||||
status: WgStatus;
|
||||
showLabel?: boolean;
|
||||
};
|
||||
|
||||
const STATUS_META: Record<
|
||||
WgStatus,
|
||||
{ label: string; dot: string; text: string }
|
||||
> = {
|
||||
connected: {
|
||||
label: "Connected",
|
||||
dot: "bg-success",
|
||||
text: "text-success dark:text-success",
|
||||
},
|
||||
idle: {
|
||||
label: "Idle",
|
||||
dot: "bg-warning",
|
||||
text: "text-warning dark:text-warning",
|
||||
},
|
||||
error: {
|
||||
label: "Error",
|
||||
dot: "bg-error",
|
||||
text: "text-error dark:text-error",
|
||||
},
|
||||
};
|
||||
|
||||
export default function StatusDot({ status, showLabel }: Props) {
|
||||
const meta = STATUS_META[status];
|
||||
|
||||
return (
|
||||
<span className="inline-flex items-center gap-1.5 shrink-0">
|
||||
<span
|
||||
className={twMerge("w-[6px] h-[6px] rounded-full", meta.dot)}
|
||||
aria-hidden="true"
|
||||
/>
|
||||
{showLabel ? (
|
||||
<span
|
||||
className={twMerge(
|
||||
"text-[12px] font-medium",
|
||||
meta.text,
|
||||
)}
|
||||
>
|
||||
{meta.label}
|
||||
</span>
|
||||
) : null}
|
||||
</span>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user