import { Sidebar, SidebarContent, SidebarFooter, SidebarGroupLabel, SidebarHeader, SidebarMenu, SidebarMenuButton, SidebarMenuItem, SidebarProvider, } from "@/components/ui/sidebar.tsx"; import { Button } from "@/components/ui/button.tsx"; import { ChevronUp, Menu, User2 } from "lucide-react"; import React, { useState, useEffect, useMemo, useCallback } from "react"; import { Separator } from "@/components/ui/separator.tsx"; import { FolderCard } from "@/ui/Mobile/Navigation/Hosts/FolderCard.tsx"; import { getSSHHosts } from "@/ui/main-axios.ts"; import { useTranslation } from "react-i18next"; import { Input } from "@/components/ui/input.tsx"; import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuTrigger, } from "@radix-ui/react-dropdown-menu"; interface SSHHost { id: number; name: string; ip: string; port: number; username: string; folder: string; tags: string[]; pin: boolean; authType: string; password?: string; key?: string; keyPassword?: string; keyType?: string; enableTerminal: boolean; enableTunnel: boolean; enableFileManager: boolean; defaultPath: string; tunnelConnections: any[]; createdAt: string; updatedAt: string; } interface LeftSidebarProps { isSidebarOpen: boolean; setIsSidebarOpen: (type: boolean) => void; onHostConnect: () => void; disabled?: boolean; username?: string | null; } function handleLogout() { document.cookie = "jwt=; expires=Thu, 01 Jan 1970 00:00:00 UTC; path=/;"; window.location.reload(); } export function LeftSidebar({ isSidebarOpen, setIsSidebarOpen, onHostConnect, disabled, username, }: LeftSidebarProps) { const { t } = useTranslation(); const [hosts, setHosts] = useState([]); const [hostsLoading, setHostsLoading] = useState(false); const [hostsError, setHostsError] = useState(null); const prevHostsRef = React.useRef([]); const [search, setSearch] = useState(""); const [debouncedSearch, setDebouncedSearch] = useState(""); const fetchHosts = useCallback(async () => { try { const newHosts = await getSSHHosts(); const prevHosts = prevHostsRef.current; if (JSON.stringify(newHosts) !== JSON.stringify(prevHosts)) { setHosts(newHosts); prevHostsRef.current = newHosts; } } catch (err: any) { setHostsError(t("leftSidebar.failedToLoadHosts")); } }, [t]); useEffect(() => { fetchHosts(); const interval = setInterval(fetchHosts, 300000); return () => clearInterval(interval); }, [fetchHosts]); useEffect(() => { const handleHostsChanged = () => { fetchHosts(); }; window.addEventListener( "ssh-hosts:changed", handleHostsChanged as EventListener, ); return () => window.removeEventListener( "ssh-hosts:changed", handleHostsChanged as EventListener, ); }, [fetchHosts]); useEffect(() => { const handler = setTimeout(() => setDebouncedSearch(search), 200); return () => clearTimeout(handler); }, [search]); const filteredHosts = useMemo(() => { if (!debouncedSearch.trim()) return hosts; const q = debouncedSearch.trim().toLowerCase(); return hosts.filter((h) => { const searchableText = [ h.name || "", h.username, h.ip, h.folder || "", ...(h.tags || []), ] .join(" ") .toLowerCase(); return searchableText.includes(q); }); }, [hosts, debouncedSearch]); const hostsByFolder = useMemo(() => { const map: Record = {}; filteredHosts.forEach((h) => { const folder = h.folder && h.folder.trim() ? h.folder : t("leftSidebar.noFolder"); if (!map[folder]) map[folder] = []; map[folder].push(h); }); return map; }, [filteredHosts, t]); const sortedFolders = useMemo(() => { const folders = Object.keys(hostsByFolder); folders.sort((a, b) => { if (a === t("leftSidebar.noFolder")) return 1; if (b === t("leftSidebar.noFolder")) return -1; return a.localeCompare(b); }); return folders; }, [hostsByFolder, t]); const getSortedHosts = useCallback((arr: SSHHost[]) => { const pinned = arr .filter((h) => h.pin) .sort((a, b) => (a.name || "").localeCompare(b.name || "")); const rest = arr .filter((h) => !h.pin) .sort((a, b) => (a.name || "").localeCompare(b.name || "")); return [...pinned, ...rest]; }, []); return (
Termix
setSearch(e.target.value)} placeholder={t("placeholders.searchHostsAny")} className="w-full h-8 text-sm border-2 !bg-dark-bg-input border-dark-border rounded-md" autoComplete="off" />
{hostsError && (
{t("leftSidebar.failedToLoadHosts")}
)} {hostsLoading && (
{t("hosts.loadingHosts")}
)} {sortedFolders.map((folder) => ( ))}
{username ? username : t("common.logout")} {t("common.logout")}
); }