frontend bug fix and lint

This commit is contained in:
phact 2025-08-14 15:22:31 -04:00
parent 91f0b608b5
commit 40cac9950c
5 changed files with 75 additions and 59 deletions

View file

@ -4,7 +4,7 @@ import { useState, useEffect, useRef } from "react"
import { Button } from "@/components/ui/button"
import { Input } from "@/components/ui/input"
import { Card, CardContent } from "@/components/ui/card"
import { Badge } from "@/components/ui/badge"
import { Label } from "@/components/ui/label"
import { Textarea } from "@/components/ui/textarea"
import { ChevronDown, Filter, Search, X, Loader2, Plus, Save } from "lucide-react"

View file

@ -1,7 +1,7 @@
"use client"
import { useState, useEffect } from 'react'
import { Filter, X, Edit3, Save, Settings, ChevronDown, ChevronUp, RefreshCw } from 'lucide-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'
@ -11,16 +11,6 @@ import { Checkbox } from '@/components/ui/checkbox'
import { Collapsible, CollapsibleContent, CollapsibleTrigger } from '@/components/ui/collapsible'
import { useKnowledgeFilter } from '@/contexts/knowledge-filter-context'
interface ParsedQueryData {
query: string
filters: {
data_sources: string[]
document_types: string[]
owners: string[]
}
limit: number
scoreThreshold: number
}
interface FacetBucket {
key: string
@ -143,14 +133,7 @@ export function KnowledgeFilterPanel() {
}))
}
const handleFilterChange = (facetType: keyof typeof selectedFilters, value: string, checked: boolean) => {
setSelectedFilters(prev => ({
...prev,
[facetType]: checked
? [...prev[facetType], value]
: prev[facetType].filter(item => item !== value)
}))
}
const selectAllFilters = () => {
// Use wildcards instead of listing all specific items
@ -276,7 +259,6 @@ export function KnowledgeFilterPanel() {
if (!buckets || buckets.length === 0) return null
const isAllSelected = selectedFilters[facetType].includes("*") // Wildcard
const hasSpecificSelections = selectedFilters[facetType].some(item => item !== "*")
const handleAllToggle = (checked: boolean) => {
if (checked) {

View file

@ -236,12 +236,6 @@ function ChatPage() {
inputRef.current?.focus()
}, [])
// Update input when global filter query changes
useEffect(() => {
if (parsedFilterData?.query) {
setInput(parsedFilterData.query)
}
}, [parsedFilterData])
const handleSSEStream = async (userMessage: Message) => {
const apiEndpoint = endpoint === "chat" ? "/api/chat" : "/api/langflow"
@ -252,17 +246,21 @@ function ChatPage() {
stream: true,
...(parsedFilterData?.filters && (() => {
const filters = parsedFilterData.filters
const processed: SelectedFilters = {}
if (!filters.data_sources.includes("*")) {
processed.data_sources = filters.data_sources
const processed: SelectedFilters = {
data_sources: [],
document_types: [],
owners: []
}
if (!filters.document_types.includes("*")) {
processed.document_types = filters.document_types
}
if (!filters.owners.includes("*")) {
processed.owners = filters.owners
}
return Object.keys(processed).length > 0 ? { filters: processed } : {}
// Only copy non-wildcard arrays
processed.data_sources = filters.data_sources.includes("*") ? [] : filters.data_sources
processed.document_types = filters.document_types.includes("*") ? [] : filters.document_types
processed.owners = filters.owners.includes("*") ? [] : filters.owners
// Only include filters if any array has values
const hasFilters = processed.data_sources.length > 0 ||
processed.document_types.length > 0 ||
processed.owners.length > 0
return hasFilters ? { filters: processed } : {}
})()),
limit: parsedFilterData?.limit ?? 10,
scoreThreshold: parsedFilterData?.scoreThreshold ?? 0
@ -725,17 +723,21 @@ function ChatPage() {
prompt: userMessage.content,
...(parsedFilterData?.filters && (() => {
const filters = parsedFilterData.filters
const processed: SelectedFilters = {}
if (!filters.data_sources.includes("*")) {
processed.data_sources = filters.data_sources
const processed: SelectedFilters = {
data_sources: [],
document_types: [],
owners: []
}
if (!filters.document_types.includes("*")) {
processed.document_types = filters.document_types
}
if (!filters.owners.includes("*")) {
processed.owners = filters.owners
}
return Object.keys(processed).length > 0 ? { filters: processed } : {}
// Only copy non-wildcard arrays
processed.data_sources = filters.data_sources.includes("*") ? [] : filters.data_sources
processed.document_types = filters.document_types.includes("*") ? [] : filters.document_types
processed.owners = filters.owners.includes("*") ? [] : filters.owners
// Only include filters if any array has values
const hasFilters = processed.data_sources.length > 0 ||
processed.document_types.length > 0 ||
processed.owners.length > 0
return hasFilters ? { filters: processed } : {}
})()),
limit: parsedFilterData?.limit ?? 10,
scoreThreshold: parsedFilterData?.scoreThreshold ?? 0

View file

@ -7,7 +7,7 @@ import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/com
import { Badge } from "@/components/ui/badge"
import { Input } from "@/components/ui/input"
import { Label } from "@/components/ui/label"
import { Upload, FolderOpen, Loader2, PlugZap, CheckCircle, XCircle, RefreshCw, Download, AlertCircle, Database } from "lucide-react"
import { Upload, FolderOpen, Loader2, PlugZap, RefreshCw, Download } from "lucide-react"
import { ProtectedRoute } from "@/components/protected-route"
import { useTask } from "@/contexts/task-context"
import { useAuth } from "@/contexts/auth-context"
@ -42,7 +42,7 @@ interface Connection {
function KnowledgeSourcesPage() {
const { isAuthenticated } = useAuth()
const { addTask, refreshTasks, tasks } = useTask()
const { addTask, tasks } = useTask()
const searchParams = useSearchParams()
// File upload state
@ -319,8 +319,8 @@ function KnowledgeSourcesPage() {
const result = await response.json()
if (response.ok) {
const aggs = result.aggregations || {}
const toBuckets = (agg: any): FacetBucket[] =>
(agg?.buckets || []).map((b: any) => ({ key: String(b.key), count: b.doc_count }))
const toBuckets = (agg: { buckets?: Array<{ key: string | number; doc_count: number }> }): FacetBucket[] =>
(agg?.buckets || []).map(b => ({ key: String(b.key), count: b.doc_count }))
const dataSourceBuckets = toBuckets(aggs.data_sources)
setFacetStats({
data_sources: dataSourceBuckets.slice(0, 10),

View file

@ -1,7 +1,7 @@
"use client"
import { useState, useEffect, useCallback } from "react"
import { useSearchParams } from "next/navigation"
import { useState, useEffect, useCallback, useRef } from "react"
import { Button } from "@/components/ui/button"
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card"
import { Input } from "@/components/ui/input"
@ -26,12 +26,13 @@ interface SearchResponse {
}
function SearchPage() {
const searchParams = useSearchParams()
const { selectedFilter, parsedFilterData } = useKnowledgeFilter()
const [query, setQuery] = useState("")
const [loading, setLoading] = useState(false)
const [results, setResults] = useState<SearchResult[]>([])
const [searchPerformed, setSearchPerformed] = useState(false)
const prevFilterDataRef = useRef<string>("")
const handleSearch = useCallback(async (e?: React.FormEvent) => {
if (e) e.preventDefault()
@ -42,7 +43,18 @@ function SearchPage() {
try {
// Build search payload with global filter data
const searchPayload: any = {
interface SearchPayload {
query: string;
limit: number;
scoreThreshold: number;
filters?: {
data_sources?: string[];
document_types?: string[];
owners?: string[];
};
}
const searchPayload: SearchPayload = {
query,
limit: parsedFilterData?.limit || 10,
scoreThreshold: parsedFilterData?.scoreThreshold || 0
@ -59,7 +71,7 @@ function SearchPage() {
!filters.owners.includes("*")
if (hasSpecificFilters) {
const processedFilters: any = {}
const processedFilters: SearchPayload['filters'] = {}
// Only add filter arrays that don't contain wildcards
if (!filters.data_sources.includes("*")) {
@ -114,12 +126,32 @@ function SearchPage() {
}
}, [parsedFilterData])
// Auto-refresh search when filter changes (if search was already performed)
// Auto-refresh search when filter changes (but only if search was already performed)
useEffect(() => {
if (searchPerformed && query.trim()) {
if (!parsedFilterData) return
// Create a stable string representation of the filter data for comparison
const currentFilterString = JSON.stringify({
filters: parsedFilterData.filters,
limit: parsedFilterData.limit,
scoreThreshold: parsedFilterData.scoreThreshold
})
// Only trigger search if filter data actually changed and we've done a search before
if (prevFilterDataRef.current !== "" &&
prevFilterDataRef.current !== currentFilterString &&
searchPerformed &&
query.trim()) {
console.log("Filter changed, auto-refreshing search")
handleSearch()
}
}, [parsedFilterData]) // Only depend on parsedFilterData to avoid infinite loop
// Update the ref with current filter data
prevFilterDataRef.current = currentFilterString
}, [parsedFilterData, searchPerformed, query, handleSearch])
return (