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:
@@ -87,8 +87,7 @@ function deriveCompat(entries: FileEntry[], selectedIndex: number) {
|
||||
files: entries.map((e) => e.file),
|
||||
currentEntry: entry,
|
||||
hasFiles: entries.length > 0,
|
||||
allProcessed:
|
||||
entries.length > 0 && entries.every((e) => e.status === "completed"),
|
||||
allProcessed: entries.length > 0 && entries.every((e) => e.status === "completed"),
|
||||
selectedFileName: entry ? entry.file.name : null,
|
||||
selectedFileSize: entry ? entry.file.size : null,
|
||||
originalBlobUrl: entry ? entry.blobUrl : null,
|
||||
@@ -183,8 +182,7 @@ export const useFileStore = create<FileState>((set, get) => ({
|
||||
set({ entries, ...deriveCompat(entries, idx) });
|
||||
},
|
||||
|
||||
setBatchZip: (blob, filename) =>
|
||||
set({ batchZipBlob: blob, batchZipFilename: filename }),
|
||||
setBatchZip: (blob, filename) => set({ batchZipBlob: blob, batchZipFilename: filename }),
|
||||
|
||||
setProcessing: (v) => set({ processing: v }),
|
||||
|
||||
|
||||
@@ -1,10 +1,5 @@
|
||||
import { create } from "zustand";
|
||||
import {
|
||||
apiListFiles,
|
||||
apiUploadUserFiles,
|
||||
apiDeleteUserFiles,
|
||||
type UserFile,
|
||||
} from "@/lib/api";
|
||||
import { apiDeleteUserFiles, apiListFiles, apiUploadUserFiles, type UserFile } from "@/lib/api";
|
||||
|
||||
interface FilesPageState {
|
||||
files: UserFile[];
|
||||
@@ -75,12 +70,15 @@ export const useFilesPageStore = create<FilesPageState>((set, get) => ({
|
||||
toggleChecked: (id) => {
|
||||
const { checkedIds } = get();
|
||||
const next = new Set(checkedIds);
|
||||
if (next.has(id)) next.delete(id); else next.add(id);
|
||||
if (next.has(id)) next.delete(id);
|
||||
else next.add(id);
|
||||
set({ checkedIds: next });
|
||||
},
|
||||
toggleCheckAll: () => {
|
||||
const { files, checkedIds } = get();
|
||||
set({ checkedIds: checkedIds.size === files.length ? new Set() : new Set(files.map(f => f.id)) });
|
||||
set({
|
||||
checkedIds: checkedIds.size === files.length ? new Set() : new Set(files.map((f) => f.id)),
|
||||
});
|
||||
},
|
||||
setSearchQuery: (q) => set({ searchQuery: q }),
|
||||
setActiveTab: (tab) => set({ activeTab: tab }),
|
||||
|
||||
Reference in New Issue
Block a user