diff --git a/src/app/[orgId]/settings/layout.tsx b/src/app/[orgId]/settings/layout.tsx index 6a3d648a4..cac644be3 100644 --- a/src/app/[orgId]/settings/layout.tsx +++ b/src/app/[orgId]/settings/layout.tsx @@ -12,7 +12,7 @@ import UserProvider from "@app/providers/UserProvider"; import { Layout } from "@app/components/Layout"; import { getTranslations } from "next-intl/server"; import { pullEnv } from "@app/lib/pullEnv"; -import { orgNavSections } from "@app/app/navigation"; +import { commandBarNavSections, orgNavSections } from "@app/app/navigation"; import { getCachedOrgUser } from "@app/lib/api/getCachedOrgUser"; export const dynamic = "force-dynamic"; @@ -82,6 +82,9 @@ export default async function SettingsLayout(props: SettingsLayoutProps) { navItems={orgNavSections(env, { isPrimaryOrg: primaryOrg })} + commandNavItems={commandBarNavSections(env, { + isPrimaryOrg: primaryOrg + })} > {children} diff --git a/src/app/navigation.tsx b/src/app/navigation.tsx index 43323ba2f..e0ada4c1c 100644 --- a/src/app/navigation.tsx +++ b/src/app/navigation.tsx @@ -340,3 +340,253 @@ export const adminNavSections = (env?: Env): SidebarNavSection[] => [ ] } ]; + +export const commandBarNavSections = ( + env?: Env, + options?: OrgNavSectionsOptions +): SidebarNavSection[] => [ + { + heading: "network", + items: [ + { + title: "sidebarSites", + href: "/{orgId}/settings/sites", + icon: + }, + { + title: "sidebarResources", + icon: , + items: [ + { + title: "sidebarProxyResources", + href: "/{orgId}/settings/resources/public", + icon: + }, + { + title: "sidebarClientResources", + href: "/{orgId}/settings/resources/private", + icon: + } + ] + }, + { + title: "sidebarClients", + icon: , + items: [ + { + href: "/{orgId}/settings/clients/user", + title: "sidebarUserDevices", + icon: + }, + { + href: "/{orgId}/settings/clients/machine", + title: "sidebarMachineClients", + icon: + } + ] + }, + { + title: "sidebarDomains", + href: "/{orgId}/settings/domains", + icon: + }, + ...(build === "saas" + ? [ + { + title: "sidebarRemoteExitNodes", + href: "/{orgId}/settings/remote-exit-nodes", + icon: + } + ] + : []) + ] + }, + { + heading: "accessControl", + items: [ + { + title: "sidebarTeam", + icon: , + items: [ + { + title: "sidebarUsers", + href: "/{orgId}/settings/access/users", + icon: + }, + { + title: "sidebarRoles", + href: "/{orgId}/settings/access/roles", + icon: + }, + { + title: "sidebarInvitations", + href: "/{orgId}/settings/access/invitations", + icon: + } + ] + }, + ...(!env?.flags.disableEnterpriseFeatures + ? [ + { + title: "sidebarPolicies", + + icon: , + items: [ + { + title: "sidebarResourcePolicies", + href: "/{orgId}/settings/policies/resources/public", + icon: ( + + ) + } + ] + } + ] + : []), + // PaidFeaturesAlert + ...((build === "oss" && !env?.flags.disableEnterpriseFeatures) || + build === "saas" || + env?.app.identityProviderMode === "org" || + (env?.app.identityProviderMode === undefined && build !== "oss") + ? [ + { + title: "sidebarIdentityProviders", + href: "/{orgId}/settings/idp", + icon: + } + ] + : []), + ...(!env?.flags.disableEnterpriseFeatures + ? [ + { + title: "sidebarApprovals", + href: "/{orgId}/settings/access/approvals", + icon: + } + ] + : []), + { + title: "sidebarShareableLinks", + href: "/{orgId}/settings/share-links", + icon: + } + ] + }, + { + heading: "sidebarOrganization", + items: [ + { + title: "sidebarLogsAndAnalytics", + icon: , + items: [ + { + title: "sidebarLogsAnalytics", + href: "/{orgId}/settings/logs/analytics", + icon: + }, + { + title: "sidebarLogsRequest", + href: "/{orgId}/settings/logs/request", + icon: ( + + ) + }, + ...(!env?.flags.disableEnterpriseFeatures + ? [ + { + title: "sidebarLogsAccess", + href: "/{orgId}/settings/logs/access", + icon: + }, + { + title: "sidebarLogsAction", + href: "/{orgId}/settings/logs/action", + icon: + }, + { + title: "sidebarLogsConnection", + href: "/{orgId}/settings/logs/connection", + icon: + }, + { + title: "sidebarLogsStreaming", + href: "/{orgId}/settings/logs/streaming", + icon: + } + ] + : []) + ] + }, + { + title: "sidebarManagement", + icon: , + items: [ + ...(!env?.flags.disableEnterpriseFeatures + ? [ + { + title: "sidebarAlerting", + href: "/{orgId}/settings/alerting", + icon: ( + + ) + }, + { + title: "sidebarProvisioning", + href: "/{orgId}/settings/provisioning", + icon: + } + ] + : []), + { + title: "sidebarBluePrints", + href: "/{orgId}/settings/blueprints", + icon: + }, + { + title: "sidebarApiKeys", + href: "/{orgId}/settings/api-keys", + icon: + }, + ...(!env?.flags.disableEnterpriseFeatures + ? [ + { + title: "labels", + href: "/{orgId}/settings/labels", + icon: + } + ] + : []) + ] + }, + ...(build === "saas" && options?.isPrimaryOrg + ? [ + { + title: "sidebarBillingAndLicenses", + icon: , + items: [ + { + title: "sidebarBilling", + href: "/{orgId}/settings/billing", + icon: ( + + ) + }, + { + title: "sidebarEnterpriseLicenses", + href: "/{orgId}/settings/license", + icon: ( + + ) + } + ] + } + ] + : []), + { + title: "sidebarSettings", + href: "/{orgId}/settings/general", + icon: + } + ] + } +]; diff --git a/src/components/Layout.tsx b/src/components/Layout.tsx index c0f814453..8dc95968f 100644 --- a/src/components/Layout.tsx +++ b/src/components/Layout.tsx @@ -13,6 +13,7 @@ interface LayoutProps { orgId?: string; orgs?: ListUserOrgsResponse["orgs"]; navItems?: SidebarNavSection[]; + commandNavItems?: SidebarNavSection[]; showSidebar?: boolean; showHeader?: boolean; showTopBar?: boolean; @@ -24,6 +25,7 @@ export async function Layout({ orgId, orgs, navItems = [], + commandNavItems = [], showSidebar = true, showHeader = true, showTopBar = true, @@ -38,7 +40,11 @@ export async function Layout({ (sidebarStateCookie !== "expanded" && defaultSidebarCollapsed); return ( - +
{/* Desktop Sidebar */} {showSidebar && ( diff --git a/src/components/SitesTable.tsx b/src/components/SitesTable.tsx index 8c3036c4a..98b69fa3e 100644 --- a/src/components/SitesTable.tsx +++ b/src/components/SitesTable.tsx @@ -113,13 +113,6 @@ export default function SitesTable({ const api = createApiClient(useEnvContext()); const t = useTranslations(); - // useEffect(() => { - // const interval = setInterval(() => { - // router.refresh(); - // }, 30_000); - // return () => clearInterval(interval); - // }, []); - const booleanSearchFilterSchema = z .enum(["true", "false"]) .optional() diff --git a/src/components/command-palette/CommandPalette.tsx b/src/components/command-palette/CommandPalette.tsx index 02556b7dd..b0536d8a9 100644 --- a/src/components/command-palette/CommandPalette.tsx +++ b/src/components/command-palette/CommandPalette.tsx @@ -14,7 +14,7 @@ import { Badge } from "@app/components/ui/badge"; import { ListUserOrgsResponse } from "@server/routers/org"; import { Loader2 } from "lucide-react"; import { useRouter } from "next/navigation"; -import { +import React, { createContext, useCallback, useContext, @@ -27,6 +27,7 @@ import { useCommandPaletteActions } from "./useCommandPaletteActions"; import { useCommandPaletteNavigation } from "./useCommandPaletteNavigation"; import { useCommandPaletteOrganizations } from "./useCommandPaletteOrganizations"; import { useCommandPaletteSearch } from "./useCommandPaletteSearch"; +import { useUserContext } from "@app/hooks/useUserContext"; type CommandPaletteProps = { orgId?: string; @@ -34,6 +35,12 @@ type CommandPaletteProps = { navItems: SidebarNavSection[]; }; +/** + * Plan for command bar: + * - the nav items should be custom items instead of all of the ones in the sidebar + * - actions should be triggered by using `>` (like in Github) + */ + export function CommandPalette({ orgId, orgs, navItems }: CommandPaletteProps) { const t = useTranslations(); const router = useRouter(); @@ -41,7 +48,7 @@ export function CommandPalette({ orgId, orgs, navItems }: CommandPaletteProps) { const [search, setSearch] = useState(""); const navigationGroups = useCommandPaletteNavigation(navItems); - const organizations = useCommandPaletteOrganizations(orgs); + // const organizations = useCommandPaletteOrganizations(orgs); const actions = useCommandPaletteActions(orgId, orgs); const { shouldSearch, sites, resources, users, machineClients, isLoading } = useCommandPaletteSearch({ @@ -69,45 +76,58 @@ export function CommandPalette({ orgId, orgs, navItems }: CommandPaletteProps) { [setOpen] ); - const hasEntityResults = - sites.length > 0 || - resources.length > 0 || - users.length > 0 || - machineClients.length > 0; + // const hasEntityResults = + // sites.length > 0 || + // resources.length > 0 || + // users.length > 0 || + // machineClients.length > 0; return ( - + {t("commandPaletteNoResults")} - {navigationGroups.map((group) => ( - - {group.items.map((item) => ( - - runCommand(() => router.push(item.href)) - } - > - {item.icon} - {item.title} - - ))} - + + + {navigationGroups.map((group, idx) => ( + + {idx > 0 && } + + {group.items.map((item) => ( + + runCommand(() => router.push(item.href)) + } + className="h-9" + > + {item.icon} + {item.title} + + ))} + + ))} - {organizations.length > 1 && ( + {/* {organizations.length > 1 && ( <> - )} + )} */} - {shouldSearch && orgId && ( + {/* {shouldSearch && orgId && ( <> {isLoading && !hasEntityResults ? ( @@ -243,12 +263,15 @@ export function CommandPalette({ orgId, orgs, navItems }: CommandPaletteProps) { )} - )} + )} */} - {actions.length > 0 && ( + {/* {actions.length > 0 && ( <> - + {actions.map((action) => ( - )} + )} */} );