Files
btpxpress-frontend/services/exportBTPService.ts
2025-10-13 05:29:32 +02:00

464 lines
12 KiB
TypeScript

import ApiService from './ApiService';
import { MaterielBTP, RechercheMaterielParams } from './materielBTPService';
import { ZoneClimatique } from './zoneClimatiqueService';
import { ResultatCalculBriques, ResultatCalculBetonArme } from './calculsTechniquesService';
/**
* Service d'export pour les données BTP ultra-détaillées
* Permet l'export en CSV, Excel et PDF des matériaux, calculs et zones climatiques
*/
export type FormatExport = 'CSV' | 'EXCEL' | 'PDF';
export interface OptionsExport {
format: FormatExport;
includeImages?: boolean;
includeCharts?: boolean;
filtres?: any;
colonnesPersonnalisees?: string[];
template?: 'STANDARD' | 'DETAILLE' | 'RESUME';
}
export interface ResultatExport {
filename: string;
blob: Blob;
size: number;
format: FormatExport;
nbLignes: number;
dateGeneration: string;
}
export class ExportBTPService {
/**
* Export des matériaux BTP avec filtres
*/
static async exporterMateriaux(options: OptionsExport & {
filtres?: RechercheMaterielParams;
}): Promise<ResultatExport> {
try {
const blob = await ApiService.post<Blob>(
'/calculs-techniques/materiaux/export',
{
format: options.format,
filtres: options.filtres,
template: options.template || 'STANDARD',
colonnesPersonnalisees: options.colonnesPersonnalisees
},
{ responseType: 'blob' }
);
const filename = this.genererNomFichier('materiaux', options.format);
return {
filename,
blob,
size: blob.size,
format: options.format,
nbLignes: 0, // Sera calculé côté serveur
dateGeneration: new Date().toISOString()
};
} catch (error) {
console.error('Erreur export matériaux:', error);
throw new Error('Impossible d\'exporter les matériaux');
}
}
/**
* Export des zones climatiques
*/
static async exporterZonesClimatiques(options: OptionsExport): Promise<ResultatExport> {
try {
const blob = await ApiService.post<Blob>(
'/calculs-techniques/zones-climatiques/export',
{
format: options.format,
template: options.template || 'STANDARD'
},
{ responseType: 'blob' }
);
const filename = this.genererNomFichier('zones-climatiques', options.format);
return {
filename,
blob,
size: blob.size,
format: options.format,
nbLignes: 0,
dateGeneration: new Date().toISOString()
};
} catch (error) {
console.error('Erreur export zones climatiques:', error);
throw new Error('Impossible d\'exporter les zones climatiques');
}
}
/**
* Export des résultats de calculs techniques
*/
static async exporterCalculs(
calculs: Array<{
type: 'BRIQUES' | 'BETON' | 'MORTIER';
resultat: ResultatCalculBriques | ResultatCalculBetonArme | any;
parametres: any;
}>,
options: OptionsExport
): Promise<ResultatExport> {
try {
const data = {
calculs: calculs,
format: options.format,
template: options.template || 'DETAILLE',
includeCharts: options.includeCharts || false
};
const blob = await ApiService.post<Blob>(
'/calculs-techniques/export-calculs',
data,
{ responseType: 'blob' }
);
const filename = this.genererNomFichier('calculs-techniques', options.format);
return {
filename,
blob,
size: blob.size,
format: options.format,
nbLignes: calculs.length,
dateGeneration: new Date().toISOString()
};
} catch (error) {
// Fallback : export côté client si serveur indisponible
console.warn('Export serveur indisponible, génération côté client');
return this.exporterCalculsCoteClient(calculs, options);
}
}
/**
* Export complet du projet BTP (matériaux + zones + calculs)
*/
static async exporterProjetComplet(
chantierId: string,
options: OptionsExport & {
includeMateriaux?: boolean;
includeZones?: boolean;
includeCalculs?: boolean;
includePhases?: boolean;
}
): Promise<ResultatExport> {
try {
const data = {
chantierId,
format: options.format,
template: options.template || 'DETAILLE',
sections: {
materiaux: options.includeMateriaux !== false,
zones: options.includeZones !== false,
calculs: options.includeCalculs !== false,
phases: options.includePhases !== false
},
includeCharts: options.includeCharts || false,
includeImages: options.includeImages || false
};
const blob = await ApiService.post<Blob>(
'/calculs-techniques/export-projet-complet',
data,
{ responseType: 'blob' }
);
const filename = this.genererNomFichier(`projet-${chantierId}`, options.format);
return {
filename,
blob,
size: blob.size,
format: options.format,
nbLignes: 0,
dateGeneration: new Date().toISOString()
};
} catch (error) {
console.error('Erreur export projet complet:', error);
throw new Error('Impossible d\'exporter le projet complet');
}
}
/**
* Génération de devis BTP détaillé avec matériaux et calculs
*/
static async genererDevisBTP(
devisData: {
chantierId: string;
phases: Array<{
nom: string;
materiaux: Array<{
code: string;
quantite: number;
prixUnitaire?: number;
}>;
calculs?: any;
}>;
client: {
nom: string;
adresse: string;
telephone?: string;
email?: string;
};
options: {
margeCommerciale: number;
tva: number;
delaiExecution: number;
validiteDevis: number; // jours
conditionsPaiement: string;
};
},
format: FormatExport = 'PDF'
): Promise<ResultatExport> {
try {
const blob = await ApiService.post<Blob>(
'/calculs-techniques/generer-devis',
{
...devisData,
format,
template: 'DEVIS_PROFESSIONNEL'
},
{ responseType: 'blob' }
);
const filename = this.genererNomFichier(`devis-${devisData.chantierId}`, format);
return {
filename,
blob,
size: blob.size,
format,
nbLignes: devisData.phases.length,
dateGeneration: new Date().toISOString()
};
} catch (error) {
console.error('Erreur génération devis:', error);
throw new Error('Impossible de générer le devis');
}
}
/**
* Export des matériaux avec QR codes pour traçabilité
*/
static async exporterMateriauxAvecQRCodes(
materiaux: MaterielBTP[],
options: OptionsExport
): Promise<ResultatExport> {
try {
const data = {
materiaux: materiaux.map(m => ({
id: m.id,
code: m.code,
nom: m.nom,
categorie: m.categorie,
specifications: {
resistance: m.resistanceCompression,
densite: m.densite,
norme: m.normePrincipale
}
})),
format: options.format,
includeQRCodes: true,
template: 'TRACABILITE'
};
const blob = await ApiService.post<Blob>(
'/calculs-techniques/export-tracabilite',
data,
{ responseType: 'blob' }
);
const filename = this.genererNomFichier('tracabilite-materiaux', options.format);
return {
filename,
blob,
size: blob.size,
format: options.format,
nbLignes: materiaux.length,
dateGeneration: new Date().toISOString()
};
} catch (error) {
console.error('Erreur export traçabilité:', error);
throw new Error('Impossible d\'exporter la traçabilité');
}
}
// =================== MÉTHODES UTILITAIRES ===================
/**
* Télécharge automatiquement le fichier exporté
*/
static telechargerFichier(resultat: ResultatExport): void {
const url = window.URL.createObjectURL(resultat.blob);
const a = document.createElement('a');
a.href = url;
a.download = resultat.filename;
document.body.appendChild(a);
a.click();
window.URL.revokeObjectURL(url);
document.body.removeChild(a);
}
/**
* Prévisualise le contenu avant export (pour PDF)
*/
static async previsualiserExport(
type: 'MATERIAUX' | 'ZONES' | 'CALCULS' | 'DEVIS',
donnees: any,
options: OptionsExport
): Promise<string> {
try {
const response = await ApiService.post<{ previewUrl: string }>(
'/calculs-techniques/previsualiser-export',
{
type,
donnees,
options
}
);
return response.previewUrl;
} catch (error) {
console.error('Erreur prévisualisation:', error);
throw new Error('Impossible de générer la prévisualisation');
}
}
/**
* Obtient les templates d'export disponibles
*/
static async getTemplatesDisponibles(): Promise<Array<{
id: string;
nom: string;
description: string;
formatsSupportes: FormatExport[];
sections: string[];
}>> {
try {
const response = await ApiService.get<Array<{
id: string;
nom: string;
description: string;
formatsSupportes: FormatExport[];
sections: string[];
}>>('/calculs-techniques/templates-export');
return response;
} catch (error) {
// Templates par défaut
return [
{
id: 'STANDARD',
nom: 'Standard',
description: 'Export standard avec informations essentielles',
formatsSupportes: ['CSV', 'EXCEL', 'PDF'],
sections: ['donnees_base']
},
{
id: 'DETAILLE',
nom: 'Détaillé',
description: 'Export complet avec toutes les spécifications techniques',
formatsSupportes: ['EXCEL', 'PDF'],
sections: ['donnees_base', 'specifications', 'calculs', 'normes']
},
{
id: 'RESUME',
nom: 'Résumé',
description: 'Export synthétique pour présentation',
formatsSupportes: ['PDF'],
sections: ['resume', 'graphiques']
}
];
}
}
/**
* Validation des options d'export
*/
static validerOptionsExport(options: OptionsExport): {
valide: boolean;
erreurs: string[];
} {
const erreurs: string[] = [];
if (!options.format) {
erreurs.push('Format d\'export requis');
}
if (options.format === 'PDF' && options.colonnesPersonnalisees?.length > 20) {
erreurs.push('Maximum 20 colonnes pour export PDF');
}
if (options.includeImages && options.format === 'CSV') {
erreurs.push('Images non supportées en format CSV');
}
return {
valide: erreurs.length === 0,
erreurs
};
}
// =================== MÉTHODES PRIVÉES ===================
/**
* Génère un nom de fichier unique
*/
private static genererNomFichier(prefix: string, format: FormatExport): string {
const timestamp = new Date().toISOString().replace(/[:.]/g, '-').slice(0, -5);
const extension = format.toLowerCase();
return `${prefix}_${timestamp}.${extension}`;
}
/**
* Export côté client en cas de problème serveur
*/
private static async exporterCalculsCoteClient(
calculs: any[],
options: OptionsExport
): Promise<ResultatExport> {
let contenu = '';
if (options.format === 'CSV') {
// Génération CSV simple
contenu = 'Type,Résultat,Date\n';
calculs.forEach(calcul => {
contenu += `${calcul.type},"${JSON.stringify(calcul.resultat)}",${new Date().toISOString()}\n`;
});
} else {
// Format texte pour autres formats
contenu = 'Export des calculs techniques BTP\n\n';
calculs.forEach(calcul => {
contenu += `Type: ${calcul.type}\n`;
contenu += `Résultat: ${JSON.stringify(calcul.resultat, null, 2)}\n\n`;
});
}
const blob = new Blob([contenu], { type: 'text/plain;charset=utf-8' });
const filename = this.genererNomFichier('calculs-fallback', 'CSV');
return {
filename,
blob,
size: blob.size,
format: 'CSV',
nbLignes: calculs.length,
dateGeneration: new Date().toISOString()
};
}
}