2025-12-01 17:02:01 +03:00

625 lines
15 KiB
TypeScript

import { defineStore } from "pinia";
import { ref, computed } from "vue";
import FileService, {
type FileResource,
type FileListParams,
type CreateFilePayload,
type UpdateFilePayload,
type FileListResponse,
type OrganizedFileStructure,
type StorageStatistics,
} from "@/services/file";
export const useFileStore = defineStore("file", () => {
// State
const files = ref<FileResource[]>([]);
const currentFile = ref<FileResource | null>(null);
const loading = ref(false);
const error = ref<string | null>(null);
const uploadProgress = ref<number>(0);
const selectedFiles = ref<number[]>([]);
// Pagination state
const pagination = ref({
current_page: 1,
last_page: 1,
per_page: 15,
total: 0,
});
// Filter state
const filters = ref<FileListParams>({
search: '',
category: '',
client_id: undefined,
mime_type: '',
date_from: '',
date_to: '',
sort_by: 'uploaded_at',
sort_direction: 'desc',
});
// Organization state
const organizedFiles = ref<OrganizedFileStructure>({});
const storageStats = ref<StorageStatistics | null>(null);
// Getters
const allFiles = computed(() => files.value);
const filesByCategory = computed(() => {
const grouped: Record<string, FileResource[]> = {};
files.value.forEach(file => {
const category = file.category || 'general';
if (!grouped[category]) {
grouped[category] = [];
}
grouped[category].push(file);
});
return grouped;
});
const filesByClient = computed(() => {
const grouped: Record<number, FileResource[]> = {};
files.value.forEach(file => {
const clientId = extractClientIdFromPath(file.storage_uri);
if (clientId) {
if (!grouped[clientId]) {
grouped[clientId] = [];
}
grouped[clientId].push(file);
}
});
return grouped;
});
const imageFiles = computed(() =>
files.value.filter(file => file.is_image)
);
const pdfFiles = computed(() =>
files.value.filter(file => file.is_pdf)
);
const recentFiles = computed(() =>
[...files.value]
.sort((a, b) => new Date(b.uploaded_at).getTime() - new Date(a.uploaded_at).getTime())
.slice(0, 10)
);
const isLoading = computed(() => loading.value);
const hasError = computed(() => error.value !== null);
const getError = computed(() => error.value);
const hasFiles = computed(() => files.value.length > 0);
const getPagination = computed(() => pagination.value);
const getFilters = computed(() => filters.value);
const getUploadProgress = computed(() => uploadProgress.value);
const getFileById = computed(() => (id: number) =>
files.value.find(file => file.id === id)
);
const getSelectedFiles = computed(() =>
files.value.filter(file => selectedFiles.value.includes(file.id))
);
const totalSizeFormatted = computed(() => {
const totalSize = files.value.reduce((sum, file) => sum + (file.size_bytes || 0), 0);
return FileService.formatFileSize(totalSize);
});
// Actions
const setLoading = (isLoading: boolean) => {
loading.value = isLoading;
};
const setError = (err: string | null) => {
error.value = err;
};
const clearError = () => {
error.value = null;
};
const setFiles = (newFiles: FileResource[]) => {
files.value = newFiles;
};
const setCurrentFile = (file: FileResource | null) => {
currentFile.value = file;
};
const setPagination = (meta: any) => {
if (meta) {
pagination.value = {
current_page: meta.current_page || 1,
last_page: meta.last_page || 1,
per_page: meta.per_page || 15,
total: meta.total || 0,
};
}
};
const setFilters = (newFilters: Partial<FileListParams>) => {
filters.value = { ...filters.value, ...newFilters };
};
const clearFilters = () => {
filters.value = {
search: '',
category: '',
client_id: undefined,
mime_type: '',
date_from: '',
date_to: '',
sort_by: 'uploaded_at',
sort_direction: 'desc',
};
};
const setUploadProgress = (progress: number) => {
uploadProgress.value = progress;
};
const selectFile = (fileId: number) => {
if (!selectedFiles.value.includes(fileId)) {
selectedFiles.value.push(fileId);
}
};
const deselectFile = (fileId: number) => {
selectedFiles.value = selectedFiles.value.filter(id => id !== fileId);
};
const selectAllFiles = () => {
selectedFiles.value = files.value.map(file => file.id);
};
const deselectAllFiles = () => {
selectedFiles.value = [];
};
/**
* Récupérer tous les fichiers avec pagination et filtres
*/
const fetchFiles = async (params?: FileListParams) => {
setLoading(true);
setError(null);
try {
const queryParams = { ...filters.value, ...params };
const response = await FileService.getAllFiles(queryParams);
setFiles(response.data);
setPagination(response.pagination);
return response;
} catch (err: any) {
const errorMessage =
err.response?.data?.message ||
err.message ||
"Échec du chargement des fichiers";
setError(errorMessage);
throw err;
} finally {
setLoading(false);
}
};
/**
* Récupérer un seul fichier par ID
*/
const fetchFile = async (id: number) => {
setLoading(true);
setError(null);
try {
const response = await FileService.getFile(id);
setCurrentFile(response.data);
return response.data;
} catch (err: any) {
const errorMessage =
err.response?.data?.message ||
err.message ||
"Échec du chargement du fichier";
setError(errorMessage);
throw err;
} finally {
setLoading(false);
}
};
/**
* Télécharger un nouveau fichier
*/
const uploadFile = async (payload: CreateFilePayload) => {
setLoading(true);
setError(null);
setUploadProgress(0);
try {
// Validate file before upload
const validation = FileService.validateFile(payload.file);
if (!validation.valid) {
throw new Error(validation.error);
}
const response = await FileService.uploadFile(payload);
// Add the new file to the beginning of the list
files.value.unshift(response.data);
setCurrentFile(response.data);
return response.data;
} catch (err: any) {
const errorMessage =
err.response?.data?.message ||
err.message ||
"Échec du téléchargement du fichier";
setError(errorMessage);
throw err;
} finally {
setLoading(false);
setUploadProgress(0);
}
};
/**
* Mettre à jour les métadonnées d'un fichier
*/
const updateFile = async (payload: UpdateFilePayload) => {
setLoading(true);
setError(null);
try {
const response = await FileService.updateFile(payload);
const updatedFile = response.data;
// Update in files list
const index = files.value.findIndex(file => file.id === updatedFile.id);
if (index !== -1) {
files.value[index] = updatedFile;
}
// Update current file if it's the one being edited
if (currentFile.value && currentFile.value.id === updatedFile.id) {
setCurrentFile(updatedFile);
}
return updatedFile;
} catch (err: any) {
const errorMessage =
err.response?.data?.message ||
err.message ||
"Échec de la mise à jour du fichier";
setError(errorMessage);
throw err;
} finally {
setLoading(false);
}
};
/**
* Supprimer un fichier
*/
const deleteFile = async (id: number) => {
setLoading(true);
setError(null);
try {
await FileService.deleteFile(id);
// Remove from files list
files.value = files.value.filter(file => file.id !== id);
// Remove from selection if selected
deselectFile(id);
// Clear current file if it's the one being deleted
if (currentFile.value && currentFile.value.id === id) {
setCurrentFile(null);
}
return { success: true, message: "Fichier supprimé avec succès" };
} catch (err: any) {
const errorMessage =
err.response?.data?.message ||
err.message ||
"Échec de la suppression du fichier";
setError(errorMessage);
throw err;
} finally {
setLoading(false);
}
};
/**
* Supprimer plusieurs fichiers
*/
const deleteMultipleFiles = async (fileIds: number[]) => {
setLoading(true);
setError(null);
try {
const results = await Promise.allSettled(
fileIds.map(id => FileService.deleteFile(id))
);
// Filter out successful deletions
const successfulIds = fileIds.filter((_, index) =>
results[index].status === 'fulfilled'
);
// Remove successful deletions from the list
files.value = files.value.filter(file => !successfulIds.includes(file.id));
// Clear selections
successfulIds.forEach(id => deselectFile(id));
return {
success: true,
deleted: successfulIds.length,
total: fileIds.length
};
} catch (err: any) {
const errorMessage =
err.response?.data?.message ||
err.message ||
"Échec de la suppression des fichiers";
setError(errorMessage);
throw err;
} finally {
setLoading(false);
}
};
/**
* Récupérer les fichiers par catégorie
*/
const fetchFilesByCategory = async (category: string, params?: { page?: number; per_page?: number }) => {
setLoading(true);
setError(null);
try {
const response = await FileService.getFilesByCategory(category, params);
setFiles(response.data);
setPagination(response.pagination);
return response;
} catch (err: any) {
const errorMessage =
err.response?.data?.message ||
err.message ||
"Échec du chargement des fichiers par catégorie";
setError(errorMessage);
throw err;
} finally {
setLoading(false);
}
};
/**
* Récupérer les fichiers par client
*/
const fetchFilesByClient = async (clientId: number, params?: { page?: number; per_page?: number }) => {
setLoading(true);
setError(null);
try {
const response = await FileService.getFilesByClient(clientId, params);
setFiles(response.data);
setPagination(response.pagination);
return response;
} catch (err: any) {
const errorMessage =
err.response?.data?.message ||
err.message ||
"Échec du chargement des fichiers du client";
setError(errorMessage);
throw err;
} finally {
setLoading(false);
}
};
/**
* Récupérer la structure organisée des fichiers
*/
const fetchOrganizedStructure = async () => {
setLoading(true);
setError(null);
try {
const response = await FileService.getOrganizedStructure();
organizedFiles.value = response.data;
return response.data;
} catch (err: any) {
const errorMessage =
err.response?.data?.message ||
err.message ||
"Échec du chargement de la structure organisée";
setError(errorMessage);
throw err;
} finally {
setLoading(false);
}
};
/**
* Récupérer les statistiques de stockage
*/
const fetchStorageStatistics = async () => {
setLoading(true);
setError(null);
try {
const response = await FileService.getStorageStatistics();
storageStats.value = response.data;
return response.data;
} catch (err: any) {
const errorMessage =
err.response?.data?.message ||
err.message ||
"Échec du chargement des statistiques";
setError(errorMessage);
throw err;
} finally {
setLoading(false);
}
};
/**
* Générer une URL de téléchargement
*/
const generateDownloadUrl = async (fileId: number) => {
try {
const response = await FileService.generateDownloadUrl(fileId);
return response.data.download_url;
} catch (err: any) {
const errorMessage =
err.response?.data?.message ||
err.message ||
"Échec de la génération de l'URL de téléchargement";
setError(errorMessage);
throw err;
}
};
/**
* Rechercher des fichiers
*/
const searchFiles = async (query: string, params?: { page?: number; per_page?: number }) => {
setLoading(true);
setError(null);
try {
const searchParams = { ...filters.value, search: query, ...params };
const response = await FileService.getAllFiles(searchParams);
setFiles(response.data);
setPagination(response.pagination);
return response;
} catch (err: any) {
const errorMessage =
err.response?.data?.message ||
err.message ||
"Échec de la recherche de fichiers";
setError(errorMessage);
throw err;
} finally {
setLoading(false);
}
};
/**
* Télécharger un fichier
*/
const downloadFile = async (fileId: number) => {
try {
const downloadUrl = await generateDownloadUrl(fileId);
// Create a temporary link to trigger download
const link = document.createElement('a');
link.href = downloadUrl;
link.download = '';
document.body.appendChild(link);
link.click();
document.body.removeChild(link);
return { success: true };
} catch (err: any) {
throw err;
}
};
/**
* Extraire l'ID client du chemin de stockage
*/
const extractClientIdFromPath = (storageUri: string): number | null => {
const pathParts = storageUri.split('/');
if (pathParts.length >= 4 && pathParts[0] === 'client') {
const clientId = parseInt(pathParts[1]);
return isNaN(clientId) ? null : clientId;
}
return null;
};
/**
* Réinitialiser l'état
*/
const resetState = () => {
files.value = [];
currentFile.value = null;
loading.value = false;
error.value = null;
uploadProgress.value = 0;
selectedFiles.value = [];
pagination.value = {
current_page: 1,
last_page: 1,
per_page: 15,
total: 0,
};
clearFilters();
organizedFiles.value = {};
storageStats.value = null;
};
return {
// State
files,
currentFile,
loading,
error,
uploadProgress,
selectedFiles,
pagination,
filters,
organizedFiles,
storageStats,
// Getters
allFiles,
filesByCategory,
filesByClient,
imageFiles,
pdfFiles,
recentFiles,
isLoading,
hasError,
getError,
hasFiles,
getPagination,
getFilters,
getUploadProgress,
getFileById,
getSelectedFiles,
totalSizeFormatted,
// Actions
setLoading,
setError,
clearError,
setFiles,
setCurrentFile,
setPagination,
setFilters,
clearFilters,
setUploadProgress,
selectFile,
deselectFile,
selectAllFiles,
deselectAllFiles,
fetchFiles,
fetchFile,
uploadFile,
updateFile,
deleteFile,
deleteMultipleFiles,
fetchFilesByCategory,
fetchFilesByClient,
fetchOrganizedStructure,
fetchStorageStatistics,
generateDownloadUrl,
searchFiles,
downloadFile,
resetState,
};
});
export default useFileStore;