Files
Termix/src/ui/desktop/apps/host-manager/hosts/HostManager.tsx
T

303 lines
9.9 KiB
TypeScript
Raw Normal View History

+7
2025-11-05 10:36:16 -06:00
import React, { useState, useEffect, useRef } from "react";
+1
2025-12-31 22:20:12 -06:00
import { HostManagerViewer } from "@/ui/desktop/apps/host-manager/hosts/HostManagerViewer.tsx";
2025-09-12 14:42:00 -05:00
import {
Tabs,
TabsContent,
TabsList,
TabsTrigger,
} from "@/components/ui/tabs.tsx";
import { Separator } from "@/components/ui/separator.tsx";
+1
2025-12-31 22:20:12 -06:00
import { HostManagerEditor } from "@/ui/desktop/apps/host-manager/hosts/HostManagerEditor.tsx";
import { CredentialsManager } from "@/ui/desktop/apps/host-manager/credentials/CredentialsManager.tsx";
import { CredentialEditor } from "@/ui/desktop/apps/host-manager/credentials/CredentialEditor.tsx";
2025-09-12 14:42:00 -05:00
import { useSidebar } from "@/components/ui/sidebar.tsx";
import { useTranslation } from "react-i18next";
+9
2026-04-22 16:55:23 -05:00
import { exportSSHHostWithCredentials } from "@/ui/main-axios.ts";
2025-09-12 14:42:00 -05:00
import type { SSHHost, HostManagerProps } from "../../../types/index";
export function HostManager({
isTopbarOpen,
+4
2026-02-12 22:28:13 -06:00
initialTab = "hosts",
+7
2025-11-05 10:36:16 -06:00
hostConfig,
+1
2025-12-31 22:20:12 -06:00
_updateTimestamp,
2025-11-17 09:46:05 -06:00
rightSidebarOpen = false,
rightSidebarWidth = 400,
+1
2025-12-31 22:20:12 -06:00
currentTabId,
updateTab,
2025-09-12 14:42:00 -05:00
}: HostManagerProps): React.ReactElement {
const { t } = useTranslation();
+4
2026-02-12 22:28:13 -06:00
const [activeTab, setActiveTab] = useState(
initialTab === "host_viewer" || initialTab === "add_host"
? "hosts"
: initialTab === "credentials" || initialTab === "add_credential"
? "credentials"
: initialTab,
);
+7
2025-11-05 10:36:16 -06:00
const [editingHost, setEditingHost] = useState<SSHHost | null>(
hostConfig || null,
);
+4
2026-02-12 22:28:13 -06:00
const [isAddingHost, setIsAddingHost] = useState(false);
const [isAddingCredential, setIsAddingCredential] = useState(false);
2025-09-12 14:42:00 -05:00
+1
2025-12-31 22:20:12 -06:00
useEffect(() => {}, [editingHost]);
+7
2025-11-05 10:36:16 -06:00
const [editingCredential, setEditingCredential] = useState<{
id: number;
name?: string;
username: string;
} | null>(null);
2025-09-12 14:42:00 -05:00
const { state: sidebarState } = useSidebar();
+7
2025-11-05 10:36:16 -06:00
const ignoreNextHostConfigChangeRef = useRef<boolean>(false);
const lastProcessedHostIdRef = useRef<number | undefined>(undefined);
+4
2026-02-12 22:28:13 -06:00
useEffect(() => {
const handleAddHostEvent = () => {
setActiveTab("hosts");
setEditingHost(null);
setIsAddingHost(true);
setIsAddingCredential(false);
};
const handleAddCredentialEvent = () => {
setActiveTab("credentials");
setEditingCredential(null);
setIsAddingCredential(true);
setIsAddingHost(false);
};
window.addEventListener("host-manager:add-host", handleAddHostEvent);
window.addEventListener(
"host-manager:add-credential",
handleAddCredentialEvent,
);
return () => {
window.removeEventListener("host-manager:add-host", handleAddHostEvent);
window.removeEventListener(
"host-manager:add-credential",
handleAddCredentialEvent,
);
};
}, []);
+7
2025-11-05 10:36:16 -06:00
useEffect(() => {
+1
2025-12-31 22:20:12 -06:00
if (_updateTimestamp !== undefined) {
+4
2026-02-12 22:28:13 -06:00
const normalizedTab =
initialTab === "host_viewer" || initialTab === "add_host"
? "hosts"
: initialTab === "credentials" || initialTab === "add_credential"
? "credentials"
: initialTab;
if (initialTab && normalizedTab !== activeTab) {
setActiveTab(normalizedTab);
+1
2025-12-31 22:20:12 -06:00
}
+4
2026-02-12 22:28:13 -06:00
if (hostConfig && hostConfig.id !== lastProcessedHostIdRef.current) {
+1
2025-12-31 22:20:12 -06:00
lastProcessedHostIdRef.current = hostConfig.id;
+9
2026-04-22 16:55:23 -05:00
setIsAddingHost(false);
exportSSHHostWithCredentials(hostConfig.id)
.then((fullHost) =>
setEditingHost({ ...hostConfig, ...fullHost } as SSHHost),
)
.catch(() => setEditingHost(hostConfig));
+4
2026-02-12 22:28:13 -06:00
} else if (!hostConfig && editingHost) {
+1
2025-12-31 22:20:12 -06:00
setEditingHost(null);
+4
2026-02-12 22:28:13 -06:00
setIsAddingHost(false);
+1
2025-12-31 22:20:12 -06:00
}
} else {
if (initialTab) {
+4
2026-02-12 22:28:13 -06:00
const normalizedTab =
initialTab === "host_viewer" || initialTab === "add_host"
? "hosts"
: initialTab === "credentials" || initialTab === "add_credential"
? "credentials"
: initialTab;
setActiveTab(normalizedTab);
+1
2025-12-31 22:20:12 -06:00
}
+4
2026-02-12 22:28:13 -06:00
if (hostConfig && hostConfig.id !== lastProcessedHostIdRef.current) {
+1
2025-12-31 22:20:12 -06:00
lastProcessedHostIdRef.current = hostConfig.id;
+9
2026-04-22 16:55:23 -05:00
setIsAddingHost(false);
exportSSHHostWithCredentials(hostConfig.id)
.then((fullHost) =>
setEditingHost({ ...hostConfig, ...fullHost } as SSHHost),
)
.catch(() => setEditingHost(hostConfig));
+1
2025-12-31 22:20:12 -06:00
}
+7
2025-11-05 10:36:16 -06:00
}
+4
2026-02-12 22:28:13 -06:00
}, [_updateTimestamp, initialTab, hostConfig]);
2025-09-12 14:42:00 -05:00
+9
2026-04-22 16:55:23 -05:00
const handleEditHost = async (host: SSHHost) => {
+4
2026-02-12 22:28:13 -06:00
setIsAddingHost(false);
+7
2025-11-05 10:36:16 -06:00
lastProcessedHostIdRef.current = host.id;
+9
2026-04-22 16:55:23 -05:00
try {
const fullHost = await exportSSHHostWithCredentials(host.id);
setEditingHost({ ...host, ...fullHost } as SSHHost);
} catch {
setEditingHost(host);
}
+4
2026-02-12 22:28:13 -06:00
};
+1
2025-12-31 22:20:12 -06:00
+4
2026-02-12 22:28:13 -06:00
const handleAddHost = () => {
setEditingHost(null);
setIsAddingHost(true);
lastProcessedHostIdRef.current = undefined;
2025-09-12 14:42:00 -05:00
};
2026-05-06 15:12:07 -05:00
const handleFormSubmit = (savedHost?: SSHHost) => {
+7
2025-11-05 10:36:16 -06:00
ignoreNextHostConfigChangeRef.current = true;
2026-05-06 15:12:07 -05:00
const isUpdatingHost = Boolean(editingHost?.id && savedHost?.id);
if (isUpdatingHost) {
setEditingHost((current) => ({ ...current, ...savedHost }) as SSHHost);
setIsAddingHost(false);
lastProcessedHostIdRef.current = savedHost.id;
if (updateTab && currentTabId !== undefined) {
updateTab(currentTabId, { hostConfig: savedHost });
}
return;
}
const savedHostId = savedHost?.id || editingHost?.id;
2025-09-12 14:42:00 -05:00
setEditingHost(null);
+4
2026-02-12 22:28:13 -06:00
setIsAddingHost(false);
+7
2025-11-05 10:36:16 -06:00
setTimeout(() => {
+4
2026-02-12 22:28:13 -06:00
lastProcessedHostIdRef.current = savedHostId;
+7
2025-11-05 10:36:16 -06:00
}, 500);
2025-09-12 14:42:00 -05:00
};
+7
2025-11-05 10:36:16 -06:00
const handleEditCredential = (credential: {
id: number;
name?: string;
username: string;
}) => {
2025-09-12 14:42:00 -05:00
setEditingCredential(credential);
+4
2026-02-12 22:28:13 -06:00
setIsAddingCredential(false);
};
+1
2025-12-31 22:20:12 -06:00
+4
2026-02-12 22:28:13 -06:00
const handleAddCredential = () => {
setEditingCredential(null);
setIsAddingCredential(true);
2025-09-12 14:42:00 -05:00
};
const handleCredentialFormSubmit = () => {
setEditingCredential(null);
+4
2026-02-12 22:28:13 -06:00
setIsAddingCredential(false);
2025-09-12 14:42:00 -05:00
};
const handleTabChange = (value: string) => {
+4
2026-02-12 22:28:13 -06:00
if (activeTab !== value) {
2025-09-12 14:42:00 -05:00
setEditingHost(null);
+4
2026-02-12 22:28:13 -06:00
setEditingCredential(null);
setIsAddingHost(false);
setIsAddingCredential(false);
2026-01-24 19:49:42 -06:00
lastProcessedHostIdRef.current = undefined;
if (updateTab && currentTabId !== undefined) {
updateTab(currentTabId, { hostConfig: null });
}
2025-09-12 14:42:00 -05:00
}
2026-01-24 19:49:42 -06:00
2025-11-17 09:46:05 -06:00
setActiveTab(value);
+1
2025-12-31 22:20:12 -06:00
if (updateTab && currentTabId !== undefined) {
updateTab(currentTabId, { initialTab: value });
}
2025-09-12 14:42:00 -05:00
};
const topMarginPx = isTopbarOpen ? 74 : 26;
const leftMarginPx = sidebarState === "collapsed" ? 26 : 8;
const bottomMarginPx = 8;
return (
<div>
<div className="w-full">
<div
+1
2025-12-31 22:20:12 -06:00
className="bg-canvas text-foreground p-4 pt-0 rounded-lg border-2 border-edge flex flex-col min-h-0 overflow-hidden"
2025-09-12 14:42:00 -05:00
style={{
marginLeft: leftMarginPx,
2025-11-17 09:46:05 -06:00
marginRight: rightSidebarOpen
? `calc(var(--right-sidebar-width, ${rightSidebarWidth}px) + 8px)`
: 17,
2025-09-12 14:42:00 -05:00
marginTop: topMarginPx,
marginBottom: bottomMarginPx,
height: `calc(100vh - ${topMarginPx + bottomMarginPx}px)`,
2025-11-17 09:46:05 -06:00
transition:
"margin-left 200ms linear, margin-right 200ms linear, margin-top 200ms linear",
2025-09-12 14:42:00 -05:00
}}
>
<Tabs
value={activeTab}
onValueChange={handleTabChange}
className="flex-1 flex flex-col h-full min-h-0"
>
+1
2025-12-31 22:20:12 -06:00
<TabsList className="bg-elevated border-2 border-edge mt-1.5">
<TabsTrigger
+4
2026-02-12 22:28:13 -06:00
value="hosts"
+1
2025-12-31 22:20:12 -06:00
className="bg-elevated data-[state=active]:bg-button data-[state=active]:border data-[state=active]:border-edge"
>
+4
2026-02-12 22:28:13 -06:00
{t("hosts.hosts")}
2025-09-12 14:42:00 -05:00
</TabsTrigger>
+1
2025-12-31 22:20:12 -06:00
<TabsTrigger
value="credentials"
className="bg-elevated data-[state=active]:bg-button data-[state=active]:border data-[state=active]:border-edge"
>
+4
2026-02-12 22:28:13 -06:00
{t("credentials.credentials")}
2025-09-12 14:42:00 -05:00
</TabsTrigger>
</TabsList>
<TabsContent
+4
2026-02-12 22:28:13 -06:00
value="hosts"
2025-09-12 14:42:00 -05:00
className="flex-1 flex flex-col h-full min-h-0"
>
<Separator className="p-0.25 -mt-0.5 mb-1" />
+4
2026-02-12 22:28:13 -06:00
{editingHost !== null || isAddingHost ? (
<div className="flex flex-col h-full min-h-0">
<HostManagerEditor
editingHost={editingHost}
onFormSubmit={handleFormSubmit}
onBack={() => {
setEditingHost(null);
setIsAddingHost(false);
lastProcessedHostIdRef.current = undefined;
}}
/>
</div>
) : (
<HostManagerViewer
onEditHost={handleEditHost}
onAddHost={handleAddHost}
2025-09-12 14:42:00 -05:00
/>
+4
2026-02-12 22:28:13 -06:00
)}
2025-09-12 14:42:00 -05:00
</TabsContent>
<TabsContent
value="credentials"
className="flex-1 flex flex-col h-full min-h-0"
>
<Separator className="p-0.25 -mt-0.5 mb-1" />
+4
2026-02-12 22:28:13 -06:00
{editingCredential !== null || isAddingCredential ? (
<div className="flex flex-col h-full min-h-0">
<CredentialEditor
editingCredential={editingCredential}
onFormSubmit={handleCredentialFormSubmit}
onBack={() => {
setEditingCredential(null);
setIsAddingCredential(false);
}}
/>
</div>
) : (
<div className="flex flex-col h-full min-h-0 overflow-auto thin-scrollbar">
<CredentialsManager
onEditCredential={handleEditCredential}
onAddCredential={handleAddCredential}
/>
</div>
)}
2025-09-12 14:42:00 -05:00
</TabsContent>
</Tabs>
</div>
</div>
</div>
);
}