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

328 lines
11 KiB
TypeScript

import { apiClient } from './api-client';
export interface DashboardMetrics {
totalChantiers: number;
chantiersActifs: number;
chantiersEnRetard: number;
chantiersTermines: number;
totalEquipes: number;
equipesDisponibles: number;
totalMateriel: number;
materielDisponible: number;
materielEnMaintenance: number;
totalDocuments: number;
totalPhotos: number;
budgetTotal: number;
coutReel: number;
chiffreAffaires: number;
objectifCA: number;
tauxReussite: number;
satisfactionClient: number;
}
export interface ChantierActif {
id: string;
nom: string;
client: string;
avancement: number;
dateDebut: string;
dateFin: string;
statut: 'EN_COURS' | 'EN_RETARD' | 'PLANIFIE' | 'TERMINE';
budget: number;
coutReel: number;
equipe?: {
id: string;
nom: string;
nombreMembres: number;
};
}
export interface ActiviteRecente {
id: string;
type: 'CHANTIER' | 'MAINTENANCE' | 'DOCUMENT' | 'EQUIPE';
titre: string;
description: string;
date: string;
utilisateur: string;
statut: 'SUCCESS' | 'WARNING' | 'ERROR' | 'INFO';
}
export interface TacheUrgente {
id: string;
titre: string;
description: string;
priorite: 'HAUTE' | 'MOYENNE' | 'BASSE';
echeance: string;
assignee: string;
statut: 'A_FAIRE' | 'EN_COURS' | 'TERMINEE';
chantier?: {
id: string;
nom: string;
};
}
export interface StatistiquesMaintenance {
totalEquipements: number;
maintenancesPreventives: number;
maintenancesCorrectives: number;
equipementsEnPanne: number;
tauxDisponibilite: number;
}
export interface DashboardData {
metrics: DashboardMetrics;
chantiersActifs: ChantierActif[];
activitesRecentes: ActiviteRecente[];
tachesUrgentes: TacheUrgente[];
statistiquesMaintenance: StatistiquesMaintenance;
graphiques: {
chiffreAffaires: {
labels: string[];
objectifs: number[];
realisations: number[];
};
avancementPhases: {
labels: string[];
pourcentages: number[];
};
};
}
class DashboardService {
private readonly baseUrl = '/api';
async getDashboardData(periode: 'semaine' | 'mois' | 'trimestre' | 'annee' = 'mois'): Promise<DashboardData> {
try {
console.log('🏗️ DashboardService: Récupération des données depuis les endpoints réels...');
// Récupérer les données depuis les différents endpoints réels
const [chantiers, clients, materiels, employes] = await Promise.all([
apiClient.get('/api/v1/chantiers').catch(() => ({ data: [] })),
apiClient.get('/api/v1/clients').catch(() => ({ data: [] })),
apiClient.get('/api/v1/materiels').catch(() => ({ data: [] })),
apiClient.get('/api/v1/employes').catch(() => ({ data: [] }))
]);
console.log('🏗️ DashboardService: Données récupérées:', {
chantiers: chantiers.data.length,
clients: clients.data.length,
materiels: materiels.data.length,
employes: employes.data.length
});
// Calculer les métriques à partir des données réelles
const metrics = this.calculateMetrics(chantiers.data, clients.data, materiels.data, employes.data);
const chantiersActifs = this.filterChantiersActifs(chantiers.data);
return {
metrics,
chantiersActifs,
activitesRecentes: [], // TODO: Implémenter avec les vraies données
tachesUrgentes: [], // TODO: Implémenter avec les vraies données
statistiquesMaintenance: this.calculateMaintenanceStats(materiels.data)
};
} catch (error) {
console.error('Erreur lors de la récupération des données du dashboard:', error);
throw error;
}
}
async getMetrics(periode: 'semaine' | 'mois' | 'trimestre' | 'annee' = 'mois'): Promise<DashboardMetrics> {
try {
// Utiliser les endpoints réels pour calculer les métriques
const [chantiers, employes, materiels] = await Promise.all([
apiClient.get('/api/chantiers').catch(() => ({ data: [] })),
apiClient.get('/api/employes').catch(() => ({ data: [] })),
apiClient.get('/api/materiels').catch(() => ({ data: [] }))
]);
return this.calculateMetrics(chantiers.data, [], materiels.data, employes.data);
} catch (error) {
console.error('Erreur lors de la récupération des métriques:', error);
throw error;
}
}
async getChantiersActifs(limit: number = 10): Promise<ChantierActif[]> {
try {
const response = await apiClient.get('/api/v1/chantiers');
const chantiers = response.data || [];
return this.filterChantiersActifs(chantiers).slice(0, limit);
} catch (error) {
console.error('Erreur lors de la récupération des chantiers actifs:', error);
return []; // Retourner un tableau vide en cas d'erreur
}
}
async getActivitesRecentes(limit: number = 20): Promise<ActiviteRecente[]> {
try {
const response = await apiClient.get(`${this.baseUrl}/activites-recentes?limit=${limit}`);
return response.data;
} catch (error) {
console.error('Erreur lors de la récupération des activités récentes:', error);
throw error;
}
}
async getTachesUrgentes(limit: number = 10): Promise<TacheUrgente[]> {
try {
const response = await apiClient.get(`${this.baseUrl}/taches-urgentes?limit=${limit}`);
return response.data;
} catch (error) {
console.error('Erreur lors de la récupération des tâches urgentes:', error);
throw error;
}
}
async getStatistiquesMaintenance(): Promise<StatistiquesMaintenance> {
try {
const response = await apiClient.get(`${this.baseUrl}/statistiques-maintenance`);
return response.data;
} catch (error) {
console.error('Erreur lors de la récupération des statistiques de maintenance:', error);
throw error;
}
}
async exportDashboard(format: 'pdf' | 'excel' = 'pdf', periode: string = 'mois'): Promise<Blob> {
try {
const response = await apiClient.get(`${this.baseUrl}/export`, {
params: { format, periode },
responseType: 'blob'
});
return response.data;
} catch (error) {
console.error('Erreur lors de l\'export du dashboard:', error);
throw error;
}
}
// Méthodes utilitaires pour calculer les métriques à partir des données réelles
private calculateMetrics(chantiers: any[], clients: any[], materiels: any[], employes: any[]): DashboardMetrics {
const chantiersActifs = chantiers.filter(c => c.statut === 'EN_COURS' || c.statut === 'ACTIF');
const chantiersEnRetard = chantiers.filter(c => c.statut === 'EN_RETARD');
const chantiersTermines = chantiers.filter(c => c.statut === 'TERMINE');
const equipesDisponibles = employes.filter(e => e.statut === 'DISPONIBLE' || e.statut === 'ACTIF');
const materielDisponible = materiels.filter(m => m.statut === 'DISPONIBLE');
const materielEnMaintenance = materiels.filter(m => m.statut === 'MAINTENANCE');
return {
totalChantiers: chantiers.length,
chantiersActifs: chantiersActifs.length,
chantiersEnRetard: chantiersEnRetard.length,
chantiersTermines: chantiersTermines.length,
totalEquipes: employes.length,
equipesDisponibles: equipesDisponibles.length,
totalMateriel: materiels.length,
materielDisponible: materielDisponible.length,
materielEnMaintenance: materielEnMaintenance.length,
totalDocuments: 0, // TODO: Implémenter quand l'endpoint sera disponible
totalPhotos: 0, // TODO: Implémenter quand l'endpoint sera disponible
budgetTotal: chantiers.reduce((sum, c) => sum + (c.budget || 0), 0),
coutReel: chantiers.reduce((sum, c) => sum + (c.coutReel || 0), 0),
chiffreAffaires: chantiersTermines.reduce((sum, c) => sum + (c.budget || 0), 0),
objectifCA: 1000000, // TODO: Récupérer depuis la configuration
tauxReussite: chantiers.length > 0 ? (chantiersTermines.length / chantiers.length) * 100 : 0,
satisfactionClient: 85 // TODO: Calculer depuis les évaluations clients
};
}
private filterChantiersActifs(chantiers: any[]): ChantierActif[] {
return chantiers
.filter(c => c.statut === 'EN_COURS' || c.statut === 'ACTIF')
.map(c => ({
id: c.id,
nom: c.nom || c.titre,
client: c.client?.nom || c.clientNom || 'Client non défini',
avancement: c.avancement || 0,
dateDebut: c.dateDebut,
dateFin: c.dateFin,
statut: c.statut,
budget: c.budget || 0,
coutReel: c.coutReel || 0,
equipe: c.equipe ? {
id: c.equipe.id,
nom: c.equipe.nom,
nombreMembres: c.equipe.nombreMembres || 0
} : undefined
}));
}
private calculateMaintenanceStats(materiels: any[]): StatistiquesMaintenance {
const materielEnMaintenance = materiels.filter(m => m.statut === 'MAINTENANCE');
const materielDisponible = materiels.filter(m => m.statut === 'DISPONIBLE');
return {
materielEnMaintenance: materielEnMaintenance.length,
materielDisponible: materielDisponible.length,
maintenancesPrevues: 0, // TODO: Implémenter avec les vraies données
maintenancesEnRetard: 0, // TODO: Implémenter avec les vraies données
coutMaintenance: 0, // TODO: Calculer depuis les coûts de maintenance
tauxDisponibilite: materiels.length > 0 ? (materielDisponible.length / materiels.length) * 100 : 0
};
}
// Méthodes utilitaires pour formatter les données
static formatCurrency(amount: number): string {
return new Intl.NumberFormat('fr-FR', {
style: 'currency',
currency: 'EUR'
}).format(amount);
}
static formatPercentage(value: number): string {
return new Intl.NumberFormat('fr-FR', {
style: 'percent',
minimumFractionDigits: 1,
maximumFractionDigits: 1
}).format(value / 100);
}
static formatDate(dateString: string): string {
return new Date(dateString).toLocaleDateString('fr-FR', {
day: '2-digit',
month: '2-digit',
year: 'numeric'
});
}
static getStatutColor(statut: string): string {
const colors: Record<string, string> = {
'EN_COURS': 'success',
'EN_RETARD': 'danger',
'PLANIFIE': 'info',
'TERMINE': 'secondary',
'SUCCESS': 'success',
'WARNING': 'warning',
'ERROR': 'danger',
'INFO': 'info',
'HAUTE': 'danger',
'MOYENNE': 'warning',
'BASSE': 'info',
'A_FAIRE': 'secondary',
'TERMINEE': 'success'
};
return colors[statut] || 'secondary';
}
static getStatutIcon(statut: string): string {
const icons: Record<string, string> = {
'EN_COURS': 'pi-play',
'EN_RETARD': 'pi-exclamation-triangle',
'PLANIFIE': 'pi-calendar',
'TERMINE': 'pi-check',
'SUCCESS': 'pi-check-circle',
'WARNING': 'pi-exclamation-triangle',
'ERROR': 'pi-times-circle',
'INFO': 'pi-info-circle',
'CHANTIER': 'pi-building',
'MAINTENANCE': 'pi-cog',
'DOCUMENT': 'pi-file',
'EQUIPE': 'pi-users'
};
return icons[statut] || 'pi-circle';
}
}
export const dashboardService = new DashboardService();