diff --git a/frontend/components/knowledge-dropdown.tsx b/frontend/components/knowledge-dropdown.tsx index 2e8d315c..325ed3ef 100644 --- a/frontend/components/knowledge-dropdown.tsx +++ b/frontend/components/knowledge-dropdown.tsx @@ -125,7 +125,6 @@ export function KnowledgeDropdown({ active, variant = 'navigation' }: KnowledgeD if (response.status === 201) { const taskId = result.task_id || result.id - const totalFiles = result.total_files || 0 if (!taskId) { throw new Error("No task ID received from server") @@ -166,7 +165,6 @@ export function KnowledgeDropdown({ active, variant = 'navigation' }: KnowledgeD if (response.status === 201) { const taskId = result.task_id || result.id - const totalFiles = result.total_files || 0 if (!taskId) { throw new Error("No task ID received from server") diff --git a/frontend/components/navigation.tsx b/frontend/components/navigation.tsx index 49fc18d4..a2d6e08b 100644 --- a/frontend/components/navigation.tsx +++ b/frontend/components/navigation.tsx @@ -6,7 +6,6 @@ import { Library, MessageSquare, Settings2, Plus, FileText } from "lucide-react" import { cn } from "@/lib/utils" import { useState, useEffect, useRef, useCallback } from "react" import { useChat } from "@/contexts/chat-context" -import { KnowledgeDropdown } from "@/components/knowledge-dropdown" import { EndpointType } from "@/contexts/chat-context" diff --git a/frontend/src/app/knowledge/page.tsx b/frontend/src/app/knowledge/page.tsx index 50baaefc..873153c2 100644 --- a/frontend/src/app/knowledge/page.tsx +++ b/frontend/src/app/knowledge/page.tsx @@ -1,12 +1,10 @@ "use client" import { useState, useEffect, useCallback, useRef } from "react" -import { useRouter } from "next/navigation" import { Button } from "@/components/ui/button" import { Input } from "@/components/ui/input" -import { Badge } from "@/components/ui/badge" -import { Search, Loader2, FileText, HardDrive, Building2, Cloud, Plus } from "lucide-react" +import { Search, Loader2, FileText, HardDrive, Building2, Cloud } from "lucide-react" import { TbBrandOnedrive } from "react-icons/tb" import { SiGoogledrive } from "react-icons/si" import { ProtectedRoute } from "@/components/protected-route" @@ -14,7 +12,6 @@ import { useKnowledgeFilter } from "@/contexts/knowledge-filter-context" import { useTask } from "@/contexts/task-context" import { KnowledgeDropdown } from "@/components/knowledge-dropdown" -type FacetBucket = { key: string; count: number } interface ChunkResult { filename: string @@ -75,24 +72,16 @@ function getSourceIcon(connectorType?: string) { } function SearchPage() { - const router = useRouter() const { isMenuOpen } = useTask() const { parsedFilterData, isPanelOpen } = useKnowledgeFilter() const [query, setQuery] = useState("*") const [loading, setLoading] = useState(false) const [chunkResults, setChunkResults] = useState([]) const [fileResults, setFileResults] = useState([]) - const [viewMode, setViewMode] = useState<'files' | 'chunks'>('files') const [selectedFile, setSelectedFile] = useState(null) const [searchPerformed, setSearchPerformed] = useState(false) const prevFilterDataRef = useRef("") - // Stats state for knowledge overview - const [statsLoading, setStatsLoading] = useState(false) - const [totalDocs, setTotalDocs] = useState(0) - const [totalChunks, setTotalChunks] = useState(0) - const [facetStats, setFacetStats] = useState<{ data_sources: FacetBucket[]; document_types: FacetBucket[]; owners: FacetBucket[]; connector_types: FacetBucket[] } | null>(null) - const handleSearch = useCallback(async (e?: React.FormEvent) => { if (e) e.preventDefault() if (!query.trim()) return @@ -116,7 +105,7 @@ function SearchPage() { const searchPayload: SearchPayload = { query, - limit: parsedFilterData?.limit || (query.trim() === "*" ? 50 : 10), // Higher limit for wildcard searches + limit: parsedFilterData?.limit || (query.trim() === "*" ? 10000 : 10), // Maximum allowed limit for wildcard searches scoreThreshold: parsedFilterData?.scoreThreshold || 0 } @@ -284,106 +273,12 @@ function SearchPage() { prevFilterDataRef.current = currentFilterString }, [parsedFilterData, searchPerformed, query, handleSearch]) - // Fetch stats with current knowledge filter applied - const fetchStats = useCallback(async () => { - try { - setStatsLoading(true) - - // Build search payload with current filter data - interface SearchPayload { - query: string; - limit: number; - scoreThreshold: number; - filters?: { - data_sources?: string[]; - document_types?: string[]; - owners?: string[]; - connector_types?: string[]; - }; - } - - const searchPayload: SearchPayload = { - query: '*', - limit: 50, // Get more results to ensure we have owner mapping data - scoreThreshold: parsedFilterData?.scoreThreshold || 0 - } - - // Add filters from global context if available and not wildcards - if (parsedFilterData?.filters) { - const filters = parsedFilterData.filters - - // Only include filters if they're not wildcards (not "*") - const hasSpecificFilters = - !filters.data_sources.includes("*") || - !filters.document_types.includes("*") || - !filters.owners.includes("*") || - (filters.connector_types && !filters.connector_types.includes("*")) - - if (hasSpecificFilters) { - const processedFilters: SearchPayload['filters'] = {} - - // Only add filter arrays that don't contain wildcards - if (!filters.data_sources.includes("*")) { - processedFilters.data_sources = filters.data_sources - } - if (!filters.document_types.includes("*")) { - processedFilters.document_types = filters.document_types - } - if (!filters.owners.includes("*")) { - processedFilters.owners = filters.owners - } - if (filters.connector_types && !filters.connector_types.includes("*")) { - processedFilters.connector_types = filters.connector_types - } - - // Only add filters object if it has any actual filters - if (Object.keys(processedFilters).length > 0) { - searchPayload.filters = processedFilters - } - } - } - - const response = await fetch('/api/search', { - method: 'POST', - headers: { 'Content-Type': 'application/json' }, - body: JSON.stringify(searchPayload) - }) - const result = await response.json() - if (response.ok) { - const aggs = result.aggregations || {} - const toBuckets = (agg: { buckets?: Array<{ key: string | number; doc_count: number }> }): FacetBucket[] => - (agg?.buckets || []).map(b => ({ key: String(b.key), count: b.doc_count })) - - // Now we can aggregate directly on owner names since they're keyword fields - - const dataSourceBuckets = toBuckets(aggs.data_sources) - setFacetStats({ - data_sources: dataSourceBuckets.slice(0, 10), - document_types: toBuckets(aggs.document_types).slice(0, 10), - owners: toBuckets(aggs.owners).slice(0, 10), - connector_types: toBuckets(aggs.connector_types || {}).slice(0, 10) - }) - setTotalDocs(dataSourceBuckets.length) - setTotalChunks(Number(result.total || 0)) - } - } catch { - // non-fatal – keep page functional without stats - } finally { - setStatsLoading(false) - } - }, [parsedFilterData]) - // Auto-search on mount with "*" useEffect(() => { - if (query === "*") { - handleSearch() - } - }, []) // Only run once on mount - - // Initial stats fetch and refresh when filter changes - useEffect(() => { - fetchStats() - }, [fetchStats]) + // Only trigger initial search on mount when query is "*" + handleSearch() + // eslint-disable-next-line react-hooks/exhaustive-deps + }, []) // Only run once on mount - ignore handleSearch dependency @@ -447,8 +342,7 @@ function SearchPage() { {/* Results Display */}
- {viewMode === 'files' ? ( - selectedFile ? ( + {selectedFile ? ( // Show chunks for selected file <>
@@ -536,29 +430,7 @@ function SearchPage() {
- ) - ) : ( - // Show chunks view - chunkResults.map((result, index) => ( -
-
-
- - {result.filename} -
- - {result.score.toFixed(2)} - -
-
- {result.mimetype} • Page {result.page} -
-

- {result.text} -

-
- )) - )} + )}
)} diff --git a/frontend/src/app/settings/page.tsx b/frontend/src/app/settings/page.tsx index e6fadcca..4461c617 100644 --- a/frontend/src/app/settings/page.tsx +++ b/frontend/src/app/settings/page.tsx @@ -7,11 +7,10 @@ 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, RefreshCw, Download, Cloud } from "lucide-react" +import { Loader2, PlugZap, RefreshCw } from "lucide-react" import { ProtectedRoute } from "@/components/protected-route" import { useTask } from "@/contexts/task-context" import { useAuth } from "@/contexts/auth-context" -import { FileUploadArea } from "@/components/file-upload-area" interface Connector { @@ -45,14 +44,6 @@ function KnowledgeSourcesPage() { const { addTask, tasks } = useTask() const searchParams = useSearchParams() - // File upload state - const [fileUploadLoading, setFileUploadLoading] = useState(false) - const [pathUploadLoading, setPathUploadLoading] = useState(false) - const [folderPath, setFolderPath] = useState("/app/documents/") - const [bucketUploadLoading, setBucketUploadLoading] = useState(false) - const [bucketUrl, setBucketUrl] = useState("s3://") - const [uploadStatus, setUploadStatus] = useState("") - const [awsEnabled, setAwsEnabled] = useState(false) // Connectors state const [connectors, setConnectors] = useState([]) @@ -62,123 +53,6 @@ function KnowledgeSourcesPage() { const [maxFiles, setMaxFiles] = useState(10) - // File upload handlers - const handleDirectFileUpload = async (file: File) => { - setFileUploadLoading(true) - setUploadStatus("") - - try { - const formData = new FormData() - formData.append("file", file) - - const response = await fetch("/api/upload", { - method: "POST", - body: formData, - }) - - const result = await response.json() - - if (response.ok) { - setUploadStatus(`File processed successfully! ID: ${result.id}`) - - } else { - setUploadStatus(`Error: ${result.error || "Processing failed"}`) - } - } catch (error) { - setUploadStatus(`Error: ${error instanceof Error ? error.message : "Processing failed"}`) - } finally { - setFileUploadLoading(false) - } - } - - const handlePathUpload = async (e: React.FormEvent) => { - e.preventDefault() - if (!folderPath.trim()) return - - setPathUploadLoading(true) - setUploadStatus("") - - try { - const response = await fetch("/api/upload_path", { - method: "POST", - headers: { - "Content-Type": "application/json", - }, - body: JSON.stringify({ path: folderPath }), - }) - - const result = await response.json() - - if (response.status === 201) { - const taskId = result.task_id || result.id - const totalFiles = result.total_files || 0 - - if (!taskId) { - throw new Error("No task ID received from server") - } - - addTask(taskId) - - setUploadStatus(`🔄 Processing started for ${totalFiles} files. Check the task notification panel for real-time progress. (Task ID: ${taskId})`) - setFolderPath("") - setPathUploadLoading(false) - - } else if (response.ok) { - const successful = result.results?.filter((r: {status: string}) => r.status === "indexed").length || 0 - const total = result.results?.length || 0 - setUploadStatus(`Path processed successfully! ${successful}/${total} files indexed.`) - setFolderPath("") - setPathUploadLoading(false) - } else { - setUploadStatus(`Error: ${result.error || "Path upload failed"}`) - setPathUploadLoading(false) - } - } catch (error) { - setUploadStatus(`Error: ${error instanceof Error ? error.message : "Path upload failed"}`) - setPathUploadLoading(false) - } - } - - const handleBucketUpload = async (e: React.FormEvent) => { - e.preventDefault() - if (!bucketUrl.trim()) return - - setBucketUploadLoading(true) - setUploadStatus("") - - try { - const response = await fetch("/api/upload_bucket", { - method: "POST", - headers: { - "Content-Type": "application/json", - }, - body: JSON.stringify({ s3_url: bucketUrl }), - }) - - const result = await response.json() - - if (response.status === 201) { - const taskId = result.task_id || result.id - const totalFiles = result.total_files || 0 - - if (!taskId) { - throw new Error("No task ID received from server") - } - - addTask(taskId) - setUploadStatus(`🔄 Processing started for ${totalFiles} files. Check the task notification panel for real-time progress. (Task ID: ${taskId})`) - setBucketUrl("s3://") - } else { - setUploadStatus(`Error: ${result.error || "Bucket processing failed"}`) - } - } catch (error) { - setUploadStatus( - `Error: ${error instanceof Error ? error.message : "Bucket processing failed"}`, - ) - } finally { - setBucketUploadLoading(false) - } - } // Helper function to get connector icon const getConnectorIcon = (iconName: string) => { @@ -379,21 +253,6 @@ function KnowledgeSourcesPage() { }, [searchParams, isAuthenticated, checkConnectorStatuses]) - // Check AWS availability - useEffect(() => { - const checkAws = async () => { - try { - const res = await fetch("/api/upload_options") - if (res.ok) { - const data = await res.json() - setAwsEnabled(Boolean(data.aws)) - } - } catch (err) { - console.error("Failed to check AWS availability", err) - } - } - checkAws() - }, []) // Track previous tasks to detect new completions diff --git a/frontend/src/components/task-notification-menu.tsx b/frontend/src/components/task-notification-menu.tsx index 2208545a..372717d4 100644 --- a/frontend/src/components/task-notification-menu.tsx +++ b/frontend/src/components/task-notification-menu.tsx @@ -60,7 +60,6 @@ export function TaskNotificationMenu() { const processed = task.processed_files || 0 const successful = task.successful_files || 0 const failed = task.failed_files || 0 - const skipped = Math.max(0, processed - successful - failed) // Calculate skipped if (total > 0) { return { @@ -70,7 +69,6 @@ export function TaskNotificationMenu() { processed, successful, failed, - skipped, remaining: total - processed } } @@ -180,12 +178,6 @@ export function TaskNotificationMenu() {
- {formatTaskProgress(task)?.detailed.skipped} skipped - -
-
-
- {formatTaskProgress(task)?.detailed.remaining} pending
@@ -269,8 +261,7 @@ export function TaskNotificationMenu() { {task.status === 'completed' && formatTaskProgress(task)?.detailed && (
{formatTaskProgress(task)?.detailed.successful} success, {' '} - {formatTaskProgress(task)?.detailed.failed} failed, {' '} - {formatTaskProgress(task)?.detailed.skipped} skipped + {formatTaskProgress(task)?.detailed.failed} failed
)} {task.status === 'failed' && task.error && (