diff --git a/surfsense_web/app/dashboard/[search_space_id]/documents/(manage)/components/DocumentsFilters.tsx b/surfsense_web/app/dashboard/[search_space_id]/documents/(manage)/components/DocumentsFilters.tsx index a0f079e7d..6a7503834 100644 --- a/surfsense_web/app/dashboard/[search_space_id]/documents/(manage)/components/DocumentsFilters.tsx +++ b/surfsense_web/app/dashboard/[search_space_id]/documents/(manage)/components/DocumentsFilters.tsx @@ -27,8 +27,8 @@ import { import { Input } from "@/components/ui/input"; import { Label } from "@/components/ui/label"; import { Popover, PopoverContent, PopoverTrigger } from "@/components/ui/popover"; +import type { DocumentTypeEnum } from "@/contracts/types/document.types"; import type { ColumnVisibility } from "./types"; -import { DocumentTypeEnum } from "@/contracts/types/document.types"; const fadeInScale: Variants = { hidden: { opacity: 0, scale: 0.95 }, @@ -157,7 +157,7 @@ export function DocumentsFilters({
Filters
- {uniqueTypes.map((value : DocumentTypeEnum, i) => ( + {uniqueTypes.map((value: DocumentTypeEnum, i) => ( (value: T, delay = 250) { const [debounced, setDebounced] = useState(value); @@ -47,8 +46,8 @@ export default function DocumentsTable() { const [sortKey, setSortKey] = useState("title"); const [sortDesc, setSortDesc] = useState(false); const [selectedIds, setSelectedIds] = useState>(new Set()); - const {data: typeCounts} = useAtomValue(documentTypeCountsAtom) ; - const {mutateAsync : deleteDocumentMutation} = useAtomValue(deleteDocumentMutationAtom); + const { data: typeCounts } = useAtomValue(documentTypeCountsAtom); + const { mutateAsync: deleteDocumentMutation } = useAtomValue(deleteDocumentMutationAtom); // Build query parameters for fetching documents const queryParams = useMemo( @@ -78,7 +77,7 @@ export default function DocumentsTable() { data: documentsResponse, isLoading: isDocumentsLoading, refetch: refetchDocuments, - error : documentsError + error: documentsError, } = useQuery({ queryKey: cacheKeys.documents.globalQueryParams(queryParams), queryFn: () => documentsApiService.getDocuments({ queryParams }), @@ -91,7 +90,7 @@ export default function DocumentsTable() { data: searchResponse, isLoading: isSearchLoading, refetch: refetchSearch, - error: searchError + error: searchError, } = useQuery({ queryKey: cacheKeys.documents.globalQueryParams(searchQueryParams), queryFn: () => documentsApiService.searchDocuments({ queryParams: searchQueryParams }), @@ -100,14 +99,12 @@ export default function DocumentsTable() { }); // Extract documents and total based on search state - const documents = debouncedSearch.trim() - ? searchResponse?.items || [] + const documents = debouncedSearch.trim() + ? searchResponse?.items || [] : documentsResponse?.items || []; - const total = debouncedSearch.trim() - ? searchResponse?.total || 0 - : documentsResponse?.total || 0; + const total = debouncedSearch.trim() ? searchResponse?.total || 0 : documentsResponse?.total || 0; const loading = debouncedSearch.trim() ? isSearchLoading : isDocumentsLoading; - const error = debouncedSearch.trim() ? searchError : documentsError + const error = debouncedSearch.trim() ? searchError : documentsError; // Display server-filtered results directly const displayDocs = documents || []; diff --git a/surfsense_web/components/chat/ChatInputGroup.tsx b/surfsense_web/components/chat/ChatInputGroup.tsx index 4e63048d1..97473891c 100644 --- a/surfsense_web/components/chat/ChatInputGroup.tsx +++ b/surfsense_web/components/chat/ChatInputGroup.tsx @@ -1,10 +1,10 @@ "use client"; import { ChatInput } from "@llamaindex/chat-ui"; +import { useAtom } from "jotai"; import { Brain, Check, FolderOpen, Minus, Plus, PlusCircle, Zap } from "lucide-react"; import { useParams, useRouter } from "next/navigation"; -import React, { Suspense, useCallback, useState, useMemo } from "react"; -import { useAtom } from "jotai"; +import React, { Suspense, useCallback, useMemo, useState } from "react"; import { documentTypeCountsAtom } from "@/atoms/documents/document-query.atoms"; import { DocumentsDataTable } from "@/components/chat/DocumentsDataTable"; import { Badge } from "@/components/ui/badge"; @@ -121,7 +121,11 @@ const ConnectorSelector = React.memo( // Use the documentTypeCountsAtom for fetching document types const [documentTypeCountsQuery] = useAtom(documentTypeCountsAtom); - const { data: documentTypeCountsData, isLoading, refetch: fetchDocumentTypes } = documentTypeCountsQuery; + const { + data: documentTypeCountsData, + isLoading, + refetch: fetchDocumentTypes, + } = documentTypeCountsQuery; // Transform the response into the expected format const documentTypes = useMemo(() => { diff --git a/surfsense_web/components/chat/DocumentsDataTable.tsx b/surfsense_web/components/chat/DocumentsDataTable.tsx index 53cee8299..77f1a05bd 100644 --- a/surfsense_web/components/chat/DocumentsDataTable.tsx +++ b/surfsense_web/components/chat/DocumentsDataTable.tsx @@ -1,5 +1,6 @@ "use client"; +import { useQuery } from "@tanstack/react-query"; import { type ColumnDef, flexRender, @@ -7,10 +8,11 @@ import { type SortingState, useReactTable, } from "@tanstack/react-table"; +import { useAtomValue } from "jotai"; import { ArrowUpDown, Calendar, FileText, Filter, Plus, Search } from "lucide-react"; import { useRouter } from "next/navigation"; import { useCallback, useEffect, useMemo, useRef, useState } from "react"; -import { useQuery } from "@tanstack/react-query"; +import { documentTypeCountsAtom } from "@/atoms/documents/document-query.atoms"; import { Button } from "@/components/ui/button"; import { Checkbox } from "@/components/ui/checkbox"; import { Input } from "@/components/ui/input"; @@ -32,11 +34,9 @@ import { TableRow, } from "@/components/ui/table"; import { getConnectorIcon } from "@/contracts/enums/connectorIcons"; +import type { Document, DocumentTypeEnum } from "@/contracts/types/document.types"; import { documentsApiService } from "@/lib/apis/documents-api.service"; import { cacheKeys } from "@/lib/query-client/cache-keys"; -import { Document, DocumentTypeEnum } from "@/contracts/types/document.types"; -import { useAtomValue } from "jotai"; -import { documentTypeCountsAtom } from "@/atoms/documents/document-query.atoms"; interface DocumentsDataTableProps { searchSpaceId: number; @@ -190,12 +190,12 @@ export function DocumentsDataTable({ const [documentTypeFilter, setDocumentTypeFilter] = useState([]); const [pageIndex, setPageIndex] = useState(0); const [pageSize, setPageSize] = useState(10); - const {data : typeCounts } = useAtomValue(documentTypeCountsAtom); + const { data: typeCounts } = useAtomValue(documentTypeCountsAtom); const fetchQueryParams = useMemo( () => ({ search_space_id: searchSpaceId, - page: pageIndex , + page: pageIndex, page_size: pageSize, ...(documentTypeFilter.length > 0 && { document_types: documentTypeFilter }), }), @@ -205,40 +205,36 @@ export function DocumentsDataTable({ const searchQueryParams = useMemo(() => { return { search_space_id: searchSpaceId, - page: pageIndex , + page: pageIndex, page_size: pageSize, ...(documentTypeFilter.length > 0 && { document_types: documentTypeFilter }), - title : debouncedSearch, - } - },[debouncedSearch, searchSpaceId, pageIndex, pageSize, documentTypeFilter, debouncedSearch]) + title: debouncedSearch, + }; + }, [debouncedSearch, searchSpaceId, pageIndex, pageSize, documentTypeFilter, debouncedSearch]); // Use query for fetching documents - const { - data: documents, - isLoading: isDocumentsLoading, - } = useQuery({ + const { data: documents, isLoading: isDocumentsLoading } = useQuery({ queryKey: cacheKeys.documents.withQueryParams(fetchQueryParams), - queryFn: () => documentsApiService.getDocuments({ queryParams : fetchQueryParams }), + queryFn: () => documentsApiService.getDocuments({ queryParams: fetchQueryParams }), staleTime: 3 * 60 * 1000, // 3 minutes - enabled: !!searchSpaceId && !debouncedSearch.trim(), + enabled: !!searchSpaceId && !debouncedSearch.trim(), }); // Seaching - const { - data: searchedDocuments, - isLoading: isSearchedDocumentsLoading, - } = useQuery({ + const { data: searchedDocuments, isLoading: isSearchedDocumentsLoading } = useQuery({ queryKey: cacheKeys.documents.withQueryParams(searchQueryParams), - queryFn: () => documentsApiService.searchDocuments({ queryParams : searchQueryParams }), + queryFn: () => documentsApiService.searchDocuments({ queryParams: searchQueryParams }), staleTime: 3 * 60 * 1000, // 3 minutes enabled: !!searchSpaceId && !!debouncedSearch.trim(), }); - - // Use query data when not searching, otherwise use hook data - const actualDocuments = debouncedSearch.trim() ? searchedDocuments?.items|| [] : documents?.items|| []; - const actualTotal = debouncedSearch.trim() ? searchedDocuments?.total || 0 : documents?.total || 0; + const actualDocuments = debouncedSearch.trim() + ? searchedDocuments?.items || [] + : documents?.items || []; + const actualTotal = debouncedSearch.trim() + ? searchedDocuments?.total || 0 + : documents?.total || 0; const actualLoading = debouncedSearch.trim() ? isSearchedDocumentsLoading : isDocumentsLoading; // Memoize initial row selection to prevent infinite loops @@ -370,7 +366,7 @@ export function DocumentsDataTable({ // Get available document types from type counts (memoized) const availableTypes = useMemo(() => { - const types = typeCounts ? Object.keys(typeCounts) as DocumentTypeEnum[] : []; + const types = typeCounts ? (Object.keys(typeCounts) as DocumentTypeEnum[]) : []; return types.length > 0 ? types.sort() : []; }, [typeCounts]); @@ -573,31 +569,31 @@ export function DocumentsDataTable({ {/* Footer Pagination */}
- Showing {pageIndex * pageSize + 1} to {Math.min((pageIndex + 1) * pageSize, actualTotal)} of{" "} - {actualTotal} documents + Showing {pageIndex * pageSize + 1} to {Math.min((pageIndex + 1) * pageSize, actualTotal)}{" "} + of {actualTotal} documents
Page - {pageIndex + 1} - of - {Math.ceil(actualTotal / pageSize)} -
+ {pageIndex + 1} + of + {Math.ceil(actualTotal / pageSize)} +
diff --git a/surfsense_web/components/sources/YouTubeTab.tsx b/surfsense_web/components/sources/YouTubeTab.tsx index 0a51d0dca..4bdaf2d37 100644 --- a/surfsense_web/components/sources/YouTubeTab.tsx +++ b/surfsense_web/components/sources/YouTubeTab.tsx @@ -2,12 +2,12 @@ import { IconBrandYoutube } from "@tabler/icons-react"; import { TagInput, type Tag as TagType } from "emblor"; +import { useAtom } from "jotai"; import { Loader2 } from "lucide-react"; import { motion } from "motion/react"; import { useRouter } from "next/navigation"; import { useTranslations } from "next-intl"; import { useState } from "react"; -import { useAtom } from "jotai"; import { toast } from "sonner"; import { createDocumentMutationAtom } from "@/atoms/documents/document-mutation.atoms"; diff --git a/surfsense_web/contracts/types/document.types.ts b/surfsense_web/contracts/types/document.types.ts index ae755f5f3..abffc68a5 100644 --- a/surfsense_web/contracts/types/document.types.ts +++ b/surfsense_web/contracts/types/document.types.ts @@ -161,7 +161,7 @@ export const deleteDocumentResponse = z.object({ message: z.literal("Document deleted successfully"), }); -export type Document = z.infer +export type Document = z.infer; export type GetDocumentsRequest = z.infer; export type GetDocumentsResponse = z.infer; export type GetDocumentRequest = z.infer; @@ -180,4 +180,4 @@ export type UpdateDocumentRequest = z.infer; export type UpdateDocumentResponse = z.infer; export type DeleteDocumentRequest = z.infer; export type DeleteDocumentResponse = z.infer; -export type DocumentTypeEnum = z.infer +export type DocumentTypeEnum = z.infer; diff --git a/surfsense_web/lib/query-client/cache-keys.ts b/surfsense_web/lib/query-client/cache-keys.ts index 72fac6dc5..033f7563b 100644 --- a/surfsense_web/lib/query-client/cache-keys.ts +++ b/surfsense_web/lib/query-client/cache-keys.ts @@ -15,7 +15,8 @@ export const cacheKeys = { documents: { globalQueryParams: (queries: GetDocumentsRequest["queryParams"]) => ["documents", ...(queries ? Object.values(queries) : [])] as const, - withQueryParams :(queries: GetDocumentsRequest["queryParams"]) => ["documents-with-queries", ...(queries ? Object.values(queries) : [])] as const, + withQueryParams: (queries: GetDocumentsRequest["queryParams"]) => + ["documents-with-queries", ...(queries ? Object.values(queries) : [])] as const, document: (documentId: string) => ["document", documentId] as const, typeCounts: (searchSpaceId?: string) => ["documents", "type-counts", searchSpaceId] as const, byChunk: (chunkId: string) => ["documents", "by-chunk", chunkId] as const,