diff --git a/src/pages/CreateCluster.tsx b/src/pages/CreateCluster.tsx index a952c74..e1817c4 100644 --- a/src/pages/CreateCluster.tsx +++ b/src/pages/CreateCluster.tsx @@ -1,136 +1,232 @@ -import { useState } from 'react' +import { useState, useEffect, useRef } from 'react' import { Link } from 'react-router-dom' -import { Download, Feather, Trash2 } from 'lucide-react' +import { Download, Feather, Trash2, Loader2, Cpu } from 'lucide-react' import type { FormEvent } from 'react' +import { fromTheme } from 'tailwind-merge' interface Cluster { - id: string Name: string + ClusterID: string + Status: string + Version: string + HealthCheck: string ControlPlane: string PlatformVersion: string - Cpu: string + Alert: string + EndPoint: string + Cpu : string Memory: string - clusterId: string - status: string - version: string - alerts: string - endpoint: string } export default function CreateCluster() { - const [clusters, setClusters] = useState(() => { - fetch('http://localhost:8082/clusters', { - method: 'GET', - headers: { - 'Content-Type': 'application/json', - 'Authorization': `${localStorage.getItem('auth:token') || ''}` - }, - }).then(async (res) => { - if (res.ok) { - const data = await res.json() - setClusters(data.clusters || []) - return data.clusters || [] - //localStorage.setItem('clusters', JSON.stringify(data.clusters || [])) - } else { - const data = await res.json() - console.error(data.message || 'Failed to fetch clusters') - } - }).catch(() => { - console.error('Failed to fetch clusters') - }) - const saved = localStorage.getItem('clusters') - if (saved) { - return JSON.parse(saved) - } - return [] - }) - + const [clusters, setClusters] = useState([]) + const [isLoading, setIsLoading] = useState(false) + const [isDeleting, setIsDeleting] = useState(false) const [showModal, setShowModal] = useState(false) + const [showDeleteModal, setShowDeleteModal] = useState(false) + const [clusterToDelete, setClusterToDelete] = useState('') const [formData, setFormData] = useState({ clusterName: '', namespace: '', - controlPlane: 'Kubernetes (k8s)', - kubernetesVersion: 'v1.31.6 (recommended)', + controlPlane: 'k8s', + PlatformVersion: 'v1.31.6 (recommended)', Cpu: 1, Memory: 1024 }) + + const pollingIntervalRef = useRef(null) - const handleSubmit = (e: FormEvent) => { + // Function to fetch clusters + const fetchClusters = async () => { + try { + const response = await fetch('http://localhost:8082/clusters', { + method: 'GET', + headers: { + 'Content-Type': 'application/json', + 'Authorization': `${localStorage.getItem('auth:token') || ''}` + }, + }) + + if (response.ok) { + const data = await response.json() + setClusters(data || []) + localStorage.setItem('clusters', JSON.stringify(data || [])) + + // Check if any cluster is still progressing + const hasProgressingClusters = data.some((cluster: Cluster) => cluster.Status === 'Progressing' || cluster.Status === '' || cluster.Status === 'Missing' || cluster.Status === 'Pendding') + + // Start or stop polling based on cluster status + if (hasProgressingClusters) { + startPolling() + } else { + stopPolling() + } + } else { + const data = await response.json() + console.error(data.message || 'Failed to fetch clusters') + } + } catch (error) { + console.error('Failed to fetch clusters', error) + } + } + + // Start polling for clusters with Progressing status + const startPolling = () => { + if (pollingIntervalRef.current) { + return // Already polling + } + + pollingIntervalRef.current = setInterval(() => { + fetchClusters() + }, 3000) // Poll every 3 seconds + } + + // Stop polling + const stopPolling = () => { + if (pollingIntervalRef.current) { + clearInterval(pollingIntervalRef.current) + pollingIntervalRef.current = null + } + } + + // Load clusters on component mount + useEffect(() => { + fetchClusters() + + // Cleanup polling on component unmount + return () => { + stopPolling() + } + }, []) + + const handleSubmit = async (e: FormEvent) => { e.preventDefault() - const newCluster: Cluster = { + setIsLoading(true) + + const newCluster = { Name: formData.clusterName, - clusterId: Math.random().toString(36).substr(2, 8), - status: 'Creating', + ClusterID: Math.random().toString(36).substr(2, 8), ControlPlane: formData.controlPlane, + Status: 'Progressing', Cpu: formData.Cpu.toString(), Memory: formData.Memory.toString(), - PlatformVersion: formData.kubernetesVersion.split(' ')[0] + PlatformVersion: formData.PlatformVersion.split(' ')[0], + HealthCheck: '', + Alert: '', + EndPoint: '' } - fetch('http://localhost:8082/createcluster', { - method: 'POST', - headers: { 'Content-Type': 'application/json', 'Authorization': localStorage.getItem('auth:token') || '' }, - body: JSON.stringify(newCluster), - }).then(async (res) => { - if (res.ok) { - const data = await res.json() - console.log(data) + try { + const response = await fetch('http://localhost:8082/createcluster', { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + 'Authorization': localStorage.getItem('auth:token') || '' + }, + body: JSON.stringify(newCluster), + }) + + if (response.ok) { + const data = await response.json() + console.log('Cluster created successfully:', data) + + // Close modal and reset form + setShowModal(false) + setFormData({ + clusterName: '', + namespace: '', + controlPlane: 'Kubernetes (k8s)', + PlatformVersion: 'v1.31.6 (recommended)', + Cpu: 1, + Memory: 1024 + }) + + // Refresh cluster list and start polling + await fetchClusters() } else { - const data = await res.json() - console.log(data) - // setError(data.message || 'Login failed') + const data = await response.json() + console.error('Failed to create cluster:', data.message) + // You can add error handling here (show toast notification, etc.) } - }).catch(() => { - //setError('Login failed') - }) - - // const updatedClusters = [...clusters, newCluster] - // setClusters(updatedClusters) - // localStorage.setItem('clusters', JSON.stringify(updatedClusters)) - // setShowModal(false) - // setFormData({ - // clusterName: '', - // namespace: '', - // controlPlane: 'Kubernetes (k8s)', - // kubernetesVersion: 'v1.31.6 (recommended)', - // Cpu: 1, - // Memory: 2048 - // }) + } catch (error) { + console.error('Error creating cluster:', error) + // You can add error handling here (show toast notification, etc.) + } finally { + setIsLoading(false) + } } - const downloadKubeconfig = (clusterId: string) => { - const kubeconfig = `apiVersion: v1 -kind: Config -clusters: -- name: ${clusterId} - cluster: - server: https://${clusterId}.example.com -contexts: -- name: ${clusterId} - context: - cluster: ${clusterId} - user: admin -current-context: ${clusterId} -users: -- name: admin - user: - token: your-token-here` - - const blob = new Blob([kubeconfig], { type: 'text/yaml' }) - const url = URL.createObjectURL(blob) - const a = document.createElement('a') - a.href = url - a.download = `kubeconfig-${clusterId}.yaml` - document.body.appendChild(a) - a.click() - document.body.removeChild(a) - URL.revokeObjectURL(url) + const downloadKubeconfig = async (clusterName: string) => { + try { + const response = await fetch(`http://localhost:8082/connect?Name=${encodeURIComponent(clusterName)}`, { + method: 'GET', + headers: { + 'Content-Type': 'application/json', + 'Authorization': `${localStorage.getItem('auth:token') || ''}` + }, + }) + + if (response.ok) { + const kubeconfig = await response.text() + + const blob = new Blob([kubeconfig], { type: 'text/yaml' }) + const url = URL.createObjectURL(blob) + const a = document.createElement('a') + a.href = url + a.download = `kubeconfig-${clusterName}.yaml` + document.body.appendChild(a) + a.click() + document.body.removeChild(a) + URL.revokeObjectURL(url) + } else { + console.error('Failed to download kubeconfig') + // You can add error handling here (show toast notification, etc.) + } + } catch (error) { + console.error('Error downloading kubeconfig:', error) + // You can add error handling here (show toast notification, etc.) + } } - const deleteCluster = (clusterId: string) => { - const updatedClusters = clusters.filter(cluster => cluster.clusterId !== clusterId) - setClusters(updatedClusters) - localStorage.setItem('clusters', JSON.stringify(updatedClusters)) + const confirmDeleteCluster = (clusterName: string) => { + setClusterToDelete(clusterName) + setShowDeleteModal(true) + } + + const deleteCluster = async () => { + if (!clusterToDelete) return + + setIsDeleting(true) + setShowDeleteModal(false) + + try { + const response = await fetch(`http://localhost:8082/deletecluster?Name=${encodeURIComponent(clusterToDelete)}`, { + method: 'DELETE', + headers: { + 'Content-Type': 'application/json', + 'Authorization': `${localStorage.getItem('auth:token') || ''}` + }, + }) + + if (response.ok) { + console.log('Cluster deleted successfully') + // Refresh the cluster list to reflect the deletion and start polling + pollingIntervalRef.current = setInterval(() => { + fetchClusters() + }, 3000) // Poll every 3 seconds + + } else { + const data = await response.json() + console.error('Failed to delete cluster:', data.message) + // You can add error handling here (show toast notification, etc.) + } + } catch (error) { + console.error('Error deleting cluster:', error) + // You can add error handling here (show toast notification, etc.) + } finally { + setIsDeleting(false) + setClusterToDelete('') + } } return ( @@ -161,49 +257,52 @@ users: Cluster ID Status Version - Alerts + Health Check + Alert Endpoint Actions {clusters.map((cluster) => ( - + {cluster.Name} - {cluster.clusterId} - - - {cluster.status} - - - {cluster.version} - {cluster.alerts} - {cluster.endpoint} + {cluster.ClusterID} + + + {cluster.Status} + + + {cluster.Version} + {cluster.HealthCheck} + {cluster.Alert} + {cluster.EndPoint}
- - + +
@@ -216,7 +315,16 @@ users: {/* Create Cluster Modal */} {showModal && (
-
+
+ {/* Loading Overlay */} + {isLoading && ( +
+
+ +

Creating cluster...

+
+
+ )}

Create Cluster

@@ -234,8 +342,9 @@ users: type="text" value={formData.clusterName} onChange={(e) => setFormData({...formData, clusterName: e.target.value})} - className="w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500" + className="w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500 disabled:opacity-50 disabled:cursor-not-allowed" required + disabled={isLoading} />
@@ -253,7 +362,8 @@ users: @@ -263,9 +373,10 @@ users: Kubernetes Version