diff --git a/src/app/servers/components/ServerConfigModal.tsx b/src/app/servers/components/ServerConfigModal.tsx index 9505f4c..dacf96a 100644 --- a/src/app/servers/components/ServerConfigModal.tsx +++ b/src/app/servers/components/ServerConfigModal.tsx @@ -1,19 +1,13 @@ "use client"; -import { type ReactNode, useCallback, useEffect, useState } from "react"; +import { useCallback, useEffect, useState } from "react"; import { createPortal } from "react-dom"; import { AlertTriangle, Eye, EyeOff, - Globe, - HardDrive, Loader2, - Power, RefreshCw, - Settings2, - Users, - Wrench, X, } from "lucide-react"; import { @@ -87,6 +81,14 @@ export default function ServerConfigModal({ const deleteConfirmed = resetConfirmation.trim() === "DELETE"; const hasSelectedDockerPruneOption = Object.values(dockerPruneOptions).some(Boolean); + const tabs: Array<{ id: ServerConfigTab; label: string }> = [ + { id: "overview", label: "Overview" }, + { id: "users", label: "Users" }, + { id: "services", label: "Services" }, + { id: "web-server", label: "Web Server" }, + { id: "mounts", label: "Disk Mounts" }, + { id: "actions", label: "Actions" }, + ]; const publishNotice = useCallback((nextNotice: ServerConfigNotice) => { setNotice(nextNotice); @@ -602,26 +604,26 @@ export default function ServerConfigModal({ } }; - const tabButton = (id: ServerConfigTab, label: string, icon: ReactNode) => ( + const tabButton = (id: ServerConfigTab, label: string) => ( ); @@ -637,523 +639,569 @@ export default function ServerConfigModal({ > -
- {pendingConfirm && typeof document !== "undefined" - ? createPortal( -
-
-
- - {pendingConfirm.title} - -

- {pendingConfirm.description} -

-
- {pendingConfirm.kind === "server" && - pendingConfirm.action === "prune-docker" ? ( -
- - Select unused Docker artifacts to clean up - - {[ - { - key: "images", - label: "Unused images", - description: "Equivalent to docker image prune -a.", - }, - { - key: "containers", - label: "Stopped containers", - description: "Equivalent to docker container prune.", - }, - { - key: "networks", - label: "Unused networks", - description: "Equivalent to docker network prune.", - }, - { - key: "volumes", - label: "Unused volumes", - description: "Equivalent to docker volume prune.", - }, - { - key: "buildCache", - label: "Build cache", - description: "Equivalent to docker builder prune.", - }, - ].map((option) => { - const checked = - dockerPruneOptions[ - option.key as keyof typeof dockerPruneOptions - ]; - - return ( - - ); - })} -
- ) : null} -
- Confirm this action only if you expect a brief service - interruption or cleanup change on the host. -
-
- - -
-
-
, - document.body, - ) - : null} -
-
-
-

- Server Config -

- -
-

- {server.name} • {server.ip}:{server.sshPort} -

-

- Snapshot refresh runs only when this modal opens or when you click - Refresh. -

-
-
- -
-
- -
- {tabButton("overview", "Overview", )} - {tabButton("users", "Users", )} - {tabButton("services", "Services", )} - {tabButton("web-server", "Web Server", )} - {tabButton("mounts", "Disk Mounts", )} - {tabButton("actions", "Actions", )} -
- - {notice && notice.tab === activeTab ? ( -
-
-
- {notice.title} -
- {notice.summary} -
-
- -
- {notice.details?.length || notice.detailText ? ( -
- - {noticeExpanded ? (
- {notice.details?.length ? ( -
    + + {pendingConfirm.title} + +

    - {notice.details.map((detail) => ( -

  • {detail}
  • - ))} -
+ {pendingConfirm.description} +

+
+ {pendingConfirm.kind === "server" && + pendingConfirm.action === "prune-docker" ? ( +
+ + Select unused Docker artifacts to clean up + + {[ + { + key: "images", + label: "Unused images", + description: "Equivalent to docker image prune -a.", + }, + { + key: "containers", + label: "Stopped containers", + description: + "Equivalent to docker container prune.", + }, + { + key: "networks", + label: "Unused networks", + description: "Equivalent to docker network prune.", + }, + { + key: "volumes", + label: "Unused volumes", + description: "Equivalent to docker volume prune.", + }, + { + key: "buildCache", + label: "Build cache", + description: "Equivalent to docker builder prune.", + }, + ].map((option) => { + const checked = + dockerPruneOptions[ + option.key as keyof typeof dockerPruneOptions + ]; + + return ( + + ); + })} +
) : null} - {notice.detailText ? ( -
+                      Confirm this action only if you expect a brief service
+                      interruption or cleanup change on the host.
+                    
+
+ + +
+
+
, + document.body, + ) + : null} + +
+
+
+

+ Server Config +

+ +
+

+ {server.name} • {server.ip}:{server.sshPort} +

+

+ Snapshot refresh runs only when this modal opens or when you + click Refresh. +

+
+
+ +
+
+ + + +
+ {notice && notice.tab === activeTab ? ( +
+
+
+ {notice.title} +
+ {notice.summary} +
+
+ +
+ {notice.details?.length || notice.detailText ? ( +
+ + {noticeExpanded ? ( +
+ {notice.details?.length ? ( +
    + {notice.details.map((detail) => ( +
  • {detail}
  • + ))} +
+ ) : null} + {notice.detailText ? ( +
+                            {notice.detailText}
+                          
+ ) : null} +
) : null}
) : null}
) : null} -
- ) : null} - {error ? ( - - ) : null} + {error ? ( + + ) : null} - {snapshotLoadError ? ( -
-
- - Live configuration snapshot unavailable - -

- The server did not return a full config snapshot, so this modal - is showing fallback information. Recovery actions remain - available, especially in the Actions tab. -

-
- -
- ) : null} +
+ + Live configuration snapshot unavailable + +

+ The server did not return a full config snapshot, so this + modal is showing fallback information. Recovery actions + remain available, especially in the Actions tab. +

+
+ + + ) : null} - {loading ? ( -
- -

- Loading configuration snapshot... -

+ {loading ? ( +
+ +

+ Loading configuration snapshot... +

+
+ ) : ( + renderActiveTab() + )}
- ) : ( - renderActiveTab() - )}