mirror of
https://github.com/snapotter-hq/SnapOtter.git
synced 2026-08-03 07:46:42 +02:00
feat: implement Files page with persistent storage and version tracking
Three-panel file manager (nav, list, details) modeled after Stirling-PDF. - Backend: user_files table, /api/v1/files/* CRUD routes, file storage helpers, thumbnail generation via Sharp, recursive CTE version chains - Frontend: FilesNav, FileList, FileDetails, FileUploadArea components, Zustand store, mobile layout with bottom sheet - Integration: tool-factory auto-saves results as new versions when fileId is provided, "Open File" loads file into tool processing flow - Search, bulk select/delete/download, version badges, tool chain tags
This commit is contained in:
+8
-10
@@ -59,14 +59,12 @@ export function clearToken() {
|
||||
|
||||
// ── File Upload / Download ──────────────────────────────────────
|
||||
|
||||
export async function apiUpload(
|
||||
files: File[],
|
||||
): Promise<{
|
||||
export async function apiUpload(files: File[]): Promise<{
|
||||
jobId: string;
|
||||
files: Array<{ name: string; size: number; format: string }>;
|
||||
}> {
|
||||
const formData = new FormData();
|
||||
files.forEach((f) => formData.append("files", f));
|
||||
for (const f of files) formData.append("files", f);
|
||||
const res = await fetch("/api/v1/upload", {
|
||||
method: "POST",
|
||||
headers: { Authorization: `Bearer ${getToken()}` },
|
||||
@@ -118,14 +116,17 @@ export async function apiListFiles(params?: {
|
||||
}
|
||||
|
||||
export async function apiGetFileDetails(id: string): Promise<UserFileDetail> {
|
||||
return apiGet(`/v1/files/${id}`);
|
||||
const res = await apiGet<{ file: UserFile; versions: UserFileDetail["versions"] }>(
|
||||
`/v1/files/${id}`,
|
||||
);
|
||||
return { ...res.file, versions: res.versions };
|
||||
}
|
||||
|
||||
export async function apiUploadUserFiles(
|
||||
files: File[],
|
||||
): Promise<{ files: Array<{ id: string; originalName: string; size: number; version: number }> }> {
|
||||
const formData = new FormData();
|
||||
files.forEach((f) => formData.append("files", f));
|
||||
for (const f of files) formData.append("files", f);
|
||||
const res = await fetch("/api/v1/files/upload", {
|
||||
method: "POST",
|
||||
headers: { Authorization: `Bearer ${getToken()}` },
|
||||
@@ -156,10 +157,7 @@ export function getFileDownloadUrl(id: string): string {
|
||||
return `/api/v1/files/${id}/download`;
|
||||
}
|
||||
|
||||
export async function apiDownloadBlob(
|
||||
jobId: string,
|
||||
filename: string,
|
||||
): Promise<Blob> {
|
||||
export async function apiDownloadBlob(jobId: string, filename: string): Promise<Blob> {
|
||||
const res = await fetch(getDownloadUrl(jobId, filename), {
|
||||
headers: { Authorization: `Bearer ${getToken()}` },
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user