import { useState, useEffect } from "react"; import { Card } from "./ui/card"; import { Button } from "./ui/button"; import { Cloud, HelpCircle, Edit2, Check, X, Loader2, AlertCircle } from "lucide-react"; import { Input, Label } from "./ui"; import { useInstanceConfig, useInstanceContext } from "../hooks"; interface CloudConfig { domain: string; internalDomain: string; dhcpRange: string; dns: { ip: string; }; router: { ip: string; }; dnsmasq: { interface: string; }; } interface ClusterConfig { endpointIp: string; hostnamePrefix?: string; nodes: { talos: { version: string; }; }; } export function CloudComponent() { const { currentInstance } = useInstanceContext(); const { config: fullConfig, isLoading, error, updateConfig, isUpdating } = useInstanceConfig(currentInstance); // Extract cloud and cluster config from full config const config = fullConfig?.cloud as CloudConfig | undefined; const clusterConfig = fullConfig?.cluster as ClusterConfig | undefined; const [editingDomains, setEditingDomains] = useState(false); const [editingNetwork, setEditingNetwork] = useState(false); const [editingCluster, setEditingCluster] = useState(false); const [formValues, setFormValues] = useState(null); const [clusterFormValues, setClusterFormValues] = useState(null); // Sync form values when config loads useEffect(() => { if (config && !formValues) { setFormValues(config as CloudConfig); } if (clusterConfig && !clusterFormValues) { setClusterFormValues(clusterConfig as ClusterConfig); } }, [config, clusterConfig, formValues, clusterFormValues]); const handleDomainsEdit = () => { if (config) { setFormValues(config as CloudConfig); setEditingDomains(true); } }; const handleNetworkEdit = () => { if (config) { setFormValues(config as CloudConfig); setEditingNetwork(true); } }; const handleDomainsSave = async () => { if (!formValues || !fullConfig) return; try { // Update only the cloud section, preserving other config sections await updateConfig({ ...fullConfig, cloud: { domain: formValues.domain, internalDomain: formValues.internalDomain, dhcpRange: formValues.dhcpRange, dns: formValues.dns, router: formValues.router, dnsmasq: formValues.dnsmasq, }, }); setEditingDomains(false); } catch (err) { console.error('Failed to save domains:', err); } }; const handleNetworkSave = async () => { if (!formValues || !fullConfig) return; try { // Update only the cloud section, preserving other config sections await updateConfig({ ...fullConfig, cloud: { domain: formValues.domain, internalDomain: formValues.internalDomain, dhcpRange: formValues.dhcpRange, dns: formValues.dns, router: formValues.router, dnsmasq: formValues.dnsmasq, }, }); setEditingNetwork(false); } catch (err) { console.error('Failed to save network settings:', err); } }; const handleDomainsCancel = () => { setFormValues(config as CloudConfig); setEditingDomains(false); }; const handleNetworkCancel = () => { setFormValues(config as CloudConfig); setEditingNetwork(false); }; const handleClusterEdit = () => { if (clusterConfig) { setClusterFormValues(clusterConfig as ClusterConfig); setEditingCluster(true); } }; const handleClusterSave = async () => { if (!clusterFormValues || !fullConfig) return; try { // Update only the cluster section, preserving other config sections await updateConfig({ ...fullConfig, cluster: clusterFormValues, }); setEditingCluster(false); } catch (err) { console.error('Failed to save cluster settings:', err); } }; const handleClusterCancel = () => { setClusterFormValues(clusterConfig as ClusterConfig); setEditingCluster(false); }; const updateFormValue = (path: string, value: string) => { if (!formValues) return; setFormValues(prev => { if (!prev) return prev; // Handle nested paths like "dns.ip" const keys = path.split('.'); if (keys.length === 1) { return { ...prev, [keys[0]]: value }; } // Handle nested object updates const [parentKey, childKey] = keys; return { ...prev, [parentKey]: { ...(prev[parentKey as keyof CloudConfig] as Record), [childKey]: value, }, }; }); }; const updateClusterFormValue = (path: string, value: string) => { if (!clusterFormValues) return; setClusterFormValues(prev => { if (!prev) return prev; // Handle nested paths like "nodes.talos.version" const keys = path.split('.'); if (keys.length === 1) { return { ...prev, [keys[0]]: value }; } if (keys.length === 3 && keys[0] === 'nodes' && keys[1] === 'talos') { return { ...prev, nodes: { ...prev.nodes, talos: { ...prev.nodes.talos, [keys[2]]: value, }, }, }; } return prev; }); }; // Show message if no instance is selected if (!currentInstance) { return (

No Instance Selected

Please select or create an instance to manage cloud configuration.

); } // Show loading state if (isLoading || !formValues) { return (

Loading cloud configuration...

); } // Show error state if (error) { return (

Error Loading Configuration

{(error as Error)?.message || 'An error occurred'}

); } return (

Cloud Configuration

Configure top-level cloud settings and domains

{/* Domains Section */}

Domain Configuration

Public and internal domain settings

{!editingDomains && ( )}
{editingDomains ? (
updateFormValue('domain', e.target.value)} placeholder="example.com" className="mt-1" />
updateFormValue('internalDomain', e.target.value)} placeholder="internal.example.com" className="mt-1" />
) : (
{formValues.domain}
{formValues.internalDomain}
)}
{/* Network Configuration Section */}

Network Configuration

Network settings and DHCP configuration

{!editingNetwork && ( )}
{editingNetwork ? (
updateFormValue('dhcpRange', e.target.value)} placeholder="192.168.1.100,192.168.1.200" className="mt-1" />

Format: start_ip,end_ip

updateFormValue('dns.ip', e.target.value)} placeholder="192.168.1.1" className="mt-1" />
updateFormValue('router.ip', e.target.value)} placeholder="192.168.1.1" className="mt-1" />
updateFormValue('dnsmasq.interface', e.target.value)} placeholder="eth0" className="mt-1" />
) : (
{formValues.dhcpRange}
{formValues.dns.ip}
{formValues.router.ip}
{formValues.dnsmasq.interface}
)}
{/* Cluster Configuration Section */} {clusterFormValues && (

Cluster Configuration

Kubernetes cluster and node settings

{!editingCluster && ( )}
{editingCluster ? (
updateClusterFormValue('endpointIp', e.target.value)} placeholder="192.168.1.60" className="mt-1" />

Virtual IP for the Kubernetes API endpoint

updateClusterFormValue('hostnamePrefix', e.target.value)} placeholder="mycluster-" className="mt-1" />

Prefix for auto-generated node hostnames (e.g., "mycluster-control-1")

updateClusterFormValue('nodes.talos.version', e.target.value)} placeholder="v1.8.0" className="mt-1" />

Talos Linux version for cluster nodes

) : (
{clusterFormValues.endpointIp}
{clusterFormValues.hostnamePrefix || '(none)'}
{clusterFormValues.nodes.talos.version}
)}
)}
); }