'use client'; import { useCallback, useEffect, useState } from 'react'; import { useRouter } from 'next/navigation'; import { CommandDialog, CommandEmpty, CommandGroup, CommandInput, CommandItem, CommandList, } from '@/components/ui/command'; import { Kbd, KbdGroup } from '@/components/ui/kbd'; import { type SearchItem, searchItems } from '@/lib/utils/search-data'; import { FileText, Home, Settings, Shield } from 'lucide-react'; import { useUser } from '@/contexts/user-context'; import { useTranslations } from 'next-intl'; interface SearchDialogProps { open: boolean; onOpenChange: (open: boolean) => void; } const categoryIcons = { page: Home, feature: FileText, setting: Settings, admin: Shield, }; const categoryLabels = { page: 'categoryPage', feature: 'categoryFeature', setting: 'categorySetting', admin: 'categoryAdmin', }; const getTips = (metaKey: string, t: (key: string) => string) => [ <> {t('tipPrefix')}{' '} / {t('tipSlash')} , <> {t('tipPrefix')}{' '} ↑ ↓ {t('tipArrow')} , <> {t('tipPrefix')}{' '} {metaKey} + ↵ {' '} {t('tipNewTab')} , <> {t('tipDidYouKnow')} , ]; export function SearchDialog({ open, onOpenChange }: SearchDialogProps) { const router = useRouter(); const { user } = useUser(); const t = useTranslations('layout.search'); const tLayout = useTranslations('layout'); const [search, setSearch] = useState(''); const [currentTip, setCurrentTip] = useState(null); const [results, setResults] = useState([]); const [metaKey, setMetaKey] = useState('⌘'); const [isCtrlPressed, setIsCtrlPressed] = useState(false); useEffect(() => { if ( typeof navigator !== 'undefined' && !navigator.userAgent?.includes('Mac') ) { setMetaKey('Ctrl'); } }, []); useEffect(() => { const handleKeyDown = (e: KeyboardEvent) => { if (e.key === 'Control' || e.key === 'Meta') { setIsCtrlPressed(true); } }; const handleKeyUp = (e: KeyboardEvent) => { if (e.key === 'Control' || e.key === 'Meta') { setIsCtrlPressed(false); } }; window.addEventListener('keydown', handleKeyDown); window.addEventListener('keyup', handleKeyUp); return () => { window.removeEventListener('keydown', handleKeyDown); window.removeEventListener('keyup', handleKeyUp); }; }, []); useEffect(() => { if (open) { const tips = getTips(metaKey, t); const randomTip = tips[Math.floor(Math.random() * tips.length)]; setCurrentTip(randomTip); } }, [open, metaKey, t]); useEffect(() => { const items = searchItems(search, user?.is_admin, (key) => tLayout(key)); setResults(items); }, [search, user?.is_admin, tLayout]); const handleSelect = useCallback( (item: SearchItem, openInNewTab = false) => { onOpenChange(false); if (openInNewTab) { window.open(item.url, '_blank'); } else { router.push(item.url); } setSearch(''); }, [onOpenChange, router], ); useEffect(() => { const down = (e: KeyboardEvent) => { if (e.key === 'k' && (e.metaKey || e.ctrlKey)) { e.preventDefault(); onOpenChange(!open); } if (e.key === '/' && !e.ctrlKey && !e.metaKey && !e.altKey) { const target = e.target as HTMLElement; const isEditing = target.tagName === 'INPUT' || target.tagName === 'TEXTAREA' || target.tagName === 'SELECT' || target.isContentEditable || target.closest('[contenteditable="true"]'); if (!isEditing) { e.preventDefault(); onOpenChange(true); } } }; document.addEventListener('keydown', down); return () => document.removeEventListener('keydown', down); }, [open, onOpenChange]); // Group results by category const groupedResults = results.reduce( (acc, item) => { if (!acc[item.category]) { acc[item.category] = []; } acc[item.category].push(item); return acc; }, {} as Record, ); return ( {t('noResults')} {Object.entries(groupedResults).map(([category, items]) => { const Icon = categoryIcons[category as keyof typeof categoryIcons]; return ( {items.map((item) => ( handleSelect(item, isCtrlPressed)} className='flex items-center justify-between' >
{item.matchRange ? ( <> {item.title.substring(0, item.matchRange[0])} {item.title.substring( item.matchRange[0], item.matchRange[1] + 1, )} {item.title.substring(item.matchRange[1] + 1)} ) : ( item.title )} {item.description}
{t( categoryLabels[ item.category as keyof typeof categoryLabels ], )}
))}
); })}
{isCtrlPressed && {metaKey}} ↵ {isCtrlPressed ? t('openInNewTab') : t('open')}
Esc {t('closeSearch')}
{currentTip}
); }