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 { Button } from "@/components/ui/button"
import { Input } from "@/components/ui/input" import { Input } from "@/components/ui/input"
import { Card, CardContent } from "@/components/ui/card" import { Card, CardContent } from "@/components/ui/card"
import { Badge } from "@/components/ui/badge"
import { Label } from "@/components/ui/label" import { Label } from "@/components/ui/label"
import { Textarea } from "@/components/ui/textarea" import { Textarea } from "@/components/ui/textarea"
import { ChevronDown, Filter, Search, X, Loader2, Plus, Save } from "lucide-react" import { ChevronDown, Filter, Search, X, Loader2, Plus, Save } from "lucide-react"

View file

@ -1,7 +1,7 @@
"use client" "use client"
import { useState, useEffect } from 'react' 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 { Button } from '@/components/ui/button'
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card' import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card'
import { Input } from '@/components/ui/input' 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 { Collapsible, CollapsibleContent, CollapsibleTrigger } from '@/components/ui/collapsible'
import { useKnowledgeFilter } from '@/contexts/knowledge-filter-context' 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 { interface FacetBucket {
key: string 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 = () => { const selectAllFilters = () => {
// Use wildcards instead of listing all specific items // Use wildcards instead of listing all specific items
@ -276,7 +259,6 @@ export function KnowledgeFilterPanel() {
if (!buckets || buckets.length === 0) return null if (!buckets || buckets.length === 0) return null
const isAllSelected = selectedFilters[facetType].includes("*") // Wildcard const isAllSelected = selectedFilters[facetType].includes("*") // Wildcard
const hasSpecificSelections = selectedFilters[facetType].some(item => item !== "*")
const handleAllToggle = (checked: boolean) => { const handleAllToggle = (checked: boolean) => {
if (checked) { if (checked) {

View file

@ -236,12 +236,6 @@ function ChatPage() {
inputRef.current?.focus() inputRef.current?.focus()
}, []) }, [])
// Update input when global filter query changes
useEffect(() => {
if (parsedFilterData?.query) {
setInput(parsedFilterData.query)
}
}, [parsedFilterData])
const handleSSEStream = async (userMessage: Message) => { const handleSSEStream = async (userMessage: Message) => {
const apiEndpoint = endpoint === "chat" ? "/api/chat" : "/api/langflow" const apiEndpoint = endpoint === "chat" ? "/api/chat" : "/api/langflow"
@ -252,17 +246,21 @@ function ChatPage() {
stream: true, stream: true,
...(parsedFilterData?.filters && (() => { ...(parsedFilterData?.filters && (() => {
const filters = parsedFilterData.filters const filters = parsedFilterData.filters
const processed: SelectedFilters = {} const processed: SelectedFilters = {
if (!filters.data_sources.includes("*")) { data_sources: [],
processed.data_sources = filters.data_sources document_types: [],
owners: []
} }
if (!filters.document_types.includes("*")) { // Only copy non-wildcard arrays
processed.document_types = filters.document_types processed.data_sources = filters.data_sources.includes("*") ? [] : filters.data_sources
} processed.document_types = filters.document_types.includes("*") ? [] : filters.document_types
if (!filters.owners.includes("*")) { processed.owners = filters.owners.includes("*") ? [] : filters.owners
processed.owners = filters.owners
} // Only include filters if any array has values
return Object.keys(processed).length > 0 ? { filters: processed } : {} const hasFilters = processed.data_sources.length > 0 ||
processed.document_types.length > 0 ||
processed.owners.length > 0
return hasFilters ? { filters: processed } : {}
})()), })()),
limit: parsedFilterData?.limit ?? 10, limit: parsedFilterData?.limit ?? 10,
scoreThreshold: parsedFilterData?.scoreThreshold ?? 0 scoreThreshold: parsedFilterData?.scoreThreshold ?? 0
@ -725,17 +723,21 @@ function ChatPage() {
prompt: userMessage.content, prompt: userMessage.content,
...(parsedFilterData?.filters && (() => { ...(parsedFilterData?.filters && (() => {
const filters = parsedFilterData.filters const filters = parsedFilterData.filters
const processed: SelectedFilters = {} const processed: SelectedFilters = {
if (!filters.data_sources.includes("*")) { data_sources: [],
processed.data_sources = filters.data_sources document_types: [],
owners: []
} }
if (!filters.document_types.includes("*")) { // Only copy non-wildcard arrays
processed.document_types = filters.document_types processed.data_sources = filters.data_sources.includes("*") ? [] : filters.data_sources
} processed.document_types = filters.document_types.includes("*") ? [] : filters.document_types
if (!filters.owners.includes("*")) { processed.owners = filters.owners.includes("*") ? [] : filters.owners
processed.owners = filters.owners
} // Only include filters if any array has values
return Object.keys(processed).length > 0 ? { filters: processed } : {} const hasFilters = processed.data_sources.length > 0 ||
processed.document_types.length > 0 ||
processed.owners.length > 0
return hasFilters ? { filters: processed } : {}
})()), })()),
limit: parsedFilterData?.limit ?? 10, limit: parsedFilterData?.limit ?? 10,
scoreThreshold: parsedFilterData?.scoreThreshold ?? 0 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 { Badge } from "@/components/ui/badge"
import { Input } from "@/components/ui/input" import { Input } from "@/components/ui/input"
import { Label } from "@/components/ui/label" 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 { ProtectedRoute } from "@/components/protected-route"
import { useTask } from "@/contexts/task-context" import { useTask } from "@/contexts/task-context"
import { useAuth } from "@/contexts/auth-context" import { useAuth } from "@/contexts/auth-context"
@ -42,7 +42,7 @@ interface Connection {
function KnowledgeSourcesPage() { function KnowledgeSourcesPage() {
const { isAuthenticated } = useAuth() const { isAuthenticated } = useAuth()
const { addTask, refreshTasks, tasks } = useTask() const { addTask, tasks } = useTask()
const searchParams = useSearchParams() const searchParams = useSearchParams()
// File upload state // File upload state
@ -319,8 +319,8 @@ function KnowledgeSourcesPage() {
const result = await response.json() const result = await response.json()
if (response.ok) { if (response.ok) {
const aggs = result.aggregations || {} const aggs = result.aggregations || {}
const toBuckets = (agg: any): FacetBucket[] => const toBuckets = (agg: { buckets?: Array<{ key: string | number; doc_count: number }> }): FacetBucket[] =>
(agg?.buckets || []).map((b: any) => ({ key: String(b.key), count: b.doc_count })) (agg?.buckets || []).map(b => ({ key: String(b.key), count: b.doc_count }))
const dataSourceBuckets = toBuckets(aggs.data_sources) const dataSourceBuckets = toBuckets(aggs.data_sources)
setFacetStats({ setFacetStats({
data_sources: dataSourceBuckets.slice(0, 10), data_sources: dataSourceBuckets.slice(0, 10),

View file

@ -1,7 +1,7 @@
"use client" "use client"
import { useState, useEffect, useCallback } from "react" import { useState, useEffect, useCallback, useRef } from "react"
import { useSearchParams } from "next/navigation"
import { Button } from "@/components/ui/button" import { Button } from "@/components/ui/button"
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card" import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card"
import { Input } from "@/components/ui/input" import { Input } from "@/components/ui/input"
@ -26,12 +26,13 @@ interface SearchResponse {
} }
function SearchPage() { function SearchPage() {
const searchParams = useSearchParams()
const { selectedFilter, parsedFilterData } = useKnowledgeFilter() const { selectedFilter, parsedFilterData } = useKnowledgeFilter()
const [query, setQuery] = useState("") const [query, setQuery] = useState("")
const [loading, setLoading] = useState(false) const [loading, setLoading] = useState(false)
const [results, setResults] = useState<SearchResult[]>([]) const [results, setResults] = useState<SearchResult[]>([])
const [searchPerformed, setSearchPerformed] = useState(false) const [searchPerformed, setSearchPerformed] = useState(false)
const prevFilterDataRef = useRef<string>("")
const handleSearch = useCallback(async (e?: React.FormEvent) => { const handleSearch = useCallback(async (e?: React.FormEvent) => {
if (e) e.preventDefault() if (e) e.preventDefault()
@ -42,7 +43,18 @@ function SearchPage() {
try { try {
// Build search payload with global filter data // 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, query,
limit: parsedFilterData?.limit || 10, limit: parsedFilterData?.limit || 10,
scoreThreshold: parsedFilterData?.scoreThreshold || 0 scoreThreshold: parsedFilterData?.scoreThreshold || 0
@ -59,7 +71,7 @@ function SearchPage() {
!filters.owners.includes("*") !filters.owners.includes("*")
if (hasSpecificFilters) { if (hasSpecificFilters) {
const processedFilters: any = {} const processedFilters: SearchPayload['filters'] = {}
// Only add filter arrays that don't contain wildcards // Only add filter arrays that don't contain wildcards
if (!filters.data_sources.includes("*")) { if (!filters.data_sources.includes("*")) {
@ -114,12 +126,32 @@ function SearchPage() {
} }
}, [parsedFilterData]) }, [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(() => { 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() 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 ( return (