Added SSH manager and terminals to tab system, now I need to do the server stats
This commit is contained in:
@@ -3,6 +3,7 @@ import {Status, StatusIndicator} from "@/components/ui/shadcn-io/status";
|
||||
import {Button} from "@/components/ui/button.tsx";
|
||||
import {ButtonGroup} from "@/components/ui/button-group.tsx";
|
||||
import {Server, Terminal} from "lucide-react";
|
||||
import {useTabs} from "@/contexts/TabContext";
|
||||
|
||||
interface SSHHost {
|
||||
id: number;
|
||||
@@ -32,9 +33,22 @@ interface HostProps {
|
||||
}
|
||||
|
||||
export function Host({ host }: HostProps): React.ReactElement {
|
||||
const { addTab } = useTabs();
|
||||
const tags = Array.isArray(host.tags) ? host.tags : [];
|
||||
const hasTags = tags.length > 0;
|
||||
|
||||
const handleTerminalClick = () => {
|
||||
console.log('Terminal button clicked for host:', host);
|
||||
const title = host.name?.trim() ? host.name : `${host.username}@${host.ip}:${host.port}`;
|
||||
console.log('Creating terminal tab with title:', title);
|
||||
const tabId = addTab({
|
||||
type: 'terminal',
|
||||
title,
|
||||
hostConfig: host,
|
||||
});
|
||||
console.log('Created terminal tab with ID:', tabId);
|
||||
};
|
||||
|
||||
return (
|
||||
<div>
|
||||
<div className="flex items-center gap-2">
|
||||
@@ -48,7 +62,11 @@ export function Host({ host }: HostProps): React.ReactElement {
|
||||
<Button variant="outline" className="!px-2 border-1 border-[#303032]">
|
||||
<Server/>
|
||||
</Button>
|
||||
<Button variant="outline" className="!px-2 border-1 border-[#303032]">
|
||||
<Button
|
||||
variant="outline"
|
||||
className="!px-2 border-1 border-[#303032]"
|
||||
onClick={handleTerminalClick}
|
||||
>
|
||||
<Terminal/>
|
||||
</Button>
|
||||
</ButtonGroup>
|
||||
|
||||
@@ -49,6 +49,7 @@ import axios from "axios";
|
||||
import {Card} from "@/components/ui/card.tsx";
|
||||
import {FolderCard} from "@/ui/Navigation/Hosts/FolderCard.tsx";
|
||||
import {getSSHHosts} from "@/ui/SSH/ssh-axios";
|
||||
import { useTabs } from "@/contexts/TabContext";
|
||||
|
||||
interface SSHHost {
|
||||
id: number;
|
||||
@@ -145,6 +146,16 @@ export function LeftSidebar({
|
||||
|
||||
const [isSidebarOpen, setIsSidebarOpen] = useState<boolean>(true);
|
||||
|
||||
// Tabs context for opening SSH Manager tab
|
||||
const { tabs: tabList, addTab, setCurrentTab, allSplitScreenTab } = useTabs() as any;
|
||||
const isSplitScreenActive = Array.isArray(allSplitScreenTab) && allSplitScreenTab.length > 0;
|
||||
const sshManagerTab = tabList.find((t) => t.type === 'ssh_manager');
|
||||
const openSshManagerTab = () => {
|
||||
if (sshManagerTab || isSplitScreenActive) return;
|
||||
const id = addTab({ type: 'ssh_manager', title: 'SSH Manager' } as any);
|
||||
setCurrentTab(id);
|
||||
};
|
||||
|
||||
// SSH Hosts state management
|
||||
const [hosts, setHosts] = useState<SSHHost[]>([]);
|
||||
const [hostsLoading, setHostsLoading] = useState(false);
|
||||
@@ -505,7 +516,7 @@ export function LeftSidebar({
|
||||
<Separator className="p-0.25"/>
|
||||
<SidebarContent>
|
||||
<SidebarGroup className="!m-0 !p-0 !-mb-2">
|
||||
<Button className="m-2 flex flex-row font-semibold" variant="outline">
|
||||
<Button className="m-2 flex flex-row font-semibold" variant="outline" onClick={openSshManagerTab} disabled={!!sshManagerTab || isSplitScreenActive} title={sshManagerTab ? 'SSH Manager already open' : isSplitScreenActive ? 'Disabled during split screen' : undefined}>
|
||||
<HardDrive strokeWidth="2.5"/>
|
||||
Host Manager
|
||||
</Button>
|
||||
@@ -632,7 +643,7 @@ export function LeftSidebar({
|
||||
<Users className="h-4 w-4"/>
|
||||
Users
|
||||
</TabsTrigger>
|
||||
<TabsTrigger value="admins" className="flex items-center gap-2">
|
||||
<TabsTrigger value="admins" className="h-4 w-4">
|
||||
<Shield className="h-4 w-4"/>
|
||||
Admins
|
||||
</TabsTrigger>
|
||||
@@ -759,7 +770,7 @@ export function LeftSidebar({
|
||||
<Input
|
||||
id="scopes"
|
||||
value={oidcConfig.scopes}
|
||||
onChange={(e) => handleOIDCConfigChange('scopes', e.target.value)}
|
||||
onChange={(e) => handleOIDCConfigChange('scopes', (e.target as HTMLInputElement).value)}
|
||||
placeholder="openid email profile"
|
||||
required
|
||||
/>
|
||||
|
||||
@@ -1,22 +1,94 @@
|
||||
import React from "react";
|
||||
import {ButtonGroup} from "@/components/ui/button-group.tsx";
|
||||
import {Button} from "@/components/ui/button.tsx";
|
||||
import {SeparatorVertical, X} from "lucide-react";
|
||||
import {Home, SeparatorVertical, X} from "lucide-react";
|
||||
|
||||
export function Tab(): React.ReactElement {
|
||||
return (
|
||||
<div>
|
||||
interface TabProps {
|
||||
tabType: string;
|
||||
title?: string;
|
||||
isActive?: boolean;
|
||||
onActivate?: () => void;
|
||||
onClose?: () => void;
|
||||
onSplit?: () => void;
|
||||
canSplit?: boolean;
|
||||
canClose?: boolean;
|
||||
disableActivate?: boolean;
|
||||
disableSplit?: boolean;
|
||||
disableClose?: boolean;
|
||||
}
|
||||
|
||||
export function Tab({tabType, title, isActive, onActivate, onClose, onSplit, canSplit = false, canClose = false, disableActivate = false, disableSplit = false, disableClose = false}: TabProps): React.ReactElement {
|
||||
if (tabType === "home") {
|
||||
return (
|
||||
<Button
|
||||
variant="outline"
|
||||
className={`!px-2 border-1 border-[#303032] ${isActive ? '!bg-[#1d1d1f] !text-white !border-[#2d2d30]' : ''}`}
|
||||
onClick={onActivate}
|
||||
disabled={disableActivate}
|
||||
>
|
||||
<Home/>
|
||||
</Button>
|
||||
);
|
||||
}
|
||||
|
||||
if (tabType === "terminal") {
|
||||
return (
|
||||
<ButtonGroup>
|
||||
<Button variant="outline" className="!px-2 border-1 border-[#303032]">
|
||||
Server Name
|
||||
<Button
|
||||
variant="outline"
|
||||
className={`!px-2 border-1 border-[#303032] ${isActive ? '!bg-[#1d1d1f] !text-white !border-[#2d2d30]' : ''}`}
|
||||
onClick={onActivate}
|
||||
disabled={disableActivate}
|
||||
>
|
||||
{title || "Terminal"}
|
||||
</Button>
|
||||
<Button variant="outline" className="!px-2 border-1 border-[#303032]">
|
||||
<SeparatorVertical className="w-[28px] h-[28px]" />
|
||||
{canSplit && (
|
||||
<Button
|
||||
variant="outline"
|
||||
className="!px-2 border-1 border-[#303032]"
|
||||
onClick={onSplit}
|
||||
disabled={disableSplit}
|
||||
title={disableSplit ? 'Cannot split this tab' : 'Split'}
|
||||
>
|
||||
<SeparatorVertical className="w-[28px] h-[28px]" />
|
||||
</Button>
|
||||
)}
|
||||
{canClose && (
|
||||
<Button
|
||||
variant="outline"
|
||||
className="!px-2 border-1 border-[#303032]"
|
||||
onClick={onClose}
|
||||
disabled={disableClose}
|
||||
>
|
||||
<X/>
|
||||
</Button>
|
||||
)}
|
||||
</ButtonGroup>
|
||||
);
|
||||
}
|
||||
|
||||
if (tabType === "ssh_manager") {
|
||||
return (
|
||||
<ButtonGroup>
|
||||
<Button
|
||||
variant="outline"
|
||||
className={`!px-2 border-1 border-[#303032] ${isActive ? '!bg-[#1d1d1f] !text-white !border-[#2d2d30]' : ''}`}
|
||||
onClick={onActivate}
|
||||
disabled={disableActivate}
|
||||
>
|
||||
{title || "SSH Manager"}
|
||||
</Button>
|
||||
<Button variant="outline" className="!px-2 border-1 border-[#303032]">
|
||||
<Button
|
||||
variant="outline"
|
||||
className="!px-2 border-1 border-[#303032]"
|
||||
onClick={onClose}
|
||||
disabled={disableClose}
|
||||
>
|
||||
<X/>
|
||||
</Button>
|
||||
</ButtonGroup>
|
||||
</div>
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@ import {useSidebar} from "@/components/ui/sidebar";
|
||||
import {Button} from "@/components/ui/button.tsx";
|
||||
import {ChevronDown, ChevronUpIcon} from "lucide-react";
|
||||
import {Tab} from "@/ui/Navigation/Tabs/Tab.tsx";
|
||||
import {useTabs} from "@/contexts/TabContext";
|
||||
|
||||
interface TopNavbarProps {
|
||||
isTopbarOpen: boolean;
|
||||
@@ -11,8 +12,26 @@ interface TopNavbarProps {
|
||||
|
||||
export function TopNavbar({isTopbarOpen, setIsTopbarOpen}: TopNavbarProps): React.ReactElement {
|
||||
const {state} = useSidebar();
|
||||
const {tabs, currentTab, setCurrentTab, setSplitScreenTab, removeTab, allSplitScreenTab} = useTabs() as any;
|
||||
const leftPosition = state === "collapsed" ? "26px" : "264px";
|
||||
|
||||
const handleTabActivate = (tabId: number) => {
|
||||
setCurrentTab(tabId);
|
||||
};
|
||||
|
||||
const handleTabSplit = (tabId: number) => {
|
||||
setSplitScreenTab(tabId);
|
||||
};
|
||||
|
||||
const handleTabClose = (tabId: number) => {
|
||||
removeTab(tabId);
|
||||
};
|
||||
|
||||
const isSplitScreenActive = Array.isArray(allSplitScreenTab) && allSplitScreenTab.length > 0;
|
||||
const currentTabObj = tabs.find((t: any) => t.id === currentTab);
|
||||
const currentTabIsHome = currentTabObj?.type === 'home';
|
||||
const currentTabIsSshManager = currentTabObj?.type === 'ssh_manager';
|
||||
|
||||
return (
|
||||
<div>
|
||||
<div
|
||||
@@ -27,15 +46,42 @@ export function TopNavbar({isTopbarOpen, setIsTopbarOpen}: TopNavbarProps): Reac
|
||||
padding: "0"
|
||||
}}
|
||||
>
|
||||
<div className="h-full p-1 pr-2 border-r-2 border-[#303032] w-[calc(100%-3rem)] flex items-center overflow-x-scroll gap-2">
|
||||
<Tab/>
|
||||
<div className="h-full p-1 pr-2 border-r-2 border-[#303032] w-[calc(100%-3rem)] flex items-center overflow-x-auto overflow-y-hidden gap-2 thin-scrollbar">
|
||||
{tabs.map((tab: any) => {
|
||||
const isActive = tab.id === currentTab;
|
||||
const isSplit = Array.isArray(allSplitScreenTab) && allSplitScreenTab.includes(tab.id);
|
||||
const isTerminal = tab.type === 'terminal';
|
||||
const isSshManager = tab.type === 'ssh_manager';
|
||||
// Old logic port:
|
||||
const isSplitButtonDisabled = (isActive && !isSplitScreenActive) || ((allSplitScreenTab?.length || 0) >= 3 && !isSplit);
|
||||
// Disable split entirely when on Home or SSH Manager
|
||||
const disableSplit = isSplitButtonDisabled || isActive || currentTabIsHome || currentTabIsSshManager || isSshManager;
|
||||
const disableActivate = isSplit || ((tab.type === 'home' || tab.type === 'ssh_manager') && isSplitScreenActive);
|
||||
const disableClose = (isSplitScreenActive && isActive) || isSplit;
|
||||
return (
|
||||
<Tab
|
||||
key={tab.id}
|
||||
tabType={tab.type}
|
||||
title={tab.title}
|
||||
isActive={isActive}
|
||||
onActivate={() => handleTabActivate(tab.id)}
|
||||
onClose={isTerminal || isSshManager ? () => handleTabClose(tab.id) : undefined}
|
||||
onSplit={isTerminal ? () => handleTabSplit(tab.id) : undefined}
|
||||
canSplit={isTerminal}
|
||||
canClose={isTerminal || isSshManager}
|
||||
disableActivate={disableActivate}
|
||||
disableSplit={disableSplit}
|
||||
disableClose={disableClose}
|
||||
/>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
|
||||
<div className="flex items-center justify-center flex-1">
|
||||
<Button
|
||||
variant="outline"
|
||||
onClick={() => setIsTopbarOpen(false)}
|
||||
className="w-[28px] h-[28px]"
|
||||
className="w-[28px] h-[28]"
|
||||
>
|
||||
<ChevronUpIcon/>
|
||||
</Button>
|
||||
|
||||
Reference in New Issue
Block a user