mirror of
https://github.com/MODSetter/SurfSense.git
synced 2026-06-28 21:49:40 +02:00
Reapply "Merge pull request #686 from AnishSarkar22/feat/replace-logs"
This reverts commit 3418c0e026.
This commit is contained in:
parent
3418c0e026
commit
8aad15d392
84 changed files with 8266 additions and 4613 deletions
|
|
@ -1,6 +1,6 @@
|
|||
"use client";
|
||||
|
||||
import { ChevronDown, ChevronUp, FileX, Plus } from "lucide-react";
|
||||
import { ChevronDown, ChevronUp, FileX, Loader2, Plus } from "lucide-react";
|
||||
import { motion } from "motion/react";
|
||||
import { useParams } from "next/navigation";
|
||||
import { useTranslations } from "next-intl";
|
||||
|
|
@ -114,7 +114,7 @@ export function DocumentsTableShell({
|
|||
{loading ? (
|
||||
<div className="flex h-[400px] w-full items-center justify-center">
|
||||
<div className="flex flex-col items-center gap-2">
|
||||
<div className="h-8 w-8 animate-spin rounded-full border-b-2 border-primary"></div>
|
||||
<Loader2 className="h-8 w-8 animate-spin text-primary" />
|
||||
<p className="text-sm text-muted-foreground">{t("loading")}</p>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -1,44 +0,0 @@
|
|||
"use client";
|
||||
|
||||
import { Loader2 } from "lucide-react";
|
||||
import { AnimatePresence, motion } from "motion/react";
|
||||
import { useTranslations } from "next-intl";
|
||||
import { Alert, AlertDescription, AlertTitle } from "@/components/ui/alert";
|
||||
|
||||
interface ProcessingIndicatorProps {
|
||||
documentProcessorTasksCount: number;
|
||||
}
|
||||
|
||||
export function ProcessingIndicator({ documentProcessorTasksCount }: ProcessingIndicatorProps) {
|
||||
const t = useTranslations("documents");
|
||||
|
||||
// Only show when there are document_processor tasks (uploads), not connector_indexing_task (periodic reindexing)
|
||||
if (documentProcessorTasksCount === 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-border 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: documentProcessorTasksCount })}
|
||||
</AlertDescription>
|
||||
</div>
|
||||
</div>
|
||||
</Alert>
|
||||
</motion.div>
|
||||
</AnimatePresence>
|
||||
);
|
||||
}
|
||||
|
|
@ -6,20 +6,18 @@ import { RefreshCw, SquarePlus, Upload } from "lucide-react";
|
|||
import { motion } from "motion/react";
|
||||
import { useParams, useRouter } from "next/navigation";
|
||||
import { useTranslations } from "next-intl";
|
||||
import { useCallback, useEffect, useId, useMemo, useRef, useState } from "react";
|
||||
import { useCallback, useEffect, useId, useMemo, useState } from "react";
|
||||
import { toast } from "sonner";
|
||||
import { deleteDocumentMutationAtom } from "@/atoms/documents/document-mutation.atoms";
|
||||
import { documentTypeCountsAtom } from "@/atoms/documents/document-query.atoms";
|
||||
import { useDocumentUploadDialog } from "@/components/assistant-ui/document-upload-popup";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import type { DocumentTypeEnum } from "@/contracts/types/document.types";
|
||||
import { useLogsSummary } from "@/hooks/use-logs";
|
||||
import { documentsApiService } from "@/lib/apis/documents-api.service";
|
||||
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) {
|
||||
|
|
@ -109,6 +107,52 @@ export default function DocumentsTable() {
|
|||
enabled: !!searchSpaceId && !!debouncedSearch.trim(),
|
||||
});
|
||||
|
||||
// Determine if we should show SurfSense docs (when no type filter or SURFSENSE_DOCS is selected)
|
||||
const showSurfsenseDocs =
|
||||
activeTypes.length === 0 || activeTypes.includes("SURFSENSE_DOCS" as DocumentTypeEnum);
|
||||
|
||||
// Use query for fetching SurfSense docs
|
||||
const {
|
||||
data: surfsenseDocsResponse,
|
||||
isLoading: isSurfsenseDocsLoading,
|
||||
refetch: refetchSurfsenseDocs,
|
||||
} = useQuery({
|
||||
queryKey: ["surfsense-docs", debouncedSearch, pageIndex, pageSize],
|
||||
queryFn: () =>
|
||||
documentsApiService.getSurfsenseDocs({
|
||||
queryParams: {
|
||||
page: pageIndex,
|
||||
page_size: pageSize,
|
||||
title: debouncedSearch.trim() || undefined,
|
||||
},
|
||||
}),
|
||||
staleTime: 3 * 60 * 1000, // 3 minutes
|
||||
enabled: showSurfsenseDocs,
|
||||
});
|
||||
|
||||
// Transform SurfSense docs to match the Document type
|
||||
const surfsenseDocsAsDocuments: Document[] = useMemo(() => {
|
||||
if (!surfsenseDocsResponse?.items) return [];
|
||||
return surfsenseDocsResponse.items.map((doc) => ({
|
||||
id: doc.id,
|
||||
title: doc.title,
|
||||
document_type: "SURFSENSE_DOCS",
|
||||
document_metadata: { source: doc.source },
|
||||
content: doc.content,
|
||||
created_at: new Date().toISOString(),
|
||||
search_space_id: -1, // Special value for global docs
|
||||
}));
|
||||
}, [surfsenseDocsResponse]);
|
||||
|
||||
// Merge type counts with SURFSENSE_DOCS count
|
||||
const typeCounts = useMemo(() => {
|
||||
const counts = { ...(rawTypeCounts || {}) };
|
||||
if (surfsenseDocsResponse?.total) {
|
||||
counts.SURFSENSE_DOCS = surfsenseDocsResponse.total;
|
||||
}
|
||||
return counts;
|
||||
}, [rawTypeCounts, surfsenseDocsResponse?.total]);
|
||||
|
||||
// Extract documents and total based on search state
|
||||
const documents = debouncedSearch.trim()
|
||||
? searchResponse?.items || []
|
||||
|
|
@ -150,30 +194,6 @@ export default function DocumentsTable() {
|
|||
}
|
||||
}, [debouncedSearch, refetchSearch, refetchDocuments, t, isRefreshing]);
|
||||
|
||||
// Set up smart polling for active tasks - only polls when tasks are in progress
|
||||
const { summary } = useLogsSummary(searchSpaceId, 24, {
|
||||
enablePolling: true,
|
||||
refetchInterval: 5000, // Poll every 5 seconds when tasks are active
|
||||
});
|
||||
|
||||
// Filter active tasks to only include document_processor tasks (uploads via "add sources")
|
||||
// Exclude connector_indexing_task tasks (periodic reindexing)
|
||||
const documentProcessorTasks =
|
||||
summary?.active_tasks.filter((task) => task.source === "document_processor") || [];
|
||||
const documentProcessorTasksCount = documentProcessorTasks.length;
|
||||
|
||||
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) => {
|
||||
|
|
@ -262,8 +282,6 @@ export default function DocumentsTable() {
|
|||
</div>
|
||||
</motion.div>
|
||||
|
||||
<ProcessingIndicator documentProcessorTasksCount={documentProcessorTasksCount} />
|
||||
|
||||
<DocumentsFilters
|
||||
typeCounts={rawTypeCounts ?? {}}
|
||||
selectedIds={selectedIds}
|
||||
|
|
|
|||
|
|
@ -438,9 +438,7 @@ export default function EditorPage() {
|
|||
{saving ? (
|
||||
<>
|
||||
<Loader2 className="h-3.5 w-3.5 md:h-4 md:w-4 animate-spin" />
|
||||
<span className="text-xs md:text-sm">
|
||||
{isNewNote ? "Creating..." : "Saving..."}
|
||||
</span>
|
||||
<span className="text-xs md:text-sm">{isNewNote ? "Creating" : "Saving"}</span>
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
|
|
|
|||
|
|
@ -1294,7 +1294,7 @@ function CreateInviteDialog({
|
|||
{creating ? (
|
||||
<>
|
||||
<Loader2 className="h-4 w-4 mr-2 animate-spin" />
|
||||
Creating...
|
||||
Creating
|
||||
</>
|
||||
) : (
|
||||
"Create Invite"
|
||||
|
|
@ -1471,7 +1471,7 @@ function CreateRoleDialog({
|
|||
{creating ? (
|
||||
<>
|
||||
<Loader2 className="h-4 w-4 mr-2 animate-spin" />
|
||||
Creating...
|
||||
Creating
|
||||
</>
|
||||
) : (
|
||||
"Create Role"
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@ import type { Metadata } from "next";
|
|||
import "./globals.css";
|
||||
import { RootProvider } from "fumadocs-ui/provider/next";
|
||||
import { Roboto } from "next/font/google";
|
||||
import { ElectricProvider } from "@/components/providers/ElectricProvider";
|
||||
import { I18nProvider } from "@/components/providers/I18nProvider";
|
||||
import { PostHogProvider } from "@/components/providers/PostHogProvider";
|
||||
import { ThemeProvider } from "@/components/theme/theme-provider";
|
||||
|
|
@ -102,7 +103,9 @@ export default function RootLayout({
|
|||
defaultTheme="light"
|
||||
>
|
||||
<RootProvider>
|
||||
<ReactQueryClientProvider>{children}</ReactQueryClientProvider>
|
||||
<ReactQueryClientProvider>
|
||||
<ElectricProvider>{children}</ElectricProvider>
|
||||
</ReactQueryClientProvider>
|
||||
<Toaster />
|
||||
</RootProvider>
|
||||
</ThemeProvider>
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue