mirror of
https://github.com/MODSetter/SurfSense.git
synced 2026-05-12 01:02:39 +02:00
feat: Introduce document processing indicator UI, update localization.
This commit is contained in:
parent
65dc97df99
commit
a956b5ff87
6 changed files with 85 additions and 8 deletions
|
|
@ -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 (
|
||||
<AnimatePresence>
|
||||
<motion.div
|
||||
initial={{ opacity: 0, height: 0, marginBottom: 0 }}
|
||||
animate={{ opacity: 1, height: "auto", marginBottom: 24 }}
|
||||
exit={{ opacity: 0, height: 0, marginBottom: 0 }}
|
||||
transition={{ duration: 0.3 }}
|
||||
>
|
||||
<Alert className="border-primary/20 bg-primary/5">
|
||||
<div className="flex items-center gap-4">
|
||||
<div className="flex h-10 w-10 items-center justify-center rounded-full bg-primary/10">
|
||||
<Loader2 className="h-5 w-5 animate-spin text-primary" />
|
||||
</div>
|
||||
<div className="flex-1">
|
||||
<AlertTitle className="text-primary font-semibold">
|
||||
{t("processing_documents")}
|
||||
</AlertTitle>
|
||||
<AlertDescription className="text-muted-foreground">
|
||||
{t("active_tasks_count", { count: activeTasksCount })}
|
||||
</AlertDescription>
|
||||
</div>
|
||||
</div>
|
||||
</Alert>
|
||||
</motion.div>
|
||||
</AnimatePresence>
|
||||
);
|
||||
}
|
||||
|
|
@ -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<T>(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() {
|
|||
</Button>
|
||||
</motion.div>
|
||||
|
||||
<ProcessingIndicator activeTasksCount={activeTasksCount} />
|
||||
|
||||
<DocumentsFilters
|
||||
typeCounts={typeCounts ?? {}}
|
||||
selectedIds={selectedIds}
|
||||
|
|
|
|||
|
|
@ -220,10 +220,7 @@ const ThinkingStepsDisplay: FC<{ steps: ThinkingStep[]; isThreadRunning?: boolea
|
|||
{step.items && step.items.length > 0 && (
|
||||
<div className="mt-1 space-y-0.5">
|
||||
{step.items.map((item, idx) => (
|
||||
<ChainOfThoughtItem
|
||||
key={`${step.id}-item-${idx}`}
|
||||
className="text-xs"
|
||||
>
|
||||
<ChainOfThoughtItem key={`${step.id}-item-${idx}`} className="text-xs">
|
||||
{item}
|
||||
</ChainOfThoughtItem>
|
||||
))}
|
||||
|
|
|
|||
|
|
@ -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<LogSummary | null>(null);
|
||||
const [loading, setLoading] = useState(true);
|
||||
const [error, setError] = useState<string | null>(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]);
|
||||
|
|
|
|||
|
|
@ -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",
|
||||
|
|
|
|||
|
|
@ -269,7 +269,9 @@
|
|||
"filter_placeholder": "按标题筛选...",
|
||||
"rows_per_page": "每页行数",
|
||||
"refresh": "刷新",
|
||||
"refresh_success": "文档已刷新"
|
||||
"refresh_success": "文档已刷新",
|
||||
"processing_documents": "正在处理文档...",
|
||||
"active_tasks_count": "{count} 个正在进行的工作项"
|
||||
},
|
||||
"add_connector": {
|
||||
"title": "连接您的工具",
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue