- Clusters page: DNS/DHCP clusters with members, transfer key, DNS
options and Kea HA settings; member picking limited to free agents of
the role, client-side checks of the membership rules.
- TSIG keys page: create, edit, rotate, delete; secrets only after an
explicit, audited retrieval and shown once.
- Zones page with derived zones ("take over") and zone detail with
type-specific record forms and hints for dynamic zones and AD.
- Networks, agents and generator settings show/assign clusters; files,
diffs and pending changes are grouped by target, secret files are
never shown.
- Pages are lazy-loaded per route (main bundle 600 KB -> 480 KB).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
37 lines
1.1 KiB
TypeScript
37 lines
1.1 KiB
TypeScript
// SPDX-License-Identifier: LicenseRef-DDI-Proprietary
|
|
import { useClusters, type AgentRole } from '../clusters';
|
|
|
|
interface Props {
|
|
id: string;
|
|
role: AgentRole;
|
|
/** Cluster ID; null for the default. */
|
|
value: string | null;
|
|
onChange: (value: string | null) => void;
|
|
/** Text of the empty option (default cluster or default group). */
|
|
emptyLabel: string;
|
|
}
|
|
|
|
/** Select of the clusters of one role with an empty option for the default. */
|
|
export function ClusterSelect({ id, role, value, onChange, emptyLabel }: Props) {
|
|
const clusters = useClusters();
|
|
const list = (clusters.data?.items ?? []).filter((c) => c.role === role);
|
|
return (
|
|
<select
|
|
id={id}
|
|
value={value ?? ''}
|
|
onChange={(e) => {
|
|
onChange(e.target.value || null);
|
|
}}
|
|
>
|
|
<option value="">{emptyLabel}</option>
|
|
{list.map((c) => (
|
|
<option key={c.id} value={c.id}>
|
|
{c.name}
|
|
</option>
|
|
))}
|
|
{/* Keep an unknown value selectable until the list has loaded. */}
|
|
{value && !list.some((c) => c.id === value) && <option value={value}>{value}</option>}
|
|
</select>
|
|
);
|
|
}
|