refactoring majeur (code dupliqué, mort, ...)

- Économie : ~1240 lignes de code dupliqué
- Réduction : ~60% du code modal
- Amélioration : Cohérence et maintenabilité
This commit is contained in:
Yannick Le Duc
2025-08-27 12:45:37 +02:00
parent 6acc7d9d35
commit dc388bf371
25 changed files with 1446 additions and 1821 deletions

120
src/lib/file-utils.ts Normal file
View File

@@ -0,0 +1,120 @@
import * as XLSX from 'xlsx';
/**
* Utilitaires centralisés pour le traitement des fichiers
*/
export interface ParsedFileData {
data: any[];
headers: string[];
error?: string;
}
export function parseCSV(file: File): Promise<ParsedFileData> {
return new Promise((resolve) => {
const reader = new FileReader();
reader.onload = (e) => {
try {
const text = e.target?.result as string;
const lines = text.split('\n').filter(line => line.trim());
if (lines.length < 2) {
resolve({ data: [], headers: [], error: 'Le fichier doit contenir au moins un en-tête et une ligne de données.' });
return;
}
const headers = lines[0].split(',').map(h => h.trim().replace(/"/g, ''));
const data = lines.slice(1).map(line => {
const values = line.split(',').map(v => v.trim().replace(/"/g, ''));
const row: any = {};
headers.forEach((header, index) => {
row[header] = values[index] || '';
});
return row;
});
resolve({ data, headers });
} catch (error) {
resolve({ data: [], headers: [], error: 'Erreur lors de la lecture du fichier CSV.' });
}
};
reader.readAsText(file);
});
}
export function parseExcel(file: File): Promise<ParsedFileData> {
return new Promise((resolve) => {
const reader = new FileReader();
reader.onload = (e) => {
try {
const fileData = new Uint8Array(e.target?.result as ArrayBuffer);
const workbook = XLSX.read(fileData, { type: 'array' });
// Prendre la première feuille
const sheetName = workbook.SheetNames[0];
const worksheet = workbook.Sheets[sheetName];
// Convertir en JSON
const jsonData = XLSX.utils.sheet_to_json(worksheet, { header: 1 });
if (jsonData.length < 2) {
resolve({ data: [], headers: [], error: 'Le fichier doit contenir au moins un en-tête et une ligne de données.' });
return;
}
const headers = jsonData[0] as string[];
const rows = jsonData.slice(1) as any[][];
const parsedData = rows.map(row => {
const rowData: any = {};
headers.forEach((header, index) => {
rowData[header] = row[index] || '';
});
return rowData;
});
resolve({ data: parsedData, headers });
} catch (error) {
resolve({ data: [], headers: [], error: 'Erreur lors de la lecture du fichier Excel.' });
}
};
reader.readAsArrayBuffer(file);
});
}
export function getExpectedColumns(type: 'propositions' | 'participants'): string[] {
if (type === 'propositions') {
return ['title', 'description', 'author_first_name', 'author_last_name', 'author_email'];
} else {
return ['first_name', 'last_name', 'email'];
}
}
export function downloadTemplate(type: 'propositions' | 'participants'): void {
const columns = getExpectedColumns(type);
const csvContent = columns.join(',') + '\n';
const blob = new Blob([csvContent], { type: 'text/csv' });
const url = window.URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = `template_${type}.csv`;
a.click();
window.URL.revokeObjectURL(url);
}
export function validateFileType(file: File): { isValid: boolean; error?: string } {
const isCSV = file.type === 'text/csv' || file.name.toLowerCase().endsWith('.csv');
const isExcel = file.type === 'application/vnd.oasis.opendocument.spreadsheet' ||
file.name.toLowerCase().endsWith('.ods') ||
file.name.toLowerCase().endsWith('.xlsx') ||
file.name.toLowerCase().endsWith('.xls');
if (!isCSV && !isExcel) {
return {
isValid: false,
error: 'Veuillez sélectionner un fichier valide (CSV, ODS, XLSX ou XLS).'
};
}
return { isValid: true };
}