diff --git a/frontend/components/knowledge-filter-list.tsx b/frontend/components/knowledge-filter-list.tsx
index 5815391a..134f09f1 100644
--- a/frontend/components/knowledge-filter-list.tsx
+++ b/frontend/components/knowledge-filter-list.tsx
@@ -1,31 +1,22 @@
"use client";
-import { useState, useEffect, useRef } from "react";
+import { useState } from "react";
import { Button } from "@/components/ui/button";
import { Input } from "@/components/ui/input";
-import { Card, CardContent } from "@/components/ui/card";
import { Label } from "@/components/ui/label";
import { Textarea } from "@/components/ui/textarea";
-import {
- ChevronDown,
- Filter,
- Search,
- X,
- Loader2,
- Plus,
- Save,
-} from "lucide-react";
+import { Filter, X, Loader2, Plus, Save } from "lucide-react";
import { cn } from "@/lib/utils";
import {
useGetFiltersSearchQuery,
type KnowledgeFilter,
} from "@/src/app/api/queries/useGetFiltersSearchQuery";
+import { useCreateFilter } from "@/src/app/api/mutations/useCreateFilter";
import {
Dialog,
DialogContent,
DialogDescription,
- DialogFooter,
DialogHeader,
DialogTitle,
} from "@/components/ui/dialog";
@@ -56,43 +47,22 @@ export function KnowledgeFilterList({
const [createDescription, setCreateDescription] = useState("");
const [creating, setCreating] = useState(false);
- const {
- data,
- isFetching: loading,
- refetch,
- } = useGetFiltersSearchQuery(searchQuery, 20, { enabled: true });
- const filters: KnowledgeFilter[] = (data ?? []) as KnowledgeFilter[];
+ const { data, isFetching: loading } = useGetFiltersSearchQuery(
+ searchQuery,
+ 20
+ );
- const deleteFilter = async (filterId: string, e: React.MouseEvent) => {
- e.stopPropagation();
+ const filters = data || [];
- try {
- const response = await fetch(`/api/knowledge-filter/${filterId}`, {
- method: "DELETE",
- });
-
- if (response.ok) {
- // If this was the selected filter, clear selection
- if (selectedFilter?.id === filterId) {
- onFilterSelect(null);
- }
- // Refresh list
- refetch();
- } else {
- console.error("Failed to delete knowledge filter");
- }
- } catch (error) {
- console.error("Error deleting knowledge filter:", error);
- }
- };
+ const createFilterMutation = useCreateFilter();
const handleFilterSelect = (filter: KnowledgeFilter) => {
onFilterSelect(filter);
};
- const handleClearFilter = () => {
- onFilterSelect(null);
- };
+ // const handleClearFilter = () => {
+ // onFilterSelect(null);
+ // };
const handleCreateNew = () => {
setShowCreateModal(true);
@@ -115,43 +85,19 @@ export function KnowledgeFilterList({
scoreThreshold: 0,
};
- const response = await fetch("/api/knowledge-filter", {
- method: "POST",
- headers: {
- "Content-Type": "application/json",
- },
- body: JSON.stringify({
- name: createName.trim(),
- description: createDescription.trim(),
- queryData: JSON.stringify(defaultFilterData),
- }),
+ const result = await createFilterMutation.mutateAsync({
+ name: createName.trim(),
+ description: createDescription.trim(),
+ queryData: JSON.stringify(defaultFilterData),
});
- const result = await response.json();
- if (response.ok && result.success) {
- // Create the new filter object
- const newFilter: KnowledgeFilter = {
- id: result.filter.id,
- name: createName.trim(),
- description: createDescription.trim(),
- query_data: JSON.stringify(defaultFilterData),
- owner: result.filter.owner,
- created_at: result.filter.created_at,
- updated_at: result.filter.updated_at,
- };
+ // Select the new filter from API response
+ onFilterSelect(result.filter);
- // Select the new filter
- onFilterSelect(newFilter);
-
- // Close modal and reset form
- setShowCreateModal(false);
- setCreateName("");
- setCreateDescription("");
- // Refresh list to include newly created filter
- refetch();
- } else {
- console.error("Failed to create knowledge filter:", result.error);
- }
+ // Close modal and reset form
+ setShowCreateModal(false);
+ setCreateName("");
+ setCreateDescription("");
} catch (error) {
console.error("Error creating knowledge filter:", error);
} finally {
@@ -169,25 +115,6 @@ export function KnowledgeFilterList({
return JSON.parse(queryData) as ParsedQueryData;
};
- const getFilterSummary = (filter: KnowledgeFilter): string => {
- try {
- const parsed = JSON.parse(filter.query_data) as ParsedQueryData;
- const parts = [];
-
- if (parsed.query) parts.push(`"${parsed.query}"`);
- if (parsed.filters.data_sources.length > 0)
- parts.push(`${parsed.filters.data_sources.length} sources`);
- if (parsed.filters.document_types.length > 0)
- parts.push(`${parsed.filters.document_types.length} types`);
- if (parsed.filters.owners.length > 0)
- parts.push(`${parsed.filters.owners.length} owners`);
-
- return parts.join(" • ") || "No filters";
- } catch {
- return "Invalid filter";
- }
- };
-
return (
<>
@@ -199,6 +126,7 @@ export function KnowledgeFilterList({
variant="ghost"
size="sm"
onClick={handleCreateNew}
+ title="Create New Filter"
className="h-8 px-3"
>
@@ -255,14 +183,6 @@ export function KnowledgeFilterList({
-
))
)}
@@ -277,62 +197,60 @@ export function KnowledgeFilterList({
knowledge base.
-
-
-
-
- setCreateName(e.target.value)}
- className="mt-1"
- />
-
-
-
-
+
+
+
+ setCreateName(e.target.value)}
+ className="mt-1"
+ />
-
-
-
+
+
+
+
+
+
+
>
diff --git a/frontend/components/knowledge-filter-panel.tsx b/frontend/components/knowledge-filter-panel.tsx
index 3511b805..b44f3219 100644
--- a/frontend/components/knowledge-filter-panel.tsx
+++ b/frontend/components/knowledge-filter-panel.tsx
@@ -1,93 +1,106 @@
-"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'
+"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
+ key: string;
+ count: number;
}
interface AvailableFacets {
- data_sources: FacetBucket[]
- document_types: FacetBucket[]
- owners: FacetBucket[]
- connector_types: FacetBucket[]
+ data_sources: FacetBucket[];
+ document_types: FacetBucket[];
+ owners: FacetBucket[];
+ connector_types: FacetBucket[];
}
export function KnowledgeFilterPanel() {
- const { selectedFilter, parsedFilterData, setSelectedFilter, isPanelOpen, closePanelOnly } = useKnowledgeFilter()
-
+ 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)
+ 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 [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)
-
+ 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: []
- })
+ connector_types: [],
+ });
// Load current filter data into controls
useEffect(() => {
if (selectedFilter && parsedFilterData) {
- setQuery(parsedFilterData.query || '')
-
+ setQuery(parsedFilterData.query || "");
+
// Set the actual filter selections from the saved knowledge filter
- const filters = parsedFilterData.filters
-
+ 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 || '')
+ 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])
+ }, [selectedFilter, parsedFilterData]);
// Load available facets from API
useEffect(() => {
if (isPanelOpen) {
- loadAvailableFacets()
+ loadAvailableFacets();
}
- }, [isPanelOpen])
+ }, [isPanelOpen]);
const loadAvailableFacets = async () => {
- console.log("[DEBUG] Loading available facets...")
+ console.log("[DEBUG] Loading available facets...");
try {
// Do a search to get facets (similar to search page)
const response = await fetch("/api/search", {
@@ -95,39 +108,36 @@ export function KnowledgeFilterPanel() {
headers: {
"Content-Type": "application/json",
},
- body: JSON.stringify({
+ body: JSON.stringify({
query: "*", // Use wildcard like search page to get all documents/facets
limit: 1,
- scoreThreshold: 0
+ scoreThreshold: 0,
// Omit filters entirely to get all available facets
}),
- })
+ });
+
+ const result = await response.json();
+ console.log("[DEBUG] Search API response:", result);
- 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)
+ 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")
+ console.log("[DEBUG] No aggregations in response or response not ok");
}
} catch (error) {
- console.error("Failed to load available facets:", 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
-
-
-
+ if (!isPanelOpen || !selectedFilter || !parsedFilterData) return null;
const selectAllFilters = () => {
// Use wildcards instead of listing all specific items
@@ -135,116 +145,135 @@ export function KnowledgeFilterPanel() {
data_sources: ["*"],
document_types: ["*"],
owners: ["*"],
- connector_types: ["*"]
- })
- }
+ connector_types: ["*"],
+ });
+ };
const clearAllFilters = () => {
setSelectedFilters({
data_sources: [],
document_types: [],
owners: [],
- connector_types: []
- })
- }
+ connector_types: [],
+ });
+ };
const handleEditMeta = () => {
- setIsEditingMeta(true)
- }
+ setIsEditingMeta(true);
+ };
const handleCancelEdit = () => {
- setIsEditingMeta(false)
- setEditingName(selectedFilter.name)
- setEditingDescription(selectedFilter.description || '')
- }
+ setIsEditingMeta(false);
+ setEditingName(selectedFilter.name);
+ setEditingDescription(selectedFilter.description || "");
+ };
const handleSaveMeta = async () => {
- if (!editingName.trim()) return
+ if (!editingName.trim()) return;
- setIsSaving(true)
+ 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 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()
+ 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)
+ };
+ setSelectedFilter(updatedFilter);
+ setIsEditingMeta(false);
}
} catch (error) {
- console.error('Error updating filter:', error)
+ console.error("Error updating filter:", error);
} finally {
- setIsSaving(false)
+ setIsSaving(false);
}
- }
+ };
const handleSaveConfiguration = async () => {
const filterData = {
query,
filters: selectedFilters,
limit: resultLimit,
- scoreThreshold
- }
+ scoreThreshold,
+ };
- setIsSaving(true)
+ 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 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()
+ 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)
+ };
+ setSelectedFilter(updatedFilter);
}
} catch (error) {
- console.error('Error updating filter configuration:', error)
+ console.error("Error updating filter configuration:", error);
} finally {
- setIsSaving(false)
+ 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'
- })
- }
+ 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 => ({
+ const handleFilterChange = (
+ facetType: keyof typeof selectedFilters,
+ newValues: string[]
+ ) => {
+ setSelectedFilters((prev) => ({
...prev,
- [facetType]: newValues
- }))
- }
+ [facetType]: newValues,
+ }));
+ };
+
+ const handleDeleteFilter = async () => {
+ const result = await deleteFilterMutation.mutateAsync({
+ id: selectedFilter.id,
+ });
+ if (result.success) {
+ setSelectedFilter(null);
+ closePanelOnly();
+ }
+ };
return (
@@ -301,7 +330,7 @@ export function KnowledgeFilterPanel() {
className="flex-1"
>
- {isSaving ? 'Saving...' : 'Save'}
+ {isSaving ? "Saving..." : "Save"}