|
@@ -11,7 +11,7 @@ import {
|
|
|
SortingState,
|
|
SortingState,
|
|
|
RowSelectionState,
|
|
RowSelectionState,
|
|
|
} from "@tanstack/react-table";
|
|
} from "@tanstack/react-table";
|
|
|
-import { useQuery } from "@tanstack/react-query";
|
|
|
|
|
|
|
+import { useQuery, useQueryClient } from "@tanstack/react-query";
|
|
|
import { Button } from "@/components/ui/button";
|
|
import { Button } from "@/components/ui/button";
|
|
|
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
|
|
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
|
|
|
import { Checkbox } from "@/components/ui/checkbox";
|
|
import { Checkbox } from "@/components/ui/checkbox";
|
|
@@ -25,6 +25,7 @@ import {
|
|
|
} from "@/components/ui/table";
|
|
} from "@/components/ui/table";
|
|
|
import { Skeleton } from "@/components/ui/skeleton";
|
|
import { Skeleton } from "@/components/ui/skeleton";
|
|
|
import { AlertCircle, Download, Trash2, RefreshCw } from "lucide-react";
|
|
import { AlertCircle, Download, Trash2, RefreshCw } from "lucide-react";
|
|
|
|
|
+import { Input } from "@/components/ui/input";
|
|
|
|
|
|
|
|
interface FileData {
|
|
interface FileData {
|
|
|
id: string;
|
|
id: string;
|
|
@@ -35,14 +36,13 @@ interface FileData {
|
|
|
updatedAt: string;
|
|
updatedAt: string;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
-interface FilesTableProps {
|
|
|
|
|
- onFileAdded?: (file: FileData) => void;
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-export function FilesTable({}: FilesTableProps) {
|
|
|
|
|
|
|
+export function FilesTable() {
|
|
|
const [sorting, setSorting] = useState<SortingState>([]);
|
|
const [sorting, setSorting] = useState<SortingState>([]);
|
|
|
const [rowSelection, setRowSelection] = useState<RowSelectionState>({});
|
|
const [rowSelection, setRowSelection] = useState<RowSelectionState>({});
|
|
|
const [files, setFiles] = useState<FileData[]>([]);
|
|
const [files, setFiles] = useState<FileData[]>([]);
|
|
|
|
|
+ const [isUploading, setIsUploading] = useState(false);
|
|
|
|
|
+ const [uploadProgress, setUploadProgress] = useState(0);
|
|
|
|
|
+ const queryClient = useQueryClient();
|
|
|
|
|
|
|
|
const { data, isLoading, isError, error, refetch } = useQuery({
|
|
const { data, isLoading, isError, error, refetch } = useQuery({
|
|
|
queryKey: ["files"],
|
|
queryKey: ["files"],
|
|
@@ -184,6 +184,45 @@ export function FilesTable({}: FilesTableProps) {
|
|
|
enableRowSelection: true,
|
|
enableRowSelection: true,
|
|
|
});
|
|
});
|
|
|
|
|
|
|
|
|
|
+ const handleFileUpload = async (event: React.ChangeEvent<HTMLInputElement>) => {
|
|
|
|
|
+ const file = event.target.files?.[0];
|
|
|
|
|
+ if (!file) return;
|
|
|
|
|
+
|
|
|
|
|
+ setIsUploading(true);
|
|
|
|
|
+ setUploadProgress(0);
|
|
|
|
|
+
|
|
|
|
|
+ const formData = new FormData();
|
|
|
|
|
+ formData.append("file", file);
|
|
|
|
|
+
|
|
|
|
|
+ try {
|
|
|
|
|
+ const response = await fetch("/api/upload", {
|
|
|
|
|
+ method: "POST",
|
|
|
|
|
+ body: formData,
|
|
|
|
|
+ });
|
|
|
|
|
+
|
|
|
|
|
+ const result = await response.json();
|
|
|
|
|
+
|
|
|
|
|
+ if (result.success) {
|
|
|
|
|
+ // Add new file to the beginning of the list
|
|
|
|
|
+ setFiles(prevFiles => [result.file, ...prevFiles]);
|
|
|
|
|
+
|
|
|
|
|
+ // Invalidate and refetch to ensure consistency
|
|
|
|
|
+ await queryClient.invalidateQueries({ queryKey: ["files"] });
|
|
|
|
|
+
|
|
|
|
|
+ // Reset file input
|
|
|
|
|
+ event.target.value = '';
|
|
|
|
|
+ } else {
|
|
|
|
|
+ alert(`Upload failed: ${result.error || 'Unknown error'}`);
|
|
|
|
|
+ }
|
|
|
|
|
+ } catch (error) {
|
|
|
|
|
+ console.error("Upload error:", error);
|
|
|
|
|
+ alert("Failed to upload file");
|
|
|
|
|
+ } finally {
|
|
|
|
|
+ setIsUploading(false);
|
|
|
|
|
+ setUploadProgress(0);
|
|
|
|
|
+ }
|
|
|
|
|
+ };
|
|
|
|
|
+
|
|
|
const handleRefresh = () => {
|
|
const handleRefresh = () => {
|
|
|
refetch();
|
|
refetch();
|
|
|
};
|
|
};
|
|
@@ -344,6 +383,17 @@ export function FilesTable({}: FilesTableProps) {
|
|
|
<div className="flex justify-between items-center">
|
|
<div className="flex justify-between items-center">
|
|
|
<CardTitle>Files in Database</CardTitle>
|
|
<CardTitle>Files in Database</CardTitle>
|
|
|
<div className="flex gap-2">
|
|
<div className="flex gap-2">
|
|
|
|
|
+ <div className="flex items-center gap-2">
|
|
|
|
|
+ <Input
|
|
|
|
|
+ type="file"
|
|
|
|
|
+ onChange={handleFileUpload}
|
|
|
|
|
+ disabled={isUploading}
|
|
|
|
|
+ className="w-64 text-sm"
|
|
|
|
|
+ />
|
|
|
|
|
+ {isUploading && (
|
|
|
|
|
+ <span className="text-sm text-muted-foreground">Uploading...</span>
|
|
|
|
|
+ )}
|
|
|
|
|
+ </div>
|
|
|
<Button
|
|
<Button
|
|
|
onClick={handleDownloadSelected}
|
|
onClick={handleDownloadSelected}
|
|
|
disabled={table.getSelectedRowModel().rows.length === 0 || isLoading}
|
|
disabled={table.getSelectedRowModel().rows.length === 0 || isLoading}
|
|
@@ -419,7 +469,7 @@ export function FilesTable({}: FilesTableProps) {
|
|
|
|
|
|
|
|
<div className="flex items-center justify-between mt-4">
|
|
<div className="flex items-center justify-between mt-4">
|
|
|
<div className="text-sm text-muted-foreground">
|
|
<div className="text-sm text-muted-foreground">
|
|
|
- {table.getRowModel().rows.length} of {files.length} row(s) selected
|
|
|
|
|
|
|
+ {table.getSelectedRowModel().rows.length} of {files.length} row(s) selected
|
|
|
</div>
|
|
</div>
|
|
|
<div className="flex gap-2">
|
|
<div className="flex gap-2">
|
|
|
<Button
|
|
<Button
|