diff --git a/frontend/src/components/layouts.tsx b/frontend/src/components/layouts.tsx index 69c7a1698..51808387d 100644 --- a/frontend/src/components/layouts.tsx +++ b/frontend/src/components/layouts.tsx @@ -32,6 +32,8 @@ export const Layout = () => { useShiftKeyListener("B", () => nav("/builds")); useShiftKeyListener("R", () => nav("/repos")); useShiftKeyListener("P", () => nav("/procedures")); + useShiftKeyListener("J", () => nav("/terminals")); + useShiftKeyListener("C", () => nav("/schedules")); useShiftKeyListener("V", () => { setSettingsView("Variables"); nav("/settings"); diff --git a/frontend/src/components/omnibar.tsx b/frontend/src/components/omnibar.tsx index c67c11f7b..5de1970c2 100644 --- a/frontend/src/components/omnibar.tsx +++ b/frontend/src/components/omnibar.tsx @@ -1,4 +1,10 @@ -import { useAllResources, useLocalStorage, useRead, useSettingsView, useUser } from "@lib/hooks"; +import { + useAllResources, + useLocalStorage, + useRead, + useSettingsView, + useUser, +} from "@lib/hooks"; import { Button } from "@ui/button"; import { CommandDialog, @@ -9,7 +15,7 @@ import { CommandSeparator, CommandItem, } from "@ui/command"; -import { Box, Home, Search, User } from "lucide-react"; +import { Box, CalendarDays, Home, Search, Terminal, User } from "lucide-react"; import { Fragment, ReactNode, useMemo, useState } from "react"; import { useNavigate } from "react-router-dom"; import { cn, RESOURCE_TARGETS, usableResourcePath } from "@lib/utils"; @@ -162,6 +168,22 @@ const useOmniItems = ( onSelect: () => nav("/containers"), template: false, }, + { + key: "Terminals", + type: "Server" as UsableResource, + label: "Terminals", + icon: , + onSelect: () => nav("/terminals"), + template: false, + }, + { + key: "Schedules", + type: "Server" as UsableResource, + label: "Schedules", + icon: , + onSelect: () => nav("/schedules"), + template: false, + }, (user?.admin && { key: "Users", type: "Server" as UsableResource, diff --git a/frontend/src/components/resources/action/index.tsx b/frontend/src/components/resources/action/index.tsx index 8e63f4fde..6d222ab5d 100644 --- a/frontend/src/components/resources/action/index.tsx +++ b/frontend/src/components/resources/action/index.tsx @@ -11,7 +11,7 @@ import { } from "@lib/color"; import { cn, updateLogToHtml } from "@lib/utils"; import { Types } from "komodo_client"; -import { DashboardPieChart } from "@pages/home/dashboard"; +import { DashboardPieChart } from "@pages/dashboard"; import { GroupActions } from "@components/group-actions"; import { Tooltip, TooltipContent, TooltipTrigger } from "@ui/tooltip"; import { Card } from "@ui/card"; diff --git a/frontend/src/components/resources/build/index.tsx b/frontend/src/components/resources/build/index.tsx index 64d27f76c..5e5513cfc 100644 --- a/frontend/src/components/resources/build/index.tsx +++ b/frontend/src/components/resources/build/index.tsx @@ -17,7 +17,7 @@ import { } from "@lib/color"; import { cn } from "@lib/utils"; import { Types } from "komodo_client"; -import { DashboardPieChart } from "@pages/home/dashboard"; +import { DashboardPieChart } from "@pages/dashboard"; import { StatusBadge } from "@components/util"; import { Card } from "@ui/card"; import { Badge } from "@ui/badge"; diff --git a/frontend/src/components/resources/deployment/index.tsx b/frontend/src/components/resources/deployment/index.tsx index f8b0bc19d..9e56bc345 100644 --- a/frontend/src/components/resources/deployment/index.tsx +++ b/frontend/src/components/resources/deployment/index.tsx @@ -24,7 +24,7 @@ import { ResourcePageHeader, } from "../common"; import { RunBuild } from "../build/actions"; -import { DashboardPieChart } from "@pages/home/dashboard"; +import { DashboardPieChart } from "@pages/dashboard"; import { ContainerPortsTableView, DockerResourceLink, diff --git a/frontend/src/components/resources/procedure/index.tsx b/frontend/src/components/resources/procedure/index.tsx index 44208a451..a63393c59 100644 --- a/frontend/src/components/resources/procedure/index.tsx +++ b/frontend/src/components/resources/procedure/index.tsx @@ -11,7 +11,7 @@ import { } from "@lib/color"; import { cn, updateLogToHtml } from "@lib/utils"; import { Types } from "komodo_client"; -import { DashboardPieChart } from "@pages/home/dashboard"; +import { DashboardPieChart } from "@pages/dashboard"; import { GroupActions } from "@components/group-actions"; import { Tooltip, TooltipContent, TooltipTrigger } from "@ui/tooltip"; import { Card } from "@ui/card"; diff --git a/frontend/src/components/resources/repo/index.tsx b/frontend/src/components/resources/repo/index.tsx index 4059efdba..dcd1652fa 100644 --- a/frontend/src/components/resources/repo/index.tsx +++ b/frontend/src/components/resources/repo/index.tsx @@ -18,7 +18,7 @@ import { import { cn } from "@lib/utils"; import { useServer } from "../server"; import { Types } from "komodo_client"; -import { DashboardPieChart } from "@pages/home/dashboard"; +import { DashboardPieChart } from "@pages/dashboard"; import { RepoLink, StatusBadge } from "@components/util"; import { Badge } from "@ui/badge"; import { useToast } from "@ui/use-toast"; diff --git a/frontend/src/components/resources/server/index.tsx b/frontend/src/components/resources/server/index.tsx index 5fc09174f..320bc0d00 100644 --- a/frontend/src/components/resources/server/index.tsx +++ b/frontend/src/components/resources/server/index.tsx @@ -25,7 +25,7 @@ import { ServerTable } from "./table"; import { DeleteResource, NewResource, ResourcePageHeader } from "../common"; import { ActionWithDialog, ConfirmButton, StatusBadge } from "@components/util"; import { Card } from "@ui/card"; -import { DashboardPieChart } from "@pages/home/dashboard"; +import { DashboardPieChart } from "@pages/dashboard"; import { ServerStatsMini } from "./stats-mini"; import { GroupActions } from "@components/group-actions"; import { usePermissions } from "@lib/hooks"; diff --git a/frontend/src/components/resources/server/monitoring-table.tsx b/frontend/src/components/resources/server/monitoring-table.tsx index 09c1a5ca5..94e43f9d2 100644 --- a/frontend/src/components/resources/server/monitoring-table.tsx +++ b/frontend/src/components/resources/server/monitoring-table.tsx @@ -4,12 +4,9 @@ import { DataTable, SortableHeader } from "@ui/data-table"; import { useRead, useSelectedResources, - useServerMonitoringTable, } from "@lib/hooks"; import { useIsServerAvailable } from "."; import { Types } from "komodo_client"; -import { Button } from "@ui/button"; -import { Eye, EyeOff } from "lucide-react"; export const ServerMonitoringTable = ({ servers, @@ -73,29 +70,6 @@ export const ServerMonitoringTable = ({ ); }; -export const ToggleServerMonitoringTableButton = () => { - const [monitoring, toggleMonitoring] = useServerMonitoringTable(); - return ( - - ); -}; - const useStats = (id: string) => { const isServerAvailable = useIsServerAvailable(id); return useRead( diff --git a/frontend/src/components/resources/server/table.tsx b/frontend/src/components/resources/server/table.tsx index bb791e1a9..681588302 100644 --- a/frontend/src/components/resources/server/table.tsx +++ b/frontend/src/components/resources/server/table.tsx @@ -2,7 +2,6 @@ import { TableTags } from "@components/tags"; import { useRead, useSelectedResources, - useServerMonitoringTable, } from "@lib/hooks"; import { DataTable, SortableHeader } from "@ui/data-table"; import { ServerComponents, ServerVersion } from "."; @@ -10,14 +9,15 @@ import { ResourceLink } from "../common"; import { Types } from "komodo_client"; import { useCallback } from "react"; import { ServerMonitoringTable } from "./monitoring-table"; +import { useDashboardPreferences } from "@lib/dashboard-preferences"; export const ServerTable = ({ servers, }: { servers: Types.ServerListItem[]; }) => { - const [monitoring] = useServerMonitoringTable(); - if (monitoring) { + const { preferences } = useDashboardPreferences(); + if (preferences.showServerStats) { return ; } else { return ; diff --git a/frontend/src/components/resources/stack/index.tsx b/frontend/src/components/resources/stack/index.tsx index 60b9dd52b..914ce70f2 100644 --- a/frontend/src/components/resources/stack/index.tsx +++ b/frontend/src/components/resources/stack/index.tsx @@ -35,7 +35,7 @@ import { import { Badge } from "@ui/badge"; import { Button } from "@ui/button"; import { useToast } from "@ui/use-toast"; -import { DashboardPieChart } from "@pages/home/dashboard"; +import { DashboardPieChart } from "@pages/dashboard"; import { StatusBadge } from "@components/util"; import { GroupActions } from "@components/group-actions"; import { Tooltip, TooltipTrigger, TooltipContent } from "@ui/tooltip"; diff --git a/frontend/src/components/resources/sync/index.tsx b/frontend/src/components/resources/sync/index.tsx index a0ffcdd5f..00db40ef1 100644 --- a/frontend/src/components/resources/sync/index.tsx +++ b/frontend/src/components/resources/sync/index.tsx @@ -18,7 +18,7 @@ import { } from "@lib/color"; import { cn } from "@lib/utils"; import { fmt_date } from "@lib/formatting"; -import { DashboardPieChart } from "@pages/home/dashboard"; +import { DashboardPieChart } from "@pages/dashboard"; import { StatusBadge } from "@components/util"; import { Badge } from "@ui/badge"; import { GroupActions } from "@components/group-actions"; diff --git a/frontend/src/components/sidebar.tsx b/frontend/src/components/sidebar.tsx index aed303020..60df51d74 100644 --- a/frontend/src/components/sidebar.tsx +++ b/frontend/src/components/sidebar.tsx @@ -4,7 +4,6 @@ import { AlertTriangle, Bell, Box, - Boxes, CalendarDays, LayoutDashboard, Settings, @@ -14,34 +13,29 @@ import { Link, useLocation } from "react-router-dom"; import { ResourceComponents } from "./resources"; import { Separator } from "@ui/separator"; import { ReactNode } from "react"; -import { useAtom } from "jotai"; -import { homeViewAtom } from "@main"; export const Sidebar = () => { - const [view, setView] = useAtom(homeViewAtom); return (
-
+
} - onClick={() => setView("Dashboard")} - highlighted={view === "Dashboard"} - /> - } - onClick={() => setView("Resources")} - highlighted={view === "Resources"} /> + } /> + } + /> +

Resources

@@ -74,12 +68,6 @@ export const Sidebar = () => { - } - /> - { const type = useResourceParamType(); const Components = type && ResourceComponents[type]; - const [view, setView] = useAtom(homeViewAtom); const [icon, title] = Components ? [, (type === "ResourceSync" ? "Sync" : type) + "s"] - : location.pathname === "/" && view === "Dashboard" + : location.pathname === "/" ? [, "Dashboard"] - : location.pathname === "/" && view === "Resources" - ? [, "Resources"] - : location.pathname === "/" && view === "Tree" - ? [, "Tree"] - : location.pathname === "/containers" - ? [, "Containers"] - : location.pathname === "/settings" - ? [, "Settings"] - : location.pathname === "/terminals" - ? [, "Terminals"] - : location.pathname === "/schedules" - ? [, "Schedules"] - : location.pathname === "/alerts" - ? [, "Alerts"] - : location.pathname === "/updates" - ? [, "Updates"] - : location.pathname.split("/")[1] === "user-groups" - ? [, "User Groups"] - : location.pathname.split("/")[1] === "users" - ? [, "Users"] - : [, "Unknown"]; + : location.pathname === "/containers" + ? [, "Containers"] + : location.pathname === "/settings" + ? [, "Settings"] + : location.pathname === "/terminals" + ? [, "Terminals"] + : location.pathname === "/schedules" + ? [, "Schedules"] + : location.pathname === "/alerts" + ? [, "Alerts"] + : location.pathname === "/updates" + ? [, "Updates"] + : location.pathname.split("/")[1] === "user-groups" + ? [, "User Groups"] + : location.pathname.split("/")[1] === "users" + ? [, "Users"] + : [, "Unknown"]; return ( @@ -117,20 +108,20 @@ export const MobileDropdown = () => { label="Dashboard" icon={} to="/" - onClick={() => setView("Dashboard")} - /> - } - to="/" - onClick={() => setView("Resources")} /> + } to="/containers" /> + } + to="/terminals" + /> + {SIDEBAR_RESOURCES.map((type) => { @@ -162,12 +153,6 @@ export const MobileDropdown = () => { - } - to="/terminals" - /> - } @@ -599,7 +584,7 @@ export const KeyboardShortcuts = () => { Keyboard Shortcuts -
+
@@ -610,6 +595,8 @@ export const KeyboardShortcuts = () => { + + diff --git a/frontend/src/lib/dashboard-preferences.ts b/frontend/src/lib/dashboard-preferences.ts index 71a18fc8d..0e71756d1 100644 --- a/frontend/src/lib/dashboard-preferences.ts +++ b/frontend/src/lib/dashboard-preferences.ts @@ -3,19 +3,23 @@ import { useAtom } from "jotai"; interface DashboardPreferences { showServerStats: boolean; + showTables: boolean; } const DEFAULT_PREFERENCES: DashboardPreferences = { showServerStats: false, + showTables: false, }; export const dashboardPreferencesAtom = atomWithStorage( - "komodo-dashboard-preferences", + "komodo-dashboard-preferences-v2", DEFAULT_PREFERENCES ); export const useDashboardPreferences = () => { - const [preferences, setPreferences] = useAtom(dashboardPreferencesAtom); + const [preferences, setPreferences] = useAtom( + dashboardPreferencesAtom + ); const updatePreference = ( key: K, @@ -24,8 +28,13 @@ export const useDashboardPreferences = () => { setPreferences({ ...preferences, [key]: value }); }; + const togglePreference = (key: K) => { + updatePreference(key, !preferences[key]); + }; + return { preferences, updatePreference, + togglePreference, }; }; diff --git a/frontend/src/lib/hooks.ts b/frontend/src/lib/hooks.ts index 5c24b8fa6..b6a2e4d46 100644 --- a/frontend/src/lib/hooks.ts +++ b/frontend/src/lib/hooks.ts @@ -712,15 +712,6 @@ export const useFilterByUpdateAvailable: () => [boolean, () => void] = () => { return [filter, () => set(!filter)]; }; -const server_monitoring_table = atomWithStorage( - "servers-monitoring-toggle-v1", - false -); -export const useServerMonitoringTable: () => [boolean, () => void] = () => { - const [filter, set] = useAtom(server_monitoring_table); - return [filter, () => set(!filter)]; -}; - export const usePermissions = ({ type, id }: Types.ResourceTarget) => { const user = useUser().data; const perms = useRead("GetPermission", { target: { type, id } }).data as diff --git a/frontend/src/main.tsx b/frontend/src/main.tsx index 69b7fc5ae..6a1ef6f21 100644 --- a/frontend/src/main.tsx +++ b/frontend/src/main.tsx @@ -5,7 +5,6 @@ import { QueryClient, QueryClientProvider } from "@tanstack/react-query"; import { Router } from "@router"; import { WebsocketProvider } from "@lib/socket"; import { Toaster } from "@ui/toaster"; -import { atomWithStorage } from "@lib/hooks"; // Run monaco setup import "./monaco"; import { init_monaco } from "./monaco/init"; @@ -18,13 +17,6 @@ const query_client = new QueryClient({ defaultOptions: { queries: { retry: false } }, }); -export type HomeView = "Dashboard" | "Tree" | "Resources"; - -export const homeViewAtom = atomWithStorage( - "home-view-v1", - "Dashboard" -); - // Don't need to await this to render. init_monaco(); diff --git a/frontend/src/pages/home/dashboard.tsx b/frontend/src/pages/dashboard.tsx similarity index 71% rename from frontend/src/pages/home/dashboard.tsx rename to frontend/src/pages/dashboard.tsx index 3d1a49e91..63131a69a 100644 --- a/frontend/src/pages/home/dashboard.tsx +++ b/frontend/src/pages/dashboard.tsx @@ -3,11 +3,11 @@ import { Page, Section } from "@components/layouts"; import { ResourceComponents } from "@components/resources"; import { ResourceLink, ResourceNameSimple } from "@components/resources/common"; import { ServerStatsMini } from "@components/resources/server"; -import { TagsWithBadge } from "@components/tags"; -import { StatusBadge, TemplateMarker } from "@components/util"; +import { TagsFilter, TagsWithBadge } from "@components/tags"; +import { ShowHideButton, StatusBadge, TemplateMarker } from "@components/util"; import { useDashboardPreferences } from "@lib/dashboard-preferences"; import { Button } from "@ui/button"; -import { Eye, EyeOff, Settings } from "lucide-react"; +import { Eye, EyeOff, Settings, Table } from "lucide-react"; import { action_state_intention, build_state_intention, @@ -17,21 +17,26 @@ import { repo_state_intention, text_color_class_by_intention, } from "@lib/color"; -import { useNoResources, useRead, useUser } from "@lib/hooks"; +import { + useFilterResources, + useNoResources, + useRead, + useUser, +} from "@lib/hooks"; import { cn, usableResourcePath } from "@lib/utils"; import { Types } from "komodo_client"; -import { UsableResource } from "@types"; +import { RequiredResourceComponents, UsableResource } from "@types"; import { DataTable, SortableHeader } from "@ui/data-table"; import { AlertTriangle, Box, Circle, History } from "lucide-react"; import { PieChart } from "react-minimal-pie-chart"; import { Link } from "react-router-dom"; import { UpdateAvailable as StackUpdateAvailable } from "@components/resources/stack"; import { UpdateAvailable as DeploymentUpdateAvailable } from "@components/resources/deployment"; +import { useState } from "react"; +import { Input } from "@ui/input"; export default function Dashboard() { - const noResources = useNoResources(); - const user = useUser().data!; - + const { preferences } = useDashboardPreferences(); return ( <> @@ -39,38 +44,123 @@ export default function Dashboard() { title="Dashboard" icon={} actions={ -
+
+
} > -
- {noResources && ( -
- -

- No resources found.{" "} - {user.admin - ? "To get started, create a server." - : "Contact an admin for access to resources."} -

-
- )} - - - - - - - - -
+ {preferences.showTables ? : } ); } +const TablesDashboard = () => { + const [search, setSearch] = useState(""); + const noResources = useNoResources(); + const user = useUser().data!; + return ( +
+
+ + setSearch(e.target.value)} + placeholder="search..." + className="w-[200px] lg:w-[300px]" + /> +
+ + {noResources && ( +
+ +

+ No resources found.{" "} + {user.admin + ? "To get started, create a server." + : "Contact an admin for access to resources."} +

+
+ )} + + {Object.entries(ResourceComponents).map(([type, Components]) => ( + + ))} +
+ ); +}; + +const TableSection = ({ + type, + Components, + search, +}: { + type: string; + Components: RequiredResourceComponents; + search?: string; +}) => { + const resources = useRead(`List${type as UsableResource}s`, {}).data; + + const filtered = useFilterResources( + resources as Types.ResourceListItem[], + search + ); + + let count = filtered.length; + + const [show, setShow] = useState(true); + + if (!count) return; + + return ( +
} + actions={} + > +
+ {show && } +
+
+ ); +}; + +const RecentsDashboard = () => { + const noResources = useNoResources(); + const user = useUser().data!; + return ( +
+ {noResources && ( +
+ +

+ No resources found.{" "} + {user.admin + ? "To get started, create a server." + : "Contact an admin for access to resources."} +

+
+ )} + + + + + + + + +
+ ); +}; + const ResourceRow = ({ type }: { type: UsableResource }) => { const _recents = useUser().data?.recents?.[type]?.slice(0, 6); const _resources = useRead(`List${type}s`, {}).data; @@ -353,15 +443,13 @@ const ActiveResources = () => { ); }; -const ShowServerStats = () => { - const { preferences, updatePreference } = useDashboardPreferences(); +export const ShowServerStats = () => { + const { preferences, togglePreference } = useDashboardPreferences(); return ( ); }; + +const ShowTables = () => { + const { preferences, updatePreference } = useDashboardPreferences(); + return ( +