- {/* Desktop Table View */}
-
-
-
-
-
-
- toggleAll(!!v)}
- aria-label={hasChatMode ? "Toggle all for chat" : "Select all"}
- className="shrink-0"
- />
-
-
-
- }
- >
- Document
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- {loading ? (
-
-
-
- {[65, 80, 45, 72, 55, 88, 40, 60, 50, 75].map((widthPercent) => (
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- ))}
-
-
-
- ) : error ? (
-
-
-
-
{t("error_loading")}
-
-
- ) : sorted.length === 0 ? (
-
- {isSearchMode ? (
-
-
-
-
- No matching documents
-
-
- Try a different search term or adjust your filters.
-
-
-
- ) : (
-
-
-
-
-
-
{t("no_documents")}
-
- Get started by uploading your first document.
-
-
-
-
- )}
-
- ) : (
-
- {bulkDeleteBar}
-
-
- {sorted.map((doc) => {
- const isMentioned = mentionedDocIds?.has(doc.id) ?? false;
- const canInteract = isSelectable(doc);
- const isBeingProcessed =
- doc.status?.state === "pending" || doc.status?.state === "processing";
- const isFileFailed =
- doc.document_type === "FILE" && doc.status?.state === "failed";
- const isEditable = EDITABLE_DOCUMENT_TYPES.includes(
- doc.document_type as (typeof EDITABLE_DOCUMENT_TYPES)[number]
- );
- const shouldShowDelete = !NON_DELETABLE_DOCUMENT_TYPES.includes(
- doc.document_type as (typeof NON_DELETABLE_DOCUMENT_TYPES)[number]
- );
- const isMenuOpen = openMenuDocId === doc.id;
- const handleRowToggle = () => {
- if (canInteract && onToggleChatMention) {
- onToggleChatMention(doc, isMentioned);
- }
- };
- const handleRowClick = (e: React.MouseEvent) => {
- if (e.ctrlKey || e.metaKey) {
- e.preventDefault();
- e.stopPropagation();
- handleViewMetadata(doc);
- return;
- }
- handleRowToggle();
- };
- return (
-
- e.stopPropagation()}
- >
-
- {(() => {
- const state = doc.status?.state ?? "ready";
- if (state === "pending" || state === "processing") {
- return ;
- }
- if (state === "failed") {
- if (isMentioned) {
- return (
- handleRowToggle()}
- aria-label="Remove from chat"
- className="shrink-0"
- />
- );
- }
- return (
- <>
-
-
-
-
- handleRowToggle()}
- aria-label="Add to chat"
- className="shrink-0"
- />
-
- >
- );
- }
- return (
- handleRowToggle()}
- aria-label={isMentioned ? "Remove from chat" : "Add to chat"}
- className="shrink-0"
- />
- );
- })()}
-
-
-
-
- {doc.title}
-
-
-
-
- {getDocumentTypeIcon(doc.document_type, "h-4 w-4")}
-
-
- e.stopPropagation()}
- >
-
- {(() => {
- const member = doc.created_by_id
- ? memberMap.get(doc.created_by_id)
- : null;
- const displayName =
- member?.name ||
- doc.created_by_name ||
- doc.created_by_email ||
- "Unknown";
- const avatarUrl = member?.avatarUrl;
- const email = member?.email || doc.created_by_email || displayName;
- return (
-
-
-
-
- {avatarUrl && (
-
- )}
-
- {getInitials(displayName)}
-
-
-
-
- {email}
-
- );
- })()}
-
-
setOpenMenuDocId(open ? doc.id : null)}
- >
-
-
-
-
-
- onOpenInTab ? onOpenInTab(doc) : handleViewDocument(doc)
- }
- disabled={isBeingProcessed}
- >
-
- Open
-
- {isEditable && (
- {
- if (!(isBeingProcessed || isFileFailed)) {
- openEditor({
- documentId: doc.id,
- searchSpaceId: Number(searchSpaceId),
- title: doc.title,
- });
- }
- }}
- disabled={isBeingProcessed || isFileFailed}
- >
-
- Edit
-
- )}
- {shouldShowDelete && (
- !isBeingProcessed && setDeleteDoc(doc)}
- disabled={isBeingProcessed}
- className=""
- >
-
- Delete
-
- )}
-
-
-
-
-
-
- );
- })}
-
-
- {hasMore &&
}
-
- )}
-
-
- {/* Mobile Card View */}
- {loading ? (
-
- {[70, 85, 55, 78, 62, 90].map((widthPercent) => (
-
- ))}
-
- ) : error ? (
-
-
-
-
{t("error_loading")}
-
-
- ) : sorted.length === 0 ? (
-
- {isSearchMode ? (
-
-
-
-
No matching documents
-
- Try a different search term or adjust your filters.
-
-
-
- ) : (
-
-
-
-
-
-
{t("no_documents")}
-
- Get started by uploading your first document.
-
-
-
-
- )}
-
- ) : (
-
- {bulkDeleteBar}
- {sorted.map((doc) => {
- const isMentioned = mentionedDocIds?.has(doc.id) ?? false;
- const statusState = doc.status?.state ?? "ready";
- const showCheckbox = statusState === "ready";
- const canInteract = showCheckbox;
- const handleCardClick = (e?: React.MouseEvent) => {
- if (e && (e.ctrlKey || e.metaKey)) {
- e.preventDefault();
- e.stopPropagation();
- handleViewMetadata(doc);
- return;
- }
- if (canInteract && onToggleChatMention) {
- onToggleChatMention(doc, isMentioned);
- }
- };
- return (
-
setMobileActionDoc(doc)}>
-
- {canInteract && hasChatMode && (
-
- )}
-
-
- {showCheckbox ? (
- handleCardClick()}
- aria-label={isMentioned ? "Remove from chat" : "Add to chat"}
- className="shrink-0"
- />
- ) : (
-
- )}
-
-
- {doc.title}
-
-
- {getDocumentTypeIcon(doc.document_type, "h-4 w-4")}
-
- {(() => {
- const member = doc.created_by_id ? memberMap.get(doc.created_by_id) : null;
- const displayName =
- member?.name || doc.created_by_name || doc.created_by_email || "Unknown";
- const avatarUrl = member?.avatarUrl;
- return (
-
-
- {avatarUrl && }
-
- {getInitials(displayName)}
-
-
-
- );
- })()}
-
-
-
- );
- })}
- {hasMore &&
}
-
- )}
-
- {/* Document Content Viewer (mobile drawer) */}
-
!open && handleCloseViewer()}>
-
-
-
-
- {viewingDoc?.title}
-
-
-
- {viewingLoading ? (
-
-
-
- ) : (
- <>
-
- {viewingDoc && (
-
-
-
- )}
- >
- )}
-
-
-
-
- {/* Document Metadata Viewer (Ctrl+Click) */}
-
{
- if (!open) {
- setMetadataDoc(null);
- setMetadataJson(null);
- setMetadataLoading(false);
- }
- }}
- />
-
- {/* Delete Confirmation Dialog */}
- !open && setDeleteDoc(null)}>
-
-
- Delete document?
-
- This action cannot be undone. This will permanently delete this document from your
- search space.
-
-
-
- Cancel
- {
- e.preventDefault();
- handleDeleteFromMenu();
- }}
- disabled={isDeleting}
- className="relative bg-destructive text-destructive-foreground hover:bg-destructive/90"
- >
- Delete
- {isDeleting && }
-
-
-
-
-
- {/* Mobile Document Actions Drawer */}
- !open && setMobileActionDoc(null)}>
-
-
-
- {mobileActionDoc?.title}
-
-
- Owner:{" "}
- {mobileActionDoc?.created_by_name || mobileActionDoc?.created_by_email || "—"}
-
-
- Created:{" "}
- {mobileActionDoc ? formatAbsoluteDate(mobileActionDoc.created_at) : ""}
-
-
-
-
-
- {mobileActionDoc &&
- EDITABLE_DOCUMENT_TYPES.includes(
- mobileActionDoc.document_type as (typeof EDITABLE_DOCUMENT_TYPES)[number]
- ) && (
-
- )}
- {mobileActionDoc &&
- !NON_DELETABLE_DOCUMENT_TYPES.includes(
- mobileActionDoc.document_type as (typeof NON_DELETABLE_DOCUMENT_TYPES)[number]
- ) && (
-
- )}
-
-
-
-
- {/* Bulk Delete Confirmation Dialog */}
- !open && !isBulkDeleting && setBulkDeleteConfirmOpen(false)}
- >
-
-
-
- Delete {deletableSelectedIds.length} document
- {deletableSelectedIds.length !== 1 ? "s" : ""}?
-
-
- This action cannot be undone.{" "}
- {deletableSelectedIds.length === 1
- ? "This document"
- : `These ${deletableSelectedIds.length} documents`}{" "}
- will be permanently deleted from your search space.
-
-
-
- Cancel
- {
- e.preventDefault();
- handleBulkDelete();
- }}
- disabled={isBulkDeleting}
- className="relative bg-destructive text-destructive-foreground hover:bg-destructive/90"
- >
- Delete
- {isBulkDeleting && }
-
-
-
-
-
- );
-}
diff --git a/surfsense_web/app/dashboard/[search_space_id]/documents/(manage)/components/PaginationControls.tsx b/surfsense_web/app/dashboard/[search_space_id]/documents/(manage)/components/PaginationControls.tsx
deleted file mode 100644
index 03d5066b2..000000000
--- a/surfsense_web/app/dashboard/[search_space_id]/documents/(manage)/components/PaginationControls.tsx
+++ /dev/null
@@ -1,90 +0,0 @@
-"use client";
-
-import { ChevronFirst, ChevronLast, ChevronLeft, ChevronRight } from "lucide-react";
-import { motion } from "motion/react";
-import { Button } from "@/components/ui/button";
-
-const PAGE_SIZE = 50;
-
-export function PaginationControls({
- pageIndex,
- total,
- onFirst,
- onPrev,
- onNext,
- onLast,
- canPrev,
- canNext,
-}: {
- pageIndex: number;
- total: number;
- onFirst: () => void;
- onPrev: () => void;
- onNext: () => void;
- onLast: () => void;
- canPrev: boolean;
- canNext: boolean;
-}) {
- const start = pageIndex * PAGE_SIZE + 1;
- const end = Math.min((pageIndex + 1) * PAGE_SIZE, total);
-
- return (
-