- Correction des erreurs TypeScript dans userService.ts et workflowTester.ts - Ajout des propriétés manquantes aux objets User mockés - Conversion des dates de string vers objets Date - Correction des appels asynchrones et des types incompatibles - Ajout de dynamic rendering pour résoudre les erreurs useSearchParams - Enveloppement de useSearchParams dans Suspense boundary - Configuration de force-dynamic au niveau du layout principal Build réussi: 126 pages générées avec succès 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
975 lines
36 KiB
TypeScript
975 lines
36 KiB
TypeScript
/**
|
|
* Services API pour BTP Xpress
|
|
*/
|
|
|
|
import axios, { AxiosInstance, AxiosResponse } from 'axios';
|
|
import { API_CONFIG } from '../config/api';
|
|
import { keycloak, KEYCLOAK_TIMEOUTS } from '../config/keycloak';
|
|
import { CacheService, CacheKeys } from './cacheService';
|
|
import {
|
|
Client,
|
|
Chantier,
|
|
Devis,
|
|
Facture,
|
|
DashboardStats,
|
|
ChantierRecent,
|
|
FactureEnRetard,
|
|
DevisEnAttente,
|
|
FilterOptions,
|
|
SearchResult,
|
|
Employe,
|
|
Equipe,
|
|
Materiel,
|
|
MaintenanceMateriel,
|
|
PlanningEvent,
|
|
PlanningCalendrierView,
|
|
PlanningConflict,
|
|
PlanningStats
|
|
} from '../types/btp';
|
|
import { UserInfo } from '../types/auth';
|
|
|
|
class ApiService {
|
|
private api: AxiosInstance;
|
|
private serverStatusListeners: ((isOnline: boolean) => void)[] = [];
|
|
|
|
constructor() {
|
|
this.api = axios.create({
|
|
baseURL: API_CONFIG.baseURL,
|
|
timeout: API_CONFIG.timeout,
|
|
headers: API_CONFIG.headers,
|
|
});
|
|
|
|
// Interceptor pour les requêtes - ajouter le token d'authentification
|
|
this.api.interceptors.request.use(
|
|
async (config) => {
|
|
// Utiliser le token stocké dans localStorage (nouveau système)
|
|
if (typeof window !== 'undefined') {
|
|
const accessToken = localStorage.getItem('accessToken');
|
|
|
|
if (accessToken) {
|
|
config.headers['Authorization'] = `Bearer ${accessToken}`;
|
|
console.log('🔐 API Request avec token:', config.url);
|
|
} else {
|
|
console.log('⚠️ API Request sans token:', config.url);
|
|
}
|
|
}
|
|
|
|
// Ajouter des en-têtes par défaut
|
|
config.headers['X-Requested-With'] = 'XMLHttpRequest';
|
|
|
|
return config;
|
|
},
|
|
(error) => {
|
|
return Promise.reject(error);
|
|
}
|
|
);
|
|
|
|
// Interceptor pour les réponses
|
|
this.api.interceptors.response.use(
|
|
(response: AxiosResponse) => response,
|
|
async (error) => {
|
|
// Ne pas logger les 404 sur les endpoints de chantiers par client (pas encore implémentés)
|
|
const is404OnChantiersByClient = error.response?.status === 404 &&
|
|
error.config?.url?.includes('/chantiers/client/');
|
|
|
|
if (!is404OnChantiersByClient) {
|
|
// Utiliser console.warn au lieu de console.error pour éviter les erreurs React DevTools
|
|
console.warn('API Error:', error.response?.status, error.response?.data || error.message);
|
|
}
|
|
|
|
// Gérer les erreurs de connexion réseau
|
|
if (!error.response) {
|
|
// Erreur réseau (serveur indisponible, pas de connexion, etc.)
|
|
if (error.code === 'ECONNABORTED') {
|
|
error.userMessage = 'Délai d\'attente dépassé. Le serveur backend met trop de temps à répondre.';
|
|
error.statusCode = 'TIMEOUT';
|
|
} else if (error.code === 'ERR_NETWORK') {
|
|
error.userMessage = 'Impossible de joindre le serveur backend. Vérifiez votre connexion internet et que le serveur backend est démarré (mvn quarkus:dev).';
|
|
error.statusCode = 'NETWORK_ERROR';
|
|
} else {
|
|
error.userMessage = 'Serveur backend indisponible. Vérifiez que le serveur backend est démarré (mvn quarkus:dev) et accessible sur le port 8080.';
|
|
error.statusCode = 'SERVER_UNAVAILABLE';
|
|
}
|
|
|
|
// Émettre un événement global pour notifier l'application
|
|
this.notifyServerStatus(false);
|
|
return Promise.reject(error);
|
|
}
|
|
|
|
// Serveur répond, donc il est disponible
|
|
this.notifyServerStatus(true);
|
|
|
|
if (error.response?.status === 401) {
|
|
// Gestion des erreurs d'authentification
|
|
if (typeof window !== 'undefined') {
|
|
const currentUrl = window.location.href;
|
|
const hasAuthCode = currentUrl.includes('code=') && currentUrl.includes('/dashboard');
|
|
|
|
if (!hasAuthCode) {
|
|
console.log('🔄 Token expiré, redirection vers la connexion...');
|
|
// Sauvegarder la page actuelle pour y revenir après reconnexion
|
|
const currentPath = window.location.pathname + window.location.search;
|
|
localStorage.setItem('returnUrl', currentPath);
|
|
|
|
// Nettoyer les tokens expirés
|
|
localStorage.removeItem('accessToken');
|
|
localStorage.removeItem('refreshToken');
|
|
localStorage.removeItem('idToken');
|
|
|
|
// Rediriger vers la page de connexion
|
|
window.location.href = '/api/auth/login';
|
|
} else {
|
|
console.log('🔄 API Service: Erreur 401 ignorée car authentification en cours...');
|
|
}
|
|
}
|
|
}
|
|
|
|
// Créer une erreur plus propre pour l'affichage
|
|
const errorMessage = error.response?.data?.message ||
|
|
error.response?.data?.error ||
|
|
error.message ||
|
|
'Une erreur est survenue';
|
|
|
|
const enhancedError = {
|
|
...error,
|
|
userMessage: errorMessage,
|
|
statusCode: error.response?.status
|
|
};
|
|
|
|
return Promise.reject(enhancedError);
|
|
}
|
|
);
|
|
}
|
|
|
|
// === GESTION STATUT SERVEUR ===
|
|
private notifyServerStatus(isOnline: boolean) {
|
|
this.serverStatusListeners.forEach(listener => {
|
|
try {
|
|
listener(isOnline);
|
|
} catch (error) {
|
|
console.error('Erreur dans listener de statut serveur:', error);
|
|
}
|
|
});
|
|
}
|
|
|
|
public onServerStatusChange(callback: (isOnline: boolean) => void) {
|
|
this.serverStatusListeners.push(callback);
|
|
return () => {
|
|
const index = this.serverStatusListeners.indexOf(callback);
|
|
if (index > -1) {
|
|
this.serverStatusListeners.splice(index, 1);
|
|
}
|
|
};
|
|
}
|
|
|
|
public async checkServerHealth(urgentCheck: boolean = false): Promise<boolean> {
|
|
const timeout = urgentCheck ? 3000 : 8000; // Timeout plus long pour checks de routine
|
|
|
|
try {
|
|
// Endpoint dédié health check ultra-léger
|
|
await this.api.get('/api/v1/health', {
|
|
timeout,
|
|
headers: { 'Cache-Control': 'no-cache' },
|
|
params: { _t: Date.now() } // Cache busting
|
|
});
|
|
this.notifyServerStatus(true);
|
|
return true;
|
|
} catch (error) {
|
|
// Fallback sur endpoint Quarkus standard puis clients
|
|
try {
|
|
await this.api.get('/q/health', { timeout: timeout * 0.8 });
|
|
this.notifyServerStatus(true);
|
|
return true;
|
|
} catch (secondError) {
|
|
try {
|
|
await this.api.get('/api/v1/clients', {
|
|
timeout: timeout * 0.6,
|
|
params: { size: 1 }
|
|
});
|
|
this.notifyServerStatus(true);
|
|
return true;
|
|
} catch (thirdError) {
|
|
this.notifyServerStatus(false);
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// === CLIENTS ===
|
|
async getClients(): Promise<Client[]> {
|
|
return CacheService.getOrSet(
|
|
CacheKeys.CLIENTS,
|
|
async () => {
|
|
const response = await this.api.get<Client[]>('/api/v1/clients');
|
|
return Array.isArray(response.data) ? response.data : [];
|
|
},
|
|
5 * 60 * 1000 // 5 minutes
|
|
);
|
|
}
|
|
|
|
async getClient(id: string): Promise<Client> {
|
|
const response = await this.api.get<Client>(`/api/v1/clients/${id}`);
|
|
return response.data;
|
|
}
|
|
|
|
async createClient(client: Partial<Client>): Promise<Client> {
|
|
const response = await this.api.post<Client>('/api/v1/clients', client);
|
|
return response.data;
|
|
}
|
|
|
|
async updateClient(id: string, client: Partial<Client>): Promise<Client> {
|
|
const response = await this.api.put<Client>(`/api/v1/clients/${id}`, client);
|
|
return response.data;
|
|
}
|
|
|
|
async deleteClient(id: string): Promise<void> {
|
|
await this.api.delete(`/api/v1/clients/${id}`);
|
|
}
|
|
|
|
async searchClients(params: {
|
|
nom?: string;
|
|
entreprise?: string;
|
|
ville?: string;
|
|
email?: string;
|
|
}): Promise<Client[]> {
|
|
const response = await this.api.get<Client[]>('/api/v1/clients/search', { params });
|
|
return response.data;
|
|
}
|
|
|
|
async searchClientsByNom(nom: string): Promise<Client[]> {
|
|
const response = await this.api.get<Client[]>('/api/v1/clients/search', { params: { nom } });
|
|
return response.data;
|
|
}
|
|
|
|
async searchClientsByEntreprise(entreprise: string): Promise<Client[]> {
|
|
const response = await this.api.get<Client[]>('/api/v1/clients/search', { params: { entreprise } });
|
|
return response.data;
|
|
}
|
|
|
|
async searchClientsByVille(ville: string): Promise<Client[]> {
|
|
const response = await this.api.get<Client[]>('/api/v1/clients/search', { params: { ville } });
|
|
return response.data;
|
|
}
|
|
|
|
async searchClientsByEmail(email: string): Promise<Client[]> {
|
|
const response = await this.api.get<Client[]>('/api/v1/clients/search', { params: { email } });
|
|
return response.data;
|
|
}
|
|
|
|
async getClientsCount(): Promise<number> {
|
|
const response = await this.api.get<number>('/api/v1/clients/count');
|
|
return response.data;
|
|
}
|
|
|
|
// === CHANTIERS ===
|
|
async getChantiers(): Promise<Chantier[]> {
|
|
const response = await this.api.get<Chantier[]>('/api/v1/chantiers');
|
|
return Array.isArray(response.data) ? response.data : [];
|
|
}
|
|
|
|
async getChantiersActifs(): Promise<Chantier[]> {
|
|
const response = await this.api.get<Chantier[]>('/api/v1/chantiers/actifs');
|
|
return Array.isArray(response.data) ? response.data : [];
|
|
}
|
|
|
|
async getChantier(id: string): Promise<Chantier> {
|
|
const response = await this.api.get<Chantier>(`/api/v1/chantiers/${id}`);
|
|
return response.data;
|
|
}
|
|
|
|
async createChantier(chantier: Partial<Chantier>): Promise<Chantier> {
|
|
const response = await this.api.post<Chantier>('/api/v1/chantiers', chantier);
|
|
return response.data;
|
|
}
|
|
|
|
async updateChantier(id: string, chantier: Partial<Chantier>): Promise<Chantier> {
|
|
const response = await this.api.put<Chantier>(`/api/v1/chantiers/${id}`, chantier);
|
|
return response.data;
|
|
}
|
|
|
|
async deleteChantier(id: string, permanent: boolean = false): Promise<void> {
|
|
await this.api.delete(`/api/v1/chantiers/${id}`, {
|
|
params: { permanent }
|
|
});
|
|
}
|
|
|
|
async getChantiersByClient(clientId: string): Promise<Chantier[]> {
|
|
try {
|
|
const response = await this.api.get<Chantier[]>(`/api/v1/chantiers/client/${clientId}`);
|
|
return response.data;
|
|
} catch (error: any) {
|
|
// Si l'endpoint n'existe pas encore côté backend (404)
|
|
if (error.response?.status === 404) {
|
|
console.debug(`Endpoint /api/v1/chantiers/client/${clientId} non implémenté, retour d'une liste vide`);
|
|
// Retourner une liste vide en attendant l'implémentation backend
|
|
return [];
|
|
}
|
|
// Relancer l'erreur pour les autres cas
|
|
throw error;
|
|
}
|
|
}
|
|
|
|
async getChantiersRecents(): Promise<ChantierRecent[]> {
|
|
const response = await this.api.get<ChantierRecent[]>('/api/v1/chantiers/recent');
|
|
return response.data;
|
|
}
|
|
|
|
// === DEVIS ===
|
|
async getDevis(): Promise<Devis[]> {
|
|
return CacheService.getOrSet(
|
|
CacheKeys.DEVIS,
|
|
async () => {
|
|
const response = await this.api.get<Devis[]>('/api/v1/devis');
|
|
return Array.isArray(response.data) ? response.data : [];
|
|
},
|
|
3 * 60 * 1000 // 3 minutes (plus court car données plus volatiles)
|
|
);
|
|
}
|
|
|
|
async getDevisById(id: string): Promise<Devis> {
|
|
const response = await this.api.get<Devis>(`/api/v1/devis/${id}`);
|
|
return response.data;
|
|
}
|
|
|
|
async getDevisEnAttente(): Promise<DevisEnAttente[]> {
|
|
const response = await this.api.get<DevisEnAttente[]>('/api/v1/devis/en-attente');
|
|
return response.data;
|
|
}
|
|
|
|
async createDevis(devis: Partial<Devis>): Promise<Devis> {
|
|
const response = await this.api.post<Devis>('/api/v1/devis', devis);
|
|
// Invalider le cache des devis
|
|
CacheService.delete(CacheKeys.DEVIS);
|
|
CacheService.invalidatePattern('devis_.*');
|
|
return response.data;
|
|
}
|
|
|
|
async updateDevis(id: string, devis: Partial<Devis>): Promise<Devis> {
|
|
const response = await this.api.put<Devis>(`/api/v1/devis/${id}`, devis);
|
|
// Invalider le cache des devis
|
|
CacheService.delete(CacheKeys.DEVIS);
|
|
CacheService.delete(CacheKeys.devisById(id));
|
|
CacheService.invalidatePattern('devis_.*');
|
|
return response.data;
|
|
}
|
|
|
|
async deleteDevis(id: string): Promise<void> {
|
|
await this.api.delete(`/api/v1/devis/${id}`);
|
|
// Invalider le cache des devis
|
|
CacheService.delete(CacheKeys.DEVIS);
|
|
CacheService.delete(CacheKeys.devisById(id));
|
|
CacheService.invalidatePattern('devis_.*');
|
|
}
|
|
|
|
// === FACTURES ===
|
|
async getFactures(): Promise<Facture[]> {
|
|
return CacheService.getOrSet(
|
|
CacheKeys.FACTURES,
|
|
async () => {
|
|
const response = await this.api.get<Facture[]>('/api/v1/factures');
|
|
return Array.isArray(response.data) ? response.data : [];
|
|
},
|
|
3 * 60 * 1000 // 3 minutes
|
|
);
|
|
}
|
|
|
|
async getFacture(id: string): Promise<Facture> {
|
|
const response = await this.api.get<Facture>(`/api/v1/factures/${id}`);
|
|
return response.data;
|
|
}
|
|
|
|
async getFacturesEnRetard(): Promise<FactureEnRetard[]> {
|
|
const response = await this.api.get<FactureEnRetard[]>('/api/v1/factures/en-retard');
|
|
return response.data;
|
|
}
|
|
|
|
async createFacture(facture: Partial<Facture>): Promise<Facture> {
|
|
const response = await this.api.post<Facture>('/api/v1/factures', facture);
|
|
// Invalider le cache des factures
|
|
CacheService.delete(CacheKeys.FACTURES);
|
|
CacheService.invalidatePattern('factures_.*');
|
|
return response.data;
|
|
}
|
|
|
|
async updateFacture(id: string, facture: Partial<Facture>): Promise<Facture> {
|
|
const response = await this.api.put<Facture>(`/api/v1/factures/${id}`, facture);
|
|
// Invalider le cache des factures
|
|
CacheService.delete(CacheKeys.FACTURES);
|
|
CacheService.delete(CacheKeys.factureById(id));
|
|
CacheService.invalidatePattern('factures_.*');
|
|
return response.data;
|
|
}
|
|
|
|
async deleteFacture(id: string): Promise<void> {
|
|
await this.api.delete(`/api/v1/factures/${id}`);
|
|
// Invalider le cache des factures
|
|
CacheService.delete(CacheKeys.FACTURES);
|
|
CacheService.delete(CacheKeys.factureById(id));
|
|
CacheService.invalidatePattern('factures_.*');
|
|
}
|
|
|
|
// === DASHBOARD ===
|
|
async getDashboardStats(): Promise<DashboardStats> {
|
|
const response = await this.api.get<DashboardStats>('/api/v1/dashboard/stats');
|
|
return response.data;
|
|
}
|
|
|
|
// === HEALTH ===
|
|
async getHealth(): Promise<{ status: string; timestamp: string }> {
|
|
const response = await this.api.get('/api/v1/health');
|
|
return response.data;
|
|
}
|
|
|
|
async getVersion(): Promise<{ version: string; environment: string }> {
|
|
const response = await this.api.get('/api/v1/version');
|
|
return response.data;
|
|
}
|
|
|
|
// === AUTH ===
|
|
async getCurrentUser(): Promise<UserInfo> {
|
|
const response = await this.api.get('/api/v1/auth/user');
|
|
return response.data;
|
|
}
|
|
|
|
async getAuthStatus(): Promise<{ authenticated: boolean; principal: string | null; hasJWT: boolean; timestamp: number }> {
|
|
const response = await this.api.get('/api/v1/auth/status');
|
|
return response.data;
|
|
}
|
|
|
|
// === BUDGETS ===
|
|
async getBudgets(params?: { search?: string; statut?: string; tendance?: string }) {
|
|
const response = await this.api.get('/api/v1/budgets', { params });
|
|
return response.data;
|
|
}
|
|
|
|
async getBudget(id: string) {
|
|
const response = await this.api.get(`/api/v1/budgets/${id}`);
|
|
return response.data;
|
|
}
|
|
|
|
async getBudgetByChantier(chantierId: string) {
|
|
const response = await this.api.get(`/api/v1/budgets/chantier/${chantierId}`);
|
|
return response.data;
|
|
}
|
|
|
|
async getBudgetsEnDepassement() {
|
|
const response = await this.api.get('/api/v1/budgets/depassement');
|
|
return response.data;
|
|
}
|
|
|
|
async getBudgetsNecessitantAttention() {
|
|
const response = await this.api.get('/api/v1/budgets/attention');
|
|
return response.data;
|
|
}
|
|
|
|
async createBudget(budget: any) {
|
|
const response = await this.api.post('/api/v1/budgets', budget);
|
|
return response.data;
|
|
}
|
|
|
|
async updateBudget(id: string, budget: any) {
|
|
const response = await this.api.put(`/api/v1/budgets/${id}`, budget);
|
|
return response.data;
|
|
}
|
|
|
|
async deleteBudget(id: string) {
|
|
const response = await this.api.delete(`/api/v1/budgets/${id}`);
|
|
return response.data;
|
|
}
|
|
|
|
async updateBudgetDepenses(id: string, depense: number) {
|
|
const response = await this.api.put(`/api/v1/budgets/${id}/depenses`, { depense });
|
|
return response.data;
|
|
}
|
|
|
|
async updateBudgetAvancement(id: string, avancement: number) {
|
|
const response = await this.api.put(`/api/v1/budgets/${id}/avancement`, { avancement });
|
|
return response.data;
|
|
}
|
|
|
|
async ajouterAlerteBudget(id: string, description: string) {
|
|
const response = await this.api.post(`/api/v1/budgets/${id}/alertes`, { description });
|
|
return response.data;
|
|
}
|
|
|
|
async supprimerAlertesBudget(id: string) {
|
|
const response = await this.api.delete(`/api/v1/budgets/${id}/alertes`);
|
|
return response.data;
|
|
}
|
|
|
|
// === EMPLOYÉS ===
|
|
async getEmployes(): Promise<Employe[]> {
|
|
const response = await this.api.get<Employe[]>('/api/v1/employes');
|
|
return Array.isArray(response.data) ? response.data : [];
|
|
}
|
|
|
|
async getEmploye(id: string): Promise<Employe> {
|
|
const response = await this.api.get<Employe>(`/api/v1/employes/${id}`);
|
|
return response.data;
|
|
}
|
|
|
|
async createEmploye(employe: Partial<Employe>): Promise<Employe> {
|
|
const response = await this.api.post<Employe>('/api/v1/employes', employe);
|
|
return response.data;
|
|
}
|
|
|
|
async updateEmploye(id: string, employe: Partial<Employe>): Promise<Employe> {
|
|
const response = await this.api.put<Employe>(`/api/v1/employes/${id}`, employe);
|
|
return response.data;
|
|
}
|
|
|
|
async deleteEmploye(id: string): Promise<void> {
|
|
await this.api.delete(`/api/v1/employes/${id}`);
|
|
}
|
|
|
|
async searchEmployes(params: {
|
|
nom?: string;
|
|
poste?: string;
|
|
specialite?: string;
|
|
statut?: string;
|
|
}): Promise<Employe[]> {
|
|
const response = await this.api.get<Employe[]>('/api/v1/employes/search', { params });
|
|
return response.data;
|
|
}
|
|
|
|
async getEmployesDisponibles(dateDebut?: string, dateFin?: string): Promise<Employe[]> {
|
|
const response = await this.api.get<Employe[]>('/api/v1/employes/disponibles', {
|
|
params: { dateDebut, dateFin }
|
|
});
|
|
return response.data;
|
|
}
|
|
|
|
async getEmployesByEquipe(equipeId: string): Promise<Employe[]> {
|
|
const response = await this.api.get<Employe[]>(`/api/v1/employes/by-equipe/${equipeId}`);
|
|
return response.data;
|
|
}
|
|
|
|
async getEmployesCount(): Promise<number> {
|
|
const response = await this.api.get<number>('/api/v1/employes/count');
|
|
return response.data;
|
|
}
|
|
|
|
async getEmployesStats(): Promise<any> {
|
|
const response = await this.api.get('/api/v1/employes/stats');
|
|
return response.data;
|
|
}
|
|
|
|
// === ÉQUIPES ===
|
|
async getEquipes(): Promise<Equipe[]> {
|
|
const response = await this.api.get<Equipe[]>('/api/v1/equipes');
|
|
return Array.isArray(response.data) ? response.data : [];
|
|
}
|
|
|
|
async getEquipe(id: string): Promise<Equipe> {
|
|
const response = await this.api.get<Equipe>(`/api/v1/equipes/${id}`);
|
|
return response.data;
|
|
}
|
|
|
|
async createEquipe(equipe: Partial<Equipe>): Promise<Equipe> {
|
|
const response = await this.api.post<Equipe>('/api/v1/equipes', equipe);
|
|
return response.data;
|
|
}
|
|
|
|
async updateEquipe(id: string, equipe: Partial<Equipe>): Promise<Equipe> {
|
|
const response = await this.api.put<Equipe>(`/api/v1/equipes/${id}`, equipe);
|
|
return response.data;
|
|
}
|
|
|
|
async deleteEquipe(id: string): Promise<void> {
|
|
await this.api.delete(`/api/v1/equipes/${id}`);
|
|
}
|
|
|
|
async searchEquipes(params: {
|
|
nom?: string;
|
|
specialite?: string;
|
|
statut?: string;
|
|
}): Promise<Equipe[]> {
|
|
const response = await this.api.get<Equipe[]>('/api/v1/equipes/search', { params });
|
|
return response.data;
|
|
}
|
|
|
|
async getEquipesDisponibles(dateDebut?: string, dateFin?: string): Promise<Equipe[]> {
|
|
const response = await this.api.get<Equipe[]>('/api/v1/equipes/disponibles', {
|
|
params: { dateDebut, dateFin }
|
|
});
|
|
return response.data;
|
|
}
|
|
|
|
async getMembresEquipe(equipeId: string): Promise<Employe[]> {
|
|
const response = await this.api.get<Employe[]>(`/api/v1/equipes/${equipeId}/membres`);
|
|
return response.data;
|
|
}
|
|
|
|
async ajouterMembreEquipe(equipeId: string, employeId: string): Promise<void> {
|
|
await this.api.post(`/api/v1/equipes/${equipeId}/membres/${employeId}`);
|
|
}
|
|
|
|
async retirerMembreEquipe(equipeId: string, employeId: string): Promise<void> {
|
|
await this.api.delete(`/api/v1/equipes/${equipeId}/membres/${employeId}`);
|
|
}
|
|
|
|
async getEquipesCount(): Promise<number> {
|
|
const response = await this.api.get<number>('/api/v1/equipes/count');
|
|
return response.data;
|
|
}
|
|
|
|
async getEquipesStats(): Promise<any> {
|
|
const response = await this.api.get('/api/v1/equipes/stats');
|
|
return response.data;
|
|
}
|
|
|
|
// === MATÉRIELS ===
|
|
async getMateriels(): Promise<Materiel[]> {
|
|
const response = await this.api.get<Materiel[]>('/api/v1/materiels');
|
|
return Array.isArray(response.data) ? response.data : [];
|
|
}
|
|
|
|
async getMateriel(id: string): Promise<Materiel> {
|
|
const response = await this.api.get<Materiel>(`/api/v1/materiels/${id}`);
|
|
return response.data;
|
|
}
|
|
|
|
async createMateriel(materiel: Partial<Materiel>): Promise<Materiel> {
|
|
const response = await this.api.post<Materiel>('/api/v1/materiels', materiel);
|
|
return response.data;
|
|
}
|
|
|
|
async updateMateriel(id: string, materiel: Partial<Materiel>): Promise<Materiel> {
|
|
const response = await this.api.put<Materiel>(`/api/v1/materiels/${id}`, materiel);
|
|
return response.data;
|
|
}
|
|
|
|
async deleteMateriel(id: string): Promise<void> {
|
|
await this.api.delete(`/api/v1/materiels/${id}`);
|
|
}
|
|
|
|
async searchMateriels(params: {
|
|
nom?: string;
|
|
type?: string;
|
|
marque?: string;
|
|
statut?: string;
|
|
localisation?: string;
|
|
}): Promise<Materiel[]> {
|
|
const response = await this.api.get<Materiel[]>('/api/v1/materiels/search', { params });
|
|
return response.data;
|
|
}
|
|
|
|
async getMaterielsDisponibles(dateDebut?: string, dateFin?: string, type?: string): Promise<Materiel[]> {
|
|
const response = await this.api.get<Materiel[]>('/api/v1/materiels/disponibles', {
|
|
params: { dateDebut, dateFin, type }
|
|
});
|
|
return response.data;
|
|
}
|
|
|
|
async getMaterielsMaintenancePrevue(jours: number = 30): Promise<Materiel[]> {
|
|
const response = await this.api.get<Materiel[]>('/api/v1/materiels/maintenance-prevue', {
|
|
params: { jours }
|
|
});
|
|
return response.data;
|
|
}
|
|
|
|
async getMaterielsByType(type: string): Promise<Materiel[]> {
|
|
const response = await this.api.get<Materiel[]>(`/api/v1/materiels/by-type/${type}`);
|
|
return response.data;
|
|
}
|
|
|
|
async reserverMateriel(id: string, dateDebut: string, dateFin: string): Promise<void> {
|
|
await this.api.post(`/api/v1/materiels/${id}/reserve`, null, {
|
|
params: { dateDebut, dateFin }
|
|
});
|
|
}
|
|
|
|
async libererMateriel(id: string): Promise<void> {
|
|
await this.api.post(`/api/v1/materiels/${id}/liberer`);
|
|
}
|
|
|
|
async getMaterielsCount(): Promise<number> {
|
|
const response = await this.api.get<number>('/api/v1/materiels/count');
|
|
return response.data;
|
|
}
|
|
|
|
async getMaterielsStats(): Promise<any> {
|
|
const response = await this.api.get('/api/v1/materiels/stats');
|
|
return response.data;
|
|
}
|
|
|
|
async getValeurTotaleMateriels(): Promise<number> {
|
|
const response = await this.api.get<number>('/api/v1/materiels/valeur-totale');
|
|
return response.data;
|
|
}
|
|
|
|
// === MAINTENANCES ===
|
|
async getMaintenances(): Promise<MaintenanceMateriel[]> {
|
|
const response = await this.api.get<MaintenanceMateriel[]>('/api/v1/maintenances');
|
|
return Array.isArray(response.data) ? response.data : [];
|
|
}
|
|
|
|
async getMaintenance(id: string): Promise<MaintenanceMateriel> {
|
|
const response = await this.api.get<MaintenanceMateriel>(`/api/v1/maintenances/${id}`);
|
|
return response.data;
|
|
}
|
|
|
|
async createMaintenance(maintenance: Partial<MaintenanceMateriel>): Promise<MaintenanceMateriel> {
|
|
const response = await this.api.post<MaintenanceMateriel>('/api/v1/maintenances', maintenance);
|
|
return response.data;
|
|
}
|
|
|
|
async updateMaintenance(id: string, maintenance: Partial<MaintenanceMateriel>): Promise<MaintenanceMateriel> {
|
|
const response = await this.api.put<MaintenanceMateriel>(`/api/v1/maintenances/${id}`, maintenance);
|
|
return response.data;
|
|
}
|
|
|
|
async deleteMaintenance(id: string): Promise<void> {
|
|
await this.api.delete(`/api/v1/maintenances/${id}`);
|
|
}
|
|
|
|
async getMaintenancesByMateriel(materielId: string): Promise<MaintenanceMateriel[]> {
|
|
const response = await this.api.get<MaintenanceMateriel[]>(`/api/v1/maintenances/by-materiel/${materielId}`);
|
|
return response.data;
|
|
}
|
|
|
|
// === PLANNING ===
|
|
async getPlanningEvents(params?: {
|
|
dateDebut?: string;
|
|
dateFin?: string;
|
|
type?: string;
|
|
statut?: string;
|
|
}): Promise<PlanningEvent[]> {
|
|
const response = await this.api.get<PlanningEvent[]>('/api/v1/planning/events', { params });
|
|
return Array.isArray(response.data) ? response.data : [];
|
|
}
|
|
|
|
async getPlanningEvent(id: string): Promise<PlanningEvent> {
|
|
const response = await this.api.get<PlanningEvent>(`/api/v1/planning/events/${id}`);
|
|
return response.data;
|
|
}
|
|
|
|
async createPlanningEvent(event: Partial<PlanningEvent>): Promise<PlanningEvent> {
|
|
const response = await this.api.post<PlanningEvent>('/api/v1/planning/events', event);
|
|
return response.data;
|
|
}
|
|
|
|
async updatePlanningEvent(id: string, event: Partial<PlanningEvent>): Promise<PlanningEvent> {
|
|
const response = await this.api.put<PlanningEvent>(`/api/v1/planning/events/${id}`, event);
|
|
return response.data;
|
|
}
|
|
|
|
async deletePlanningEvent(id: string): Promise<void> {
|
|
await this.api.delete(`/api/v1/planning/events/${id}`);
|
|
}
|
|
|
|
async getCalendrierView(annee: number, mois: number): Promise<PlanningCalendrierView> {
|
|
const response = await this.api.get<PlanningCalendrierView>('/api/v1/planning/calendrier', {
|
|
params: { annee, mois }
|
|
});
|
|
return response.data;
|
|
}
|
|
|
|
async detecterConflitsPlanification(dateDebut?: string, dateFin?: string): Promise<PlanningConflict[]> {
|
|
const response = await this.api.get<PlanningConflict[]>('/api/v1/planning/conflits', {
|
|
params: { dateDebut, dateFin }
|
|
});
|
|
return response.data;
|
|
}
|
|
|
|
async getPlanningEmploye(employeId: string, dateDebut?: string, dateFin?: string): Promise<PlanningEvent[]> {
|
|
const response = await this.api.get<PlanningEvent[]>(`/api/v1/planning/employe/${employeId}`, {
|
|
params: { dateDebut, dateFin }
|
|
});
|
|
return response.data;
|
|
}
|
|
|
|
async getPlanningEquipe(equipeId: string, dateDebut?: string, dateFin?: string): Promise<PlanningEvent[]> {
|
|
const response = await this.api.get<PlanningEvent[]>(`/api/v1/planning/equipe/${equipeId}`, {
|
|
params: { dateDebut, dateFin }
|
|
});
|
|
return response.data;
|
|
}
|
|
|
|
async getPlanningMateriel(materielId: string, dateDebut?: string, dateFin?: string): Promise<PlanningEvent[]> {
|
|
const response = await this.api.get<PlanningEvent[]>(`/api/v1/planning/materiel/${materielId}`, {
|
|
params: { dateDebut, dateFin }
|
|
});
|
|
return response.data;
|
|
}
|
|
|
|
async assignEmployesToEvent(eventId: string, employeIds: string[]): Promise<void> {
|
|
await this.api.post(`/api/v1/planning/events/${eventId}/assign-employes`, employeIds);
|
|
}
|
|
|
|
async assignMaterielsToEvent(eventId: string, materielIds: string[]): Promise<void> {
|
|
await this.api.post(`/api/v1/planning/events/${eventId}/assign-materiels`, materielIds);
|
|
}
|
|
|
|
async getPlanningStats(): Promise<PlanningStats> {
|
|
const response = await this.api.get<PlanningStats>('/api/v1/planning/stats');
|
|
return response.data;
|
|
}
|
|
|
|
async getTauxOccupation(dateDebut?: string, dateFin?: string): Promise<any> {
|
|
const response = await this.api.get('/api/v1/planning/occupation', {
|
|
params: { dateDebut, dateFin }
|
|
});
|
|
return response.data;
|
|
}
|
|
|
|
// === TEST ===
|
|
async testCreateChantier(chantier: any): Promise<string> {
|
|
const response = await this.api.post('/api/v1/test/chantier', chantier);
|
|
return response.data;
|
|
}
|
|
}
|
|
|
|
// Instance singleton
|
|
export const apiService = new ApiService();
|
|
|
|
// Services spécialisés
|
|
export const clientService = {
|
|
getAll: () => apiService.getClients(),
|
|
getById: (id: string) => apiService.getClient(id),
|
|
create: (client: Partial<Client>) => apiService.createClient(client),
|
|
update: (id: string, client: Partial<Client>) => apiService.updateClient(id, client),
|
|
delete: (id: string) => apiService.deleteClient(id),
|
|
search: (params: any) => apiService.searchClients(params),
|
|
searchByNom: (nom: string) => apiService.searchClientsByNom(nom),
|
|
searchByEntreprise: (entreprise: string) => apiService.searchClientsByEntreprise(entreprise),
|
|
searchByVille: (ville: string) => apiService.searchClientsByVille(ville),
|
|
searchByEmail: (email: string) => apiService.searchClientsByEmail(email),
|
|
count: () => apiService.getClientsCount(),
|
|
};
|
|
|
|
export const chantierService = {
|
|
getAll: () => apiService.getChantiers(),
|
|
getAllActive: () => apiService.getChantiersActifs(),
|
|
getById: (id: string) => apiService.getChantier(id),
|
|
create: (chantier: Partial<Chantier>) => apiService.createChantier(chantier),
|
|
update: (id: string, chantier: Partial<Chantier>) => apiService.updateChantier(id, chantier),
|
|
delete: (id: string, permanent: boolean = false) => apiService.deleteChantier(id, permanent),
|
|
getByClient: (clientId: string) => apiService.getChantiersByClient(clientId),
|
|
getRecents: () => apiService.getChantiersRecents(),
|
|
};
|
|
|
|
export const devisService = {
|
|
getAll: () => apiService.getDevis(),
|
|
getById: (id: string) => apiService.getDevisById(id),
|
|
getEnAttente: () => apiService.getDevisEnAttente(),
|
|
create: (devis: Partial<Devis>) => apiService.createDevis(devis),
|
|
update: (id: string, devis: Partial<Devis>) => apiService.updateDevis(id, devis),
|
|
delete: (id: string) => apiService.deleteDevis(id),
|
|
};
|
|
|
|
export const factureService = {
|
|
getAll: () => apiService.getFactures(),
|
|
getById: (id: string) => apiService.getFacture(id),
|
|
getEnRetard: () => apiService.getFacturesEnRetard(),
|
|
create: (facture: Partial<Facture>) => apiService.createFacture(facture),
|
|
update: (id: string, facture: Partial<Facture>) => apiService.updateFacture(id, facture),
|
|
delete: (id: string) => apiService.deleteFacture(id),
|
|
};
|
|
|
|
export const dashboardService = {
|
|
getStats: () => apiService.getDashboardStats(),
|
|
};
|
|
|
|
export const employeService = {
|
|
getAll: () => apiService.getEmployes(),
|
|
getById: (id: string) => apiService.getEmploye(id),
|
|
create: (employe: Partial<Employe>) => apiService.createEmploye(employe),
|
|
update: (id: string, employe: Partial<Employe>) => apiService.updateEmploye(id, employe),
|
|
delete: (id: string) => apiService.deleteEmploye(id),
|
|
search: (params: any) => apiService.searchEmployes(params),
|
|
getDisponibles: (dateDebut?: string, dateFin?: string) => apiService.getEmployesDisponibles(dateDebut, dateFin),
|
|
getByEquipe: (equipeId: string) => apiService.getEmployesByEquipe(equipeId),
|
|
count: () => apiService.getEmployesCount(),
|
|
getStats: () => apiService.getEmployesStats(),
|
|
};
|
|
|
|
export const equipeService = {
|
|
getAll: () => apiService.getEquipes(),
|
|
getById: (id: string) => apiService.getEquipe(id),
|
|
create: (equipe: Partial<Equipe>) => apiService.createEquipe(equipe),
|
|
update: (id: string, equipe: Partial<Equipe>) => apiService.updateEquipe(id, equipe),
|
|
delete: (id: string) => apiService.deleteEquipe(id),
|
|
search: (params: any) => apiService.searchEquipes(params),
|
|
getDisponibles: (dateDebut?: string, dateFin?: string) => apiService.getEquipesDisponibles(dateDebut, dateFin),
|
|
getMembres: (equipeId: string) => apiService.getMembresEquipe(equipeId),
|
|
ajouterMembre: (equipeId: string, employeId: string) => apiService.ajouterMembreEquipe(equipeId, employeId),
|
|
retirerMembre: (equipeId: string, employeId: string) => apiService.retirerMembreEquipe(equipeId, employeId),
|
|
count: () => apiService.getEquipesCount(),
|
|
getStats: () => apiService.getEquipesStats(),
|
|
};
|
|
|
|
export const materielService = {
|
|
getAll: () => apiService.getMateriels(),
|
|
getById: (id: string) => apiService.getMateriel(id),
|
|
create: (materiel: Partial<Materiel>) => apiService.createMateriel(materiel),
|
|
update: (id: string, materiel: Partial<Materiel>) => apiService.updateMateriel(id, materiel),
|
|
delete: (id: string) => apiService.deleteMateriel(id),
|
|
search: (params: any) => apiService.searchMateriels(params),
|
|
getDisponibles: (dateDebut?: string, dateFin?: string, type?: string) => apiService.getMaterielsDisponibles(dateDebut, dateFin, type),
|
|
getMaintenancePrevue: (jours?: number) => apiService.getMaterielsMaintenancePrevue(jours),
|
|
getByType: (type: string) => apiService.getMaterielsByType(type),
|
|
reserver: (id: string, dateDebut: string, dateFin: string) => apiService.reserverMateriel(id, dateDebut, dateFin),
|
|
liberer: (id: string) => apiService.libererMateriel(id),
|
|
count: () => apiService.getMaterielsCount(),
|
|
getStats: () => apiService.getMaterielsStats(),
|
|
getValeurTotale: () => apiService.getValeurTotaleMateriels(),
|
|
};
|
|
|
|
export const budgetService = {
|
|
getAll: (params?: { search?: string; statut?: string; tendance?: string }) => apiService.getBudgets(params),
|
|
getById: (id: string) => apiService.getBudget(id),
|
|
getByChantier: (chantierId: string) => apiService.getBudgetByChantier(chantierId),
|
|
getEnDepassement: () => apiService.getBudgetsEnDepassement(),
|
|
getNecessitantAttention: () => apiService.getBudgetsNecessitantAttention(),
|
|
create: (budget: any) => apiService.createBudget(budget),
|
|
update: (id: string, budget: any) => apiService.updateBudget(id, budget),
|
|
delete: (id: string) => apiService.deleteBudget(id),
|
|
updateDepenses: (id: string, depense: number) => apiService.updateBudgetDepenses(id, depense),
|
|
updateAvancement: (id: string, avancement: number) => apiService.updateBudgetAvancement(id, avancement),
|
|
ajouterAlerte: (id: string, description: string) => apiService.ajouterAlerteBudget(id, description),
|
|
supprimerAlertes: (id: string) => apiService.supprimerAlertesBudget(id),
|
|
};
|
|
|
|
export const maintenanceService = {
|
|
getAll: () => apiService.getMaintenances(),
|
|
getById: (id: string) => apiService.getMaintenance(id),
|
|
create: (maintenance: Partial<MaintenanceMateriel>) => apiService.createMaintenance(maintenance),
|
|
update: (id: string, maintenance: Partial<MaintenanceMateriel>) => apiService.updateMaintenance(id, maintenance),
|
|
delete: (id: string) => apiService.deleteMaintenance(id),
|
|
getByMateriel: (materielId: string) => apiService.getMaintenancesByMateriel(materielId),
|
|
};
|
|
|
|
export const planningService = {
|
|
getEvents: (params?: any) => apiService.getPlanningEvents(params),
|
|
getEvent: (id: string) => apiService.getPlanningEvent(id),
|
|
createEvent: (event: Partial<PlanningEvent>) => apiService.createPlanningEvent(event),
|
|
updateEvent: (id: string, event: Partial<PlanningEvent>) => apiService.updatePlanningEvent(id, event),
|
|
deleteEvent: (id: string) => apiService.deletePlanningEvent(id),
|
|
getCalendrierView: (annee: number, mois: number) => apiService.getCalendrierView(annee, mois),
|
|
detecterConflits: (dateDebut?: string, dateFin?: string) => apiService.detecterConflitsPlanification(dateDebut, dateFin),
|
|
getPlanningEmploye: (employeId: string, dateDebut?: string, dateFin?: string) => apiService.getPlanningEmploye(employeId, dateDebut, dateFin),
|
|
getPlanningEquipe: (equipeId: string, dateDebut?: string, dateFin?: string) => apiService.getPlanningEquipe(equipeId, dateDebut, dateFin),
|
|
getPlanningMateriel: (materielId: string, dateDebut?: string, dateFin?: string) => apiService.getPlanningMateriel(materielId, dateDebut, dateFin),
|
|
assignEmployes: (eventId: string, employeIds: string[]) => apiService.assignEmployesToEvent(eventId, employeIds),
|
|
assignMateriels: (eventId: string, materielIds: string[]) => apiService.assignMaterielsToEvent(eventId, materielIds),
|
|
getStats: () => apiService.getPlanningStats(),
|
|
getTauxOccupation: (dateDebut?: string, dateFin?: string) => apiService.getTauxOccupation(dateDebut, dateFin),
|
|
};
|
|
|
|
// Service pour les types de chantier
|
|
// TODO: Implement proper methods in ApiService class
|
|
export const typeChantierService = {
|
|
getAll: () => Promise.resolve([]),
|
|
getByCategorie: () => Promise.resolve([]),
|
|
getById: (id: string) => Promise.resolve(null),
|
|
getByCode: (code: string) => Promise.resolve(null),
|
|
create: (typeChantier: any) => Promise.resolve(typeChantier),
|
|
update: (id: string, typeChantier: any) => Promise.resolve(typeChantier),
|
|
delete: (id: string) => Promise.resolve(true),
|
|
reactivate: (id: string) => Promise.resolve(true),
|
|
getStatistiques: () => Promise.resolve({}),
|
|
};
|
|
|
|
export default apiService; |