import { request } from "./http"; export interface FileUser { id: number; name: string; email: string; } export interface FileResource { id: number; file_name: string; mime_type: string | null; size_bytes: number | null; size_formatted: string; extension: string; storage_uri: string; organized_path: string; sha256: string | null; uploaded_by: number; uploader_name: string; uploaded_at: string; created_at: string; updated_at: string; is_image: boolean; is_pdf: boolean; url?: string; user: FileUser; category: string; subcategory: string; } export interface FileListResponse { data: FileResource[]; pagination: { current_page: number; from: number; last_page: number; per_page: number; to: number; total: number; has_more_pages: boolean; }; summary: { total_files: number; total_size: number; total_size_formatted: string; categories: Record< string, { count: number; total_size: number; total_size_formatted: string; } >; }; } export interface FileResponse { data: FileResource; } export interface FileCollectionResponse { data: FileResource[]; pagination: { current_page: number; from: number; last_page: number; per_page: number; to: number; total: number; has_more_pages: boolean; }; } export interface OrganizedFileStructure { [key: string]: { category: string; subcategory: string; files: FileResource[]; count: number; }; } export interface StorageStatistics { total_files: number; total_size_bytes: number; total_size_formatted: string; by_type: Record< string, { count: number; total_size: number; } >; by_category: Record< string, { count: number; total_size: number; } >; } export interface DownloadResponse { data: { download_url: string; file_name: string; mime_type: string; }; message: string; } export interface CreateFilePayload { file: File; file_name?: string; category: "devis" | "facture" | "contrat" | "document" | "image" | "autre"; client_id?: number; subcategory?: string; description?: string; tags?: string[]; is_public?: boolean; } export interface UpdateFilePayload { id: number; file_name?: string; description?: string; tags?: string[]; is_public?: boolean; category?: "devis" | "facture" | "contrat" | "document" | "image" | "autre"; client_id?: number; subcategory?: string; } export interface FileListParams { page?: number; per_page?: number; search?: string; mime_type?: string; uploaded_by?: number; category?: string; client_id?: number; date_from?: string; date_to?: string; sort_by?: string; sort_direction?: "asc" | "desc"; } export const FileService = { /** * Get all files with pagination and filters */ async getAllFiles(params?: FileListParams): Promise { const response = await request({ url: "/api/files", method: "get", params, }); return response; }, /** * Get a specific file by ID */ async getFile(id: number): Promise { const response = await request({ url: `/api/files/${id}`, method: "get", }); return response; }, /** * Upload a new file */ async uploadFile(payload: CreateFilePayload): Promise { const formData = new FormData(); formData.append("file", payload.file); if (payload.file_name) { formData.append("file_name", payload.file_name); } formData.append("category", payload.category); if (payload.client_id) { formData.append("client_id", payload.client_id.toString()); } if (payload.subcategory) { formData.append("subcategory", payload.subcategory); } if (payload.description) { formData.append("description", payload.description); } if (payload.tags && payload.tags.length > 0) { payload.tags.forEach((tag, index) => { formData.append(`tags[${index}]`, tag); }); } if (payload.is_public !== undefined) { formData.append("is_public", payload.is_public.toString()); } const response = await request({ url: "/api/files", method: "post", data: formData, headers: { "Content-Type": "multipart/form-data", }, }); return response; }, /** * Update file metadata */ async updateFile(payload: UpdateFilePayload): Promise { const { id, ...updateData } = payload; const response = await request({ url: `/api/files/${id}`, method: "put", data: updateData, }); return response; }, /** * Delete a file */ async deleteFile(id: number): Promise<{ message: string }> { const response = await request<{ message: string }>({ url: `/api/files/${id}`, method: "delete", }); return response; }, /** * Get files by category */ async getFilesByCategory( category: string, params?: { page?: number; per_page?: number } ): Promise { const response = await request({ url: `/api/files/by-category/${category}`, method: "get", params, }); return response; }, /** * Get files by client */ async getFilesByClient( clientId: number, params?: { page?: number; per_page?: number } ): Promise { const response = await request({ url: `/api/files/by-client/${clientId}`, method: "get", params, }); return response; }, /** * Get organized file structure */ async getOrganizedStructure(): Promise<{ data: OrganizedFileStructure; message: string; }> { const response = await request<{ data: OrganizedFileStructure; message: string; }>({ url: "/api/files/organized", method: "get", }); return response; }, /** * Get storage statistics */ async getStorageStatistics(): Promise<{ data: StorageStatistics; message: string; }> { const response = await request<{ data: StorageStatistics; message: string; }>({ url: "/api/files/statistics", method: "get", }); return response; }, /** * Generate download URL for a file */ async generateDownloadUrl(id: number): Promise { const response = await request({ url: `/api/files/${id}/download`, method: "get", }); return response; }, /** * Format file size for display */ formatFileSize(bytes: number | null): string { if (!bytes || bytes === 0) return "0 B"; const k = 1024; const sizes = ["B", "KB", "MB", "GB", "TB"]; const i = Math.floor(Math.log(bytes) / Math.log(k)); return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + " " + sizes[i]; }, /** * Get file icon based on MIME type */ getFileIcon(mimeType: string | null): string { if (!mimeType) return "๐Ÿ“„"; if (mimeType.startsWith("image/")) return "๐Ÿ–ผ๏ธ"; if (mimeType === "application/pdf") return "๐Ÿ“„"; if (mimeType.includes("word") || mimeType.includes("document")) return "๐Ÿ“"; if (mimeType.includes("sheet") || mimeType.includes("excel")) return "๐Ÿ“Š"; if (mimeType.includes("presentation") || mimeType.includes("powerpoint")) return "๐Ÿ“‹"; if (mimeType.includes("zip") || mimeType.includes("archive")) return "๐Ÿ—œ๏ธ"; return "๐Ÿ“„"; }, /** * Check if file is an image */ isImageFile(mimeType: string | null): boolean { return mimeType ? mimeType.startsWith("image/") : false; }, /** * Check if file is a PDF */ isPdfFile(mimeType: string | null): boolean { return mimeType === "application/pdf"; }, /** * Get file extension from filename */ getFileExtension(filename: string): string { return filename.split(".").pop()?.toLowerCase() || ""; }, /** * Validate file before upload */ validateFile( file: File, maxSize: number = 10 * 1024 * 1024 ): { valid: boolean; error?: string } { if (file.size > maxSize) { return { valid: false, error: `La taille du fichier ne doit pas dรฉpasser ${this.formatFileSize( maxSize )}`, }; } // Allow all file types for now, but you can add restrictions here const allowedTypes = [ "application/pdf", "image/jpeg", "image/png", "image/gif", "image/webp", "application/msword", "application/vnd.openxmlformats-officedocument.wordprocessingml.document", "application/vnd.ms-excel", "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", "application/vnd.ms-powerpoint", "application/vnd.openxmlformats-officedocument.presentationml.presentation", "text/plain", "application/zip", "application/x-zip-compressed", ]; if (!allowedTypes.includes(file.type)) { return { valid: false, error: "Type de fichier non autorisรฉ", }; } return { valid: true }; }, }; export default FileService;