"use client"; import { useState, useEffect } from "react"; import { X, Edit3, Save, Settings, 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 { MultiSelect } from "@/components/ui/multi-select"; import { Slider } from "@/components/ui/slider"; import { useKnowledgeFilter } from "@/contexts/knowledge-filter-context"; import { useDeleteFilter } from "@/app/api/mutations/useDeleteFilter"; interface FacetBucket { key: string; count: number; } interface AvailableFacets { data_sources: FacetBucket[]; document_types: FacetBucket[]; owners: FacetBucket[]; connector_types: FacetBucket[]; } export function KnowledgeFilterPanel() { const { selectedFilter, parsedFilterData, setSelectedFilter, isPanelOpen, closePanelOnly, } = useKnowledgeFilter(); const deleteFilterMutation = useDeleteFilter(); // 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 connector_types: ["*"] as string[], // Default to wildcard }); const [resultLimit, setResultLimit] = useState(10); const [scoreThreshold, setScoreThreshold] = useState(0); // Available facets (loaded from API) const [availableFacets, setAvailableFacets] = useState({ data_sources: [], document_types: [], owners: [], connector_types: [], }); // 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; // Use the exact selections from the saved filter // Empty arrays mean "none selected" not "all selected" const processedFilters = { data_sources: filters.data_sources, document_types: filters.document_types, owners: filters.owners, connector_types: filters.connector_types || ["*"], }; 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 || [], connector_types: result.aggregations.connector_types?.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 selectAllFilters = () => { // Use wildcards instead of listing all specific items setSelectedFilters({ data_sources: ["*"], document_types: ["*"], owners: ["*"], connector_types: ["*"], }); }; const clearAllFilters = () => { setSelectedFilters({ data_sources: [], document_types: [], owners: [], connector_types: [], }); }; 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 handleFilterChange = ( facetType: keyof typeof selectedFilters, newValues: string[] ) => { setSelectedFilters((prev) => ({ ...prev, [facetType]: newValues, })); }; const handleDeleteFilter = async () => { const result = await deleteFilterMutation.mutateAsync({ id: selectedFilter.id, }); if (result.success) { setSelectedFilter(null); closePanelOnly(); } }; return (
Knowledge Filter
Configure your knowledge filter settings
{/* Filter Name and Description */}
{isEditingMeta ? (
setEditingName(e.target.value)} placeholder="Filter name" />