mirror of
https://github.com/MODSetter/SurfSense.git
synced 2026-05-17 18:35:19 +02:00
feat: implement document deletion functionality and streamline column visibility management in DocumentsTable
This commit is contained in:
parent
d5fd4c2863
commit
e615a6478c
3 changed files with 50 additions and 68 deletions
|
|
@ -4,7 +4,6 @@ import { useSetAtom } from "jotai";
|
|||
import {
|
||||
CircleAlert,
|
||||
CircleX,
|
||||
Columns3,
|
||||
FilePlus2,
|
||||
FileType,
|
||||
ListFilter,
|
||||
|
|
@ -31,11 +30,9 @@ import {
|
|||
import { Button } from "@/components/ui/button";
|
||||
import { Checkbox } from "@/components/ui/checkbox";
|
||||
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 { getDocumentTypeIcon, getDocumentTypeLabel } from "./DocumentTypeIcon";
|
||||
import type { ColumnVisibility } from "./types";
|
||||
|
||||
export function DocumentsFilters({
|
||||
typeCounts: typeCountsRecord,
|
||||
|
|
@ -45,8 +42,6 @@ export function DocumentsFilters({
|
|||
onBulkDelete,
|
||||
onToggleType,
|
||||
activeTypes,
|
||||
columnVisibility,
|
||||
onToggleColumn,
|
||||
}: {
|
||||
typeCounts: Partial<Record<DocumentTypeEnum, number>>;
|
||||
selectedIds: Set<number>;
|
||||
|
|
@ -55,8 +50,6 @@ export function DocumentsFilters({
|
|||
onBulkDelete: () => Promise<void>;
|
||||
onToggleType: (type: DocumentTypeEnum, checked: boolean) => void;
|
||||
activeTypes: DocumentTypeEnum[];
|
||||
columnVisibility: ColumnVisibility;
|
||||
onToggleColumn: (id: keyof ColumnVisibility, checked: boolean) => void;
|
||||
}) {
|
||||
const t = useTranslations("documents");
|
||||
const id = React.useId();
|
||||
|
|
@ -252,57 +245,7 @@ export function DocumentsFilters({
|
|||
</PopoverContent>
|
||||
</Popover>
|
||||
|
||||
{/* View/Columns Popover */}
|
||||
<Popover>
|
||||
<PopoverTrigger asChild>
|
||||
<Button
|
||||
variant="outline"
|
||||
size="sm"
|
||||
className="h-9 gap-2 border-dashed border-border/60 text-muted-foreground hover:text-foreground hover:border-border"
|
||||
>
|
||||
<Columns3 size={14} className="text-muted-foreground" />
|
||||
<span className="hidden sm:inline">View</span>
|
||||
</Button>
|
||||
</PopoverTrigger>
|
||||
<PopoverContent className="w-36 !p-0 overflow-hidden" align="end">
|
||||
<div className="px-2.5 pt-3 pb-2">
|
||||
<div className="mb-1.5 px-1 text-[11px] font-medium text-muted-foreground">
|
||||
Toggle columns
|
||||
</div>
|
||||
<div className="space-y-0.5">
|
||||
{(
|
||||
[
|
||||
["document_type", "Source"],
|
||||
["created_by", "User"],
|
||||
["created_at", "Created"],
|
||||
] as Array<[keyof ColumnVisibility, string]>
|
||||
).map(([key, label], i) => (
|
||||
<button
|
||||
key={key}
|
||||
type="button"
|
||||
className="flex w-full items-center gap-2 py-1 px-2.5 rounded-md hover:bg-muted/50 transition-colors cursor-pointer text-left"
|
||||
onClick={() => onToggleColumn(key, !columnVisibility[key])}
|
||||
>
|
||||
<Checkbox
|
||||
id={`${id}-col-${i}`}
|
||||
checked={columnVisibility[key]}
|
||||
onCheckedChange={(checked: boolean) => onToggleColumn(key, !!checked)}
|
||||
className="h-3.5 w-3.5 flex-shrink-0 data-[state=checked]:bg-primary data-[state=checked]:border-primary"
|
||||
/>
|
||||
<Label
|
||||
htmlFor={`${id}-col-${i}`}
|
||||
className="flex flex-1 items-center gap-2 font-normal text-xs cursor-pointer min-w-0"
|
||||
>
|
||||
<span className="truncate min-w-0">{label}</span>
|
||||
</Label>
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</PopoverContent>
|
||||
</Popover>
|
||||
|
||||
{/* Bulk Delete Button - positioned next to View on mobile */}
|
||||
{/* Bulk Delete Button */}
|
||||
{selectedIds.size > 0 && (
|
||||
<AlertDialog>
|
||||
<AlertDialogTrigger asChild>
|
||||
|
|
|
|||
|
|
@ -28,6 +28,7 @@ import {
|
|||
import { Tooltip, TooltipContent, TooltipTrigger } from "@/components/ui/tooltip";
|
||||
import { documentsApiService } from "@/lib/apis/documents-api.service";
|
||||
import { DocumentTypeChip } from "./DocumentTypeIcon";
|
||||
import { RowActions } from "./RowActions";
|
||||
import type { ColumnVisibility, Document } from "./types";
|
||||
|
||||
export type SortKey = keyof Pick<Document, "title" | "document_type" | "created_at">;
|
||||
|
|
@ -142,6 +143,8 @@ export function DocumentsTableShell({
|
|||
sortKey,
|
||||
sortDesc,
|
||||
onSortChange,
|
||||
deleteDocument,
|
||||
searchSpaceId,
|
||||
}: {
|
||||
documents: Document[];
|
||||
loading: boolean;
|
||||
|
|
@ -153,6 +156,8 @@ export function DocumentsTableShell({
|
|||
sortKey: SortKey;
|
||||
sortDesc: boolean;
|
||||
onSortChange: (key: SortKey) => void;
|
||||
deleteDocument: (id: number) => Promise<boolean>;
|
||||
searchSpaceId: string;
|
||||
}) {
|
||||
const t = useTranslations("documents");
|
||||
const { openDialog } = useDocumentUploadDialog();
|
||||
|
|
@ -273,7 +278,7 @@ export function DocumentsTableShell({
|
|||
<Table className="table-fixed w-full">
|
||||
<TableHeader>
|
||||
<TableRow className="hover:bg-transparent border-b border-border/40">
|
||||
<TableHead className="w-8 px-0 text-center border-r border-border/40">
|
||||
<TableHead className="w-8 px-0 text-center">
|
||||
<div className="flex items-center justify-center h-full">
|
||||
<Skeleton className="h-4 w-4 rounded" />
|
||||
</div>
|
||||
|
|
@ -296,6 +301,9 @@ export function DocumentsTableShell({
|
|||
<Skeleton className="h-3 w-16" />
|
||||
</TableHead>
|
||||
)}
|
||||
<TableHead className="w-10">
|
||||
<span className="sr-only">Actions</span>
|
||||
</TableHead>
|
||||
</TableRow>
|
||||
</TableHeader>
|
||||
</Table>
|
||||
|
|
@ -307,7 +315,7 @@ export function DocumentsTableShell({
|
|||
key={`skeleton-${index}`}
|
||||
className="border-b border-border/40 hover:bg-transparent"
|
||||
>
|
||||
<TableCell className="w-8 px-0 py-2.5 text-center border-r border-border/40">
|
||||
<TableCell className="w-8 px-0 py-2.5 text-center">
|
||||
<div className="flex items-center justify-center h-full">
|
||||
<Skeleton className="h-4 w-4 rounded" />
|
||||
</div>
|
||||
|
|
@ -333,6 +341,9 @@ export function DocumentsTableShell({
|
|||
<Skeleton className="h-4 w-20" />
|
||||
</TableCell>
|
||||
)}
|
||||
<TableCell className="w-10 py-2.5 text-center">
|
||||
<Skeleton className="h-6 w-6 mx-auto rounded" />
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
))}
|
||||
</TableBody>
|
||||
|
|
@ -406,7 +417,7 @@ export function DocumentsTableShell({
|
|||
<Table className="table-fixed w-full">
|
||||
<TableHeader>
|
||||
<TableRow className="hover:bg-transparent border-b border-border/40">
|
||||
<TableHead className="w-8 px-0 text-center border-r border-border/40">
|
||||
<TableHead className="w-8 px-0 text-center">
|
||||
<div className="flex items-center justify-center h-full">
|
||||
<Checkbox
|
||||
checked={allSelectedOnPage || (someSelectedOnPage && "indeterminate")}
|
||||
|
|
@ -461,6 +472,9 @@ export function DocumentsTableShell({
|
|||
</SortableHeader>
|
||||
</TableHead>
|
||||
)}
|
||||
<TableHead className="w-10">
|
||||
<span className="sr-only">Actions</span>
|
||||
</TableHead>
|
||||
</TableRow>
|
||||
</TableHeader>
|
||||
</Table>
|
||||
|
|
@ -488,7 +502,7 @@ export function DocumentsTableShell({
|
|||
: "hover:bg-muted/30"
|
||||
}`}
|
||||
>
|
||||
<TableCell className="w-8 px-0 py-2.5 text-center border-r border-border/40">
|
||||
<TableCell className="w-8 px-0 py-2.5 text-center">
|
||||
<div className="flex items-center justify-center h-full">
|
||||
<Checkbox
|
||||
checked={isSelected}
|
||||
|
|
@ -549,6 +563,13 @@ export function DocumentsTableShell({
|
|||
</Tooltip>
|
||||
</TableCell>
|
||||
)}
|
||||
<TableCell className="w-10 py-2.5 text-center">
|
||||
<RowActions
|
||||
document={doc}
|
||||
deleteDocument={deleteDocument}
|
||||
searchSpaceId={searchSpaceId}
|
||||
/>
|
||||
</TableCell>
|
||||
</motion.tr>
|
||||
);
|
||||
})}
|
||||
|
|
@ -626,6 +647,11 @@ export function DocumentsTableShell({
|
|||
)}
|
||||
</div>
|
||||
</div>
|
||||
<RowActions
|
||||
document={doc}
|
||||
deleteDocument={deleteDocument}
|
||||
searchSpaceId={searchSpaceId}
|
||||
/>
|
||||
</div>
|
||||
</motion.div>
|
||||
);
|
||||
|
|
|
|||
|
|
@ -138,10 +138,6 @@ export default function DocumentsTable() {
|
|||
setPageIndex(0);
|
||||
};
|
||||
|
||||
const onToggleColumn = (id: keyof ColumnVisibility, checked: boolean) => {
|
||||
setColumnVisibility((prev) => ({ ...prev, [id]: checked }));
|
||||
};
|
||||
|
||||
const [isRefreshing, setIsRefreshing] = useState(false);
|
||||
|
||||
const refreshCurrentView = useCallback(async () => {
|
||||
|
|
@ -193,6 +189,23 @@ export default function DocumentsTable() {
|
|||
}
|
||||
};
|
||||
|
||||
// Single document delete handler for RowActions
|
||||
const handleDeleteDocument = useCallback(async (id: number): Promise<boolean> => {
|
||||
try {
|
||||
await deleteDocumentMutation({ id });
|
||||
toast.success(t("delete_success") || "Document deleted");
|
||||
// If in search mode, refetch search results to reflect deletion
|
||||
if (isSearchMode) {
|
||||
await refetchSearch();
|
||||
}
|
||||
// Real-time mode: Electric will sync the deletion automatically
|
||||
return true;
|
||||
} catch (e) {
|
||||
console.error("Error deleting document:", e);
|
||||
return false;
|
||||
}
|
||||
}, [deleteDocumentMutation, isSearchMode, refetchSearch, t]);
|
||||
|
||||
const handleSortChange = useCallback((key: SortKey) => {
|
||||
setSortKey((currentKey) => {
|
||||
if (currentKey === key) {
|
||||
|
|
@ -237,8 +250,6 @@ export default function DocumentsTable() {
|
|||
onBulkDelete={onBulkDelete}
|
||||
onToggleType={onToggleType}
|
||||
activeTypes={activeTypes}
|
||||
columnVisibility={columnVisibility}
|
||||
onToggleColumn={onToggleColumn}
|
||||
/>
|
||||
|
||||
{/* Table */}
|
||||
|
|
@ -253,6 +264,8 @@ export default function DocumentsTable() {
|
|||
sortKey={sortKey}
|
||||
sortDesc={sortDesc}
|
||||
onSortChange={handleSortChange}
|
||||
deleteDocument={handleDeleteDocument}
|
||||
searchSpaceId={String(searchSpaceId)}
|
||||
/>
|
||||
|
||||
{/* Pagination */}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue