328 lines
11 KiB
TypeScript
328 lines
11 KiB
TypeScript
/**
|
|
* Service pour la gestion des templates de phases BTP via l'API backend
|
|
* Remplace l'utilisation des données statiques par des appels API
|
|
*/
|
|
|
|
import { TypeChantier, PhaseTemplate, ChantierTemplate, TYPE_CHANTIER_LABELS, CATEGORIES_CHANTIER } from '../types/chantier-templates';
|
|
import { PhaseChantier } from '../types/btp-extended';
|
|
|
|
class PhaseTemplateService {
|
|
|
|
private apiBaseUrl = process.env.NEXT_PUBLIC_API_URL || 'http://localhost:8080/api/v1';
|
|
|
|
/**
|
|
* Récupérer la liste des types de chantiers disponibles depuis l'API
|
|
*/
|
|
async getAvailableChantierTypes(): Promise<{ value: TypeChantier; label: string; categorie: string }[]> {
|
|
try {
|
|
const response = await fetch(`${this.apiBaseUrl}/phase-templates/types-chantier`, {
|
|
headers: {
|
|
'Authorization': `Bearer ${localStorage.getItem('token')}`,
|
|
'Content-Type': 'application/json'
|
|
}
|
|
});
|
|
|
|
if (!response.ok) {
|
|
throw new Error('Erreur lors de la récupération des types de chantiers');
|
|
}
|
|
|
|
const typesFromAPI = await response.json();
|
|
|
|
// Mapper les types de l'API vers le format attendu par le frontend
|
|
const mappedTypes = typesFromAPI.map((type: any) => {
|
|
// Trouver la catégorie correspondante
|
|
let categorie = 'Autre';
|
|
for (const [cat, config] of Object.entries(CATEGORIES_CHANTIER)) {
|
|
if (config.types.includes(type as TypeChantier)) {
|
|
categorie = config.label;
|
|
break;
|
|
}
|
|
}
|
|
|
|
return {
|
|
value: type as TypeChantier,
|
|
label: TYPE_CHANTIER_LABELS[type as TypeChantier] || type,
|
|
categorie: categorie
|
|
};
|
|
});
|
|
|
|
return mappedTypes;
|
|
|
|
} catch (error) {
|
|
console.error('Erreur lors de la récupération des types de chantiers:', error);
|
|
// Fallback vers les données locales si l'API est indisponible
|
|
return this.getLocalChantierTypes();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Récupérer les templates de phases pour un type de chantier
|
|
*/
|
|
async getTemplatesByType(typeChantier: TypeChantier): Promise<PhaseTemplate[]> {
|
|
try {
|
|
const response = await fetch(`${this.apiBaseUrl}/phase-templates/by-type/${typeChantier}`, {
|
|
headers: {
|
|
'Authorization': `Bearer ${localStorage.getItem('token')}`,
|
|
'Content-Type': 'application/json'
|
|
}
|
|
});
|
|
|
|
if (!response.ok) {
|
|
throw new Error('Erreur lors de la récupération des templates');
|
|
}
|
|
|
|
const templates = await response.json();
|
|
return templates;
|
|
|
|
} catch (error) {
|
|
console.error('Erreur lors de la récupération des templates:', error);
|
|
throw error;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Récupérer un template par son ID avec ses sous-phases
|
|
*/
|
|
async getTemplateById(id: string): Promise<PhaseTemplate | null> {
|
|
try {
|
|
const response = await fetch(`${this.apiBaseUrl}/phase-templates/${id}`, {
|
|
headers: {
|
|
'Authorization': `Bearer ${localStorage.getItem('token')}`,
|
|
'Content-Type': 'application/json'
|
|
}
|
|
});
|
|
|
|
if (response.status === 404) {
|
|
return null;
|
|
}
|
|
|
|
if (!response.ok) {
|
|
throw new Error('Erreur lors de la récupération du template');
|
|
}
|
|
|
|
const template = await response.json();
|
|
return template;
|
|
|
|
} catch (error) {
|
|
console.error('Erreur lors de la récupération du template:', error);
|
|
throw error;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Prévisualiser les phases qui seraient générées pour un type de chantier
|
|
*/
|
|
async previewPhases(typeChantier: TypeChantier): Promise<PhaseTemplate[]> {
|
|
try {
|
|
const response = await fetch(`${this.apiBaseUrl}/phase-templates/previsualisation/${typeChantier}`, {
|
|
headers: {
|
|
'Authorization': `Bearer ${localStorage.getItem('token')}`,
|
|
'Content-Type': 'application/json'
|
|
}
|
|
});
|
|
|
|
if (!response.ok) {
|
|
throw new Error('Erreur lors de la prévisualisation des phases');
|
|
}
|
|
|
|
const phases = await response.json();
|
|
return phases;
|
|
|
|
} catch (error) {
|
|
console.error('Erreur lors de la prévisualisation des phases:', error);
|
|
throw error;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Calculer la durée totale estimée pour un type de chantier
|
|
*/
|
|
async calculateDureeTotale(typeChantier: TypeChantier): Promise<number> {
|
|
try {
|
|
const response = await fetch(`${this.apiBaseUrl}/phase-templates/duree-estimee/${typeChantier}`, {
|
|
headers: {
|
|
'Authorization': `Bearer ${localStorage.getItem('token')}`,
|
|
'Content-Type': 'application/json'
|
|
}
|
|
});
|
|
|
|
if (!response.ok) {
|
|
throw new Error('Erreur lors du calcul de la durée');
|
|
}
|
|
|
|
const duree = await response.json();
|
|
return duree;
|
|
|
|
} catch (error) {
|
|
console.error('Erreur lors du calcul de la durée:', error);
|
|
throw error;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Analyser la complexité d'un type de chantier
|
|
*/
|
|
async analyzeComplexity(typeChantier: TypeChantier): Promise<{
|
|
typeChantier: TypeChantier;
|
|
nombrePhases: number;
|
|
nombrePhasesCritiques: number;
|
|
dureeTotal: number;
|
|
niveauComplexite: string;
|
|
}> {
|
|
try {
|
|
const response = await fetch(`${this.apiBaseUrl}/phase-templates/complexite/${typeChantier}`, {
|
|
headers: {
|
|
'Authorization': `Bearer ${localStorage.getItem('token')}`,
|
|
'Content-Type': 'application/json'
|
|
}
|
|
});
|
|
|
|
if (!response.ok) {
|
|
throw new Error('Erreur lors de l\'analyse de complexité');
|
|
}
|
|
|
|
const complexite = await response.json();
|
|
return complexite;
|
|
|
|
} catch (error) {
|
|
console.error('Erreur lors de l\'analyse de complexité:', error);
|
|
throw error;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Générer automatiquement les phases pour un chantier
|
|
*/
|
|
async generatePhases(
|
|
chantierId: string,
|
|
dateDebutChantier: Date,
|
|
inclureSousPhases: boolean = true
|
|
): Promise<PhaseChantier[]> {
|
|
try {
|
|
const response = await fetch(`${this.apiBaseUrl}/phase-templates/generer-phases`, {
|
|
method: 'POST',
|
|
headers: {
|
|
'Authorization': `Bearer ${localStorage.getItem('token')}`,
|
|
'Content-Type': 'application/json'
|
|
},
|
|
body: JSON.stringify({
|
|
chantierId: chantierId,
|
|
dateDebutChantier: dateDebutChantier.toISOString().split('T')[0],
|
|
inclureSousPhases: inclureSousPhases
|
|
})
|
|
});
|
|
|
|
if (!response.ok) {
|
|
const error = await response.text();
|
|
throw new Error(error || 'Erreur lors de la génération des phases');
|
|
}
|
|
|
|
const phasesGenerees = await response.json();
|
|
return phasesGenerees;
|
|
|
|
} catch (error) {
|
|
console.error('Erreur lors de la génération des phases:', error);
|
|
throw error;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Créer un nouveau template de phase
|
|
*/
|
|
async createTemplate(template: Partial<PhaseTemplate>): Promise<PhaseTemplate> {
|
|
try {
|
|
const response = await fetch(`${this.apiBaseUrl}/phase-templates`, {
|
|
method: 'POST',
|
|
headers: {
|
|
'Authorization': `Bearer ${localStorage.getItem('token')}`,
|
|
'Content-Type': 'application/json'
|
|
},
|
|
body: JSON.stringify(template)
|
|
});
|
|
|
|
if (!response.ok) {
|
|
const error = await response.text();
|
|
throw new Error(error || 'Erreur lors de la création du template');
|
|
}
|
|
|
|
const nouveauTemplate = await response.json();
|
|
return nouveauTemplate;
|
|
|
|
} catch (error) {
|
|
console.error('Erreur lors de la création du template:', error);
|
|
throw error;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Mettre à jour un template de phase
|
|
*/
|
|
async updateTemplate(id: string, template: Partial<PhaseTemplate>): Promise<PhaseTemplate> {
|
|
try {
|
|
const response = await fetch(`${this.apiBaseUrl}/phase-templates/${id}`, {
|
|
method: 'PUT',
|
|
headers: {
|
|
'Authorization': `Bearer ${localStorage.getItem('token')}`,
|
|
'Content-Type': 'application/json'
|
|
},
|
|
body: JSON.stringify(template)
|
|
});
|
|
|
|
if (!response.ok) {
|
|
const error = await response.text();
|
|
throw new Error(error || 'Erreur lors de la mise à jour du template');
|
|
}
|
|
|
|
const templateMisAJour = await response.json();
|
|
return templateMisAJour;
|
|
|
|
} catch (error) {
|
|
console.error('Erreur lors de la mise à jour du template:', error);
|
|
throw error;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Supprimer (désactiver) un template de phase
|
|
*/
|
|
async deleteTemplate(id: string): Promise<void> {
|
|
try {
|
|
const response = await fetch(`${this.apiBaseUrl}/phase-templates/${id}`, {
|
|
method: 'DELETE',
|
|
headers: {
|
|
'Authorization': `Bearer ${localStorage.getItem('token')}`
|
|
}
|
|
});
|
|
|
|
if (!response.ok) {
|
|
const error = await response.text();
|
|
throw new Error(error || 'Erreur lors de la suppression du template');
|
|
}
|
|
|
|
} catch (error) {
|
|
console.error('Erreur lors de la suppression du template:', error);
|
|
throw error;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Fallback: Récupérer les types de chantiers depuis les données locales
|
|
*/
|
|
private getLocalChantierTypes(): { value: TypeChantier; label: string; categorie: string }[] {
|
|
const types: { value: TypeChantier; label: string; categorie: string }[] = [];
|
|
|
|
for (const [categorie, config] of Object.entries(CATEGORIES_CHANTIER)) {
|
|
for (const type of config.types) {
|
|
types.push({
|
|
value: type,
|
|
label: TYPE_CHANTIER_LABELS[type],
|
|
categorie: config.label
|
|
});
|
|
}
|
|
}
|
|
|
|
return types;
|
|
}
|
|
}
|
|
|
|
export default new PhaseTemplateService(); |