import { ApiRequestOptions } from '../hooks/useApi'; // ============================================================================ // TYPES & INTERFACES // ============================================================================ export interface FileInfo { id: string; mandateId: string; fileName: string; mimeType: string; fileHash: string; fileSize: number; creationDate: number; [key: string]: any; // Allow additional properties } export interface AttributeDefinition { name: string; label: string; type: 'string' | 'number' | 'date' | 'boolean' | 'enum'; sortable?: boolean; filterable?: boolean; searchable?: boolean; width?: number; minWidth?: number; maxWidth?: number; filterOptions?: string[]; } export interface PaginationParams { page?: number; pageSize?: number; sort?: Array<{ field: string; direction: 'asc' | 'desc' }>; filters?: Record; search?: string; viewKey?: string; groupByLevels?: Array<{ field: string; nullLabel?: string; direction?: 'asc' | 'desc' }>; } export interface PaginatedResponse { items: T[]; pagination?: { currentPage: number; pageSize: number; totalItems: number; totalPages: number; }; groupLayout?: import('./connectionApi').GroupLayout; appliedView?: { viewKey?: string; displayName?: string }; } // Type for the request function passed to API functions export type ApiRequestFunction = (options: ApiRequestOptions) => Promise; // ============================================================================ // API REQUEST FUNCTIONS // ============================================================================ /** * Fetch file attributes from backend * Endpoint: GET /api/attributes/FileItem */ export async function fetchFileAttributes(request: ApiRequestFunction): Promise { const data = await request({ url: '/api/attributes/FileItem', method: 'get' }); // Handle different response formats if (Array.isArray(data)) { return data; } if (data && typeof data === 'object' && 'attributes' in data && Array.isArray(data.attributes)) { return data.attributes; } // Try to find any array property in the response if (data && typeof data === 'object') { const keys = Object.keys(data); for (const key of keys) { if (Array.isArray((data as any)[key])) { return (data as any)[key]; } } } return []; } /** * Fetch list of files with optional pagination * Endpoint: GET /api/files/list */ export async function fetchFiles( request: ApiRequestFunction, params?: PaginationParams ): Promise | FileInfo[]> { const requestParams: any = {}; // Build pagination object if provided if (params) { const paginationObj: any = {}; if (params.page !== undefined) paginationObj.page = params.page; if (params.pageSize !== undefined) paginationObj.pageSize = params.pageSize; if (params.sort) paginationObj.sort = params.sort; if (params.filters) paginationObj.filters = params.filters; if (params.search) paginationObj.search = params.search; if (params.viewKey) paginationObj.viewKey = params.viewKey; if (params.groupByLevels !== undefined) paginationObj.groupByLevels = params.groupByLevels; if (Object.keys(paginationObj).length > 0) { requestParams.pagination = JSON.stringify(paginationObj); } } const data = await request({ url: '/api/files/list', method: 'get', params: requestParams }); return data; } /** * Fetch a single file by ID * Endpoint: GET /api/files/{fileId} */ export async function fetchFileById( request: ApiRequestFunction, fileId: string ): Promise { try { const data = await request({ url: `/api/files/${fileId}`, method: 'get' }); return data || null; } catch (error: any) { console.error('Error fetching file by ID:', error); return null; } } /** * Update a file * Endpoint: PUT /api/files/{fileId} */ export async function updateFile( request: ApiRequestFunction, fileId: string, fileData: Partial ): Promise { return await request({ url: `/api/files/${fileId}`, method: 'put', data: fileData }); } /** * Delete a file * Endpoint: DELETE /api/files/{fileId} */ export async function deleteFile( request: ApiRequestFunction, fileId: string ): Promise { await request({ url: `/api/files/${fileId}`, method: 'delete' }); } /** * Delete multiple files * Endpoint: DELETE /api/files/{fileId} (called multiple times) */ export async function deleteFiles( request: ApiRequestFunction, fileIds: string[] ): Promise> { const uniqueIds = [...new Set(fileIds.filter(Boolean))]; if (uniqueIds.length === 0) return []; await request({ url: '/api/files/batch-delete', method: 'post', data: { fileIds: uniqueIds } }); return uniqueIds.map(fileId => ({ success: true, fileId })); } // ============================================================================ // GROUP BULK API FUNCTIONS // ============================================================================ /** Patch scope for all files in a group (recursive) */ export async function patchGroupScope( request: ApiRequestFunction, groupId: string, scope: string ): Promise { return await request({ url: `/api/files/groups/${groupId}/scope`, method: 'patch', data: { scope }, }); } /** Patch neutralize for all files in a group (recursive, incl. knowledge purge/reindex) */ export async function patchGroupNeutralize( request: ApiRequestFunction, groupId: string, neutralize: boolean ): Promise { return await request({ url: `/api/files/groups/${groupId}/neutralize`, method: 'patch', data: { neutralize }, }); } /** Download all files in a group as ZIP */ export async function downloadGroupZip(groupId: string): Promise { const { default: api } = await import('../api'); const response = await api.get(`/api/files/groups/${groupId}/download`, { responseType: 'blob', }); const url = window.URL.createObjectURL(response.data); const link = document.createElement('a'); link.href = url; link.setAttribute('download', `group-${groupId}.zip`); document.body.appendChild(link); link.click(); link.remove(); window.URL.revokeObjectURL(url); } /** Delete a group and optionally all its files */ export async function deleteGroup( request: ApiRequestFunction, groupId: string, deleteItems: boolean = false ): Promise { return await request({ url: `/api/files/groups/${groupId}`, method: 'delete', params: { deleteItems }, }); } /** @deprecated Group tree removed — use view-based grouping (viewKey). Returns empty array. */ export function collectGroupItemIds( _groupTree: Array<{ id: string; itemIds: string[]; subGroups: any[] }>, _groupId: string ): string[] { const collect = (): string[] | null => null; return collect() ?? []; } // Note: The following operations require special handling (FormData, blob responses) // and should use the api instance directly from '../api' rather than the request function: // - uploadFile: Requires FormData with multipart/form-data // - downloadFile: Requires blob responseType // - previewFile: Requires flexible responseType (json or blob) // These are kept in the hooks for now due to their special requirements // ============================================================================ // FOLDER TYPES & API FUNCTIONS // ============================================================================ export interface FolderInfo { id: string; name: string; parentId: string | null; mandateId: string; featureInstanceId: string; scope: string; neutralize: boolean; contextOrphan?: boolean; sysCreatedBy?: string; sysCreatedAt?: number; sysModifiedAt?: number; } export async function getFolderTree( request: ApiRequestFunction, owner: 'me' | 'shared' = 'me', ): Promise { const data = await request({ url: '/api/files/folders/tree', method: 'get', params: { owner }, }); return Array.isArray(data) ? data : []; } export async function createFolder( request: ApiRequestFunction, name: string, parentId?: string | null, ): Promise { return await request({ url: '/api/files/folders', method: 'post', data: { name, parentId: parentId ?? null }, }); } export async function renameFolder( request: ApiRequestFunction, folderId: string, name: string, ): Promise { return await request({ url: `/api/files/folders/${folderId}`, method: 'patch', data: { name }, }); } export async function moveFolder( request: ApiRequestFunction, folderId: string, parentId: string | null, ): Promise { return await request({ url: `/api/files/folders/${folderId}/move`, method: 'post', data: { parentId }, }); } export async function deleteFolderCascade( request: ApiRequestFunction, folderId: string, ): Promise<{ deletedFolders: number; deletedFiles: number }> { return await request({ url: `/api/files/folders/${folderId}`, method: 'delete', params: { cascade: true }, }); } export async function patchFolderScope( request: ApiRequestFunction, folderId: string, scope: string, cascadeToFiles: boolean = false, ): Promise<{ folderId: string; scope: string; filesUpdated: number }> { return await request({ url: `/api/files/folders/${folderId}/scope`, method: 'patch', data: { scope, cascadeToFiles }, }); } export async function patchFolderNeutralize( request: ApiRequestFunction, folderId: string, neutralize: boolean, ): Promise<{ folderId: string; neutralize: boolean; filesUpdated: number }> { return await request({ url: `/api/files/folders/${folderId}/neutralize`, method: 'patch', data: { neutralize }, }); } export async function moveFiles( request: ApiRequestFunction, fileIds: string[], targetFolderId: string | null, ): Promise { await Promise.all( fileIds.map((fileId) => request({ url: `/api/files/${fileId}`, method: 'put', data: { folderId: targetFolderId }, }), ), ); }