Extract shared ClientsTable component and useClientRulesMap hook
This commit is contained in:
@@ -0,0 +1,57 @@
|
||||
import ClientRow from "@/src/components/general/client-row";
|
||||
import type {
|
||||
BUN_SQLITE_WGUI_CLIENTS,
|
||||
BUN_SQLITE_WGUI_CLIENT_RULES,
|
||||
} from "@/db/types/db";
|
||||
|
||||
type Props = {
|
||||
clients: BUN_SQLITE_WGUI_CLIENTS[];
|
||||
rules_by_client_id: Map<number, BUN_SQLITE_WGUI_CLIENT_RULES[]>;
|
||||
accessLabel?: string;
|
||||
minWidthClass?: string;
|
||||
};
|
||||
|
||||
const thClass =
|
||||
"px-4 py-2 text-left text-[11px] font-semibold uppercase tracking-[0.08em] " +
|
||||
"text-foreground-light/40 dark:text-foreground-dark/40 whitespace-nowrap";
|
||||
|
||||
const thRightClass = `${thClass} text-right`;
|
||||
|
||||
export default function ClientsTable({
|
||||
clients,
|
||||
rules_by_client_id,
|
||||
accessLabel = "Access",
|
||||
minWidthClass = "min-w-[860px]",
|
||||
}: Props) {
|
||||
return (
|
||||
<div className="overflow-x-auto w-full">
|
||||
<table className={`w-full ${minWidthClass}`}>
|
||||
<thead className="w-full">
|
||||
<tr className="border-y border-slate-200 dark:border-white/10 bg-foreground-light/[0.02] dark:bg-foreground-dark/[0.03] w-full">
|
||||
<th className={thClass}>Client</th>
|
||||
<th className={thClass}>Host</th>
|
||||
<th className={thClass}>Tunnel IP</th>
|
||||
<th className={thClass}>Allowed IPs</th>
|
||||
<th className={thClass}>{accessLabel}</th>
|
||||
<th className={thClass}>Public key</th>
|
||||
<th className={thRightClass}>Created</th>
|
||||
<th className={thRightClass}>Actions</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{clients.map((client) => (
|
||||
<ClientRow
|
||||
key={client.id}
|
||||
client={client}
|
||||
rules={
|
||||
client.id
|
||||
? rules_by_client_id.get(Number(client.id))
|
||||
: undefined
|
||||
}
|
||||
/>
|
||||
))}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
import { useMemo } from "react";
|
||||
import { useAdminCrudGet } from "@/src/hooks/use-admin-crud-get";
|
||||
import type { BUN_SQLITE_WGUI_CLIENT_RULES } from "@/db/types/db";
|
||||
|
||||
type ClientRulesMap = Map<number, BUN_SQLITE_WGUI_CLIENT_RULES[]>;
|
||||
|
||||
export default function useClientRulesMap(
|
||||
initial_res?: BUN_SQLITE_WGUI_CLIENT_RULES[] | null,
|
||||
): ClientRulesMap {
|
||||
const { res: client_rules } = useAdminCrudGet<BUN_SQLITE_WGUI_CLIENT_RULES>(
|
||||
{
|
||||
table: "client_rules",
|
||||
sql_query: {
|
||||
limit: 500,
|
||||
},
|
||||
initial_res: initial_res ?? undefined,
|
||||
},
|
||||
);
|
||||
|
||||
return useMemo(() => {
|
||||
const map: ClientRulesMap = new Map();
|
||||
|
||||
for (let i = 0; i < (client_rules || []).length; i++) {
|
||||
const rule = client_rules?.[i];
|
||||
const client_id = Number(rule?.client_id);
|
||||
|
||||
if (!rule || !client_id) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const existing = map.get(client_id) || [];
|
||||
existing.push(rule);
|
||||
map.set(client_id, existing);
|
||||
}
|
||||
|
||||
return map;
|
||||
}, [client_rules]);
|
||||
}
|
||||
@@ -5,56 +5,20 @@ import Row from "@/src/components/twui/layout/Row";
|
||||
import Stack from "@/src/components/twui/layout/Stack";
|
||||
import Span from "@/src/components/twui/layout/Span";
|
||||
import AdminCard from "@/src/components/general/admin-card";
|
||||
import ClientRow from "@/src/components/general/client-row";
|
||||
import type {
|
||||
BUN_SQLITE_WGUI_CLIENT_RULES,
|
||||
BUN_SQLITE_WGUI_CLIENTS,
|
||||
} from "@/db/types/db";
|
||||
import { useAdminCrudGet } from "@/src/hooks/use-admin-crud-get";
|
||||
import { useMemo } from "react";
|
||||
import ClientsTable from "@/src/components/general/clients-table";
|
||||
import useClientRulesMap from "@/src/hooks/use-client-rules-map";
|
||||
import type { BUN_SQLITE_WGUI_CLIENTS } from "@/db/types/db";
|
||||
|
||||
type Props = {
|
||||
clients?: BUN_SQLITE_WGUI_CLIENTS[];
|
||||
};
|
||||
|
||||
const thClass =
|
||||
"px-4 py-2 text-left text-[11px] font-semibold uppercase tracking-[0.08em] " +
|
||||
"text-foreground-light/40 dark:text-foreground-dark/40 whitespace-nowrap";
|
||||
|
||||
const thRightClass = `${thClass} text-right`;
|
||||
|
||||
const RECENT_LIMIT = 8;
|
||||
|
||||
export default function PeersTableSection({ clients }: Props) {
|
||||
const recent = (clients || []).slice(0, RECENT_LIMIT);
|
||||
|
||||
const { res: client_rules } = useAdminCrudGet<BUN_SQLITE_WGUI_CLIENT_RULES>(
|
||||
{
|
||||
table: "client_rules",
|
||||
sql_query: {
|
||||
limit: 500,
|
||||
},
|
||||
},
|
||||
);
|
||||
|
||||
const rules_by_client_id = useMemo(() => {
|
||||
const map = new Map<number, BUN_SQLITE_WGUI_CLIENT_RULES[]>();
|
||||
|
||||
for (let i = 0; i < (client_rules || []).length; i++) {
|
||||
const rule = client_rules?.[i];
|
||||
const client_id = Number(rule?.client_id);
|
||||
|
||||
if (!rule || !client_id) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const existing = map.get(client_id) || [];
|
||||
existing.push(rule);
|
||||
map.set(client_id, existing);
|
||||
}
|
||||
|
||||
return map;
|
||||
}, [client_rules]);
|
||||
const rules_by_client_id = useClientRulesMap();
|
||||
|
||||
return (
|
||||
<AdminCard className="w-full overflow-hidden">
|
||||
@@ -71,37 +35,11 @@ export default function PeersTableSection({ clients }: Props) {
|
||||
</Link>
|
||||
</Row>
|
||||
{recent.length ? (
|
||||
<div className="overflow-x-auto">
|
||||
<table className="w-full min-w-[860px]">
|
||||
<thead className="w-full">
|
||||
<tr className="border-y border-slate-200 dark:border-white/10 bg-foreground-light/[0.02] dark:bg-foreground-dark/[0.03] w-full">
|
||||
<th className={thClass}>Client</th>
|
||||
<th className={thClass}>Host</th>
|
||||
<th className={thClass}>Tunnel IP</th>
|
||||
<th className={thClass}>Allowed IPs</th>
|
||||
<th className={thClass}>Access List</th>
|
||||
<th className={thClass}>Public key</th>
|
||||
<th className={thRightClass}>Created</th>
|
||||
<th className={thRightClass}>Actions</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{recent.map((client) => (
|
||||
<ClientRow
|
||||
key={client.id}
|
||||
client={client}
|
||||
rules={
|
||||
client.id
|
||||
? rules_by_client_id.get(
|
||||
Number(client.id),
|
||||
)
|
||||
: undefined
|
||||
}
|
||||
/>
|
||||
))}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<ClientsTable
|
||||
clients={recent}
|
||||
rules_by_client_id={rules_by_client_id}
|
||||
accessLabel="Access List"
|
||||
/>
|
||||
) : (
|
||||
<Stack
|
||||
center
|
||||
@@ -117,4 +55,4 @@ export default function PeersTableSection({ clients }: Props) {
|
||||
)}
|
||||
</AdminCard>
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -2,29 +2,21 @@ import { useMemo, useState, type Dispatch, type SetStateAction } from "react";
|
||||
import Search from "@/src/components/twui/elements/Search";
|
||||
import Loading from "@/src/components/twui/elements/Loading";
|
||||
import AdminCard from "@/src/components/general/admin-card";
|
||||
import ClientRow from "@/src/components/general/client-row";
|
||||
import ClientsTable from "@/src/components/general/clients-table";
|
||||
import H2 from "@/src/components/twui/layout/H2";
|
||||
import Span from "@/src/components/twui/layout/Span";
|
||||
import Row from "@/src/components/twui/layout/Row";
|
||||
import Stack from "@/src/components/twui/layout/Stack";
|
||||
import ClientFormModal from "../(partials)/client-form-modal";
|
||||
import useClientRulesMap from "@/src/hooks/use-client-rules-map";
|
||||
import { useAdminCrudGet } from "@/src/hooks/use-admin-crud-get";
|
||||
import type {
|
||||
BUN_SQLITE_WGUI_CLIENTS,
|
||||
BUN_SQLITE_WGUI_CLIENT_RULES,
|
||||
} from "@/db/types/db";
|
||||
import type { BUN_SQLITE_WGUI_CLIENTS } from "@/db/types/db";
|
||||
|
||||
type Props = {
|
||||
addOpen: boolean;
|
||||
setAddOpen: Dispatch<SetStateAction<boolean>>;
|
||||
};
|
||||
|
||||
const thClass =
|
||||
"px-4 py-2 text-left text-[11px] font-semibold uppercase tracking-[0.08em] " +
|
||||
"text-foreground-light/40 dark:text-foreground-dark/40 whitespace-nowrap";
|
||||
|
||||
const thRightClass = `${thClass} text-right`;
|
||||
|
||||
export default function ClientsTableSection({ addOpen, setAddOpen }: Props) {
|
||||
const [query, setQuery] = useState("");
|
||||
|
||||
@@ -36,31 +28,7 @@ export default function ClientsTableSection({ addOpen, setAddOpen }: Props) {
|
||||
},
|
||||
});
|
||||
|
||||
const { res: client_rules } = useAdminCrudGet<BUN_SQLITE_WGUI_CLIENT_RULES>({
|
||||
table: "client_rules",
|
||||
sql_query: {
|
||||
limit: 500,
|
||||
},
|
||||
});
|
||||
|
||||
const rules_by_client_id = useMemo(() => {
|
||||
const map = new Map<number, BUN_SQLITE_WGUI_CLIENT_RULES[]>();
|
||||
|
||||
for (let i = 0; i < (client_rules || []).length; i++) {
|
||||
const rule = client_rules?.[i];
|
||||
const client_id = Number(rule?.client_id);
|
||||
|
||||
if (!rule || !client_id) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const existing = map.get(client_id) || [];
|
||||
existing.push(rule);
|
||||
map.set(client_id, existing);
|
||||
}
|
||||
|
||||
return map;
|
||||
}, [client_rules]);
|
||||
const rules_by_client_id = useClientRulesMap();
|
||||
|
||||
const filtered = useMemo(() => {
|
||||
const clients = res || [];
|
||||
@@ -102,37 +70,11 @@ export default function ClientsTableSection({ addOpen, setAddOpen }: Props) {
|
||||
<Loading />
|
||||
</Stack>
|
||||
) : filtered.length ? (
|
||||
<div className="overflow-x-auto">
|
||||
<table className="w-full min-w-[940px]">
|
||||
<thead>
|
||||
<tr className="border-y border-slate-200 dark:border-white/10 bg-foreground-light/[0.02] dark:bg-foreground-dark/[0.03]">
|
||||
<th className={thClass}>Client</th>
|
||||
<th className={thClass}>Host</th>
|
||||
<th className={thClass}>Tunnel IP</th>
|
||||
<th className={thClass}>Allowed IPs</th>
|
||||
<th className={thClass}>Access</th>
|
||||
<th className={thClass}>Public key</th>
|
||||
<th className={thRightClass}>Created</th>
|
||||
<th className={thRightClass}>Actions</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{filtered.map((client) => (
|
||||
<ClientRow
|
||||
key={client.id}
|
||||
client={client}
|
||||
rules={
|
||||
client.id
|
||||
? rules_by_client_id.get(
|
||||
Number(client.id),
|
||||
)
|
||||
: undefined
|
||||
}
|
||||
/>
|
||||
))}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<ClientsTable
|
||||
clients={filtered}
|
||||
rules_by_client_id={rules_by_client_id}
|
||||
minWidthClass="min-w-[940px]"
|
||||
/>
|
||||
) : (
|
||||
<Stack center className="w-full justify-center py-14 px-6 text-center gap-1">
|
||||
<Span className="text-[13.5px] font-medium text-foreground-light/50 dark:text-foreground-dark/50">
|
||||
|
||||
@@ -2,27 +2,21 @@ import Loading from "@/src/components/twui/elements/Loading";
|
||||
import Paper from "@/src/components/twui/elements/Paper";
|
||||
import Center from "@/src/components/twui/layout/Center";
|
||||
import Span from "@/src/components/twui/layout/Span";
|
||||
import Stack from "@/src/components/twui/layout/Stack";
|
||||
import ClientRow from "@/src/components/general/client-row";
|
||||
import ClientsTable from "@/src/components/general/clients-table";
|
||||
import useClientRulesMap from "@/src/hooks/use-client-rules-map";
|
||||
import { useAdminCrudGet } from "@/src/hooks/use-admin-crud-get";
|
||||
import { AppContext } from "@/src/pages/__root";
|
||||
import { useContext, useMemo } from "react";
|
||||
import type { BUN_SQLITE_WGUI_CLIENT_RULES } from "@/db/types/db";
|
||||
import { useContext } from "react";
|
||||
import type { BUN_SQLITE_WGUI_CLIENTS } from "@/db/types/db";
|
||||
|
||||
type Props = {
|
||||
host_id: string | number;
|
||||
};
|
||||
|
||||
const thClass =
|
||||
"px-4 py-2 text-left text-[11px] font-semibold uppercase tracking-[0.08em] " +
|
||||
"text-foreground-light/40 dark:text-foreground-dark/40 whitespace-nowrap";
|
||||
|
||||
const thRightClass = `${thClass} text-right`;
|
||||
|
||||
export default function ClientsList({ host_id }: Props) {
|
||||
const { pageProps } = useContext(AppContext);
|
||||
|
||||
const { res: clients } = useAdminCrudGet({
|
||||
const { res: clients } = useAdminCrudGet<BUN_SQLITE_WGUI_CLIENTS>({
|
||||
table: "clients",
|
||||
sql_query: {
|
||||
query: {
|
||||
@@ -33,32 +27,7 @@ export default function ClientsList({ host_id }: Props) {
|
||||
initial_res: pageProps?.clients || undefined,
|
||||
});
|
||||
|
||||
const { res: client_rules } = useAdminCrudGet<BUN_SQLITE_WGUI_CLIENT_RULES>({
|
||||
table: "client_rules",
|
||||
sql_query: {
|
||||
limit: 500,
|
||||
},
|
||||
initial_res: pageProps?.client_rules || undefined,
|
||||
});
|
||||
|
||||
const rules_by_client_id = useMemo(() => {
|
||||
const map = new Map<number, BUN_SQLITE_WGUI_CLIENT_RULES[]>();
|
||||
|
||||
for (let i = 0; i < (client_rules || []).length; i++) {
|
||||
const rule = client_rules?.[i];
|
||||
const client_id = Number(rule?.client_id);
|
||||
|
||||
if (!rule || !client_id) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const existing = map.get(client_id) || [];
|
||||
existing.push(rule);
|
||||
map.set(client_id, existing);
|
||||
}
|
||||
|
||||
return map;
|
||||
}, [client_rules]);
|
||||
const rules_by_client_id = useClientRulesMap(pageProps?.client_rules);
|
||||
|
||||
if (!clients) {
|
||||
return (
|
||||
@@ -81,33 +50,11 @@ export default function ClientsList({ host_id }: Props) {
|
||||
}
|
||||
|
||||
return (
|
||||
<Paper className="overflow-x-auto">
|
||||
<table className="w-full min-w-[860px]">
|
||||
<thead>
|
||||
<tr className="border-y border-slate-200 dark:border-white/10 bg-foreground-light/[0.02] dark:bg-foreground-dark/[0.03]">
|
||||
<th className={thClass}>Client</th>
|
||||
<th className={thClass}>Tunnel IP</th>
|
||||
<th className={thClass}>Allowed IPs</th>
|
||||
<th className={thClass}>Access</th>
|
||||
<th className={thClass}>Public key</th>
|
||||
<th className={thRightClass}>Created</th>
|
||||
<th className={thRightClass}>Actions</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{clients.map((client) => (
|
||||
<ClientRow
|
||||
key={client.id}
|
||||
client={client}
|
||||
rules={
|
||||
client.id
|
||||
? rules_by_client_id.get(Number(client.id))
|
||||
: undefined
|
||||
}
|
||||
/>
|
||||
))}
|
||||
</tbody>
|
||||
</table>
|
||||
<Paper>
|
||||
<ClientsTable
|
||||
clients={clients}
|
||||
rules_by_client_id={rules_by_client_id}
|
||||
/>
|
||||
</Paper>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user