'use client' import { useState, useRef, useEffect } from 'react' import { DIMENSION_LABELS, OPERATORS, OPERATOR_LABELS, type DimensionFilter } from '@/lib/filters' export interface FilterSuggestion { value: string label: string count?: number } export interface FilterSuggestions { [dimension: string]: FilterSuggestion[] } interface AddFilterDropdownProps { onAdd: (filter: DimensionFilter) => void suggestions?: FilterSuggestions } // Which dimensions show as always-visible chips vs hidden in "More" const PRIMARY_DIMS = ['page', 'referrer', 'country', 'browser', 'os', 'device'] const SECONDARY_DIMS = ['region', 'city', 'utm_source', 'utm_medium', 'utm_campaign'] function DimensionPopover({ dimension, suggestions, onApply, onClose, }: { dimension: string suggestions: FilterSuggestion[] onApply: (filter: DimensionFilter) => void onClose: () => void }) { const [operator, setOperator] = useState('is') const [search, setSearch] = useState('') const ref = useRef(null) const inputRef = useRef(null) useEffect(() => { inputRef.current?.focus() }, []) useEffect(() => { function handleClick(e: MouseEvent) { if (ref.current && !ref.current.contains(e.target as Node)) { onClose() } } function handleEsc(e: KeyboardEvent) { if (e.key === 'Escape') onClose() } document.addEventListener('mousedown', handleClick) document.addEventListener('keydown', handleEsc) return () => { document.removeEventListener('mousedown', handleClick) document.removeEventListener('keydown', handleEsc) } }, [onClose]) const filtered = suggestions.filter(s => s.label.toLowerCase().includes(search.toLowerCase()) || s.value.toLowerCase().includes(search.toLowerCase()) ) function handleSelectValue(value: string) { onApply({ dimension, operator, values: [value] }) onClose() } function handleSubmitCustom() { if (!search.trim()) return onApply({ dimension, operator, values: [search.trim()] }) onClose() } return (
{/* Operator pills */}
{OPERATORS.map(op => ( ))}
{/* Search input */}
setSearch(e.target.value)} onKeyDown={e => { if (e.key === 'Enter') { if (filtered.length === 1) { handleSelectValue(filtered[0].value) } else { handleSubmitCustom() } } }} placeholder={`Search or type ${DIMENSION_LABELS[dimension]?.toLowerCase()}...`} className="w-full px-3 py-2 text-sm bg-neutral-50 dark:bg-neutral-800 border border-neutral-200 dark:border-neutral-700 rounded-lg text-neutral-900 dark:text-white placeholder-neutral-400 focus:outline-none focus:ring-2 focus:ring-brand-orange/40 focus:border-brand-orange transition-colors" />
{/* Suggestions list */} {filtered.length > 0 && (
{filtered.map(s => ( ))}
)} {/* Custom value apply when no matches */} {search.trim() && filtered.length === 0 && (
)}
) } export default function AddFilterDropdown({ onAdd, suggestions = {} }: AddFilterDropdownProps) { const [openDim, setOpenDim] = useState(null) const [showMore, setShowMore] = useState(false) const moreRef = useRef(null) useEffect(() => { if (!showMore) return function handleClick(e: MouseEvent) { if (moreRef.current && !moreRef.current.contains(e.target as Node)) { setShowMore(false) } } document.addEventListener('mousedown', handleClick) return () => document.removeEventListener('mousedown', handleClick) }, [showMore]) function renderChip(dim: string) { const isOpen = openDim === dim return (
{isOpen && ( setOpenDim(null)} /> )}
) } return (
{PRIMARY_DIMS.map(renderChip)} {/* More dropdown for secondary dimensions */}
{showMore && (
{SECONDARY_DIMS.map(dim => ( ))}
)} {/* Render popover for secondary dims inline here */} {openDim && SECONDARY_DIMS.includes(openDim) && ( setOpenDim(null)} /> )}
) }