"use client"; import React, { useState, useEffect } from "react"; import { ColumnDef, flexRender, getCoreRowModel, getPaginationRowModel, getSortedRowModel, useReactTable, SortingState, RowSelectionState, } from "@tanstack/react-table"; import { useQuery } from "@tanstack/react-query"; interface FileData { id: string; filename: string; mimetype: string; size: number; createdAt: string; updatedAt: string; } interface FilesTableProps { onFileAdded?: (file: FileData) => void; } export function FilesTable({ onFileAdded }: FilesTableProps) { const [sorting, setSorting] = useState([]); const [rowSelection, setRowSelection] = useState({}); const [files, setFiles] = useState([]); const { data, isLoading, isError, error, refetch } = useQuery({ queryKey: ["files"], queryFn: async () => { const response = await fetch("/api/files"); if (!response.ok) { throw new Error("Failed to fetch files"); } const data = await response.json(); return data.files as FileData[]; }, }); // Update local files state when data changes useEffect(() => { if (data) { setFiles(data); } }, [data]); // Method to add a new file to the table const addFile = (file: FileData) => { setFiles(prevFiles => [file, ...prevFiles]); // Call the onFileAdded prop if provided if (onFileAdded) { onFileAdded(file); } }; // Make addFile available globally for the upload form useEffect(() => { (window as any).addFileToTable = addFile; return () => { delete (window as any).addFileToTable; }; }, [onFileAdded]); const columns: ColumnDef[] = [ { id: "select", header: ({ table }) => (
table.toggleAllPageRowsSelected(!!e.target.checked)} className="w-4 h-4 text-blue-600 bg-gray-100 border-gray-300 rounded focus:ring-blue-500" />
), cell: ({ row }) => (
row.toggleSelected(!!e.target.checked)} className="w-4 h-4 text-blue-600 bg-gray-100 border-gray-300 rounded focus:ring-blue-500" />
), enableSorting: false, enableHiding: false, }, { accessorKey: "filename", header: "File Name", cell: ({ row }) => (
{row.getValue("filename")}
), }, { accessorKey: "mimetype", header: "Type", cell: ({ row }) => (
{row.getValue("mimetype")}
), }, { accessorKey: "size", header: "Size", cell: ({ row }) => { const bytes = row.getValue("size") as number; if (bytes === 0) return "0 Bytes"; const k = 1024; const sizes = ["Bytes", "KB", "MB", "GB"]; const i = Math.floor(Math.log(bytes) / Math.log(k)); return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + " " + sizes[i]; }, }, { accessorKey: "createdAt", header: "Created", cell: ({ row }) => { const date = new Date(row.getValue("createdAt")); return date.toLocaleDateString("en-US", { year: "numeric", month: "short", day: "numeric", hour: "2-digit", minute: "2-digit", }); }, }, { accessorKey: "updatedAt", header: "Updated", cell: ({ row }) => { const date = new Date(row.getValue("updatedAt")); return date.toLocaleDateString("en-US", { year: "numeric", month: "short", day: "numeric", hour: "2-digit", minute: "2-digit", }); }, }, { id: "actions", header: "Actions", cell: ({ row }) => (
), enableSorting: false, }, ]; const table = useReactTable({ data: files, columns, state: { sorting, rowSelection, }, onSortingChange: setSorting, onRowSelectionChange: setRowSelection, getCoreRowModel: getCoreRowModel(), getPaginationRowModel: getPaginationRowModel(), getSortedRowModel: getSortedRowModel(), enableRowSelection: true, }); const handleRefresh = () => { refetch(); }; const handleDownload = (fileId: string, filename: string) => { const downloadUrl = `/api/files/${fileId}`; const link = document.createElement('a'); link.href = downloadUrl; link.download = filename; document.body.appendChild(link); link.click(); document.body.removeChild(link); }; const handleDownloadSelected = () => { const selectedRows = table.getSelectedRowModel().rows; if (selectedRows.length === 0) return; if (selectedRows.length === 1) { // Single file download const selected = selectedRows[0]; handleDownload(selected.original.id, selected.original.filename); } else { // Multiple files - download each one selectedRows.forEach((row, index) => { setTimeout(() => { handleDownload(row.original.id, row.original.filename); }, index * 500); // Stagger downloads by 500ms to avoid browser blocking }); } }; const handleDeleteSelected = async () => { const selectedRows = table.getSelectedRowModel().rows; if (selectedRows.length === 0) return; const confirmDelete = window.confirm( `Are you sure you want to delete ${selectedRows.length} file${selectedRows.length > 1 ? 's' : ''}? This action cannot be undone.` ); if (!confirmDelete) return; try { const fileIds = selectedRows.map(row => row.original.id); const fileNames = selectedRows.map(row => row.original.filename); console.log(`Deleting ${fileIds.length} files:`, fileNames); // Process deletions sequentially to avoid race conditions const results = []; for (const row of selectedRows) { try { const response = await fetch(`/api/files/${row.original.id}`, { method: 'DELETE', }); if (!response.ok) { const errorText = await response.text(); console.error(`Failed to delete ${row.original.filename}:`, errorText); results.push({ id: row.original.id, success: false, error: errorText }); } else { console.log(`Successfully deleted: ${row.original.filename}`); results.push({ id: row.original.id, success: true }); } } catch (error) { console.error(`Error deleting ${row.original.filename}:`, error); results.push({ id: row.original.id, success: false, error: String(error) }); } } const successful = results.filter(r => r.success).length; const failed = results.filter(r => !r.success).length; if (failed > 0) { alert(`${successful} file(s) deleted successfully, ${failed} failed. Check console for details.`); } else { alert(`${successful} file(s) deleted successfully.`); } // Clear selection and refresh the table setRowSelection({}); refetch(); } catch (error) { console.error('Error in delete process:', error); alert('Failed to delete files. Please try again.'); } }; const handleDeleteFile = async (fileId: string, filename: string) => { const confirmDelete = window.confirm( `Are you sure you want to delete "${filename}"? This action cannot be undone.` ); if (!confirmDelete) return; try { const response = await fetch(`/api/files/${fileId}`, { method: 'DELETE', }); if (!response.ok) { throw new Error('Failed to delete file'); } refetch(); } catch (error) { console.error('Error deleting file:', error); alert('Failed to delete file. Please try again.'); } }; if (isLoading) { return (

Files in Database

{[...Array(5)].map((_, i) => (
))}
); } if (isError) { return (
Error: {error?.message || "Failed to load files"}
); } if (!data || data.length === 0) { return (

No files found

Upload some files to get started.

); } return (

Files in Database

{table.getHeaderGroups().map((headerGroup) => ( {headerGroup.headers.map((header) => ( ))} ))} {table.getRowModel().rows.map((row) => ( {row.getVisibleCells().map((cell) => ( ))} ))}
{flexRender(header.column.columnDef.header, header.getContext())} {header.column.getIsSorted() && ( {header.column.getIsSorted() === "asc" ? "↑" : "↓"} )}
{flexRender(cell.column.columnDef.cell, cell.getContext())}
{/* Pagination */}
Showing {table.getRowModel().rows.length} of {files.length} results
); }