"use client"; import { CommandDialog, CommandEmpty, CommandGroup, CommandInput, CommandItem, CommandList, CommandSeparator } from "@app/components/ui/command"; import type { SidebarNavSection } from "@app/app/navigation"; import { Badge } from "@app/components/ui/badge"; import { ListUserOrgsResponse } from "@server/routers/org"; import { Loader2 } from "lucide-react"; import { useRouter } from "next/navigation"; import React, { createContext, useCallback, useContext, useEffect, useMemo, useState } from "react"; import { useTranslations } from "next-intl"; 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; orgs?: ListUserOrgsResponse["orgs"]; 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(); const { open, setOpen } = useCommandPalette(); const [search, setSearch] = useState(""); const navigationGroups = useCommandPaletteNavigation(navItems); // const organizations = useCommandPaletteOrganizations(orgs); const actions = useCommandPaletteActions(orgId, orgs); const { shouldSearch, sites, resources, users, machineClients, isLoading } = useCommandPaletteSearch({ orgId, query: search, enabled: open }); const handleOpenChange = useCallback( (nextOpen: boolean) => { setOpen(nextOpen); if (!nextOpen) { setSearch(""); } }, [setOpen] ); const runCommand = useCallback( (command: () => void) => { setOpen(false); setSearch(""); command(); }, [setOpen] ); // const hasEntityResults = // sites.length > 0 || // resources.length > 0 || // users.length > 0 || // machineClients.length > 0; return ( {t("commandPaletteNoResults")} {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.map((org) => ( runCommand(() => router.push(org.href)) } > {org.name} {org.orgId} {org.isPrimaryOrg && ( {t("primary")} )} ))} )} */} {/* {shouldSearch && orgId && ( <> {isLoading && !hasEntityResults ? (
{t("commandPaletteSearching")}
) : ( <> {sites.length > 0 && ( {sites.map((site) => ( runCommand(() => router.push(site.href) ) } > {site.name} ))} )} {resources.length > 0 && ( {resources.map((resource) => ( runCommand(() => router.push( resource.href ) ) } > {resource.name} ))} )} {users.length > 0 && ( {users.map((user) => ( runCommand(() => router.push(user.href) ) } >
{user.name} {user.email}
))}
)} {machineClients.length > 0 && ( {machineClients.map((client) => ( runCommand(() => router.push(client.href) ) } > {client.name} ))} )} )} )} */} {/* {actions.length > 0 && ( <> {actions.map((action) => ( runCommand(() => { if (action.onSelect) { action.onSelect(); } else if (action.href) { router.push(action.href); } }) } > {action.icon} {action.label} ))} )} */}
); } /*******************************/ /* COMMAND PALETTE CONTEXT */ /*******************************/ export type CommandPaletteContextValue = { open: boolean; setOpen: (open: boolean) => void; toggle: () => void; }; const CommandPaletteContext = createContext( null ); export function useCommandPalette() { const context = useContext(CommandPaletteContext); if (!context) { throw new Error( "useCommandPalette must be used within CommandPaletteProvider" ); } return context; } //*******************************/ /* COMMAND PALETTE PROVIDER */ /*******************************/ type CommandPaletteProviderProps = { children: React.ReactNode; orgId?: string; orgs?: ListUserOrgsResponse["orgs"]; navItems: SidebarNavSection[]; }; function isEditableTarget(target: EventTarget | null) { if (!(target instanceof HTMLElement)) return false; if (target.isContentEditable) return true; const tagName = target.tagName; return ( tagName === "INPUT" || tagName === "TEXTAREA" || tagName === "SELECT" ); } export function CommandPaletteProvider({ children, orgId, orgs, navItems }: CommandPaletteProviderProps) { const [open, setOpen] = useState(false); const toggle = useCallback(() => { setOpen((current) => !current); }, []); const contextValue = useMemo( () => ({ open, setOpen, toggle }), [open, toggle] ); useEffect(() => { function onKeyDown(event: KeyboardEvent) { if ( event.key.toLowerCase() !== "k" || !(event.metaKey || event.ctrlKey) ) { return; } if (!open && isEditableTarget(event.target)) { return; } event.preventDefault(); toggle(); } document.addEventListener("keydown", onKeyDown); return () => document.removeEventListener("keydown", onKeyDown); }, [open, toggle]); return ( {children} ); }