Add host setup functions and client components
This commit is contained in:
@@ -1,12 +1,20 @@
|
||||
import type { Dispatch, ReactNode, SetStateAction } from "react";
|
||||
import { useState, type Dispatch, type ReactNode, type SetStateAction } from "react";
|
||||
import { X } from "lucide-react";
|
||||
import Modal from "@/src/components/twui/elements/Modal";
|
||||
import Input from "@/src/components/twui/form/Input";
|
||||
import AdminButton from "@/src/components/general/admin-button";
|
||||
import Form from "@/src/components/twui/form/Form";
|
||||
import Stack from "@/src/components/twui/layout/Stack";
|
||||
import Span from "@/src/components/twui/layout/Span";
|
||||
import H3 from "@/src/components/twui/layout/H3";
|
||||
import Row from "@/src/components/twui/layout/Row";
|
||||
import adminCrudHandler from "@/src/functions/frontend/admin-crud-handler";
|
||||
import type { BUN_SQLITE_WGUI_CLIENTS } from "@/db/types/db";
|
||||
|
||||
type Props = {
|
||||
open: boolean;
|
||||
setOpen: Dispatch<SetStateAction<boolean>>;
|
||||
onCreated?: () => void;
|
||||
};
|
||||
|
||||
type FormFieldProps = {
|
||||
@@ -15,94 +23,104 @@ type FormFieldProps = {
|
||||
children: ReactNode;
|
||||
};
|
||||
|
||||
function FormField({ label, htmlFor, children }: FormFieldProps) {
|
||||
return (
|
||||
<div className="flex flex-col gap-1.5">
|
||||
<label
|
||||
htmlFor={htmlFor}
|
||||
className="text-[11.5px] font-semibold uppercase tracking-[0.08em] text-foreground-light/50 dark:text-foreground-dark/50"
|
||||
>
|
||||
{label}
|
||||
</label>
|
||||
{children}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
type FormData = {
|
||||
client_name?: string;
|
||||
tunnel_ip?: string;
|
||||
allowed_ips?: string;
|
||||
};
|
||||
|
||||
export default function ClientFormModal({ open, setOpen, onCreated }: Props) {
|
||||
const [busy, setBusy] = useState(false);
|
||||
const [error, setError] = useState<string>();
|
||||
|
||||
async function handleSubmit(data: FormData) {
|
||||
setBusy(true);
|
||||
setError(undefined);
|
||||
|
||||
const res = await adminCrudHandler<BUN_SQLITE_WGUI_CLIENTS>({
|
||||
action: "insert",
|
||||
table: "clients",
|
||||
insert_data: [
|
||||
{
|
||||
name: data.client_name || "",
|
||||
wg_ip_address: data.tunnel_ip || "",
|
||||
allowed_ips: data.allowed_ips || "",
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
setBusy(false);
|
||||
|
||||
if (!res.success) {
|
||||
setError(res.msg || "Could not add client");
|
||||
return;
|
||||
}
|
||||
|
||||
setOpen(false);
|
||||
onCreated?.();
|
||||
}
|
||||
|
||||
export default function ClientFormModal({ open, setOpen }: Props) {
|
||||
return (
|
||||
<Modal
|
||||
open={open}
|
||||
setOpen={setOpen}
|
||||
no_cancel_button
|
||||
className="p-6"
|
||||
>
|
||||
<div className="flex items-start justify-between gap-4 mb-6">
|
||||
<div>
|
||||
<h3 className="text-lg font-bold text-foreground-light dark:text-foreground-dark">
|
||||
Add client
|
||||
</h3>
|
||||
<p className="text-xs text-foreground-light/50 dark:text-foreground-dark/50 mt-1">
|
||||
A WireGuard config will be generated on save
|
||||
</p>
|
||||
</div>
|
||||
<button
|
||||
type="button"
|
||||
aria-label="Close"
|
||||
onClick={() => setOpen(false)}
|
||||
className="p-1 cursor-pointer text-foreground-light/60 dark:text-foreground-dark/60 hover:text-foreground-light dark:hover:text-foreground-dark transition-colors"
|
||||
>
|
||||
<X size={18} />
|
||||
</button>
|
||||
</div>
|
||||
<form
|
||||
className="flex flex-col gap-4"
|
||||
onSubmit={(e) => {
|
||||
e.preventDefault();
|
||||
setOpen(false);
|
||||
<Modal open={open} setOpen={setOpen} className="p-6">
|
||||
<Stack className="gap-4 mb-6">
|
||||
<H3 className="text-lg font-bold text-foreground-light dark:text-foreground-dark">
|
||||
Add client
|
||||
</H3>
|
||||
<Span className="" variant="faded">
|
||||
The client is added to your WireGuard network
|
||||
</Span>
|
||||
</Stack>
|
||||
|
||||
<Form
|
||||
submitHandler={(e, data) => {
|
||||
handleSubmit(data as FormData);
|
||||
}}
|
||||
>
|
||||
<FormField label="Client name" htmlFor="client_name">
|
||||
<Stack className="w-full items-stretch gap-6">
|
||||
<Input
|
||||
name="client_name"
|
||||
id="client_name"
|
||||
placeholder="e.g. MacBook Pro"
|
||||
label="Client name"
|
||||
autoFocus
|
||||
showLabel
|
||||
required
|
||||
/>
|
||||
</FormField>
|
||||
<div className="grid grid-cols-1 sm:grid-cols-2 gap-4">
|
||||
<FormField label="Tunnel IP" htmlFor="tunnel_ip">
|
||||
<Row className="grid grid-cols-1 sm:grid-cols-2 gap-4">
|
||||
<Input
|
||||
name="tunnel_ip"
|
||||
id="tunnel_ip"
|
||||
placeholder="10.0.0.10 · auto-assigned"
|
||||
placeholder="10.0.0.10"
|
||||
label="Tunnel IP"
|
||||
showLabel
|
||||
/>
|
||||
</FormField>
|
||||
<FormField label="Allowed IPs" htmlFor="allowed_ips">
|
||||
<Input
|
||||
name="allowed_ips"
|
||||
id="allowed_ips"
|
||||
placeholder="10.0.0.10/32"
|
||||
label="Allowed IPs"
|
||||
showLabel
|
||||
/>
|
||||
</FormField>
|
||||
</div>
|
||||
<FormField label="Notes" htmlFor="client_notes">
|
||||
<Input
|
||||
name="client_notes"
|
||||
id="client_notes"
|
||||
istextarea
|
||||
placeholder="Optional description for this client"
|
||||
/>
|
||||
</FormField>
|
||||
<div className="flex justify-end gap-2 mt-2">
|
||||
<AdminButton onClick={() => setOpen(false)}>
|
||||
Cancel
|
||||
</AdminButton>
|
||||
<AdminButton variant="primary" type="submit">
|
||||
Add client
|
||||
</AdminButton>
|
||||
</div>
|
||||
</form>
|
||||
</Row>
|
||||
{error ? (
|
||||
<Span className="text-[12.5px] text-error dark:text-error">
|
||||
{error}
|
||||
</Span>
|
||||
) : null}
|
||||
<Row className="justify-end gap-2 mt-2">
|
||||
<AdminButton onClick={() => setOpen(false)} disabled={busy}>
|
||||
Cancel
|
||||
</AdminButton>
|
||||
<AdminButton
|
||||
variant="primary"
|
||||
type="submit"
|
||||
disabled={busy}
|
||||
>
|
||||
{busy ? "Adding…" : "Add client"}
|
||||
</AdminButton>
|
||||
</Row>
|
||||
</Stack>
|
||||
</Form>
|
||||
</Modal>
|
||||
);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user