This commit is contained in:
2026-09-20 04:14:32 +01:00
parent cda6cff34f
commit d5536b49d9
42 changed files with 1544 additions and 143 deletions
+62 -2
View File
@@ -1,5 +1,10 @@
import { useState } from "react";
import { Pencil, Trash2 } from "lucide-react";
import Link from "@/src/components/twui/layout/Link";
import Row from "@/src/components/twui/layout/Row";
import Span from "@/src/components/twui/layout/Span";
import formatUnixTimestamp from "@/src/utils/format-unix-timestamp";
import adminCrudHandler from "@/src/functions/frontend/admin-crud-handler";
import type { BUN_SQLITE_WGUI_CLIENTS } from "@/db/types/db";
type Props = {
@@ -7,12 +12,45 @@ type Props = {
};
export default function ClientRow({ client }: Props) {
const [deleting, setDeleting] = useState(false);
const host_id = Number(client.host_id || 0);
async function handleDelete() {
if (
!window.confirm(
`Delete client "${client?.name || `#${client?.id}`}"? The client will be removed from the host and its config file deleted.`,
)
) {
return;
}
setDeleting(true);
const res = await adminCrudHandler({
action: "delete",
table: "clients",
id: client?.id,
});
if (!res.success) {
setDeleting(false);
window.alert(res.msg || "Could not delete client");
return;
}
window.location.reload();
}
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">
<Link
href={`/admin/hosts/${host_id}/clients/${client.id}`}
className="text-[13.5px] font-medium text-foreground-light dark:text-foreground-dark hover:text-secondary dark:hover:text-secondary"
>
{client.name || "—"}
</Span>
</Link>
</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 || "—"}
@@ -30,6 +68,28 @@ export default function ClientRow({ client }: Props) {
<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>
<td className="px-4 py-[9px] text-right">
<Row className="gap-1.5 items-center">
<Link
href={`/admin/hosts/${host_id}/clients/${client.id}/edit`}
title={`Edit client ${client.name || `#${client.id}`}`}
aria-label={`Edit client ${client.name || `#${client.id}`}`}
className="p-1.5 text-foreground-light/40 dark:text-foreground-dark/40 hover:text-foreground-light dark:hover:text-foreground-dark focus:outline-2"
>
<Pencil size={14} />
</Link>
<button
type="button"
title={`Delete client ${client.name || `#${client.id}`}`}
aria-label={`Delete client ${client.name || `#${client.id}`}`}
disabled={deleting}
onClick={handleDelete}
className="p-1.5 text-error hover:bg-error/10 focus:outline-2 transition-colors duration-150"
>
<Trash2 size={14} />
</button>
</Row>
</td>
</tr>
);
}
+7 -4
View File
@@ -1,8 +1,8 @@
import React, { type RefObject } from "react";
import { twMerge } from "tailwind-merge";
import AceEditorModes from "./ace-editor-modes";
// @ts-ignore
import { AceEditorOptions } from "@moduletrace/datasquirel/dist/package-shared/types";
import type { AceEditorOptions } from "../types";
import type { Ace as AceAjax } from "ace-builds";
export type AceEditorComponentType = {
editorRef?: RefObject<AceAjax.Editor | undefined>;
@@ -60,6 +60,9 @@ export default function AceEditor({
React.useEffect(() => {
if (!ready) return;
// @ts-ignore
const ace = window.ace as AceAjax;
if (!ace?.edit || !editorElementRef.current) {
setTimeout(() => {
setRefresh((prev) => prev + 1);
@@ -95,13 +98,13 @@ export default function AceEditor({
editor.commands.addCommand({
name: "myCommand",
bindKey: { win: "Ctrl-Enter", mac: "Command-Enter" },
exec: function (editor) {
exec: function (editor: any) {
if (ctrlEnterFn) ctrlEnterFn(editor);
},
readOnly: true,
});
editor.getSession().on("change", function (e) {
editor.getSession().on("change", function () {
if (onChange) {
clearTimeout(timeout);