import { useState } from "react"; import { useMutation, useQueryClient, useSuspenseQuery } from "@tanstack/react-query"; import { clientDeleteMutation, clientPrefixesQuery, clientsQuery, groupsQuery } from "@/lib/queries"; import { formatTime } from "@/lib/format"; import type { Client } from "@/lib/types"; import ClientEditDialog from "./ClientEditDialog"; import PrefixesEditor from "./PrefixesEditor"; import InlineError from "@/lib/InlineError"; import { smallButtonClass, tableWrapClass } from "@/ui/classes"; import { READ_ONLY_HINT, useReadOnlyConfig } from "@/features/settings/authority"; const cellClass = "px-3 py-2"; /** * Deleting an observed row discards runtime state the file never declared, so * it stays live under file authority; deleting a hand-edited row contradicts * the file and is the one client DELETE the server answers 403 (ruling 7). */ const DECLARED_CLIENT_NOTE = "This client is declared in the configuration file; remove it there and restart."; export default function ClientsPage() { const { data: clients } = useSuspenseQuery(clientsQuery()); const { data: prefixes } = useSuspenseQuery(clientPrefixesQuery()); const { data: groups } = useSuspenseQuery(groupsQuery()); const queryClient = useQueryClient(); const deleteMutation = useMutation(clientDeleteMutation(queryClient)); const [editing, setEditing] = useState(null); const [confirmingId, setConfirmingId] = useState(null); const readOnly = useReadOnlyConfig(); return (

Clients

{clients.length === 0 ? (

No clients yet. Rows appear automatically as devices on the network make DNS queries — there is nothing to create by hand.

) : (
{clients.map((client) => ( ))}
IP Name Group First seen Last seen Actions
{client.ip} {client.name === "" ? : client.name} {client.hand_edited && ( edited )} {client.group} {formatTime(client.first_seen)} {formatTime(client.last_seen)} {confirmingId === client.id ? ( Deleted clients re-materialize on their next DNS query. ) : ( )}
)} {editing !== null && setEditing(null)} />}
); }