"use client"; import { useState, useEffect } from "react"; import { X, Plus, Trash2, Network, Save, AlertTriangle } from "lucide-react"; interface SubnetConfigModalProps { agentUuid: string; agentLabel: string; open: boolean; onClose: () => void; } export function SubnetConfigModal({ agentUuid, agentLabel, open, onClose }: SubnetConfigModalProps) { const [subnets, setSubnets] = useState([]); const [newSubnet, setNewSubnet] = useState(""); const [loading, setLoading] = useState(false); const [saving, setSaving] = useState(false); const [error, setError] = useState(""); const [success, setSuccess] = useState(false); useEffect(() => { if (!open || !agentUuid) return; setLoading(true); setError(""); setSuccess(false); fetch(`/api/dashboard/agents/${agentUuid}/subnets`) .then(res => res.json()) .then(data => { if (data.ok) setSubnets(data.data?.allowed_subnets || []); else setError(data.error || "Gagal memuat data"); }) .catch(() => setError("Koneksi gagal")) .finally(() => setLoading(false)); }, [open, agentUuid]); const addSubnet = () => { let val = newSubnet.trim(); if (!val) return; if (val.endsWith('.')) val = val.slice(0, -1); if (val.includes('/')) { const [ip, bits] = val.split('/'); const ipParts = ip.split('.'); if (ipParts.length !== 4 || ipParts.some(p => p === "" || isNaN(Number(p)) || Number(p) > 255) || isNaN(Number(bits)) || Number(bits) < 0 || Number(bits) > 32) { setError("Format CIDR tidak valid. Contoh: 192.168.15.0/24"); return; } } else { const parts = val.split("."); if (parts.some(p => p !== "" && (isNaN(Number(p)) || Number(p) > 255))) { setError("Format subnet tidak valid. Contoh: 192.168 atau 10.21.5"); return; } } if (subnets.includes(val)) { setError("Subnet sudah ada"); return; } setError(""); setSubnets(prev => [...prev, val]); setNewSubnet(""); }; const removeSubnet = (idx: number) => { setSubnets(prev => prev.filter((_, i) => i !== idx)); }; const save = async () => { setSaving(true); setError(""); setSuccess(false); try { const res = await fetch(`/api/dashboard/agents/${agentUuid}/subnets`, { method: "PUT", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ allowed_subnets: subnets }), }); const data = await res.json(); if (data.ok) { setSuccess(true); setTimeout(() => setSuccess(false), 3000); } else { setError(data.error || "Gagal menyimpan"); } } catch { setError("Koneksi gagal"); } finally { setSaving(false); } }; if (!open) return null; return (
{/* Header */}

Konfigurasi Subnet IP

{agentLabel || agentUuid}

{/* Body */}
{/* Info banner */}

Daftarkan prefix subnet IP yang hanya milik agen ini. Saat sinkronisasi data, IP yang tidak cocok dengan subnet di bawah ini akan otomatis dibuang agar tidak bercampur dengan agen lain.

Contoh format valid:
• 192.168.10.0/24 atau 10.8.60.0/20 (Format CIDR)
• 192.168.10 (Semua IP berawalan 192.168.10)
• 10.21.7.72 (Hanya IP Spesifik)

{/* Subnet list */}
{loading ? (
) : subnets.length === 0 ? (

Belum ada subnet dikonfigurasi.

Semua IP akan diterima (tidak difilter).

) : (
{subnets.map((s, i) => (
{s.includes('/') || s.split('.').length === 4 ? s : `${s}.x`}
))}
)}
{/* Add new subnet */}
{ setNewSubnet(e.target.value); setError(""); }} onKeyDown={e => e.key === "Enter" && addSubnet()} placeholder="Contoh: 192.168.10.0/24 atau 10.21" className="flex-1 bg-white/[0.03] border border-white/10 rounded-xl px-3 py-2 text-sm text-white font-mono placeholder:text-slate-600 focus:outline-none focus:border-violet-500/50 focus:bg-white/[0.05] transition-all" />
{/* Error / Success */} {error &&

{error}

} {success &&

✓ Konfigurasi subnet berhasil disimpan!

}
{/* Footer */}
); }