Files
btpxpress-frontend/hooks/useBTPServices.ts

491 lines
16 KiB
TypeScript
Executable File

import { useState, useEffect, useCallback } from 'react';
import {
MaterielBTPService,
MaterielBTP,
CategorieMateriel,
RechercheMaterielParams
} from '../services/materielBTPService';
import {
ZoneClimatiqueService,
ZoneClimatique,
CriteresRecherche
} from '../services/zoneClimatiqueService';
import {
CalculsTechniquesService,
ParametresCalculBriques,
ResultatCalculBriques,
ParametresCalculBetonArme,
ResultatCalculBetonArme
} from '../services/calculsTechniquesService';
import {
ExportBTPService,
FormatExport,
OptionsExport,
ResultatExport
} from '../services/exportBTPService';
/**
* Hook personnalisé pour l'utilisation des services BTP ultra-détaillés
* Système le plus ambitieux d'Afrique pour la gestion BTP
*/
export interface UseBTPServicesReturn {
// États de chargement
loading: {
materiaux: boolean;
zones: boolean;
calculs: boolean;
export: boolean;
};
// États d'erreur
errors: {
materiaux: string | null;
zones: string | null;
calculs: string | null;
export: string | null;
};
// Données
data: {
materiaux: MaterielBTP[];
zones: ZoneClimatique[];
dernierCalcul: any;
dernierExport: ResultatExport | null;
};
// Actions matériaux
materiaux: {
charger: (params?: RechercheMaterielParams) => Promise<void>;
rechercher: (criteres: RechercheMaterielParams) => Promise<MaterielBTP[]>;
obtenirParCode: (code: string) => Promise<MaterielBTP>;
obtenirParCategorie: (categorie: CategorieMateriel) => Promise<MaterielBTP[]>;
validerPourZone: (codeMateriel: string, zoneCode: string) => Promise<any>;
obtenirAlternatives: (codeMateriel: string, zoneCode?: string) => Promise<MaterielBTP[]>;
};
// Actions zones climatiques
zones: {
charger: () => Promise<void>;
rechercher: (criteres: CriteresRecherche) => Promise<ZoneClimatique[]>;
obtenirParCode: (code: string) => Promise<ZoneClimatique>;
obtenirMeilleureAdaptation: (temp: number, humidite: number, vents: number) => Promise<ZoneClimatique | null>;
obtenirRecommandations: (zoneCode: string) => Promise<any>;
simulerImpact: (zoneCode: string, parametres: any) => Promise<any>;
};
// Actions calculs techniques
calculs: {
calculerBriques: (params: ParametresCalculBriques) => Promise<ResultatCalculBriques>;
calculerBetonArme: (params: ParametresCalculBetonArme) => Promise<ResultatCalculBetonArme>;
obtenirDosagesBeton: () => Promise<any>;
estimationRapideBriques: (surface: number, typeBrique?: string) => Promise<any>;
estimationRapideBeton: (volume: number, classeBeton?: string) => Promise<any>;
genererDevis: (calculs: any, options?: any) => Promise<any>;
};
// Actions export
export: {
exporterMateriaux: (options: OptionsExport & { filtres?: RechercheMaterielParams }) => Promise<void>;
exporterZones: (options: OptionsExport) => Promise<void>;
exporterCalculs: (calculs: any[], options: OptionsExport) => Promise<void>;
genererDevis: (devisData: any, format?: FormatExport) => Promise<void>;
telechargerDernier: () => void;
};
// Utilitaires
utils: {
reinitialiser: () => void;
obtenirStatistiques: () => any;
validerParametres: (type: string, params: any) => any;
};
}
export const useBTPServices = (): UseBTPServicesReturn => {
// États de chargement
const [loading, setLoading] = useState({
materiaux: false,
zones: false,
calculs: false,
export: false
});
// États d'erreur
const [errors, setErrors] = useState({
materiaux: null as string | null,
zones: null as string | null,
calculs: null as string | null,
export: null as string | null
});
// Données
const [data, setData] = useState({
materiaux: [] as MaterielBTP[],
zones: [] as ZoneClimatique[],
dernierCalcul: null as any,
dernierExport: null as ResultatExport | null
});
// =================== FONCTIONS UTILITAIRES ===================
const setLoadingState = useCallback((key: keyof typeof loading, value: boolean) => {
setLoading(prev => ({ ...prev, [key]: value }));
}, []);
const setErrorState = useCallback((key: keyof typeof errors, error: string | null) => {
setErrors(prev => ({ ...prev, [key]: error }));
}, []);
const handleError = useCallback((key: keyof typeof errors, error: any) => {
const message = error?.message || error?.toString() || 'Une erreur est survenue';
setErrorState(key, message);
console.error(`Erreur ${key}:`, error);
}, [setErrorState]);
// =================== ACTIONS MATÉRIAUX ===================
const chargerMateriaux = useCallback(async (params?: RechercheMaterielParams) => {
setLoadingState('materiaux', true);
setErrorState('materiaux', null);
try {
const response = await MaterielBTPService.getMateriaux(params);
setData(prev => ({ ...prev, materiaux: response.materiaux }));
} catch (error) {
handleError('materiaux', error);
} finally {
setLoadingState('materiaux', false);
}
}, [setLoadingState, setErrorState, handleError]);
const rechercherMateriaux = useCallback(async (criteres: RechercheMaterielParams): Promise<MaterielBTP[]> => {
try {
return await MaterielBTPService.rechercherMateriaux(criteres);
} catch (error) {
handleError('materiaux', error);
return [];
}
}, [handleError]);
const obtenirMaterielParCode = useCallback(async (code: string): Promise<MaterielBTP> => {
try {
return await MaterielBTPService.getMaterielByCode(code);
} catch (error) {
handleError('materiaux', error);
throw error;
}
}, [handleError]);
const obtenirMateriauxParCategorie = useCallback(async (categorie: CategorieMateriel): Promise<MaterielBTP[]> => {
try {
return await MaterielBTPService.getMateriauxByCategorie(categorie);
} catch (error) {
handleError('materiaux', error);
return [];
}
}, [handleError]);
const validerMaterielPourZone = useCallback(async (codeMateriel: string, zoneCode: string) => {
try {
return await MaterielBTPService.validerMaterielPourZone(codeMateriel, zoneCode);
} catch (error) {
handleError('materiaux', error);
return { adapte: false, warnings: ['Validation impossible'], recommendations: [] };
}
}, [handleError]);
const obtenirAlternativesMateriel = useCallback(async (codeMateriel: string, zoneCode?: string): Promise<MaterielBTP[]> => {
try {
return await MaterielBTPService.getAlternativesMateriel(codeMateriel, zoneCode);
} catch (error) {
handleError('materiaux', error);
return [];
}
}, [handleError]);
// =================== ACTIONS ZONES CLIMATIQUES ===================
const chargerZones = useCallback(async () => {
setLoadingState('zones', true);
setErrorState('zones', null);
try {
const response = await ZoneClimatiqueService.getZonesClimatiques();
setData(prev => ({ ...prev, zones: response.zones }));
} catch (error) {
handleError('zones', error);
} finally {
setLoadingState('zones', false);
}
}, [setLoadingState, setErrorState, handleError]);
const rechercherZones = useCallback(async (criteres: CriteresRecherche): Promise<ZoneClimatique[]> => {
try {
return await ZoneClimatiqueService.rechercherZones(criteres);
} catch (error) {
handleError('zones', error);
return [];
}
}, [handleError]);
const obtenirZoneParCode = useCallback(async (code: string): Promise<ZoneClimatique> => {
try {
return await ZoneClimatiqueService.getZoneByCode(code);
} catch (error) {
handleError('zones', error);
throw error;
}
}, [handleError]);
const obtenirMeilleureAdaptation = useCallback(async (temp: number, humidite: number, vents: number) => {
try {
return await ZoneClimatiqueService.getMeilleureAdaptation(temp, humidite, vents);
} catch (error) {
handleError('zones', error);
return null;
}
}, [handleError]);
const obtenirRecommandationsZone = useCallback(async (zoneCode: string) => {
try {
return await ZoneClimatiqueService.getRecommandationsConstruction(zoneCode);
} catch (error) {
handleError('zones', error);
return { fondations: [], structure: [], enveloppe: [], finitions: [], equipements: [] };
}
}, [handleError]);
const simulerImpactClimatique = useCallback(async (zoneCode: string, parametres: any) => {
try {
return await ZoneClimatiqueService.simulerImpactClimatique(zoneCode, parametres);
} catch (error) {
handleError('zones', error);
return null;
}
}, [handleError]);
// =================== ACTIONS CALCULS TECHNIQUES ===================
const calculerBriquesMur = useCallback(async (params: ParametresCalculBriques): Promise<ResultatCalculBriques> => {
setLoadingState('calculs', true);
setErrorState('calculs', null);
try {
const resultat = await CalculsTechniquesService.calculerBriquesMur(params);
setData(prev => ({ ...prev, dernierCalcul: { type: 'BRIQUES', resultat, params } }));
return resultat;
} catch (error) {
handleError('calculs', error);
throw error;
} finally {
setLoadingState('calculs', false);
}
}, [setLoadingState, setErrorState, handleError]);
const calculerBetonArme = useCallback(async (params: ParametresCalculBetonArme): Promise<ResultatCalculBetonArme> => {
setLoadingState('calculs', true);
setErrorState('calculs', null);
try {
const resultat = await CalculsTechniquesService.calculerBetonArme(params);
setData(prev => ({ ...prev, dernierCalcul: { type: 'BETON', resultat, params } }));
return resultat;
} catch (error) {
handleError('calculs', error);
throw error;
} finally {
setLoadingState('calculs', false);
}
}, [setLoadingState, setErrorState, handleError]);
const obtenirDosagesBeton = useCallback(async () => {
try {
return await CalculsTechniquesService.getDosagesBeton();
} catch (error) {
handleError('calculs', error);
return { dosages: {}, notes: [] };
}
}, [handleError]);
const estimationRapideBriques = useCallback(async (surface: number, typeBrique?: string) => {
try {
return await CalculsTechniquesService.estimationRapideBriques(surface, typeBrique);
} catch (error) {
handleError('calculs', error);
return { estimationBasse: 0, estimationHaute: 0, estimationMoyenne: 0, baseCalcul: '' };
}
}, [handleError]);
const estimationRapideBeton = useCallback(async (volume: number, classeBeton?: string) => {
try {
return await CalculsTechniquesService.estimationRapideBeton(volume, classeBeton);
} catch (error) {
handleError('calculs', error);
return { cimentSacs: 0, sableM3: 0, graviersM3: 0, eauLitres: 0, coutEstime: 0 };
}
}, [handleError]);
const genererDevis = useCallback(async (calculs: any, options?: any) => {
try {
return await CalculsTechniquesService.genererDevis(calculs, options);
} catch (error) {
handleError('calculs', error);
return { lignesDevis: [], totalHT: 0, totalTTC: 0, delaiExecution: 0 };
}
}, [handleError]);
// =================== ACTIONS EXPORT ===================
const exporterMateriaux = useCallback(async (options: OptionsExport & { filtres?: RechercheMaterielParams }) => {
setLoadingState('export', true);
setErrorState('export', null);
try {
const resultat = await ExportBTPService.exporterMateriaux(options);
setData(prev => ({ ...prev, dernierExport: resultat }));
ExportBTPService.telechargerFichier(resultat);
} catch (error) {
handleError('export', error);
} finally {
setLoadingState('export', false);
}
}, [setLoadingState, setErrorState, handleError]);
const exporterZones = useCallback(async (options: OptionsExport) => {
setLoadingState('export', true);
setErrorState('export', null);
try {
const resultat = await ExportBTPService.exporterZonesClimatiques(options);
setData(prev => ({ ...prev, dernierExport: resultat }));
ExportBTPService.telechargerFichier(resultat);
} catch (error) {
handleError('export', error);
} finally {
setLoadingState('export', false);
}
}, [setLoadingState, setErrorState, handleError]);
const exporterCalculs = useCallback(async (calculs: any[], options: OptionsExport) => {
setLoadingState('export', true);
setErrorState('export', null);
try {
const resultat = await ExportBTPService.exporterCalculs(calculs, options);
setData(prev => ({ ...prev, dernierExport: resultat }));
ExportBTPService.telechargerFichier(resultat);
} catch (error) {
handleError('export', error);
} finally {
setLoadingState('export', false);
}
}, [setLoadingState, setErrorState, handleError]);
const genererDevisBTP = useCallback(async (devisData: any, format: FormatExport = 'PDF') => {
setLoadingState('export', true);
setErrorState('export', null);
try {
const resultat = await ExportBTPService.genererDevisBTP(devisData, format);
setData(prev => ({ ...prev, dernierExport: resultat }));
ExportBTPService.telechargerFichier(resultat);
} catch (error) {
handleError('export', error);
} finally {
setLoadingState('export', false);
}
}, [setLoadingState, setErrorState, handleError]);
const telechargerDernierExport = useCallback(() => {
if (data.dernierExport) {
ExportBTPService.telechargerFichier(data.dernierExport);
}
}, [data.dernierExport]);
// =================== UTILITAIRES ===================
const reinitialiser = useCallback(() => {
setData({
materiaux: [],
zones: [],
dernierCalcul: null,
dernierExport: null
});
setErrors({
materiaux: null,
zones: null,
calculs: null,
export: null
});
}, []);
const obtenirStatistiques = useCallback(() => {
return {
nbMateriaux: data.materiaux.length,
nbZones: data.zones.length,
categoriesMateriaux: [...new Set(data.materiaux.map(m => m.categorie))],
derniereActivite: data.dernierCalcul ? new Date(data.dernierCalcul.date || Date.now()) : null,
tailleDernierExport: data.dernierExport?.size || 0
};
}, [data]);
const validerParametres = useCallback((type: string, params: any) => {
return CalculsTechniquesService.validerParametres(type as any, params);
}, []);
// =================== CHARGEMENT INITIAL ===================
useEffect(() => {
// Chargement automatique des données de base
chargerZones();
}, [chargerZones]);
// =================== RETURN ===================
return {
loading,
errors,
data,
materiaux: {
charger: chargerMateriaux,
rechercher: rechercherMateriaux,
obtenirParCode: obtenirMaterielParCode,
obtenirParCategorie: obtenirMateriauxParCategorie,
validerPourZone: validerMaterielPourZone,
obtenirAlternatives: obtenirAlternativesMateriel
},
zones: {
charger: chargerZones,
rechercher: rechercherZones,
obtenirParCode: obtenirZoneParCode,
obtenirMeilleureAdaptation,
obtenirRecommandations: obtenirRecommandationsZone,
simulerImpact: simulerImpactClimatique
},
calculs: {
calculerBriques: calculerBriquesMur,
calculerBetonArme,
obtenirDosagesBeton,
estimationRapideBriques,
estimationRapideBeton,
genererDevis
},
export: {
exporterMateriaux,
exporterZones,
exporterCalculs,
genererDevis: genererDevisBTP,
telechargerDernier: telechargerDernierExport
},
utils: {
reinitialiser,
obtenirStatistiques,
validerParametres
}
};
};