"use client"; import { WithStringId } from "../../../../lib/types/types"; import { DataSourceDoc, DataSource } from "../../../../lib/types/datasource_types"; import { z } from "zod"; import { useCallback, useEffect, useState } from "react"; import { useDropzone } from "react-dropzone"; import { deleteDocsFromDataSource, getUploadUrlsForFilesDataSource, addDocsToDataSource, getDownloadUrlForFile, listDocsInDataSource } from "../../../../actions/datasource_actions"; import { RelativeTime } from "@primer/react"; import { Pagination, Spinner } from "@heroui/react"; import { DownloadIcon } from "lucide-react"; import { Section } from "./section"; function FileListItem({ projectId, sourceId, file, onDelete, }: { projectId: string, sourceId: string, file: WithStringId>, onDelete: (fileId: string) => Promise; }) { const [isDeleting, setIsDeleting] = useState(false); const [isDownloading, setIsDownloading] = useState(false); const handleDeleteClick = async () => { setIsDeleting(true); try { await onDelete(file._id); } finally { setIsDeleting(false); } }; const handleDownloadClick = async () => { setIsDownloading(true); try { const url = await getDownloadUrlForFile(projectId, sourceId, file._id); window.open(url, '_blank'); } catch (error) { console.error('Download failed:', error); // TODO: Add error handling } finally { setIsDownloading(false); } }; if (file.data.type !== 'file_local' && file.data.type !== 'file_s3') { return null; } return (

{file.name}

{isDownloading ? ( ) : ( )}

uploaded - {formatFileSize(file.data.size)}

); } function PaginatedFileList({ projectId, sourceId, handleReload, onDelete, }: { projectId: string, sourceId: string, handleReload: () => void; onDelete: (fileId: string) => Promise; }) { const [files, setFiles] = useState>[]>([]); const [page, setPage] = useState(1); const [total, setTotal] = useState(0); const [loading, setLoading] = useState(false); const totalPages = Math.ceil(total / 10); useEffect(() => { let ignore = false; async function fetchFiles() { setLoading(true); try { const { files, total } = await listDocsInDataSource({ projectId, sourceId, page, limit: 10, }); if (!ignore) { setFiles(files); setTotal(total); } } catch (error) { console.error('Error fetching files:', error); } finally { setLoading(false); } } fetchFiles(); return () => { ignore = true; } }, [projectId, sourceId, page]); return (
UPLOADED FILES ({total})
{loading ? (

Loading files...

) : files.length === 0 ? (

No files uploaded yet

) : (
{files.map(file => ( ))} {totalPages > 1 && (
)}
)}
); } export function FilesSource({ projectId, dataSource, handleReload, type, }: { projectId: string, dataSource: WithStringId>, handleReload: () => void; type: 'files_local' | 'files_s3'; }) { const [uploading, setUploading] = useState(false); const [fileListKey, setFileListKey] = useState(0); const onDrop = useCallback(async (acceptedFiles: File[]) => { setUploading(true); try { const urls = await getUploadUrlsForFilesDataSource(projectId, dataSource._id, acceptedFiles.map(file => ({ name: file.name, type: file.type, size: file.size, }))); // Upload files in parallel await Promise.all(acceptedFiles.map(async (file, index) => { await fetch(urls[index].uploadUrl, { method: 'PUT', body: file, headers: { 'Content-Type': file.type, }, }); })); // After successful uploads, update the database with file information let docData: { _id: string, name: string, data: z.infer['data'] }[] = []; if (type === 'files_s3') { docData = acceptedFiles.map((file, index) => ({ _id: urls[index].fileId, name: file.name, data: { type: 'file_s3' as const, name: file.name, size: file.size, mimeType: file.type, s3Key: urls[index].path, }, })); } else { docData = acceptedFiles.map((file, index) => ({ _id: urls[index].fileId, name: file.name, data: { type: 'file_local' as const, name: file.name, size: file.size, mimeType: file.type, }, })); } await addDocsToDataSource({ projectId, sourceId: dataSource._id, docData, }); handleReload(); setFileListKey(prev => prev + 1); } catch (error) { console.error('Upload failed:', error); // TODO: Add error handling } finally { setUploading(false); } }, [projectId, dataSource._id, handleReload, type]); const { getRootProps, getInputProps, isDragActive } = useDropzone({ onDrop, disabled: uploading, accept: { 'application/pdf': ['.pdf'], // 'text/plain': ['.txt'], // 'application/msword': ['.doc'], // 'application/vnd.openxmlformats-officedocument.wordprocessingml.document': ['.docx'], }, }); return (
{uploading ? (

Uploading files...

) : isDragActive ? (

Drop the files here...

) : (

Drag and drop files here, or click to select files

Only PDF files are supported for now.

)}
{ await deleteDocsFromDataSource({ projectId, sourceId: dataSource._id, docIds: [docId], }); handleReload(); setFileListKey(prev => prev + 1); }} />
); } function formatFileSize(bytes: number): string { 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]; }