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 */}
handleDateRangeChange('start', e.target.value)} placeholder="Start date" /> handleDateRangeChange('end', e.target.value)} placeholder="End date" />
{/* 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" />
) }