491 lines
16 KiB
TypeScript
Executable File
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
|
|
}
|
|
};
|
|
}; |