import { useId, useState, type FormEvent } from "react"; import { useMutation, useQueryClient, useSuspenseQuery } from "@tanstack/react-query"; import { forwardZoneCreateMutation, forwardZoneDeleteMutation, forwardZoneUpdateMutation, forwardZonesQuery, } from "@/lib/queries"; import type { ForwardZone, ForwardZoneInput } from "@/lib/types"; import InlineError from "@/lib/InlineError"; const inputClass = "mt-1 w-full rounded border border-zinc-300 bg-white px-3 py-2 focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-blue-600 dark:border-zinc-700 dark:bg-zinc-900"; const primaryButtonClass = "rounded bg-blue-600 px-3 py-2 font-medium text-white disabled:opacity-50 focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-blue-600"; const secondaryButtonClass = "rounded border border-zinc-300 px-3 py-2 font-medium focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-blue-600 dark:border-zinc-700"; const rowButtonClass = "rounded px-2 py-1 text-sm text-blue-600 focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-blue-600 dark:text-blue-400"; type FormState = { mode: "create" } | { mode: "edit"; zone: ForwardZone }; function ZoneForm({ initial, busy, error, onSubmit, onCancel, }: { initial?: ForwardZone; busy: boolean; error: unknown; onSubmit: (input: ForwardZoneInput) => void; onCancel: () => void; }) { const id = useId(); const [zone, setZone] = useState(initial?.zone ?? ""); const [resolver, setResolver] = useState(initial?.resolver ?? ""); function submit(event: FormEvent) { event.preventDefault(); onSubmit({ zone: zone.trim(), resolver: resolver.trim() }); } return (

{initial === undefined ? "New forward zone" : `Edit ${initial.zone}`}

setZone(event.target.value)} placeholder="lan.home" className={inputClass} />
setResolver(event.target.value)} placeholder="udp://192.168.1.1:53" className={inputClass} />
); } export default function ZonesTab() { const zones = useSuspenseQuery(forwardZonesQuery()).data; const queryClient = useQueryClient(); const create = useMutation(forwardZoneCreateMutation(queryClient)); const update = useMutation(forwardZoneUpdateMutation(queryClient)); const remove = useMutation(forwardZoneDeleteMutation(queryClient)); const [form, setForm] = useState(null); function openForm(next: FormState) { create.reset(); update.reset(); setForm(next); } function onSubmit(input: ForwardZoneInput) { if (form === null) return; if (form.mode === "create") { create.mutate(input, { onSuccess: () => setForm(null) }); } else { update.mutate({ id: form.zone.id, input }, { onSuccess: () => setForm(null) }); } } function onDelete(zone: ForwardZone) { if (!window.confirm(`Delete forward zone "${zone.zone}"?`)) return; remove.mutate(zone.id); } return (

Names under these zones go to their own resolver. Changes apply live.

{form?.mode === "create" && ( setForm(null)} /> )}
{zones.length === 0 && ( )} {zones.map((zone) => ( {form?.mode === "edit" && form.zone.id === zone.id ? ( ) : ( <> )} ))}
Zone Resolver Actions
No forward zones yet.
setForm(null)} /> {zone.zone} {zone.resolver}
); }