195 lines
5.8 KiB
TypeScript
195 lines
5.8 KiB
TypeScript
import { create } from 'zustand'
|
|
import { libraryApi } from './api'
|
|
import type { DocumentMeta, DocumentUpdateRequest } from './types'
|
|
|
|
interface LibraryState {
|
|
documents: DocumentMeta[]
|
|
total: number
|
|
isLoading: boolean
|
|
error: string | null
|
|
searchQuery: string
|
|
|
|
// Selection
|
|
selectedIds: Set<string>
|
|
toggleSelection: (id: string) => void
|
|
clearSelection: () => void
|
|
selectAll: () => void
|
|
|
|
// Actions
|
|
fetchDocuments: (query?: string) => Promise<void>
|
|
fetchTrash: () => Promise<void>
|
|
uploadDocument: (file: File) => Promise<DocumentMeta>
|
|
deleteDocument: (id: string, permanent?: boolean) => Promise<void>
|
|
restoreDocument: (id: string) => Promise<void>
|
|
bulkDelete: (permanent?: boolean) => Promise<void>
|
|
bulkRestore: () => Promise<void>
|
|
emptyTrash: () => Promise<void>
|
|
setSearchQuery: (query: string) => void
|
|
updateDocument: (id: string, data: DocumentUpdateRequest) => Promise<void>
|
|
}
|
|
|
|
export const useLibrary = create<LibraryState>((set, get) => ({
|
|
documents: [],
|
|
total: 0,
|
|
isLoading: false,
|
|
error: null,
|
|
searchQuery: '',
|
|
selectedIds: new Set(),
|
|
|
|
toggleSelection: (id) => set((state) => {
|
|
const newSelection = new Set(state.selectedIds)
|
|
if (newSelection.has(id)) newSelection.delete(id)
|
|
else newSelection.add(id)
|
|
return { selectedIds: newSelection }
|
|
}),
|
|
|
|
clearSelection: () => set({ selectedIds: new Set() }),
|
|
|
|
selectAll: () => set((state) => ({
|
|
selectedIds: new Set(state.documents.map(d => d.id))
|
|
})),
|
|
|
|
setSearchQuery: (query) => set({ searchQuery: query }),
|
|
|
|
fetchDocuments: async (query = '') => {
|
|
set({ isLoading: true, error: null, searchQuery: query })
|
|
try {
|
|
const data = await libraryApi.getDocuments(query)
|
|
set({ documents: data.items, total: data.total, isLoading: false })
|
|
} catch (err: any) {
|
|
set({ error: err.error?.message || err.message, isLoading: false })
|
|
}
|
|
},
|
|
|
|
fetchTrash: async () => {
|
|
set({ isLoading: true, error: null, searchQuery: '' })
|
|
try {
|
|
const data = await libraryApi.getTrash()
|
|
set({ documents: data.items, total: data.total, isLoading: false })
|
|
} catch (err: any) {
|
|
set({ error: err.error?.message || err.message, isLoading: false })
|
|
}
|
|
},
|
|
|
|
uploadDocument: async (file: File) => {
|
|
set({ isLoading: true, error: null })
|
|
try {
|
|
const newDoc = await libraryApi.uploadDocument(file)
|
|
set((state) => ({
|
|
documents: [newDoc, ...state.documents],
|
|
total: state.total + 1,
|
|
isLoading: false
|
|
}))
|
|
return newDoc
|
|
} catch (err: any) {
|
|
set({ error: err.error?.message || err.message, isLoading: false })
|
|
throw err
|
|
}
|
|
},
|
|
|
|
deleteDocument: async (id: string, permanent = false) => {
|
|
set({ isLoading: true, error: null })
|
|
try {
|
|
await libraryApi.deleteDocument(id, permanent)
|
|
set((state) => ({
|
|
documents: state.documents.filter(d => d.id !== id),
|
|
total: state.total - 1,
|
|
selectedIds: new Set(Array.from(state.selectedIds).filter(sid => sid !== id)),
|
|
isLoading: false
|
|
}))
|
|
} catch (err: any) {
|
|
set({ error: err.error?.message || err.message, isLoading: false })
|
|
throw err
|
|
}
|
|
},
|
|
|
|
restoreDocument: async (id: string) => {
|
|
set({ isLoading: true, error: null })
|
|
try {
|
|
await libraryApi.restoreDocument(id)
|
|
set((state) => ({
|
|
documents: state.documents.filter(d => d.id !== id),
|
|
total: state.total - 1,
|
|
selectedIds: new Set(Array.from(state.selectedIds).filter(sid => sid !== id)),
|
|
isLoading: false
|
|
}))
|
|
} catch (err: any) {
|
|
set({ error: err.error?.message || err.message, isLoading: false })
|
|
throw err
|
|
}
|
|
},
|
|
|
|
bulkDelete: async (permanent = false) => {
|
|
const { selectedIds } = get()
|
|
if (selectedIds.size === 0) return
|
|
|
|
set({ isLoading: true, error: null })
|
|
try {
|
|
if (permanent) {
|
|
// Must delete one by one if permanent
|
|
await Promise.all(Array.from(selectedIds).map(id => libraryApi.deleteDocument(id, true)))
|
|
} else {
|
|
await libraryApi.bulkDelete({ ids: Array.from(selectedIds) })
|
|
}
|
|
|
|
set((state) => ({
|
|
documents: state.documents.filter(d => !state.selectedIds.has(d.id)),
|
|
total: state.total - state.selectedIds.size,
|
|
selectedIds: new Set(),
|
|
isLoading: false
|
|
}))
|
|
} catch (err: any) {
|
|
set({ error: err.error?.message || err.message, isLoading: false })
|
|
throw err
|
|
}
|
|
},
|
|
|
|
bulkRestore: async () => {
|
|
const { selectedIds } = get()
|
|
if (selectedIds.size === 0) return
|
|
|
|
set({ isLoading: true, error: null })
|
|
try {
|
|
await libraryApi.bulkRestore({ ids: Array.from(selectedIds) })
|
|
set((state) => ({
|
|
documents: state.documents.filter(d => !state.selectedIds.has(d.id)),
|
|
total: state.total - state.selectedIds.size,
|
|
selectedIds: new Set(),
|
|
isLoading: false
|
|
}))
|
|
} catch (err: any) {
|
|
set({ error: err.error?.message || err.message, isLoading: false })
|
|
throw err
|
|
}
|
|
},
|
|
|
|
emptyTrash: async () => {
|
|
set({ isLoading: true, error: null })
|
|
try {
|
|
await libraryApi.emptyTrash()
|
|
set({
|
|
documents: [],
|
|
total: 0,
|
|
selectedIds: new Set(),
|
|
isLoading: false
|
|
})
|
|
} catch (err: any) {
|
|
set({ error: err.error?.message || err.message, isLoading: false })
|
|
throw err
|
|
}
|
|
},
|
|
|
|
updateDocument: async (id: string, data: DocumentUpdateRequest) => {
|
|
set({ isLoading: true, error: null })
|
|
try {
|
|
const updatedDoc = await libraryApi.updateDocument(id, data)
|
|
set((state) => ({
|
|
documents: state.documents.map(d => d.id === id ? updatedDoc : d),
|
|
isLoading: false
|
|
}))
|
|
} catch (err: any) {
|
|
set({ error: err.error?.message || err.message, isLoading: false })
|
|
throw err
|
|
}
|
|
}
|
|
}))
|