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

244 lines
8.2 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";
import type { SSHHost, HostManagerProps } from "../../../types/index";
export function HostManager({
isTopbarOpen,
+7
2025-11-05 10:36:16 -06:00
initialTab = "host_viewer",
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();
+7
2025-11-05 10:36:16 -06:00
const [activeTab, setActiveTab] = useState(initialTab);
const [editingHost, setEditingHost] = useState<SSHHost | null>(
hostConfig || null,
);
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);
useEffect(() => {
+1
2025-12-31 22:20:12 -06:00
if (_updateTimestamp !== undefined) {
if (initialTab && initialTab !== activeTab) {
setActiveTab(initialTab);
}
if (hostConfig && hostConfig.id !== editingHost?.id) {
setEditingHost(hostConfig);
lastProcessedHostIdRef.current = hostConfig.id;
} else if (
!hostConfig &&
editingHost &&
editingHost.id !== lastProcessedHostIdRef.current
) {
setEditingHost(null);
}
if (initialTab !== "add_credential" && editingCredential) {
setEditingCredential(null);
}
} else {
if (initialTab) {
setActiveTab(initialTab);
}
if (hostConfig) {
setEditingHost(hostConfig);
lastProcessedHostIdRef.current = hostConfig.id;
}
+7
2025-11-05 10:36:16 -06:00
}
+1
2025-12-31 22:20:12 -06:00
}, [_updateTimestamp, initialTab, hostConfig?.id]);
2025-09-12 14:42:00 -05:00
const handleEditHost = (host: SSHHost) => {
setEditingHost(host);
setActiveTab("add_host");
+7
2025-11-05 10:36:16 -06:00
lastProcessedHostIdRef.current = host.id;
+1
2025-12-31 22:20:12 -06:00
if (updateTab && currentTabId !== undefined) {
updateTab(currentTabId, { initialTab: "add_host" });
}
2025-09-12 14:42:00 -05:00
};
+7
2025-11-05 10:36:16 -06:00
const handleFormSubmit = () => {
ignoreNextHostConfigChangeRef.current = true;
2025-09-12 14:42:00 -05:00
setEditingHost(null);
setActiveTab("host_viewer");
+7
2025-11-05 10:36:16 -06:00
setTimeout(() => {
lastProcessedHostIdRef.current = undefined;
}, 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);
setActiveTab("add_credential");
+1
2025-12-31 22:20:12 -06:00
if (updateTab && currentTabId !== undefined) {
updateTab(currentTabId, { initialTab: "add_credential" });
}
2025-09-12 14:42:00 -05:00
};
const handleCredentialFormSubmit = () => {
setEditingCredential(null);
setActiveTab("credentials");
};
const handleTabChange = (value: string) => {
2025-11-17 09:46:05 -06:00
if (activeTab === "add_host" && value !== "add_host") {
2025-09-12 14:42:00 -05:00
setEditingHost(null);
2026-01-24 19:49:42 -06:00
lastProcessedHostIdRef.current = undefined;
// Clear hostConfig from tab data when leaving add_host tab
if (updateTab && currentTabId !== undefined) {
updateTab(currentTabId, { hostConfig: null });
}
2025-09-12 14:42:00 -05:00
}
2025-11-17 09:46:05 -06:00
if (activeTab === "add_credential" && value !== "add_credential") {
2025-09-12 14:42:00 -05:00
setEditingCredential(null);
}
2026-01-24 19:49:42 -06:00
// Clear editing state when switching TO add_host tab (to ensure fresh state)
if (value === "add_host" && activeTab !== "add_host") {
setEditingHost(null);
lastProcessedHostIdRef.current = undefined;
}
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
value="host_viewer"
className="bg-elevated data-[state=active]:bg-button data-[state=active]:border data-[state=active]:border-edge"
>
2025-09-12 14:42:00 -05:00
{t("hosts.hostViewer")}
</TabsTrigger>
+1
2025-12-31 22:20:12 -06:00
<TabsTrigger
value="add_host"
className="bg-elevated data-[state=active]:bg-button data-[state=active]:border data-[state=active]:border-edge"
>
2025-10-01 15:40:10 -05:00
{editingHost
? editingHost.id
? t("hosts.editHost")
: t("hosts.cloneHost")
: t("hosts.addHost")}
2025-09-12 14:42:00 -05:00
</TabsTrigger>
+1
2025-12-31 22:20:12 -06:00
<div className="h-6 w-px bg-border-base mx-1"></div>
<TabsTrigger
value="credentials"
className="bg-elevated data-[state=active]:bg-button data-[state=active]:border data-[state=active]:border-edge"
>
2025-09-12 14:42:00 -05:00
{t("credentials.credentialsViewer")}
</TabsTrigger>
+1
2025-12-31 22:20:12 -06:00
<TabsTrigger
value="add_credential"
className="bg-elevated data-[state=active]:bg-button data-[state=active]:border data-[state=active]:border-edge"
>
2025-09-12 14:42:00 -05:00
{editingCredential
? t("credentials.editCredential")
: t("credentials.addCredential")}
</TabsTrigger>
</TabsList>
<TabsContent
value="host_viewer"
className="flex-1 flex flex-col h-full min-h-0"
>
<Separator className="p-0.25 -mt-0.5 mb-1" />
<HostManagerViewer onEditHost={handleEditHost} />
</TabsContent>
<TabsContent
value="add_host"
className="flex-1 flex flex-col h-full min-h-0"
>
<Separator className="p-0.25 -mt-0.5 mb-1" />
<div className="flex flex-col h-full min-h-0">
<HostManagerEditor
editingHost={editingHost}
onFormSubmit={handleFormSubmit}
/>
</div>
</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" />
+1
2025-12-31 22:20:12 -06:00
<div className="flex flex-col h-full min-h-0 overflow-auto thin-scrollbar">
2025-09-12 14:42:00 -05:00
<CredentialsManager onEditCredential={handleEditCredential} />
</div>
</TabsContent>
<TabsContent
value="add_credential"
className="flex-1 flex flex-col h-full min-h-0"
>
<Separator className="p-0.25 -mt-0.5 mb-1" />
<div className="flex flex-col h-full min-h-0">
<CredentialEditor
editingCredential={editingCredential}
onFormSubmit={handleCredentialFormSubmit}
/>
</div>
</TabsContent>
</Tabs>
</div>
</div>
</div>
);
}