diff --git a/app/sites/[id]/page.tsx b/app/sites/[id]/page.tsx
index d5b7a94..f9b0fb3 100644
--- a/app/sites/[id]/page.tsx
+++ b/app/sites/[id]/page.tsx
@@ -460,7 +460,7 @@ export default function SiteDashboardPage() {
{/* Dimension Filters */}
-
+
diff --git a/components/dashboard/AddFilterDropdown.tsx b/components/dashboard/AddFilterDropdown.tsx
index 6599be3..e0068ce 100644
--- a/components/dashboard/AddFilterDropdown.tsx
+++ b/components/dashboard/AddFilterDropdown.tsx
@@ -18,38 +18,26 @@ interface AddFilterDropdownProps {
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']
+const ALL_DIMS = ['page', 'referrer', 'country', 'region', 'city', 'browser', 'os', 'device', 'utm_source', 'utm_medium', 'utm_campaign']
-function DimensionPopover({
- dimension,
- suggestions,
- onApply,
- onClose,
-}: {
- dimension: string
- suggestions: FilterSuggestion[]
- onApply: (filter: DimensionFilter) => void
- onClose: () => void
-}) {
+export default function AddFilterDropdown({ onAdd, suggestions = {} }: AddFilterDropdownProps) {
+ const [isOpen, setIsOpen] = useState(false)
+ const [selectedDim, setSelectedDim] = useState
(null)
const [operator, setOperator] = useState('is')
const [search, setSearch] = useState('')
const ref = useRef(null)
const inputRef = useRef(null)
+ // Close on outside click or Escape
useEffect(() => {
- inputRef.current?.focus()
- }, [])
-
- useEffect(() => {
+ if (!isOpen) return
function handleClick(e: MouseEvent) {
if (ref.current && !ref.current.contains(e.target as Node)) {
- onClose()
+ handleClose()
}
}
function handleEsc(e: KeyboardEvent) {
- if (e.key === 'Escape') onClose()
+ if (e.key === 'Escape') handleClose()
}
document.addEventListener('mousedown', handleClick)
document.addEventListener('keydown', handleEsc)
@@ -57,197 +45,163 @@ function DimensionPopover({
document.removeEventListener('mousedown', handleClick)
document.removeEventListener('keydown', handleEsc)
}
- }, [onClose])
+ }, [isOpen])
- const filtered = suggestions.filter(s =>
+ // Focus search input when a dimension is selected
+ useEffect(() => {
+ if (selectedDim) inputRef.current?.focus()
+ }, [selectedDim])
+
+ function handleClose() {
+ setIsOpen(false)
+ setSelectedDim(null)
+ setOperator('is')
+ setSearch('')
+ }
+
+ function handleSelectValue(value: string) {
+ onAdd({ dimension: selectedDim!, operator, values: [value] })
+ handleClose()
+ }
+
+ function handleSubmitCustom() {
+ if (!search.trim() || !selectedDim) return
+ onAdd({ dimension: selectedDim, operator, values: [search.trim()] })
+ handleClose()
+ }
+
+ const dimSuggestions = selectedDim ? (suggestions[selectedDim] || []) : []
+ const filtered = dimSuggestions.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 => (
-
)
-}
-
-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 (
-
-
{
- setOpenDim(isOpen ? null : dim)
- setShowMore(false)
- }}
- className={`inline-flex items-center gap-1.5 px-3 py-1.5 text-xs font-medium rounded-lg transition-all cursor-pointer ${
- isOpen
- ? 'bg-brand-orange/10 text-brand-orange border border-brand-orange/30'
- : 'bg-neutral-100 dark:bg-neutral-800 text-neutral-600 dark:text-neutral-400 hover:bg-neutral-200 dark:hover:bg-neutral-700 hover:text-neutral-900 dark:hover:text-white border border-transparent'
- }`}
- >
- {DIMENSION_LABELS[dim]}
-
-
- {isOpen && (
-
setOpenDim(null)}
- />
- )}
-
- )
- }
-
- return (
-
- {PRIMARY_DIMS.map(renderChip)}
-
- {/* More dropdown for secondary dimensions */}
-
-
{
- setShowMore(!showMore)
- setOpenDim(null)
- }}
- className={`inline-flex items-center gap-1.5 px-3 py-1.5 text-xs font-medium rounded-lg transition-all cursor-pointer ${
- showMore || SECONDARY_DIMS.includes(openDim || '')
- ? 'bg-brand-orange/10 text-brand-orange border border-brand-orange/30'
- : 'bg-neutral-100 dark:bg-neutral-800 text-neutral-600 dark:text-neutral-400 hover:bg-neutral-200 dark:hover:bg-neutral-700 hover:text-neutral-900 dark:hover:text-white border border-transparent'
- }`}
- >
- More
-
-
- {showMore && (
-
- {SECONDARY_DIMS.map(dim => (
- {
- setShowMore(false)
- setOpenDim(dim)
- }}
- className="w-full text-left px-4 py-2 text-sm text-neutral-700 dark:text-neutral-300 hover:bg-neutral-50 dark:hover:bg-neutral-800 transition-colors cursor-pointer"
- >
- {DIMENSION_LABELS[dim]}
-
- ))}
-
- )}
- {/* Render popover for secondary dims inline here */}
- {openDim && SECONDARY_DIMS.includes(openDim) && (
-
setOpenDim(null)}
- />
- )}
-
-
- )
-}
+}
\ No newline at end of file