"use client" import { useState, useEffect } from 'react' import { X, Edit3, Save, Settings, ChevronDown, ChevronUp, RefreshCw } from 'lucide-react' import { Button } from '@/components/ui/button' import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card' import { Input } from '@/components/ui/input' import { Label } from '@/components/ui/label' import { Textarea } from '@/components/ui/textarea' import { Checkbox } from '@/components/ui/checkbox' import { Collapsible, CollapsibleContent, CollapsibleTrigger } from '@/components/ui/collapsible' import { useKnowledgeFilter } from '@/contexts/knowledge-filter-context' interface FacetBucket { key: string count: number } interface AvailableFacets { data_sources: FacetBucket[] document_types: FacetBucket[] owners: FacetBucket[] } export function KnowledgeFilterPanel() { const { selectedFilter, parsedFilterData, setSelectedFilter, isPanelOpen, closePanelOnly } = useKnowledgeFilter() // Edit mode states const [isEditingMeta, setIsEditingMeta] = useState(false) const [editingName, setEditingName] = useState('') const [editingDescription, setEditingDescription] = useState('') const [isSaving, setIsSaving] = useState(false) // Filter configuration states (mirror search page exactly) const [query, setQuery] = useState('') const [selectedFilters, setSelectedFilters] = useState({ data_sources: ["*"] as string[], // Default to wildcard document_types: ["*"] as string[], // Default to wildcard owners: ["*"] as string[] // Default to wildcard }) const [resultLimit, setResultLimit] = useState(10) const [scoreThreshold, setScoreThreshold] = useState(0) const [openSections, setOpenSections] = useState({ data_sources: true, document_types: true, owners: true }) // Available facets (loaded from API) const [availableFacets, setAvailableFacets] = useState({ data_sources: [], document_types: [], owners: [] }) // Load current filter data into controls useEffect(() => { if (selectedFilter && parsedFilterData) { setQuery(parsedFilterData.query || '') // Set the actual filter selections from the saved knowledge filter const filters = parsedFilterData.filters // If arrays are empty, default to wildcard (match everything) // Otherwise use the specific selections from the saved filter const processedFilters = { data_sources: filters.data_sources.length === 0 ? ["*"] : filters.data_sources, document_types: filters.document_types.length === 0 ? ["*"] : filters.document_types, owners: filters.owners.length === 0 ? ["*"] : filters.owners } console.log("[DEBUG] Loading filter selections:", processedFilters) setSelectedFilters(processedFilters) setResultLimit(parsedFilterData.limit || 10) setScoreThreshold(parsedFilterData.scoreThreshold || 0) setEditingName(selectedFilter.name) setEditingDescription(selectedFilter.description || '') } }, [selectedFilter, parsedFilterData]) // Load available facets from API useEffect(() => { if (isPanelOpen) { loadAvailableFacets() } }, [isPanelOpen]) const loadAvailableFacets = async () => { console.log("[DEBUG] Loading available facets...") try { // Do a search to get facets (similar to search page) const response = await fetch("/api/search", { method: "POST", headers: { "Content-Type": "application/json", }, body: JSON.stringify({ query: "*", // Use wildcard like search page to get all documents/facets limit: 1, scoreThreshold: 0 // Omit filters entirely to get all available facets }), }) const result = await response.json() console.log("[DEBUG] Search API response:", result) if (response.ok && result.aggregations) { const facets = { data_sources: result.aggregations.data_sources?.buckets || [], document_types: result.aggregations.document_types?.buckets || [], owners: result.aggregations.owners?.buckets || [] } console.log("[DEBUG] Setting facets:", facets) setAvailableFacets(facets) } else { console.log("[DEBUG] No aggregations in response or response not ok") } } catch (error) { console.error("Failed to load available facets:", error) } } // Don't render if panel is closed or no filter selected if (!isPanelOpen || !selectedFilter || !parsedFilterData) return null const toggleSection = (section: keyof typeof openSections) => { setOpenSections(prev => ({ ...prev, [section]: !prev[section] })) } const selectAllFilters = () => { // Use wildcards instead of listing all specific items setSelectedFilters({ data_sources: ["*"], document_types: ["*"], owners: ["*"] }) } const clearAllFilters = () => { setSelectedFilters({ data_sources: [], document_types: [], owners: [] }) } const handleEditMeta = () => { setIsEditingMeta(true) } const handleCancelEdit = () => { setIsEditingMeta(false) setEditingName(selectedFilter.name) setEditingDescription(selectedFilter.description || '') } const handleSaveMeta = async () => { if (!editingName.trim()) return setIsSaving(true) try { const response = await fetch(`/api/knowledge-filter/${selectedFilter.id}`, { method: 'PUT', headers: { 'Content-Type': 'application/json', }, body: JSON.stringify({ name: editingName.trim(), description: editingDescription.trim(), }), }) const result = await response.json() if (response.ok && result.success) { const updatedFilter = { ...selectedFilter, name: editingName.trim(), description: editingDescription.trim(), updated_at: new Date().toISOString(), } setSelectedFilter(updatedFilter) setIsEditingMeta(false) } } catch (error) { console.error('Error updating filter:', error) } finally { setIsSaving(false) } } const handleSaveConfiguration = async () => { const filterData = { query, filters: selectedFilters, limit: resultLimit, scoreThreshold } setIsSaving(true) try { const response = await fetch(`/api/knowledge-filter/${selectedFilter.id}`, { method: 'PUT', headers: { 'Content-Type': 'application/json', }, body: JSON.stringify({ queryData: JSON.stringify(filterData) }), }) const result = await response.json() if (response.ok && result.success) { // Update the filter in context const updatedFilter = { ...selectedFilter, query_data: JSON.stringify(filterData), updated_at: new Date().toISOString(), } setSelectedFilter(updatedFilter) } } catch (error) { console.error('Error updating filter configuration:', error) } finally { setIsSaving(false) } } const formatDate = (dateString: string) => { return new Date(dateString).toLocaleDateString('en-US', { year: 'numeric', month: 'short', day: 'numeric', hour: '2-digit', minute: '2-digit' }) } const FacetSection = ({ title, buckets, facetType, isOpen, onToggle }: { title: string buckets: FacetBucket[] facetType: keyof typeof selectedFilters isOpen: boolean onToggle: () => void }) => { if (!buckets || buckets.length === 0) return null const isAllSelected = selectedFilters[facetType].includes("*") // Wildcard const handleAllToggle = (checked: boolean) => { if (checked) { // Select "All" - clear specific selections and add wildcard setSelectedFilters(prev => ({ ...prev, [facetType]: ["*"] })) } else { // Unselect "All" - remove wildcard but keep any specific selections setSelectedFilters(prev => ({ ...prev, [facetType]: prev[facetType].filter(item => item !== "*") })) } } const handleSpecificToggle = (value: string, checked: boolean) => { setSelectedFilters(prev => { let newValues = [...prev[facetType]] // Remove wildcard if selecting specific items newValues = newValues.filter(item => item !== "*") if (checked) { newValues.push(value) } else { newValues = newValues.filter(item => item !== value) } return { ...prev, [facetType]: newValues } }) } return ( {/* "All" wildcard option */}
{/* Individual items - disabled if "All" is selected */} {buckets.map((bucket, index) => { const isSelected = selectedFilters[facetType].includes(bucket.key) const isDisabled = isAllSelected return (
handleSpecificToggle(bucket.key, checked as boolean) } />
) })}
) } return (
Knowledge Filter
Configure your knowledge filter settings
{/* Filter Name and Description */}
{isEditingMeta ? (
setEditingName(e.target.value)} placeholder="Filter name" />