diff --git a/surfsense_web/app/dashboard/[search_space_id]/documents/(manage)/components/ProcessingIndicator.tsx b/surfsense_web/app/dashboard/[search_space_id]/documents/(manage)/components/ProcessingIndicator.tsx new file mode 100644 index 000000000..f0d20644c --- /dev/null +++ b/surfsense_web/app/dashboard/[search_space_id]/documents/(manage)/components/ProcessingIndicator.tsx @@ -0,0 +1,43 @@ +"use client"; + +import { Loader2, RefreshCw } from "lucide-react"; +import { motion, AnimatePresence } from "motion/react"; +import { useTranslations } from "next-intl"; +import { Alert, AlertDescription, AlertTitle } from "@/components/ui/alert"; + +interface ProcessingIndicatorProps { + activeTasksCount: number; +} + +export function ProcessingIndicator({ activeTasksCount }: ProcessingIndicatorProps) { + const t = useTranslations("documents"); + + if (activeTasksCount === 0) return null; + + return ( + + + +
+
+ +
+
+ + {t("processing_documents")} + + + {t("active_tasks_count", { count: activeTasksCount })} + +
+
+
+
+
+ ); +} diff --git a/surfsense_web/app/dashboard/[search_space_id]/documents/(manage)/page.tsx b/surfsense_web/app/dashboard/[search_space_id]/documents/(manage)/page.tsx index d4c1a4578..69458d5bc 100644 --- a/surfsense_web/app/dashboard/[search_space_id]/documents/(manage)/page.tsx +++ b/surfsense_web/app/dashboard/[search_space_id]/documents/(manage)/page.tsx @@ -6,8 +6,9 @@ import { RefreshCw } from "lucide-react"; import { motion } from "motion/react"; import { useParams } from "next/navigation"; import { useTranslations } from "next-intl"; -import { useCallback, useEffect, useId, useMemo, useState } from "react"; +import { useCallback, useEffect, useId, useMemo, useRef, useState } from "react"; import { toast } from "sonner"; +import { useLogsSummary } from "@/hooks/use-logs"; import { Button } from "@/components/ui/button"; import { deleteDocumentMutationAtom } from "@/atoms/documents/document-mutation.atoms"; import { documentTypeCountsAtom } from "@/atoms/documents/document-query.atoms"; @@ -17,6 +18,7 @@ import { cacheKeys } from "@/lib/query-client/cache-keys"; import { DocumentsFilters } from "./components/DocumentsFilters"; import { DocumentsTableShell, type SortKey } from "./components/DocumentsTableShell"; import { PaginationControls } from "./components/PaginationControls"; +import { ProcessingIndicator } from "./components/ProcessingIndicator"; import type { ColumnVisibility } from "./components/types"; function useDebounced(value: T, delay = 250) { @@ -132,6 +134,20 @@ export default function DocumentsTable() { toast.success(t("refresh_success") || "Documents refreshed"); }, [debouncedSearch, refetchSearch, refetchDocuments, t]); + // Set up polling for active tasks + const { summary } = useLogsSummary(searchSpaceId, 24, { refetchInterval: 5000 }); + const activeTasksCount = summary?.active_tasks.length || 0; + const prevActiveTasksCount = useRef(activeTasksCount); + + // Auto-refresh when a task finishes + useEffect(() => { + if (prevActiveTasksCount.current > activeTasksCount) { + // A task has finished! + refreshCurrentView(); + } + prevActiveTasksCount.current = activeTasksCount; + }, [activeTasksCount, refreshCurrentView]); + // Create a delete function for single document deletion const deleteDocument = useCallback( async (id: number) => { @@ -210,6 +226,8 @@ export default function DocumentsTable() { + + 0 && (
{step.items.map((item, idx) => ( - + {item} ))} diff --git a/surfsense_web/hooks/use-logs.ts b/surfsense_web/hooks/use-logs.ts index cfd161de0..51d8402d7 100644 --- a/surfsense_web/hooks/use-logs.ts +++ b/surfsense_web/hooks/use-logs.ts @@ -271,7 +271,11 @@ export function useLogs(searchSpaceId?: number, filters: LogFilters = {}) { } // Separate hook for log summary -export function useLogsSummary(searchSpaceId: number, hours: number = 24) { +export function useLogsSummary( + searchSpaceId: number, + hours: number = 24, + options: { refetchInterval?: number } = {} +) { const [summary, setSummary] = useState(null); const [loading, setLoading] = useState(true); const [error, setError] = useState(null); @@ -308,6 +312,17 @@ export function useLogsSummary(searchSpaceId: number, hours: number = 24) { fetchSummary(); }, [fetchSummary]); + // Set up polling if refetchInterval is provided + useEffect(() => { + if (!options.refetchInterval || options.refetchInterval <= 0) return; + + const intervalId = setInterval(() => { + fetchSummary(); + }, options.refetchInterval); + + return () => clearInterval(intervalId); + }, [fetchSummary, options.refetchInterval]); + const refreshSummary = useCallback(() => { return fetchSummary(); }, [fetchSummary]); diff --git a/surfsense_web/messages/en.json b/surfsense_web/messages/en.json index b15b611c6..167a87dbc 100644 --- a/surfsense_web/messages/en.json +++ b/surfsense_web/messages/en.json @@ -269,7 +269,9 @@ "filter_placeholder": "Filter by title...", "rows_per_page": "Rows per page", "refresh": "Refresh", - "refresh_success": "Documents refreshed" + "refresh_success": "Documents refreshed", + "processing_documents": "Processing documents...", + "active_tasks_count": "{count} active task(s)" }, "add_connector": { "title": "Connect Your Tools", diff --git a/surfsense_web/messages/zh.json b/surfsense_web/messages/zh.json index efadf3a4e..3701a220d 100644 --- a/surfsense_web/messages/zh.json +++ b/surfsense_web/messages/zh.json @@ -269,7 +269,9 @@ "filter_placeholder": "按标题筛选...", "rows_per_page": "每页行数", "refresh": "刷新", - "refresh_success": "文档已刷新" + "refresh_success": "文档已刷新", + "processing_documents": "正在处理文档...", + "active_tasks_count": "{count} 个正在进行的工作项" }, "add_connector": { "title": "连接您的工具",