import { useState } from "react"
import { Input } from "@/components/ui/input"
import { Button } from "@/components/ui/button"
import { Badge } from "@/components/ui/badge"
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select"
import { AdvancedFilters, MemoryCategory, ContentType } from "@/types"
import { Search, Filter, X, ChevronDown, ChevronUp, Plus, Minus } from "lucide-react"
import { FilterPresets } from "@/components/FilterPresets"
interface AdvancedSearchProps {
query: string
filters: AdvancedFilters
onQueryChange: (query: string) => void
onFiltersChange: (filters: AdvancedFilters) => void
availableTags: string[]
availableProjects: string[]
className?: string
}
const categories: { value: MemoryCategory; label: string }[] = [
{ value: 'personal', label: 'Personal' },
{ value: 'work', label: 'Work' },
{ value: 'code', label: 'Code' },
{ value: 'research', label: 'Research' },
{ value: 'conversations', label: 'Conversations' },
{ value: 'preferences', label: 'Preferences' }
]
const contentTypes: { value: ContentType; label: string }[] = [
{ value: 'text', label: 'Text' },
{ value: 'code', label: 'Code' },
{ value: 'structured', label: 'Structured' }
]
export function AdvancedSearch({
query,
filters,
onQueryChange,
onFiltersChange,
availableTags,
availableProjects,
className = ""
}: AdvancedSearchProps) {
const [showFilters, setShowFilters] = useState(false)
const [tagInput, setTagInput] = useState("")
const [showLogicalOperators, setShowLogicalOperators] = useState(false)
const hasActiveFilters = Boolean(
filters.tags?.length ||
filters.project ||
filters.category ||
filters.contentType ||
filters.dateRange ||
filters.AND?.length ||
filters.OR?.length ||
filters.NOT
)
const handleAddTag = (tag: string) => {
if (!tag.trim() || filters.tags?.includes(tag)) return
onFiltersChange({
...filters,
tags: [...(filters.tags || []), tag]
})
setTagInput("")
}
const handleRemoveTag = (tagToRemove: string) => {
onFiltersChange({
...filters,
tags: filters.tags?.filter(tag => tag !== tagToRemove) || []
})
}
const handleClearFilters = () => {
onFiltersChange({})
}
const handleDateRangeChange = (field: 'start' | 'end', value: string) => {
onFiltersChange({
...filters,
dateRange: {
...filters.dateRange,
[field]: value
}
})
}
const addLogicalGroup = (operator: 'AND' | 'OR') => {
const newGroup: AdvancedFilters = {}
onFiltersChange({
...filters,
[operator]: [...(filters[operator] || []), newGroup]
})
}
const updateLogicalGroup = (operator: 'AND' | 'OR', index: number, groupFilters: AdvancedFilters) => {
const groups = [...(filters[operator] || [])]
groups[index] = groupFilters
onFiltersChange({
...filters,
[operator]: groups
})
}
const removeLogicalGroup = (operator: 'AND' | 'OR', index: number) => {
const groups = [...(filters[operator] || [])]
groups.splice(index, 1)
onFiltersChange({
...filters,
[operator]: groups.length > 0 ? groups : undefined
})
}
const setNotFilter = (notFilters?: AdvancedFilters) => {
onFiltersChange({
...filters,
NOT: notFilters
})
}
return (
{/* Main Search Bar with Presets */}
onQueryChange(e.target.value)}
placeholder="Search memories..."
className="pl-10 pr-4"
/>
{/* Filter Presets Component */}
{
onFiltersChange({ ...filters, ...presetFilters });
}}
onFiltersChange={onFiltersChange}
className="flex-shrink-0"
/>
{hasActiveFilters && (
)}
{/* Active Filters Display */}
{hasActiveFilters && (
{filters.tags?.map((tag) => (
#{tag}
handleRemoveTag(tag)}
/>
))}
{filters.project && (
Project: {filters.project}
onFiltersChange({ ...filters, project: undefined })}
/>
)}
{filters.category && (
Category: {filters.category}
onFiltersChange({ ...filters, category: undefined })}
/>
)}
{filters.contentType && (
Type: {filters.contentType}
onFiltersChange({ ...filters, contentType: undefined })}
/>
)}
{filters.dateRange && (
Date Range
onFiltersChange({ ...filters, dateRange: undefined })}
/>
)}
{filters.AND?.map((_, index) => (
AND Group {index + 1}
removeLogicalGroup('AND', index)}
/>
))}
{filters.OR?.map((_, index) => (
OR Group {index + 1}
removeLogicalGroup('OR', index)}
/>
))}
{filters.NOT && (
NOT Filter
setNotFilter(undefined)}
/>
)}
)}
{/* Expanded Filters Panel */}
{showFilters && (
{/* Category Filter */}
{/* Project Filter */}
{/* Content Type Filter */}
{/* Tags Filter */}
setTagInput(e.target.value)}
placeholder="Add tag..."
onKeyPress={(e) => {
if (e.key === 'Enter') {
e.preventDefault()
handleAddTag(tagInput)
}
}}
className="flex-1"
/>
{/* Available Tags */}
{availableTags.length > 0 && (
Quick add:
{availableTags.slice(0, 10).map((tag) => (
handleAddTag(tag)}
>
{tag}
))}
)}
{/* Date Range Filter */}
{/* Logical Operators Section */}
{showLogicalOperators && (
{/* AND Groups */}
{filters.AND?.map((group, index) => (
AND Group {index + 1}
updateLogicalGroup('AND', index, groupFilters)}
availableTags={availableTags}
availableProjects={availableProjects}
/>
))}
{/* OR Groups */}
{filters.OR?.map((group, index) => (
OR Group {index + 1}
updateLogicalGroup('OR', index, groupFilters)}
availableTags={availableTags}
availableProjects={availableProjects}
/>
))}
{/* NOT Filter */}
{filters.NOT && (
)}
)}
)}
)
}
// Logical Group Editor Component
interface LogicalGroupEditorProps {
filters: AdvancedFilters
onFiltersChange: (filters: AdvancedFilters) => void
availableTags: string[]
availableProjects: string[]
}
function LogicalGroupEditor({
filters,
onFiltersChange,
availableTags,
availableProjects
}: LogicalGroupEditorProps) {
const [tagInput, setTagInput] = useState("")
const handleAddTag = (tag: string) => {
if (!tag.trim() || filters.tags?.includes(tag)) return
onFiltersChange({
...filters,
tags: [...(filters.tags || []), tag]
})
setTagInput("")
}
const handleRemoveTag = (tagToRemove: string) => {
onFiltersChange({
...filters,
tags: filters.tags?.filter(tag => tag !== tagToRemove) || []
})
}
return (
{/* Mini filters for logical groups */}
{/* Category */}
{/* Project */}
{/* Tags */}
setTagInput(e.target.value)}
placeholder="Add tag..."
onKeyPress={(e) => {
if (e.key === 'Enter') {
e.preventDefault()
handleAddTag(tagInput)
}
}}
className="flex-1 h-8 text-xs"
/>
{/* Current Tags */}
{filters.tags && filters.tags.length > 0 && (
{filters.tags.map((tag) => (
#{tag}
handleRemoveTag(tag)}
/>
))}
)}
{/* Text Search */}
onFiltersChange({ ...filters, text: e.target.value })}
placeholder="Search text..."
className="h-8 text-xs"
/>
)
}