import { Alert, Button, Checkbox, Input, Label, Modal, TextField } from "@heroui/react"; import { useEffect, useMemo, useState } from "react"; import { useMutation, useQuery } from "@tanstack/react-query"; import { createManagedNe, fetchManagedNeMeta, updateManagedNe } from "../../services/api"; import { HopProxyFields } from "../../components/HopProxyFields"; import { AppModalShell } from "../../components/ui/AppModalShell"; import { FieldSelect } from "../../components/ui/FieldSelect"; import { queryKeys } from "../../constants/queryKeys"; import { useI18n } from "../../i18n"; import { patchHopVendorChange } from "../../utils/hopProxy"; import type { ManagedNeItem } from "../../types"; import { applyHopTemplate, buildManagedNeSaveBody, emptyManagedNeForm, formFromManagedNe, type ManagedNeFormState, } from "./formState"; export type ManagedNeFormDialogProps = { open: boolean; /** null = create mode */ editing: ManagedNeItem | null; /** Prefill when creating (e.g. topology deep-link fields). Ignored when editing. */ initialValues?: Partial; onClose: () => void; onSaved: (item: ManagedNeItem) => void; }; export function ManagedNeFormDialog({ open, editing, initialValues, onClose, onSaved, }: ManagedNeFormDialogProps) { const { t } = useI18n(); const [form, setForm] = useState(emptyManagedNeForm); const metaQuery = useQuery({ queryKey: queryKeys.managedNeMeta, queryFn: fetchManagedNeMeta, staleTime: 60_000, enabled: open, }); const editingId = editing?.id ?? ""; useEffect(() => { if (!open) return; if (editing) { setForm(formFromManagedNe(editing)); return; } setForm({ ...emptyManagedNeForm(), ...(initialValues || {}) }); // eslint-disable-next-line react-hooks/exhaustive-deps -- seed once per open/create }, [open, editingId]); const vendors = metaQuery.data?.vendors ?? []; const deviceTypes = useMemo(() => { const base = metaQuery.data?.device_types ?? []; const cur = String(form.device_type || "").trim(); if (cur && !base.includes(cur)) return [cur, ...base]; return base; }, [metaQuery.data?.device_types, form.device_type]); const saveMutation = useMutation({ mutationFn: async () => { const body = buildManagedNeSaveBody(form, { editing: Boolean(editing), hopHostRequired: t("managedNe.hop.hostRequired"), hopUserRequired: t("managedNe.hop.userRequired"), hopPasswordRequired: t("managedNe.hop.passwordRequired"), }); if (editing) { return updateManagedNe(editing.id, body); } return createManagedNe(body); }, onSuccess: (item) => { onSaved(item); }, }); return ( {editing ? t("managedNe.form.editTitle") : t("managedNe.form.createTitle")}

{t("managedNe.form.requiredHint")}

setForm({ ...form, name })} > {t("managedNe.form.nameConnectHint")} { const vendor = e.target.value; setForm((prev) => { const next = { ...prev, vendor }; const dt = String(prev.device_type || "").trim().toLowerCase(); if (!dt || dt === "generic" || dt === "other" || dt === "linux") { if (vendor === "ZTE") next.device_type = "zte_zxros"; else if (vendor === "Huawei") next.device_type = "huawei"; else if (vendor === "Cisco") next.device_type = "cisco_ios"; else if (vendor === "Juniper") next.device_type = "juniper_junos"; else if (vendor === "Nokia") next.device_type = "nokia_sros"; } return next; }); }} > {vendors.map((v) => ( ))} setForm({ ...form, device_type: e.target.value })} > {deviceTypes.map((dt) => ( ))} setForm({ ...form, ip_address })} > setForm({ ...form, port: Number(port) || 22 })} > setForm({ ...form, protocol: e.target.value })} > setForm({ ...form, username })} > setForm({ ...form, password })} > setForm({ ...form, tags })}> setForm({ ...form, remark })} >
{t("managedNe.hop.sectionTitle")} { setForm((prev) => ({ ...prev, hop_enabled, ...(hop_enabled ? { ...patchHopVendorChange(prev.hop_vendor, prev), ...applyHopTemplate(prev, prev.hop_protocol, prev.hop_vrf, true), } : {}), })); }} > {t("managedNe.hop.enable")} {form.hop_enabled ? ( setForm((prev) => ({ ...prev, ...patch }))} hopPasswordRequired={!editing} hopPasswordOptional={Boolean(editing)} /> ) : null}
{saveMutation.isError ? ( {String(saveMutation.error)} ) : null}
); }