From ebf3612c62451cbc063b7af782f73bf1449fd015 Mon Sep 17 00:00:00 2001 From: Paul Payne Date: Mon, 24 Nov 2025 17:36:19 +0000 Subject: [PATCH] Separate pages for controls and workers --- package.json | 4 + pnpm-lock.yaml | 162 +++++++++++++++++++++++ src/components/AppSidebar.tsx | 17 ++- src/components/ClusterNodesComponent.tsx | 57 +++++--- src/components/ControlNodesComponent.tsx | 5 + src/components/WorkerNodesComponent.tsx | 5 + src/components/index.ts | 1 - src/components/ui/checkbox.tsx | 30 +++++ src/components/ui/switch.tsx | 29 ++++ src/components/ui/tabs.tsx | 64 +++++++++ src/router/pages/ControlNodesPage.tsx | 10 ++ src/router/pages/InfrastructurePage.tsx | 11 -- src/router/pages/WorkerNodesPage.tsx | 10 ++ src/router/routes.tsx | 16 ++- 14 files changed, 388 insertions(+), 33 deletions(-) create mode 100644 src/components/ControlNodesComponent.tsx create mode 100644 src/components/WorkerNodesComponent.tsx create mode 100644 src/components/ui/checkbox.tsx create mode 100644 src/components/ui/switch.tsx create mode 100644 src/components/ui/tabs.tsx create mode 100644 src/router/pages/ControlNodesPage.tsx delete mode 100644 src/router/pages/InfrastructurePage.tsx create mode 100644 src/router/pages/WorkerNodesPage.tsx diff --git a/package.json b/package.json index bf1b549..ea309ee 100644 --- a/package.json +++ b/package.json @@ -18,12 +18,15 @@ "dependencies": { "@heroicons/react": "^2.2.0", "@hookform/resolvers": "^5.1.1", + "@radix-ui/react-checkbox": "^1.3.3", "@radix-ui/react-collapsible": "^1.1.11", "@radix-ui/react-dialog": "^1.1.14", "@radix-ui/react-label": "^2.1.7", "@radix-ui/react-select": "^2.2.6", "@radix-ui/react-separator": "^1.1.7", "@radix-ui/react-slot": "^1.2.3", + "@radix-ui/react-switch": "^1.2.6", + "@radix-ui/react-tabs": "^1.1.13", "@radix-ui/react-tooltip": "^1.2.7", "@tailwindcss/vite": "^4.1.10", "@tanstack/react-query": "^5.62.10", @@ -36,6 +39,7 @@ "react-markdown": "^10.1.0", "react-router": "^7.9.4", "react-router-dom": "^7.9.4", + "sonner": "^2.0.7", "tailwind-merge": "^3.3.1", "tailwindcss": "^4.1.10", "zod": "^3.25.67" diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index d38d6bf..ec76617 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -14,6 +14,9 @@ importers: '@hookform/resolvers': specifier: ^5.1.1 version: 5.1.1(react-hook-form@7.58.1(react@19.1.0)) + '@radix-ui/react-checkbox': + specifier: ^1.3.3 + version: 1.3.3(@types/react-dom@19.1.6(@types/react@19.1.8))(@types/react@19.1.8)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) '@radix-ui/react-collapsible': specifier: ^1.1.11 version: 1.1.11(@types/react-dom@19.1.6(@types/react@19.1.8))(@types/react@19.1.8)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) @@ -32,6 +35,12 @@ importers: '@radix-ui/react-slot': specifier: ^1.2.3 version: 1.2.3(@types/react@19.1.8)(react@19.1.0) + '@radix-ui/react-switch': + specifier: ^1.2.6 + version: 1.2.6(@types/react-dom@19.1.6(@types/react@19.1.8))(@types/react@19.1.8)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-tabs': + specifier: ^1.1.13 + version: 1.1.13(@types/react-dom@19.1.6(@types/react@19.1.8))(@types/react@19.1.8)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) '@radix-ui/react-tooltip': specifier: ^1.2.7 version: 1.2.7(@types/react-dom@19.1.6(@types/react@19.1.8))(@types/react@19.1.8)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) @@ -68,6 +77,9 @@ importers: react-router-dom: specifier: ^7.9.4 version: 7.9.4(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + sonner: + specifier: ^2.0.7 + version: 2.0.7(react-dom@19.1.0(react@19.1.0))(react@19.1.0) tailwind-merge: specifier: ^3.3.1 version: 3.3.1 @@ -564,6 +576,19 @@ packages: '@types/react-dom': optional: true + '@radix-ui/react-checkbox@1.3.3': + resolution: {integrity: sha512-wBbpv+NQftHDdG86Qc0pIyXk5IR3tM8Vd0nWLKDcX8nNn4nXFOFwsKuqw2okA/1D/mpaAkmuyndrPJTYDNZtFw==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + '@radix-ui/react-collapsible@1.1.11': resolution: {integrity: sha512-2qrRsVGSCYasSz1RFOorXwl0H7g7J1frQtgpQgYrt+MOidtPAINHn9CPovQXb83r8ahapdx3Tu0fa/pdFFSdPg==} peerDependencies: @@ -761,6 +786,19 @@ packages: '@types/react-dom': optional: true + '@radix-ui/react-presence@1.1.5': + resolution: {integrity: sha512-/jfEwNDdQVBCNvjkGit4h6pMOzq8bHkopq458dPt2lMjx+eBQUohZNG9A7DtO/O5ukSbxuaNGXMjHicgwy6rQQ==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + '@radix-ui/react-primitive@2.1.3': resolution: {integrity: sha512-m9gTwRkhy2lvCPe6QJp4d3G1TYEUHn/FzJUtq9MjH46an1wJU+GdoGC5VLof8RX8Ft/DlpshApkhswDLZzHIcQ==} peerDependencies: @@ -774,6 +812,19 @@ packages: '@types/react-dom': optional: true + '@radix-ui/react-roving-focus@1.1.11': + resolution: {integrity: sha512-7A6S9jSgm/S+7MdtNDSb+IU859vQqJ/QAtcYQcfFC6W8RS4IxIZDldLR0xqCFZ6DCyrQLjLPsxtTNch5jVA4lA==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + '@radix-ui/react-select@2.2.6': resolution: {integrity: sha512-I30RydO+bnn2PQztvo25tswPH+wFBjehVGtmagkU78yMdwTwVf12wnAOF+AeP8S2N8xD+5UPbGhkUfPyvT+mwQ==} peerDependencies: @@ -809,6 +860,32 @@ packages: '@types/react': optional: true + '@radix-ui/react-switch@1.2.6': + resolution: {integrity: sha512-bByzr1+ep1zk4VubeEVViV592vu2lHE2BZY5OnzehZqOOgogN80+mNtCqPkhn2gklJqOpxWgPoYTSnhBCqpOXQ==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + + '@radix-ui/react-tabs@1.1.13': + resolution: {integrity: sha512-7xdcatg7/U+7+Udyoj2zodtI9H/IIopqo+YOIcZOq1nJwXWBZ9p8xiu5llXlekDbZkca79a/fozEYQXIA4sW6A==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + '@radix-ui/react-tooltip@1.2.7': resolution: {integrity: sha512-Ap+fNYwKTYJ9pzqW+Xe2HtMRbQ/EeWkj2qykZ6SuEV4iS/o1bZI5ssJbk4D2r8XuDuOBVz/tIx2JObtuqU+5Zw==} peerDependencies: @@ -2273,6 +2350,12 @@ packages: siginfo@2.0.0: resolution: {integrity: sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==} + sonner@2.0.7: + resolution: {integrity: sha512-W6ZN4p58k8aDKA4XPcx2hpIQXBRAgyiWVkYhT7CvK6D3iAu7xjvVyhQHg2/iaKJZ1XVJ4r7XuwGL+WGEK37i9w==} + peerDependencies: + react: ^18.0.0 || ^19.0.0 || ^19.0.0-rc + react-dom: ^18.0.0 || ^19.0.0 || ^19.0.0-rc + source-map-js@1.2.1: resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==} engines: {node: '>=0.10.0'} @@ -2977,6 +3060,22 @@ snapshots: '@types/react': 19.1.8 '@types/react-dom': 19.1.6(@types/react@19.1.8) + '@radix-ui/react-checkbox@1.3.3(@types/react-dom@19.1.6(@types/react@19.1.8))(@types/react@19.1.8)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': + dependencies: + '@radix-ui/primitive': 1.1.3 + '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.1.8)(react@19.1.0) + '@radix-ui/react-context': 1.1.2(@types/react@19.1.8)(react@19.1.0) + '@radix-ui/react-presence': 1.1.5(@types/react-dom@19.1.6(@types/react@19.1.8))(@types/react@19.1.8)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.1.6(@types/react@19.1.8))(@types/react@19.1.8)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.1.8)(react@19.1.0) + '@radix-ui/react-use-previous': 1.1.1(@types/react@19.1.8)(react@19.1.0) + '@radix-ui/react-use-size': 1.1.1(@types/react@19.1.8)(react@19.1.0) + react: 19.1.0 + react-dom: 19.1.0(react@19.1.0) + optionalDependencies: + '@types/react': 19.1.8 + '@types/react-dom': 19.1.6(@types/react@19.1.8) + '@radix-ui/react-collapsible@1.1.11(@types/react-dom@19.1.6(@types/react@19.1.8))(@types/react@19.1.8)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': dependencies: '@radix-ui/primitive': 1.1.2 @@ -3166,6 +3265,16 @@ snapshots: '@types/react': 19.1.8 '@types/react-dom': 19.1.6(@types/react@19.1.8) + '@radix-ui/react-presence@1.1.5(@types/react-dom@19.1.6(@types/react@19.1.8))(@types/react@19.1.8)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': + dependencies: + '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.1.8)(react@19.1.0) + '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.1.8)(react@19.1.0) + react: 19.1.0 + react-dom: 19.1.0(react@19.1.0) + optionalDependencies: + '@types/react': 19.1.8 + '@types/react-dom': 19.1.6(@types/react@19.1.8) + '@radix-ui/react-primitive@2.1.3(@types/react-dom@19.1.6(@types/react@19.1.8))(@types/react@19.1.8)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': dependencies: '@radix-ui/react-slot': 1.2.3(@types/react@19.1.8)(react@19.1.0) @@ -3175,6 +3284,23 @@ snapshots: '@types/react': 19.1.8 '@types/react-dom': 19.1.6(@types/react@19.1.8) + '@radix-ui/react-roving-focus@1.1.11(@types/react-dom@19.1.6(@types/react@19.1.8))(@types/react@19.1.8)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': + dependencies: + '@radix-ui/primitive': 1.1.3 + '@radix-ui/react-collection': 1.1.7(@types/react-dom@19.1.6(@types/react@19.1.8))(@types/react@19.1.8)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.1.8)(react@19.1.0) + '@radix-ui/react-context': 1.1.2(@types/react@19.1.8)(react@19.1.0) + '@radix-ui/react-direction': 1.1.1(@types/react@19.1.8)(react@19.1.0) + '@radix-ui/react-id': 1.1.1(@types/react@19.1.8)(react@19.1.0) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.1.6(@types/react@19.1.8))(@types/react@19.1.8)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@19.1.8)(react@19.1.0) + '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.1.8)(react@19.1.0) + react: 19.1.0 + react-dom: 19.1.0(react@19.1.0) + optionalDependencies: + '@types/react': 19.1.8 + '@types/react-dom': 19.1.6(@types/react@19.1.8) + '@radix-ui/react-select@2.2.6(@types/react-dom@19.1.6(@types/react@19.1.8))(@types/react@19.1.8)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': dependencies: '@radix-ui/number': 1.1.1 @@ -3220,6 +3346,37 @@ snapshots: optionalDependencies: '@types/react': 19.1.8 + '@radix-ui/react-switch@1.2.6(@types/react-dom@19.1.6(@types/react@19.1.8))(@types/react@19.1.8)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': + dependencies: + '@radix-ui/primitive': 1.1.3 + '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.1.8)(react@19.1.0) + '@radix-ui/react-context': 1.1.2(@types/react@19.1.8)(react@19.1.0) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.1.6(@types/react@19.1.8))(@types/react@19.1.8)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.1.8)(react@19.1.0) + '@radix-ui/react-use-previous': 1.1.1(@types/react@19.1.8)(react@19.1.0) + '@radix-ui/react-use-size': 1.1.1(@types/react@19.1.8)(react@19.1.0) + react: 19.1.0 + react-dom: 19.1.0(react@19.1.0) + optionalDependencies: + '@types/react': 19.1.8 + '@types/react-dom': 19.1.6(@types/react@19.1.8) + + '@radix-ui/react-tabs@1.1.13(@types/react-dom@19.1.6(@types/react@19.1.8))(@types/react@19.1.8)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': + dependencies: + '@radix-ui/primitive': 1.1.3 + '@radix-ui/react-context': 1.1.2(@types/react@19.1.8)(react@19.1.0) + '@radix-ui/react-direction': 1.1.1(@types/react@19.1.8)(react@19.1.0) + '@radix-ui/react-id': 1.1.1(@types/react@19.1.8)(react@19.1.0) + '@radix-ui/react-presence': 1.1.5(@types/react-dom@19.1.6(@types/react@19.1.8))(@types/react@19.1.8)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.1.6(@types/react@19.1.8))(@types/react@19.1.8)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-roving-focus': 1.1.11(@types/react-dom@19.1.6(@types/react@19.1.8))(@types/react@19.1.8)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.1.8)(react@19.1.0) + react: 19.1.0 + react-dom: 19.1.0(react@19.1.0) + optionalDependencies: + '@types/react': 19.1.8 + '@types/react-dom': 19.1.6(@types/react@19.1.8) + '@radix-ui/react-tooltip@1.2.7(@types/react-dom@19.1.6(@types/react@19.1.8))(@types/react@19.1.8)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': dependencies: '@radix-ui/primitive': 1.1.2 @@ -4781,6 +4938,11 @@ snapshots: siginfo@2.0.0: {} + sonner@2.0.7(react-dom@19.1.0(react@19.1.0))(react@19.1.0): + dependencies: + react: 19.1.0 + react-dom: 19.1.0(react@19.1.0) + source-map-js@1.2.1: {} space-separated-tokens@2.0.2: {} diff --git a/src/components/AppSidebar.tsx b/src/components/AppSidebar.tsx index a8bdca2..8abf29f 100644 --- a/src/components/AppSidebar.tsx +++ b/src/components/AppSidebar.tsx @@ -188,11 +188,22 @@ export function AppSidebar() { - +
- +
- Cluster Nodes + Control Nodes +
+
+
+ + + + +
+ +
+ Worker Nodes
diff --git a/src/components/ClusterNodesComponent.tsx b/src/components/ClusterNodesComponent.tsx index a24a1ea..361d1bc 100644 --- a/src/components/ClusterNodesComponent.tsx +++ b/src/components/ClusterNodesComponent.tsx @@ -15,7 +15,17 @@ import { NodeFormDrawer } from './nodes/NodeFormDrawer'; import type { NodeFormData } from './nodes/NodeForm'; import type { Node, HardwareInfo, DiscoveredNode } from '../services/api/types'; -export function ClusterNodesComponent() { +interface ClusterNodesComponentProps { + filterRole?: 'controlplane' | 'worker'; + hideDiscoveryWhenNodesGte?: number; + showBootstrap?: boolean; +} + +export function ClusterNodesComponent({ + filterRole, + hideDiscoveryWhenNodesGte, + showBootstrap = true +}: ClusterNodesComponentProps = {}) { const { currentInstance } = useInstanceContext(); const { nodes, @@ -64,6 +74,7 @@ export function ClusterNodesComponent() { open: false, mode: 'add', }); + const [drawerEverOpened, setDrawerEverOpened] = useState(false); const [deletingNodeHostname, setDeletingNodeHostname] = useState(null); const closeDrawer = () => setDrawerState({ ...drawerState, open: false }); @@ -121,6 +132,7 @@ export function ClusterNodesComponent() { // Fetch full hardware details for the discovered node try { const hardware = await getHardware(discovered.ip); + setDrawerEverOpened(true); setDrawerState({ open: true, mode: 'add', @@ -137,6 +149,7 @@ export function ClusterNodesComponent() { try { const hardware = await getHardware(addNodeIp); + setDrawerEverOpened(true); setDrawerState({ open: true, mode: 'add', @@ -153,6 +166,7 @@ export function ClusterNodesComponent() { if (node.target_ip) { try { const hardware = await getHardware(node.target_ip); + setDrawerEverOpened(true); setDrawerState({ open: true, mode: 'configure', @@ -167,6 +181,7 @@ export function ClusterNodesComponent() { } // Open drawer without detection data (either no target_ip or detection failed) + setDrawerEverOpened(true); setDrawerState({ open: true, mode: 'configure', @@ -177,7 +192,7 @@ export function ClusterNodesComponent() { const handleAddSubmit = async (data: NodeFormData) => { const nodeData = { hostname: data.hostname, - role: data.role, + role: filterRole || data.role, disk: data.disk, target_ip: data.targetIp, interface: data.interface, @@ -244,7 +259,8 @@ export function ClusterNodesComponent() { // Derive status from backend state flags for each node - const assignedNodes = nodes.map(node => { + const assignedNodes = useMemo(() => { + const allNodes = nodes.map(node => { // Get runtime status from cluster status const runtimeStatus = clusterStatusData?.node_statuses?.[node.hostname]; @@ -266,6 +282,13 @@ export function ClusterNodesComponent() { }; }); + // Filter by role if specified + if (filterRole) { + return allNodes.filter(node => node.role === filterRole); + } + return allNodes; + }, [nodes, clusterStatusData, filterRole]); + // Check if cluster needs bootstrap const needsBootstrap = useMemo(() => { // Find first ready control plane node @@ -345,7 +368,7 @@ export function ClusterNodesComponent() { {/* Bootstrap Alert */} - {needsBootstrap && firstReadyControl && ( + {showBootstrap && needsBootstrap && firstReadyControl && (
@@ -443,6 +466,7 @@ export function ClusterNodesComponent() { )} {/* ADD NODES SECTION - Discovery and manual add combined */} + {(!hideDiscoveryWhenNodesGte || assignedNodes.length < hideDiscoveryWhenNodesGte) && (

Add Nodes to Cluster @@ -535,6 +559,7 @@ export function ClusterNodesComponent() {

+ )}
@@ -664,17 +689,19 @@ export function ClusterNodesComponent() { /> )} - {/* Node Form Drawer */} - + {/* Node Form Drawer - only render after first open to prevent infinite loop on initial mount */} + {drawerEverOpened && ( + + )}
); } \ No newline at end of file diff --git a/src/components/ControlNodesComponent.tsx b/src/components/ControlNodesComponent.tsx new file mode 100644 index 0000000..bd815c6 --- /dev/null +++ b/src/components/ControlNodesComponent.tsx @@ -0,0 +1,5 @@ +import { ClusterNodesComponent } from './ClusterNodesComponent'; + +export function ControlNodesComponent() { + return ; +} diff --git a/src/components/WorkerNodesComponent.tsx b/src/components/WorkerNodesComponent.tsx new file mode 100644 index 0000000..5a752f2 --- /dev/null +++ b/src/components/WorkerNodesComponent.tsx @@ -0,0 +1,5 @@ +import { ClusterNodesComponent } from './ClusterNodesComponent'; + +export function WorkerNodesComponent() { + return ; +} diff --git a/src/components/index.ts b/src/components/index.ts index 64304c1..4b478dc 100644 --- a/src/components/index.ts +++ b/src/components/index.ts @@ -14,7 +14,6 @@ export { CentralComponent } from './CentralComponent'; export { DnsComponent } from './DnsComponent'; export { DhcpComponent } from './DhcpComponent'; export { PxeComponent } from './PxeComponent'; -export { ClusterNodesComponent } from './ClusterNodesComponent'; export { ClusterServicesComponent } from './ClusterServicesComponent'; export { AppsComponent } from './AppsComponent'; export { SecretInput } from './SecretInput'; diff --git a/src/components/ui/checkbox.tsx b/src/components/ui/checkbox.tsx new file mode 100644 index 0000000..0e2a6cd --- /dev/null +++ b/src/components/ui/checkbox.tsx @@ -0,0 +1,30 @@ +import * as React from "react" +import * as CheckboxPrimitive from "@radix-ui/react-checkbox" +import { CheckIcon } from "lucide-react" + +import { cn } from "@/lib/utils" + +function Checkbox({ + className, + ...props +}: React.ComponentProps) { + return ( + + + + + + ) +} + +export { Checkbox } diff --git a/src/components/ui/switch.tsx b/src/components/ui/switch.tsx new file mode 100644 index 0000000..b0363e3 --- /dev/null +++ b/src/components/ui/switch.tsx @@ -0,0 +1,29 @@ +import * as React from "react" +import * as SwitchPrimitive from "@radix-ui/react-switch" + +import { cn } from "@/lib/utils" + +function Switch({ + className, + ...props +}: React.ComponentProps) { + return ( + + + + ) +} + +export { Switch } diff --git a/src/components/ui/tabs.tsx b/src/components/ui/tabs.tsx new file mode 100644 index 0000000..3d6f3ac --- /dev/null +++ b/src/components/ui/tabs.tsx @@ -0,0 +1,64 @@ +import * as React from "react" +import * as TabsPrimitive from "@radix-ui/react-tabs" + +import { cn } from "@/lib/utils" + +function Tabs({ + className, + ...props +}: React.ComponentProps) { + return ( + + ) +} + +function TabsList({ + className, + ...props +}: React.ComponentProps) { + return ( + + ) +} + +function TabsTrigger({ + className, + ...props +}: React.ComponentProps) { + return ( + + ) +} + +function TabsContent({ + className, + ...props +}: React.ComponentProps) { + return ( + + ) +} + +export { Tabs, TabsList, TabsTrigger, TabsContent } diff --git a/src/router/pages/ControlNodesPage.tsx b/src/router/pages/ControlNodesPage.tsx new file mode 100644 index 0000000..0a0b0ec --- /dev/null +++ b/src/router/pages/ControlNodesPage.tsx @@ -0,0 +1,10 @@ +import { ErrorBoundary } from '../../components'; +import { ControlNodesComponent } from '../../components/ControlNodesComponent'; + +export function ControlNodesPage() { + return ( + + + + ); +} diff --git a/src/router/pages/InfrastructurePage.tsx b/src/router/pages/InfrastructurePage.tsx deleted file mode 100644 index 86749a9..0000000 --- a/src/router/pages/InfrastructurePage.tsx +++ /dev/null @@ -1,11 +0,0 @@ -import { ErrorBoundary } from '../../components'; -import { ClusterNodesComponent } from '../../components/ClusterNodesComponent'; - -export function InfrastructurePage() { - // Note: onComplete callback removed as phase management will be handled differently with routing - return ( - - - - ); -} diff --git a/src/router/pages/WorkerNodesPage.tsx b/src/router/pages/WorkerNodesPage.tsx new file mode 100644 index 0000000..4f262c3 --- /dev/null +++ b/src/router/pages/WorkerNodesPage.tsx @@ -0,0 +1,10 @@ +import { ErrorBoundary } from '../../components'; +import { WorkerNodesComponent } from '../../components/WorkerNodesComponent'; + +export function WorkerNodesPage() { + return ( + + + + ); +} diff --git a/src/router/routes.tsx b/src/router/routes.tsx index c71a5cc..cd9d744 100644 --- a/src/router/routes.tsx +++ b/src/router/routes.tsx @@ -15,9 +15,11 @@ import { DnsPage } from './pages/DnsPage'; import { DhcpPage } from './pages/DhcpPage'; import { PxePage } from './pages/PxePage'; import { IsoPage } from './pages/IsoPage'; -import { InfrastructurePage } from './pages/InfrastructurePage'; +import { ControlNodesPage } from './pages/ControlNodesPage'; +import { WorkerNodesPage } from './pages/WorkerNodesPage'; import { ClusterPage } from './pages/ClusterPage'; import { AppsPage } from './pages/AppsPage'; +import { BackupsPage } from './pages/BackupsPage'; import { AdvancedPage } from './pages/AdvancedPage'; import { AssetsIsoPage } from './pages/AssetsIsoPage'; import { AssetsPxePage } from './pages/AssetsPxePage'; @@ -92,8 +94,12 @@ export const routes: RouteObject[] = [ element: , }, { - path: 'infrastructure', - element: , + path: 'control', + element: , + }, + { + path: 'worker', + element: , }, { path: 'cluster', @@ -116,6 +122,10 @@ export const routes: RouteObject[] = [ }, ], }, + { + path: 'backups', + element: , + }, { path: 'advanced', element: ,