Add shared file storage with folder tree, permissions, and file manager UI
New document hub for centralized file storage — replaces scattered drives and USB sticks for non-technical SMBs. Three new tables: storage_folder (nested hierarchy), storage_folder_permission (role and user-level access control), storage_file. Backend: folder CRUD with nested paths, file upload/download via signed URLs, permission checks (view/edit/admin with inheritance from parent folders), public/private toggle, breadcrumb navigation, file search. Frontend: two-panel file manager — collapsible folder tree on left, icon grid view on right. Folder icons by type, file size display, upload button, context menu for download/delete. Breadcrumb nav. Files sidebar link added.
This commit is contained in:
73
packages/admin/src/api/storage.ts
Normal file
73
packages/admin/src/api/storage.ts
Normal file
@@ -0,0 +1,73 @@
|
||||
import { queryOptions } from '@tanstack/react-query'
|
||||
import { api } from '@/lib/api-client'
|
||||
import type { StorageFolder, StorageFolderPermission, StorageFile } from '@/types/storage'
|
||||
import type { PaginatedResponse, PaginationInput } from '@forte/shared/schemas'
|
||||
|
||||
// --- Folders ---
|
||||
|
||||
export const storageFolderKeys = {
|
||||
all: ['storage-folders'] as const,
|
||||
tree: ['storage-folders', 'tree'] as const,
|
||||
children: (parentId: string | null) => ['storage-folders', 'children', parentId] as const,
|
||||
detail: (id: string) => ['storage-folders', 'detail', id] as const,
|
||||
permissions: (id: string) => ['storage-folders', id, 'permissions'] as const,
|
||||
}
|
||||
|
||||
export function storageFolderTreeOptions() {
|
||||
return queryOptions({
|
||||
queryKey: storageFolderKeys.tree,
|
||||
queryFn: () => api.get<{ data: StorageFolder[] }>('/v1/storage/folders/tree'),
|
||||
})
|
||||
}
|
||||
|
||||
export function storageFolderChildrenOptions(parentId: string | null) {
|
||||
return queryOptions({
|
||||
queryKey: storageFolderKeys.children(parentId),
|
||||
queryFn: () => api.get<{ data: StorageFolder[] }>('/v1/storage/folders', parentId ? { parentId } : {}),
|
||||
})
|
||||
}
|
||||
|
||||
export function storageFolderDetailOptions(id: string) {
|
||||
return queryOptions({
|
||||
queryKey: storageFolderKeys.detail(id),
|
||||
queryFn: () => api.get<StorageFolder & { breadcrumbs: { id: string; name: string }[] }>(`/v1/storage/folders/${id}`),
|
||||
enabled: !!id,
|
||||
})
|
||||
}
|
||||
|
||||
export function storageFolderPermissionsOptions(id: string) {
|
||||
return queryOptions({
|
||||
queryKey: storageFolderKeys.permissions(id),
|
||||
queryFn: () => api.get<{ data: StorageFolderPermission[] }>(`/v1/storage/folders/${id}/permissions`),
|
||||
enabled: !!id,
|
||||
})
|
||||
}
|
||||
|
||||
export const storageFolderMutations = {
|
||||
create: (data: Record<string, unknown>) => api.post<StorageFolder>('/v1/storage/folders', data),
|
||||
update: (id: string, data: Record<string, unknown>) => api.patch<StorageFolder>(`/v1/storage/folders/${id}`, data),
|
||||
delete: (id: string) => api.del<StorageFolder>(`/v1/storage/folders/${id}`),
|
||||
addPermission: (folderId: string, data: Record<string, unknown>) => api.post<StorageFolderPermission>(`/v1/storage/folders/${folderId}/permissions`, data),
|
||||
removePermission: (permId: string) => api.del<StorageFolderPermission>(`/v1/storage/folder-permissions/${permId}`),
|
||||
}
|
||||
|
||||
// --- Files ---
|
||||
|
||||
export const storageFileKeys = {
|
||||
all: (folderId: string) => ['storage-files', folderId] as const,
|
||||
list: (folderId: string, params: PaginationInput) => ['storage-files', folderId, params] as const,
|
||||
search: (q: string) => ['storage-files', 'search', q] as const,
|
||||
}
|
||||
|
||||
export function storageFileListOptions(folderId: string, params: PaginationInput) {
|
||||
return queryOptions({
|
||||
queryKey: storageFileKeys.list(folderId, params),
|
||||
queryFn: () => api.get<PaginatedResponse<StorageFile>>(`/v1/storage/folders/${folderId}/files`, params),
|
||||
enabled: !!folderId,
|
||||
})
|
||||
}
|
||||
|
||||
export const storageFileMutations = {
|
||||
delete: (id: string) => api.del<StorageFile>(`/v1/storage/files/${id}`),
|
||||
getSignedUrl: (id: string) => api.get<{ url: string }>(`/v1/storage/files/${id}/signed-url`),
|
||||
}
|
||||
Reference in New Issue
Block a user