import { useMemo, useState } from 'react'; import { Spotlight, type SpotlightActionData } from '@mantine/spotlight'; import { useDebouncedValue } from '@mantine/hooks'; import { IconFileText, IconSearch } from '@tabler/icons-react'; import { useTranslation } from 'react-i18next'; import { useNavigate } from 'react-router-dom'; import { flattenNav, type NavSection } from '@ema-platform/ui'; import { useGetAllApplicationsQuery } from '@ema-platform/api'; /** Long enough that typing a company name does not fire a request per keystroke. */ const SEARCH_DEBOUNCE_MS = 250; /** Below this, a server search matches too much to be useful. */ const MIN_SEARCH_LENGTH = 2; interface CommandPaletteProps { /** Already permission-filtered, so the palette cannot reach a hidden route. */ sections: NavSection[]; } /** * ⌘K search over every destination and recent application. * * With twenty-plus destinations plus five licence types, hunting through * nested menus is the slow path. This makes nesting cheap: anything reachable * by clicking is reachable by typing, including applications by number, * company or TIN. */ export function CommandPalette({ sections }: CommandPaletteProps) { const { t } = useTranslation(); const navigate = useNavigate(); const [query, setQuery] = useState(''); const [debounced] = useDebouncedValue(query, SEARCH_DEBOUNCE_MS); const term = debounced.trim(); // Only hits the API once the palette is open and the query is meaningful. const { data: applications } = useGetAllApplicationsQuery( { search: term, take: 8 }, { skip: term.length < MIN_SEARCH_LENGTH }, ); const destinationActions = useMemo( () => flattenNav(sections) .filter((item) => item.to && !item.soon) .map((item) => ({ id: item.to as string, label: t(item.label), description: item.to, leftSection: , onClick: () => navigate(item.to as string), })), [sections, navigate, t], ); const applicationActions = useMemo( () => (applications?.items ?? []).map((app) => ({ id: `application-${app.id}`, label: app.companyName ?? app.applicationNumber, description: [app.applicationNumber, app.tinNumber] .filter(Boolean) .join(' · '), leftSection: , onClick: () => navigate(`/licence-review/${app.id}`), })), [applications, navigate], ); return ( , placeholder: t( 'nav.commandPlaceholder', 'Search screens, applications, companies, TIN…', ), }} /> ); }