Przeglądaj źródła

feat(files): add row selection and download functionality to file management table

vtugulan 6 miesięcy temu
rodzic
commit
b887bf2356
1 zmienionych plików z 104 dodań i 22 usunięć
  1. 104 22
      app/components/filesTable.tsx

+ 104 - 22
app/components/filesTable.tsx

@@ -9,8 +9,9 @@ import {
   getSortedRowModel,
   useReactTable,
   SortingState,
+  RowSelectionState,
 } from "@tanstack/react-table";
-import { useQuery, useQueryClient } from "@tanstack/react-query";
+import { useQuery } from "@tanstack/react-query";
 
 interface FileData {
   id: string;
@@ -22,6 +23,31 @@ interface FileData {
 }
 
 const columns: ColumnDef<FileData>[] = [
+  {
+    id: "select",
+    header: ({ table }) => (
+      <div className="px-6 py-3">
+        <input
+          type="checkbox"
+          checked={table.getIsAllPageRowsSelected()}
+          onChange={(e) => table.toggleAllPageRowsSelected(!!e.target.checked)}
+          className="w-4 h-4 text-blue-600 bg-gray-100 border-gray-300 rounded focus:ring-blue-500"
+        />
+      </div>
+    ),
+    cell: ({ row }) => (
+      <div className="px-6 py-4">
+        <input
+          type="checkbox"
+          checked={row.getIsSelected()}
+          onChange={(e) => row.toggleSelected(!!e.target.checked)}
+          className="w-4 h-4 text-blue-600 bg-gray-100 border-gray-300 rounded focus:ring-blue-500"
+        />
+      </div>
+    ),
+    enableSorting: false,
+    enableHiding: false,
+  },
   {
     accessorKey: "filename",
     header: "File Name",
@@ -82,7 +108,7 @@ const columns: ColumnDef<FileData>[] = [
 
 export function FilesTable() {
   const [sorting, setSorting] = useState<SortingState>([]);
-  const queryClient = useQueryClient();
+  const [rowSelection, setRowSelection] = useState<RowSelectionState>({});
 
   const { data, isLoading, isError, error, refetch } = useQuery({
     queryKey: ["files"],
@@ -101,17 +127,48 @@ export function FilesTable() {
     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
+      });
+    }
+  };
+
   if (isLoading) {
     return (
       <div className="bg-white rounded-lg shadow-sm border border-gray-200">
@@ -168,26 +225,48 @@ export function FilesTable() {
       <div className="p-6">
         <div className="flex justify-between items-center mb-4">
           <h2 className="text-xl font-semibold text-gray-900">Files in Database</h2>
-          <button
-            onClick={handleRefresh}
-            disabled={isLoading}
-            className="px-4 py-2 bg-blue-600 text-white rounded-md hover:bg-blue-700 transition-colors disabled:opacity-50 disabled:cursor-not-allowed flex items-center"
-          >
-            <svg
-              className={`w-4 h-4 mr-2 ${isLoading ? "animate-spin" : ""}`}
-              fill="none"
-              stroke="currentColor"
-              viewBox="0 0 24 24"
+          <div className="flex gap-2">
+            <button
+              onClick={handleDownloadSelected}
+              disabled={table.getSelectedRowModel().rows.length === 0 || isLoading}
+              className="px-4 py-2 bg-green-600 text-white rounded-md hover:bg-green-700 transition-colors disabled:opacity-50 disabled:cursor-not-allowed flex items-center"
             >
-              <path
-                strokeLinecap="round"
-                strokeLinejoin="round"
-                strokeWidth={2}
-                d="M4 4v5h.582m15.356 2A8.001 8.001 0 004.582 9m0 0H9m11 11v-5h-.581m0 0a8.003 8.003 0 01-15.357-2m15.357 2H15"
-              />
-            </svg>
-            Refresh
-          </button>
+              <svg
+                className="w-4 h-4 mr-2"
+                fill="none"
+                stroke="currentColor"
+                viewBox="0 0 24 24"
+              >
+                <path
+                  strokeLinecap="round"
+                  strokeLinejoin="round"
+                  strokeWidth={2}
+                  d="M12 10v6m0 0l-3-3m3 3l3-3m2 8H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z"
+                />
+              </svg>
+              Download Selected
+            </button>
+            <button
+              onClick={handleRefresh}
+              disabled={isLoading}
+              className="px-4 py-2 bg-blue-600 text-white rounded-md hover:bg-blue-700 transition-colors disabled:opacity-50 disabled:cursor-not-allowed flex items-center"
+            >
+              <svg
+                className={`w-4 h-4 mr-2 ${isLoading ? "animate-spin" : ""}`}
+                fill="none"
+                stroke="currentColor"
+                viewBox="0 0 24 24"
+              >
+                <path
+                  strokeLinecap="round"
+                  strokeLinejoin="round"
+                  strokeWidth={2}
+                  d="M4 4v5h.582m15.356 2A8.001 8.001 0 004.582 9m0 0H9m11 11v-5h-.581m0 0a8.003 8.003 0 01-15.357-2m15.357 2H15"
+                />
+              </svg>
+              Refresh
+            </button>
+          </div>
         </div>
 
         <div className="overflow-x-auto">
@@ -216,7 +295,10 @@ export function FilesTable() {
             </thead>
             <tbody className="bg-white divide-y divide-gray-200">
               {table.getRowModel().rows.map((row) => (
-                <tr key={row.id} className="hover:bg-gray-50">
+                <tr
+                  key={row.id}
+                  className={`hover:bg-gray-50 ${row.getIsSelected() ? 'bg-blue-50' : ''}`}
+                >
                   {row.getVisibleCells().map((cell) => (
                     <td key={cell.id} className="px-6 py-4 whitespace-nowrap text-sm text-gray-900">
                       {flexRender(cell.column.columnDef.cell, cell.getContext())}