328 lines
11 KiB
TypeScript
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(); |