// ============================================ // api/api_admin.ts - ADMIN API // ============================================ // ✅ Gestion complète de l'authentification admin // ✅ Utilise /api/v2/admin/* endpoints // ✅ sessionStorage pour la persistance import type { AdminResponse } from "./api_admin_types"; import type { ProductListResponse, ProductResponse, Product, DeleteResponse, CreateProductData, DeliveryPersonDetails, DeliveryPersonStats, } from "./api_admin_types"; const API_URL = "/api/v2"; // ============================================ // 🔐 TYPES - ADMIN // ============================================ /** * ✅ Admin User Interface */ export interface AdminUser { id: number; username: string; role: string; } /** * ✅ Admin Auth Response */ export interface AdminAuthResponse { success: boolean; message?: string; access_token?: string; token_type?: string; expires_in?: number; user?: AdminUser; } /** * ✅ Client Response - Mise à jour avec champs optionnels */ export interface ClientResponse { id: number; username: string; nom: string; prenom: string; telephone: string; adresse?: string; role?: string; created_at?: string; updated_at?: string; is_active?: boolean; command: number; point: number; // Points weed/hash points_zipette: number; // ✅ NOUVEAU: Points zipette amende: number; cancellations_count: number; last_penalty_reason?: string; total_orders?: number; completed_deliveries?: number; total_spent?: number; } export interface CommandResponse { id: number; username: string; status: string; adresse: string; total_prix: number; livreur_assign?: string | null; // peut être null created_at: string; // ISO date string updated_at: string; // ISO date string } export interface AllCommandResponse { success: boolean; commands: CommandResponse[]; count: number; } export interface Alert { id: number; username: string; status: string; created_at: string; updated_at: string; } // ============================================ // 🔐 GESTION JWT ADMIN // ============================================ /** * ✅ Extraire username du JWT Admin */ export const extractAdminUsernameFromToken = (): string | null => { try { const token = sessionStorage.getItem("admin_token"); if (!token) { console.log("❌ [ADMIN_JWT] Aucun token dans sessionStorage"); return null; } console.log("🔐 [ADMIN_JWT] Token trouvé, décodage..."); // JWT format: header.payload.signature const parts = token.split("."); if (parts.length !== 3) { console.error("❌ [ADMIN_JWT] Format invalide"); return null; } // Décoder le payload (base64 -> JSON) const base64Url = parts[1]; const base64 = base64Url.replace(/-/g, "+").replace(/_/g, "/"); const jsonPayload = decodeURIComponent( atob(base64) .split("") .map( (c) => "%" + ("00" + c.charCodeAt(0).toString(16)).slice(-2), ) .join(""), ); const payload = JSON.parse(jsonPayload); console.log("📋 [ADMIN_JWT] Payload:", payload); // Extraire le username const username = payload.username; if (!username) { console.error("❌ [ADMIN_JWT] Username non trouvé dans payload"); return null; } console.log("✅ [ADMIN_JWT] Username extrait:", username); return username; } catch (error) { console.error("❌ [ADMIN_JWT] Erreur décodage:", error); return null; } }; /** * ✅ Extraire le rôle du JWT Admin */ export const extractAdminRoleFromToken = (): string | null => { try { const token = sessionStorage.getItem("admin_token"); if (!token) { return null; } const parts = token.split("."); if (parts.length !== 3) { return null; } const base64Url = parts[1]; const base64 = base64Url.replace(/-/g, "+").replace(/_/g, "/"); const jsonPayload = decodeURIComponent( atob(base64) .split("") .map( (c) => "%" + ("00" + c.charCodeAt(0).toString(16)).slice(-2), ) .join(""), ); const payload = JSON.parse(jsonPayload); const role = payload.role; if (!role) { console.error("❌ [ADMIN_JWT] Role non trouvé dans payload"); return null; } console.log("✅ [ADMIN_JWT] Role extrait:", role); return role; } catch (error) { console.error("❌ [ADMIN_JWT] Erreur extraction role:", error); return null; } }; /** * ✅ Synchroniser sessionStorage avec JWT Admin */ export const syncAdminUsernameFromJWT = (): string | null => { const jwtUsername = extractAdminUsernameFromToken(); if (!jwtUsername) { console.log("❌ [ADMIN_SYNC] Impossible d'extraire username du JWT"); return null; } const storedUsername = sessionStorage.getItem("admin_username"); // Mismatch détecté if (storedUsername && storedUsername !== jwtUsername) { console.warn(`⚠️ [ADMIN_SYNC] MISMATCH!`); console.warn(` Ancien: ${storedUsername}`); console.warn(` JWT: ${jwtUsername}`); sessionStorage.removeItem("admin_username"); } // Toujours stocker le JWT username (source de vérité) sessionStorage.setItem("admin_username", jwtUsername); console.log(`✅ [ADMIN_SYNC] Username synchronisé: ${jwtUsername}`); return jwtUsername; }; /** * ✅ Récupérer le username admin authentifié */ export const getAuthenticatedAdminUsername = (): string | null => { return extractAdminUsernameFromToken(); }; /** * ✅ Vérifier si admin est authentifié */ export const isAdminAuthenticated = (): boolean => { const token = sessionStorage.getItem("admin_token"); const username = extractAdminUsernameFromToken(); const role = extractAdminRoleFromToken(); // Vérifier que c'est bien un admin return !!(token && username && role === "admin"); }; /** * ✅ Récupérer le token admin */ export const getAdminAuthToken = (): string | null => { return sessionStorage.getItem("admin_token"); }; // ============================================ // 🔐 AUTHENTIFICATION ADMIN // ============================================ /** * ✅ REGISTER ADMIN * POST /api/v2/admin/auth/register */ export const registerAdmin = async ( username: string, password: string, ): Promise => { try { console.log("📝 [ADMIN_REGISTER] Appel API..."); const response = await fetch(`${API_URL}/admin/auth/register`, { method: "POST", headers: { "Content-Type": "application/json", }, body: JSON.stringify({ username, password }), }); if (!response.ok) { const errorData = await response.json(); console.error("❌ [ADMIN_REGISTER] Erreur API:", errorData); return { success: false, message: errorData.error || "Erreur d'inscription admin", }; } const data = await response.json(); console.log("📋 [ADMIN_REGISTER] Réponse:", data); // ✅ Vérifier access_token if (!data.access_token) { console.error("❌ [ADMIN_REGISTER] Pas de access_token"); return { success: false, message: "Token non reçu du serveur", }; } // ✅ Stocker en sessionStorage avec préfixe admin_ sessionStorage.setItem("admin_token", data.access_token); console.log("✅ [ADMIN_REGISTER] Token admin stocké"); // ✅ Synchroniser username const jwtUsername = syncAdminUsernameFromJWT(); if (!jwtUsername) { console.warn( "⚠️ [ADMIN_REGISTER] Impossible de synchroniser username", ); return { success: false, message: "Erreur synchronisation JWT", }; } // ✅ Vérifier le rôle const role = extractAdminRoleFromToken(); if (role !== "admin") { console.error("❌ [ADMIN_REGISTER] Rôle incorrect:", role); sessionStorage.removeItem("admin_token"); sessionStorage.removeItem("admin_username"); return { success: false, message: "Accès admin requis", }; } console.log( `✅ [ADMIN_REGISTER] Admin créé et connecté: ${jwtUsername}`, ); return { success: true, message: "Inscription admin réussie", access_token: data.access_token, token_type: data.token_type, expires_in: data.expires_in, user: data.user, }; } catch (error) { console.error("❌ [ADMIN_REGISTER] Erreur:", error); return { success: false, message: error instanceof Error ? error.message : "Erreur d'inscription", }; } }; /** * ✅ LOGIN ADMIN * POST /api/v2/admin/auth/login */ export const loginAdmin = async ( username: string, password: string, ): Promise => { try { console.log("🔐 [ADMIN_LOGIN] Appel API..."); const response = await fetch(`${API_URL}/admin/auth/login`, { method: "POST", headers: { "Content-Type": "application/json", }, body: JSON.stringify({ username, password }), }); if (!response.ok) { const errorData = await response.json(); console.error("❌ [ADMIN_LOGIN] Erreur API:", errorData); return { success: false, message: errorData.error || "Erreur de connexion admin", }; } const data = await response.json(); console.log("📋 [ADMIN_LOGIN] Réponse:", data); // ✅ Vérifier access_token if (!data.access_token) { console.error("❌ [ADMIN_LOGIN] Pas de access_token"); return { success: false, message: "Token non reçu du serveur", }; } // ✅ Stocker en sessionStorage avec préfixe admin_ sessionStorage.setItem("admin_token", data.access_token); console.log("✅ [ADMIN_LOGIN] Token admin stocké"); // ✅ Synchroniser username const jwtUsername = syncAdminUsernameFromJWT(); if (!jwtUsername) { console.warn( "⚠️ [ADMIN_LOGIN] Impossible de synchroniser username", ); return { success: false, message: "Erreur synchronisation JWT", }; } // ✅ Vérifier le rôle const role = extractAdminRoleFromToken(); if (role !== "admin" && role !== "livreur" && role !== "cabine") { console.error("❌ [ADMIN_LOGIN] Rôle incorrect:", role); sessionStorage.removeItem("admin_token"); sessionStorage.removeItem("admin_username"); return { success: false, message: "Accès admin requis", }; } console.log(`✅ [ADMIN_LOGIN] Admin connecté: ${jwtUsername}`); return { success: true, message: "Connexion admin réussie", access_token: data.access_token, token_type: data.token_type, expires_in: data.expires_in, user: data.user, }; } catch (error) { console.error("❌ [ADMIN_LOGIN] Erreur:", error); return { success: false, message: error instanceof Error ? error.message : "Erreur de connexion", }; } }; /** * ✅ LOGOUT ADMIN * POST /api/v2/admin/auth/logout */ export const logoutAdmin = async (): Promise => { const token = sessionStorage.getItem("admin_token"); if (token) { try { console.log("🚪 [ADMIN_LOGOUT] Appel API..."); await fetch(`${API_URL}/admin/auth/logout`, { method: "POST", headers: { Authorization: `Bearer ${token}`, }, }); console.log("✅ [ADMIN_LOGOUT] Déconnexion backend réussie"); } catch (error) { console.warn("⚠️ [ADMIN_LOGOUT] Erreur backend:", error); } } // Nettoyer sessionStorage sessionStorage.removeItem("admin_token"); sessionStorage.removeItem("admin_username"); console.log("✅ [ADMIN_LOGOUT] sessionStorage nettoyé"); }; // ============================================ // 📊 FONCTIONS UTILITAIRES ADMIN // ============================================ /** * ✅ Vérifier si l'utilisateur actuel est admin */ export const checkAdminRole = (): boolean => { const role = extractAdminRoleFromToken(); return role === "admin"; }; /** * ✅ Récupérer les informations admin depuis le JWT */ export const getAdminInfo = (): { username: string; role: string } | null => { const username = extractAdminUsernameFromToken(); const role = extractAdminRoleFromToken(); if (!username || !role) { return null; } return { username, role }; }; /** * ✅ Faire une requête authentifiée admin */ export const adminAuthenticatedFetch = async ( endpoint: string, options: RequestInit = {}, ): Promise => { const token = getAdminAuthToken(); if (!token) { throw new Error("Token admin non trouvé"); } const headers = { "Content-Type": "application/json", Authorization: `Bearer ${token}`, ...options.headers, }; return fetch(`${API_URL}${endpoint}`, { ...options, headers, }); }; /** * ✅ Récupérer tous les utilisateurs (Admin) */ export const getAllUsers = async (): Promise => { const token = sessionStorage.getItem("admin_token"); if (!token) { throw new Error("Token admin non trouvé"); } try { console.log("🔍 [GET_ALL_USERS] Appel API..."); const response = await fetch(`${API_URL}/admin/protected/all/users`, { method: "GET", headers: { "Content-Type": "application/json", Authorization: `Bearer ${token}`, }, }); const data = await response.json(); if (!response.ok) { console.error("❌ [GET_ALL_USERS] Erreur API:", data); throw new Error(data.error || "Erreur récupération utilisateurs"); } console.log("✅ [GET_ALL_USERS] Réponse:", data); // Retourner le tableau d'utilisateurs return data.users || []; // ⬅️ Ajustez selon la structure de votre réponse API } catch (error) { console.error("❌ [GET_ALL_USERS] Erreur fetch:", error); throw error; } }; export const getAllClients = async (): Promise => { const token = sessionStorage.getItem("admin_token"); const response = await fetch(`${API_URL}/admin/protected/all/clients`, { method: "GET", headers: { "Content-Type": "application/json", Authorization: `Bearer ${token}`, }, }); const data = await response.json(); if (!response.ok) { console.error("❌ [CLIENTS] Erreur API:", data); throw new Error(data.error || "Erreur récupération clients"); } console.log("📋 [CLIENTS] Réponse:", data); return data.clients; // ⬅️ retourne un tableau }; export const getCommandCount = async (): Promise => { const token = sessionStorage.getItem("admin_token"); if (!token) { throw new Error("Token admin non trouvé"); } try { const response = await fetch(`${API_URL}/admin/protected/orders`, { method: "GET", headers: { "Content-Type": "application/json", Authorization: `Bearer ${token}`, }, }); const data = await response.json(); if (!response.ok) { console.error("❌ [COMMANDS] Erreur API:", data); throw new Error(data.error || "Erreur récupération commandes"); } // Retourne juste le count return data.count ?? 0; } catch (error) { console.error("❌ [COMMANDS] Erreur fetch:", error); return 0; } }; export const getCommandCountCompleted = async (): Promise => { const token = sessionStorage.getItem("admin_token"); if (!token) { throw new Error("Token admin non trouvé"); } const status = "approved"; const seenIds = new Set(); let totalCount = 0; try { const response = await fetch( `${API_URL}/admin/protected/orders?status=${status}`, { method: "GET", headers: { "Content-Type": "application/json", Authorization: `Bearer ${token}`, }, }, ); const data = await response.json(); if (!response.ok) { console.error(`❌ [COMMANDS] Erreur pour status=${status}:`, data); } if (data.commands && Array.isArray(data.commands)) { data.commands.forEach((cmd: CommandResponse) => { if (!seenIds.has(cmd.id)) { seenIds.add(cmd.id); totalCount++; } }); } return totalCount; } catch (error) { console.error("❌ [COMMANDS] Erreur fetch:", error); return 0; } }; export const getCommandCountInRoute = async (): Promise => { const token = sessionStorage.getItem("admin_token"); if (!token) { throw new Error("Token admin non trouvé"); } const status = "en_route"; const seenIds = new Set(); let totalCount = 0; try { const response = await fetch( `${API_URL}/admin/protected/orders?status=${status}`, { method: "GET", headers: { "Content-Type": "application/json", Authorization: `Bearer ${token}`, }, }, ); const data = await response.json(); if (!response.ok) { console.error(`❌ [COMMANDS] Erreur pour status=${status}:`, data); } if (data.commands && Array.isArray(data.commands)) { data.commands.forEach((cmd: CommandResponse) => { if (!seenIds.has(cmd.id)) { seenIds.add(cmd.id); totalCount++; } }); } return totalCount; } catch (error) { console.error("❌ [COMMANDS] Erreur fetch:", error); return 0; } }; export const getCommandCountByStatus = async (): Promise => { const token = sessionStorage.getItem("admin_token"); if (!token) { throw new Error("Token admin non trouvé"); } const status = ["pending"]; let totalCount = 0; const seenIds = new Set(); try { for (const s of status) { const response = await fetch( `${API_URL}/admin/protected/orders?status=${s}`, { method: "GET", headers: { "Content-Type": "application/json", Authorization: `Bearer ${token}`, }, }, ); const data = await response.json(); if (!response.ok) { console.error(`❌ [COMMANDS] Erreur pour status=${s}:`, data); continue; } if (data.commands && Array.isArray(data.commands)) { data.commands.forEach((cmd: CommandResponse) => { if (!seenIds.has(cmd.id)) { seenIds.add(cmd.id); totalCount++; } }); } } console.log(`✅ [COMMANDS] Total commandes en attente: ${totalCount}`); return totalCount; } catch (error) { console.error("❌ [COMMANDS] Erreur fetch:", error); return 0; } }; export const getAllCommands = async (status?: string, username?: string) => { const token = sessionStorage.getItem("admin_token"); if (!token) { throw new Error("Token admin non trouvé"); } try { let url = `${API_URL}/admin/protected/orders`; const params = new URLSearchParams(); if (status) params.append("status", status); if (username) params.append("username", username); if (params.toString()) { url += `?${params.toString()}`; } console.log("🔍 [GET_ALL_COMMANDS] Appel:", url); const response = await fetch(url, { method: "GET", headers: { "Content-Type": "application/json", Authorization: `Bearer ${token}`, }, }); const data = await response.json(); if (!response.ok) { console.error("❌ [GET_ALL_COMMANDS] Erreur API:", data); throw new Error(data.error || "Erreur récupération commandes"); } console.log("✅ [GET_ALL_COMMANDS] Réponse:", data); return { success: true, commands: data.commands || [], count: data.count || 0, }; } catch (error) { console.error("❌ [GET_ALL_COMMANDS] Erreur fetch:", error); return { success: false, commands: [], count: 0, }; } }; /** * ✅ Récupérer une commande par ID */ export const getCommandByID = async (commandId: number) => { const token = sessionStorage.getItem("admin_token"); if (!token) { throw new Error("Token admin non trouvé"); } try { console.log("🔍 [GET_COMMAND_BY_ID] Appel:", commandId); const response = await fetch( `${API_URL}/admin/protected/orders/${commandId}`, { method: "GET", headers: { "Content-Type": "application/json", Authorization: `Bearer ${token}`, }, }, ); const data = await response.json(); if (!response.ok) { console.error("❌ [GET_COMMAND_BY_ID] Erreur API:", data); throw new Error(data.error || "Erreur récupération commande"); } console.log("✅ [GET_COMMAND_BY_ID] Réponse:", data); return { success: true, command: data.command, }; } catch (error) { console.error("❌ [GET_COMMAND_BY_ID] Erreur fetch:", error); throw error; } }; /** * ✅ Mettre à jour le statut d'une commande */ export const updateCommandStatus = async ( commandId: number, newStatus: string, ) => { const token = sessionStorage.getItem("admin_token"); if (!token) { throw new Error("Token admin non trouvé"); } try { console.log("📝 [UPDATE_STATUS] Appel:", commandId, newStatus); const response = await fetch( `${API_URL}/admin/protected/orders/${commandId}/status`, { method: "PUT", headers: { "Content-Type": "application/json", Authorization: `Bearer ${token}`, }, body: JSON.stringify({ status: newStatus }), }, ); const data = await response.json(); if (!response.ok) { console.error("❌ [UPDATE_STATUS] Erreur API:", data); throw new Error(data.error || "Erreur mise à jour statut"); } console.log("✅ [UPDATE_STATUS] Réponse:", data); return { success: true, message: data.message, }; } catch (error) { console.error("❌ [UPDATE_STATUS] Erreur fetch:", error); throw error; } }; /** * ✅ Assigner un livreur à une commande */ export const assignDeliveryPerson = async ( commandId: number, deliveryPersonUsername: string, ) => { const token = sessionStorage.getItem("admin_token"); if (!token) { throw new Error("Token admin non trouvé"); } try { console.log( "👤 [ASSIGN_DELIVERY] Appel:", commandId, deliveryPersonUsername, ); const response = await fetch( `${API_URL}/admin/protected/delivery-persons/${deliveryPersonUsername}/assign/${commandId}`, { method: "POST", headers: { "Content-Type": "application/json", Authorization: `Bearer ${token}`, }, }, ); const data = await response.json(); if (!response.ok) { console.error("❌ [ASSIGN_DELIVERY] Erreur API:", data); throw new Error(data.error || "Erreur assignation livreur"); } console.log("✅ [ASSIGN_DELIVERY] Réponse:", data); return { success: true, message: data.message, }; } catch (error) { console.error("❌ [ASSIGN_DELIVERY] Erreur fetch:", error); throw error; } }; /** * ✅ Récupérer les livreurs disponibles */ export const getAvailableDeliveryPersons = async () => { const token = sessionStorage.getItem("admin_token"); if (!token) { throw new Error("Token admin non trouvé"); } try { console.log("🔍 [GET_DELIVERY_PERSONS] Appel"); const response = await fetch( `${API_URL}/admin/protected/delivery-persons`, { method: "GET", headers: { "Content-Type": "application/json", Authorization: `Bearer ${token}`, }, }, ); const data = await response.json(); if (!response.ok) { console.error("❌ [GET_DELIVERY_PERSONS] Erreur API:", data); throw new Error(data.error || "Erreur récupération livreurs"); } console.log("✅ [GET_DELIVERY_PERSONS] Réponse:", data); return { success: true, livreurs: data.livreurs || [], count: data.count || 0, }; } catch (error) { console.error("❌ [GET_DELIVERY_PERSONS] Erreur fetch:", error); return { success: false, livreurs: [], count: 0, }; } }; /** * ✅ Récupérer tous les livreurs avec détails complets et stats * GET /api/v2/admin/protected/delivery-persons * Enrichit automatiquement avec les détails et stats de chaque livreur */ export const getAllDeliveryPersonsWithDetails = async () => { const token = sessionStorage.getItem("admin_token"); if (!token) { throw new Error("Token admin non trouvé"); } /** * ✅ Helper: Parser le statut qui peut être une chaîne JSON */ const parseStatus = (status: any): "available" | "busy" | "offline" => { if (!status) return "offline"; // Si c'est déjà une valeur valide if ( status === "available" || status === "busy" || status === "offline" ) { return status; } // Si c'est une chaîne JSON if (typeof status === "string" && status.startsWith("{")) { try { const statusObj = JSON.parse(status); const parsedStatus = statusObj.status; if ( parsedStatus === "available" || parsedStatus === "busy" || parsedStatus === "offline" ) { console.log( `🔄 [PARSE_STATUS] Statut parsé: "${parsedStatus}"`, ); return parsedStatus; } } catch (e) { console.warn(`⚠️ [PARSE_STATUS] Impossible de parser:`, status); } } // Par défaut return "offline"; }; try { console.log("🔍 [GET_ALL_DELIVERY_PERSONS] Appel"); // Récupérer la liste de base const response = await fetch( `${API_URL}/admin/protected/delivery-persons`, { method: "GET", headers: { "Content-Type": "application/json", Authorization: `Bearer ${token}`, }, }, ); const data = await response.json(); if (!response.ok) { console.error("❌ [GET_ALL_DELIVERY_PERSONS] Erreur API:", data); return { success: false, deliveryPersons: [], count: 0, stats: { total: 0, available: 0, busy: 0, offline: 0, active_deliveries: 0, }, }; } console.log("✅ [GET_ALL_DELIVERY_PERSONS] Réponse de base:", data); const livreurs = data.livreurs || []; // Enrichir chaque livreur avec ses détails et stats const enrichedDeliveryPersons = await Promise.all( livreurs.map(async (livreur: any) => { try { // Récupérer les détails du livreur const details = await getDeliveryPersonDetails( livreur.username, ); // Récupérer les stats du livreur const stats = await getDeliveryPersonStats( livreur.username, ); // ✅ Parser le statut (gère les cas où c'est du JSON) const parsedStatus = parseStatus(details.status); return { id: livreur.id, username: livreur.username, nom: livreur.nom || "", prenom: livreur.prenom || "", telephone: livreur.telephone || "", status: parsedStatus, location: { latitude: details.location?.latitude || 0, longitude: details.location?.longitude || 0, last_update: details.location?.last_update ? new Date( details.location.last_update * 1000, ).toISOString() : new Date().toISOString(), is_recent: details.location?.is_recent || false, }, stats: { total_deliveries: stats.total_deliveries || 0, completed_today: stats.completed_deliveries || 0, queue_size: details.queue_size || 0, current_command: details.current_command || null, }, }; } catch (error) { console.warn( `⚠️ [GET_ALL_DELIVERY_PERSONS] Erreur enrichissement ${livreur.username}:`, error, ); // Retourner des données de base en cas d'erreur return { id: livreur.id, username: livreur.username, nom: livreur.nom || "", prenom: livreur.prenom || "", telephone: livreur.telephone || "", status: "offline" as const, location: { latitude: 0, longitude: 0, last_update: new Date().toISOString(), is_recent: false, }, stats: { total_deliveries: 0, completed_today: 0, queue_size: 0, current_command: null, }, }; } }), ); // Calculer les stats globales const globalStats = { total: enrichedDeliveryPersons.length, available: enrichedDeliveryPersons.filter( (d) => d.status === "available", ).length, busy: enrichedDeliveryPersons.filter((d) => d.status === "busy") .length, offline: enrichedDeliveryPersons.filter( (d) => d.status === "offline", ).length, active_deliveries: enrichedDeliveryPersons.filter( (d) => d.stats.current_command !== null, ).length, }; console.log( "✅ [GET_ALL_DELIVERY_PERSONS] Données enrichies:", enrichedDeliveryPersons.length, "livreurs", ); console.log( "📊 [GET_ALL_DELIVERY_PERSONS] Stats globales:", globalStats, ); return { success: true, deliveryPersons: enrichedDeliveryPersons, count: enrichedDeliveryPersons.length, stats: globalStats, }; } catch (error) { console.error("❌ [GET_ALL_DELIVERY_PERSONS] Erreur fetch:", error); return { success: false, deliveryPersons: [], count: 0, stats: { total: 0, available: 0, busy: 0, offline: 0, active_deliveries: 0, }, }; } }; /** * ✅ Valider/Approuver une commande (Admin) */ export const validateCommand = async (commandId: number) => { const token = sessionStorage.getItem("admin_token"); if (!token) { throw new Error("Token admin non trouvé"); } try { console.log("✅ [VALIDATE_COMMAND] Appel:", commandId); const response = await fetch( `${API_URL}/admin/protected/orders/${commandId}/force-validate`, { method: "POST", headers: { "Content-Type": "application/json", Authorization: `Bearer ${token}`, }, }, ); const data = await response.json(); if (!response.ok) { console.error("❌ [VALIDATE_COMMAND] Erreur API:", data); throw new Error(data.error || "Erreur validation commande"); } console.log("✅ [VALIDATE_COMMAND] Réponse:", data); return { success: true, message: data.message, }; } catch (error) { console.error("❌ [VALIDATE_COMMAND] Erreur fetch:", error); throw error; } }; /** * ✅ Mettre à jour l'adresse de livraison */ export const updateCommandAddress = async ( commandId: number, newAddress: string, ) => { const token = sessionStorage.getItem("admin_token"); if (!token) { throw new Error("Token admin non trouvé"); } try { console.log("📝 [UPDATE_ADDRESS] Appel:", commandId, newAddress); const response = await fetch( `${API_URL}/admin/protected/orders/${commandId}/address`, { method: "PUT", headers: { "Content-Type": "application/json", Authorization: `Bearer ${token}`, }, body: JSON.stringify({ delivery_address: newAddress }), }, ); const data = await response.json(); if (!response.ok) { console.error("❌ [UPDATE_ADDRESS] Erreur API:", data); throw new Error(data.error || "Erreur mise à jour adresse"); } console.log("✅ [UPDATE_ADDRESS] Réponse:", data); return { success: true, message: data.message, }; } catch (error) { console.error("❌ [UPDATE_ADDRESS] Erreur fetch:", error); throw error; } }; /** * ✅ Récupérer la position GPS du livreur assigné à une commande (Admin) * GET /api/v2/admin/protected/commands/:id/deliveryman/location */ export const getDeliverymanLocationForCommand = async (commandId: number) => { const token = sessionStorage.getItem("admin_token"); if (!token) { throw new Error("Token admin non trouvé"); } try { console.log( "📍 [GET_DELIVERYMAN_LOCATION] Appel pour commande:", commandId, ); const response = await fetch( `${API_URL}/admin/protected/commands/${commandId}/deliveryman/location`, { method: "GET", headers: { "Content-Type": "application/json", Authorization: `Bearer ${token}`, }, }, ); const data = await response.json(); if (!response.ok) { console.error("❌ [GET_DELIVERYMAN_LOCATION] Erreur API:", data); // Si pas de livreur assigné ou pas de position, retourner info utile if (response.status === 404) { return { success: false, error: data.error, message: data.message, command_info: data.command_info || null, }; } throw new Error( data.error || "Erreur récupération position livreur", ); } console.log("✅ [GET_DELIVERYMAN_LOCATION] Réponse:", data); return { success: true, data: { command_id: data.data.command_id, client: data.data.client, command_status: data.data.command_status, deliveryman: { username: data.data.deliveryman.username, status: data.data.deliveryman.status, current_command: data.data.deliveryman.current_command, queue_size: data.data.deliveryman.queue_size, location: { latitude: data.data.deliveryman.location.latitude, longitude: data.data.deliveryman.location.longitude, last_update: data.data.deliveryman.location.last_update, last_update_ago: data.data.deliveryman.location.last_update_ago, is_recent: data.data.deliveryman.location.is_recent, }, }, eta: { minutes: data.data.eta.minutes, has_eta: data.data.eta.has_eta, set_at: data.data.eta.set_at, }, }, requested_by: data.requested_by, }; } catch (error) { console.error("❌ [GET_DELIVERYMAN_LOCATION] Erreur fetch:", error); throw error; } }; export const updateClientByAdmin = async ( clientId: number, updates: { username?: string; password?: string; nom?: string; prenom?: string; telephone?: string; command?: number; point?: number; amende?: number; }, ) => { const token = sessionStorage.getItem("admin_token"); if (!token) { throw new Error("Token admin non trouvé"); } try { console.log("📝 [UPDATE_CLIENT_ADMIN] Appel:", clientId, updates); const response = await fetch( `${API_URL}/admin/protected/clients/${clientId}`, { method: "PUT", headers: { "Content-Type": "application/json", Authorization: `Bearer ${token}`, }, body: JSON.stringify(updates), }, ); const data = await response.json(); if (!response.ok) { console.error("❌ [UPDATE_CLIENT_ADMIN] Erreur API:", data); throw new Error(data.error || "Erreur mise à jour client"); } console.log("✅ [UPDATE_CLIENT_ADMIN] Réponse:", data); return { success: true, message: data.message, client: data.client, }; } catch (error) { console.error("❌ [UPDATE_CLIENT_ADMIN] Erreur fetch:", error); throw error; } }; /** * ✅ Mettre à jour un profil USER (Admin/Cabine/Livreur) par Admin * PUT /api/v2/admin/protected/users/:id * * @param userId - ID de l'utilisateur à modifier * @param updates - Champs à mettre à jour * @returns Réponse avec l'utilisateur mis à jour * * @example * // Modifier le username * await updateUserByAdmin(1, { username: "nouveau_username" }); * * // Modifier le mot de passe * await updateUserByAdmin(1, { password: "nouveau_password_secure" }); * * // Changer le rôle * await updateUserByAdmin(1, { role: "livreur" }); */ export const updateUserByAdmin = async ( userId: number, updates: { username?: string; password?: string; role?: "admin" | "cabine" | "livreur"; }, ) => { const token = sessionStorage.getItem("admin_token"); if (!token) { throw new Error("Token admin non trouvé"); } try { console.log("📝 [UPDATE_USER_ADMIN] Appel:", userId, updates); const response = await fetch( `${API_URL}/admin/protected/users/${userId}`, { method: "PUT", headers: { "Content-Type": "application/json", Authorization: `Bearer ${token}`, }, body: JSON.stringify(updates), }, ); const data = await response.json(); if (!response.ok) { console.error("❌ [UPDATE_USER_ADMIN] Erreur API:", data); throw new Error(data.error || "Erreur mise à jour utilisateur"); } console.log("✅ [UPDATE_USER_ADMIN] Réponse:", data); return { success: true, message: data.message, user: data.user, }; } catch (error) { console.error("❌ [UPDATE_USER_ADMIN] Erreur fetch:", error); throw error; } }; export const getAllProductsAdmin = async (): Promise => { const token = sessionStorage.getItem("admin_token"); if (!token) { throw new Error("Token admin non trouvé"); } try { console.log("📦 [GET_ALL_PRODUCTS_ADMIN] Appel API..."); const response = await fetch(`${API_URL}/admin/protected/products`, { method: "GET", headers: { "Content-Type": "application/json", Authorization: `Bearer ${token}`, }, }); const data = await response.json(); if (!response.ok) { console.error("❌ [GET_ALL_PRODUCTS_ADMIN] Erreur API:", data); return { success: false, error: data.error || "Erreur récupération produits", }; } console.log("✅ [GET_ALL_PRODUCTS_ADMIN] Réponse:", data); return { success: true, data: data.data || [], count: data.count || 0, message: data.message, }; } catch (error) { console.error("❌ [GET_ALL_PRODUCTS_ADMIN] Erreur fetch:", error); return { success: false, error: error instanceof Error ? error.message : "Erreur réseau", }; } }; /** * ✅ Récupérer un produit par ID (Admin) * GET /api/v2/admin/protected/products/:id */ export const getProductByIdAdmin = async ( productId: number, ): Promise => { const token = sessionStorage.getItem("admin_token"); if (!token) { throw new Error("Token admin non trouvé"); } try { console.log("📦 [GET_PRODUCT_BY_ID_ADMIN] Appel API:", productId); const response = await fetch( `${API_URL}/admin/protected/products/${productId}`, { method: "GET", headers: { "Content-Type": "application/json", Authorization: `Bearer ${token}`, }, }, ); const data = await response.json(); if (!response.ok) { console.error("❌ [GET_PRODUCT_BY_ID_ADMIN] Erreur API:", data); return { success: false, error: data.error || "Erreur récupération produit", }; } console.log("✅ [GET_PRODUCT_BY_ID_ADMIN] Réponse:", data); return { success: true, data: data.data, message: data.message, }; } catch (error) { console.error("❌ [GET_PRODUCT_BY_ID_ADMIN] Erreur fetch:", error); return { success: false, error: error instanceof Error ? error.message : "Erreur réseau", }; } }; // ============================================ // ➕ CRÉATION DE PRODUIT // ============================================ /** * ✅ Créer un nouveau produit avec médias (Admin) * POST /api/v2/admin/protected/products * * @param productData - Données du produit * @param mediaFiles - Fichiers médias (images/vidéos) * * IMPORTANT: Envoie en multipart/form-data */ export const createProductAdmin = async ( productData: CreateProductData, mediaFiles?: File[], ): Promise => { const token = sessionStorage.getItem("admin_token"); if (!token) { throw new Error("Token admin non trouvé"); } try { console.log("➕ [CREATE_PRODUCT_ADMIN] Préparation FormData..."); console.log("📋 [CREATE_PRODUCT_ADMIN] Données:", productData); // ✅ Créer FormData pour multipart/form-data const formData = new FormData(); // ✅ Ajouter les champs basiques formData.append("name", productData.name); formData.append("category", productData.category); formData.append("description", productData.description); formData.append("stock", productData.stock.toString()); console.log("📝 [CREATE_PRODUCT_ADMIN] Champs basiques ajoutés"); // ✅ Ajouter les prix productData.prices.forEach((price, index) => { formData.append( `prices[${index}][quantity]`, price.quantity.toString(), ); formData.append(`prices[${index}][price]`, price.price.toString()); console.log( `💰 [CREATE_PRODUCT_ADMIN] Prix[${index}]: ${price.quantity}g = ${price.price}€`, ); }); // ✅ Ajouter les fichiers médias if (mediaFiles && mediaFiles.length > 0) { console.log( `📁 [CREATE_PRODUCT_ADMIN] Ajout de ${mediaFiles.length} fichiers médias`, ); mediaFiles.forEach((file, index) => { formData.append("media", file); console.log( ` 📄 [${index + 1}] ${file.name} (${file.type}, ${file.size} bytes)`, ); }); } else { console.log("⚠️ [CREATE_PRODUCT_ADMIN] Aucun média à ajouter"); } // ✅ Log du contenu FormData (debug) console.log("📦 [CREATE_PRODUCT_ADMIN] Contenu FormData:"); for (const [key, value] of formData.entries()) { if (value instanceof File) { console.log(` ${key}: File(${value.name})`); } else { console.log(` ${key}: ${value}`); } } console.log("🚀 [CREATE_PRODUCT_ADMIN] Envoi requête..."); // ✅ Envoyer la requête (sans Content-Type - le navigateur le gère automatiquement) const response = await fetch(`${API_URL}/admin/protected/products`, { method: "POST", headers: { Authorization: `Bearer ${token}`, }, body: formData, }); const data = await response.json(); if (!response.ok) { console.error("❌ [CREATE_PRODUCT_ADMIN] Erreur API:", data); return { success: false, error: data.error || "Erreur création produit", }; } console.log("✅ [CREATE_PRODUCT_ADMIN] Produit créé:", data); return { success: true, product: data.product, message: data.message, }; } catch (error) { console.error("❌ [CREATE_PRODUCT_ADMIN] Erreur fetch:", error); return { success: false, error: error instanceof Error ? error.message : "Erreur réseau", }; } }; // ============================================ // ✏️ MODIFICATION DE PRODUIT // ============================================ /** * ✅ Mettre à jour un produit (Admin) * PUT /api/v2/admin/protected/products/:id */ export const updateProductAdmin = async ( productId: number, updates: Partial, ): Promise => { const token = sessionStorage.getItem("admin_token"); if (!token) { throw new Error("Token admin non trouvé"); } try { console.log("✏️ [UPDATE_PRODUCT_ADMIN] Appel API:", productId, updates); const response = await fetch( `${API_URL}/admin/protected/products/update/${productId}`, { method: "PUT", headers: { "Content-Type": "application/json", Authorization: `Bearer ${token}`, }, body: JSON.stringify(updates), }, ); const data = await response.json(); if (!response.ok) { console.error("❌ [UPDATE_PRODUCT_ADMIN] Erreur API:", data); return { success: false, error: data.error || "Erreur mise à jour produit", }; } console.log("✅ [UPDATE_PRODUCT_ADMIN] Produit mis à jour:", data); return { success: true, product: data.product, message: data.message, }; } catch (error) { console.error("❌ [UPDATE_PRODUCT_ADMIN] Erreur fetch:", error); return { success: false, error: error instanceof Error ? error.message : "Erreur réseau", }; } }; // ============================================ // 🗑️ SUPPRESSION DE PRODUIT // ============================================ /** * ✅ Supprimer un produit (Admin) * DELETE /api/v2/admin/protected/products/:id */ export const deleteProductAdmin = async ( productId: number, ): Promise => { const token = sessionStorage.getItem("admin_token"); if (!token) { throw new Error("Token admin non trouvé"); } try { console.log("🗑️ [DELETE_PRODUCT_ADMIN] Appel API:", productId); const response = await fetch( `${API_URL}/admin/protected/products/${productId}`, { method: "DELETE", headers: { "Content-Type": "application/json", Authorization: `Bearer ${token}`, }, }, ); const data = await response.json(); if (!response.ok) { console.error("❌ [DELETE_PRODUCT_ADMIN] Erreur API:", data); return { success: false, error: data.error || "Erreur suppression produit", }; } console.log("✅ [DELETE_PRODUCT_ADMIN] Produit supprimé:", data); return { success: true, message: data.message, }; } catch (error) { console.error("❌ [DELETE_PRODUCT_ADMIN] Erreur fetch:", error); return { success: false, error: error instanceof Error ? error.message : "Erreur réseau", }; } }; // ============================================ // 📊 GESTION DES PRIX // ============================================ /** * ✅ Récupérer les prix d'un produit * GET /api/v2/admin/protected/products/:id/prices */ export const getProductPricesAdmin = async (productId: number) => { const token = sessionStorage.getItem("admin_token"); if (!token) { throw new Error("Token admin non trouvé"); } try { const response = await fetch( `${API_URL}/admin/protected/products/${productId}/prices`, { method: "GET", headers: { "Content-Type": "application/json", Authorization: `Bearer ${token}`, }, }, ); const data = await response.json(); if (!response.ok) { console.error("❌ [GET_PRICES] Erreur API:", data); return { success: false, error: data.error || "Erreur récupération prix", }; } return { success: true, data: data.data || [], count: data.count || 0, }; } catch (error) { console.error("❌ [GET_PRICES] Erreur fetch:", error); return { success: false, error: error instanceof Error ? error.message : "Erreur réseau", }; } }; /** * ✅ Ajouter un prix à un produit * POST /api/v2/admin/protected/products/:id/prices */ export const addProductPriceAdmin = async ( productId: number, quantity: number, price: number, ) => { const token = sessionStorage.getItem("admin_token"); if (!token) { throw new Error("Token admin non trouvé"); } try { const response = await fetch( `${API_URL}/admin/protected/products/${productId}/prices`, { method: "POST", headers: { "Content-Type": "application/json", Authorization: `Bearer ${token}`, }, body: JSON.stringify({ quantity, price }), }, ); const data = await response.json(); if (!response.ok) { console.error("❌ [ADD_PRICE] Erreur API:", data); return { success: false, error: data.error || "Erreur ajout prix", }; } return { success: true, message: data.message, }; } catch (error) { console.error("❌ [ADD_PRICE] Erreur fetch:", error); return { success: false, error: error instanceof Error ? error.message : "Erreur réseau", }; } }; // ============================================ // 🎬 GESTION DES MÉDIAS // ============================================ /** * ✅ Uploader un média pour un produit existant * POST /api/v2/admin/protected/products/:id/media */ export const uploadProductMediaAdmin = async ( productId: number, file: File, type: "image" | "video", ) => { const token = sessionStorage.getItem("admin_token"); if (!token) { throw new Error("Token admin non trouvé"); } try { console.log("📤 [UPLOAD_MEDIA] Upload fichier:", file.name); const formData = new FormData(); formData.append("file", file); formData.append("type", type); const response = await fetch( `${API_URL}/admin/protected/products/${productId}/media`, { method: "POST", headers: { Authorization: `Bearer ${token}`, }, body: formData, }, ); const data = await response.json(); if (!response.ok) { console.error("❌ [UPLOAD_MEDIA] Erreur API:", data); return { success: false, error: data.error || "Erreur upload média", }; } console.log("✅ [UPLOAD_MEDIA] Média uploadé:", data); return { success: true, media: data.media, message: data.message, }; } catch (error) { console.error("❌ [UPLOAD_MEDIA] Erreur fetch:", error); return { success: false, error: error instanceof Error ? error.message : "Erreur réseau", }; } }; /** * ✅ Supprimer un média * DELETE /api/v2/admin/protected/products/:productId/media/:mediaId */ export const deleteProductMediaAdmin = async ( productId: number, mediaId: number, ) => { const token = sessionStorage.getItem("admin_token"); if (!token) { throw new Error("Token admin non trouvé"); } try { console.log("🗑️ [DELETE_MEDIA] Suppression média:", mediaId); const response = await fetch( `${API_URL}/admin/protected/products/${productId}/media/${mediaId}`, { method: "DELETE", headers: { "Content-Type": "application/json", Authorization: `Bearer ${token}`, }, }, ); const data = await response.json(); if (!response.ok) { console.error("❌ [DELETE_MEDIA] Erreur API:", data); return { success: false, error: data.error || "Erreur suppression média", }; } console.log("✅ [DELETE_MEDIA] Média supprimé"); return { success: true, message: data.message, }; } catch (error) { console.error("❌ [DELETE_MEDIA] Erreur fetch:", error); return { success: false, error: error instanceof Error ? error.message : "Erreur réseau", }; } }; export const getDeliveryPersonDetails = async ( username: string, ): Promise => { const token = sessionStorage.getItem("admin_token"); if (!token) { throw new Error("Token admin non trouvé"); } try { console.log("👤 [GET_DELIVERY_DETAILS] Appel pour:", username); const response = await fetch( `${API_URL}/admin/protected/delivery-persons/${username}`, { method: "GET", headers: { "Content-Type": "application/json", Authorization: `Bearer ${token}`, }, }, ); const data = await response.json(); if (!response.ok) { console.error("❌ [GET_DELIVERY_DETAILS] Erreur API:", data); throw new Error( data.error || "Erreur récupération détails livreur", ); } console.log("✅ [GET_DELIVERY_DETAILS] Réponse:", data); return data.deliveryman; } catch (error) { console.error("❌ [GET_DELIVERY_DETAILS] Erreur fetch:", error); throw error; } }; /** * ✅ Modifier le statut d'un livreur (Admin) * PUT /api/v2/admin/protected/delivery-persons/:username/status */ export const updateDeliveryPersonStatusAdmin = async ( username: string, status: "available" | "busy" | "offline", ) => { const token = sessionStorage.getItem("admin_token"); if (!token) { throw new Error("Token admin non trouvé"); } try { console.log("📝 [UPDATE_DELIVERY_STATUS] Appel:", username, status); const response = await fetch( `${API_URL}/admin/protected/delivery-persons/${username}/status`, { method: "PUT", headers: { "Content-Type": "application/json", Authorization: `Bearer ${token}`, }, body: JSON.stringify({ status }), }, ); const data = await response.json(); if (!response.ok) { console.error("❌ [UPDATE_DELIVERY_STATUS] Erreur API:", data); throw new Error(data.error || "Erreur mise à jour statut livreur"); } console.log("✅ [UPDATE_DELIVERY_STATUS] Réponse:", data); return { success: true, message: data.message, deliveryman: data.deliveryman, }; } catch (error) { console.error("❌ [UPDATE_DELIVERY_STATUS] Erreur fetch:", error); throw error; } }; /** * ✅ Récupérer les statistiques d'un livreur * GET /api/v2/admin/protected/delivery-persons/:username/stats */ export const getDeliveryPersonStats = async ( username: string, ): Promise => { const token = sessionStorage.getItem("admin_token"); if (!token) { throw new Error("Token admin non trouvé"); } try { console.log("📊 [GET_DELIVERY_STATS] Appel pour:", username); const response = await fetch( `${API_URL}/admin/protected/delivery-persons/${username}/stats`, { method: "GET", headers: { "Content-Type": "application/json", Authorization: `Bearer ${token}`, }, }, ); const data = await response.json(); if (!response.ok) { console.error("❌ [GET_DELIVERY_STATS] Erreur API:", data); throw new Error(data.error || "Erreur récupération stats livreur"); } console.log("✅ [GET_DELIVERY_STATS] Réponse:", data); return data.stats; } catch (error) { console.error("❌ [GET_DELIVERY_STATS] Erreur fetch:", error); throw error; } }; /** * ✅ Récupérer l'historique des livraisons d'un livreur * GET /api/v2/admin/protected/delivery-persons/:username/history */ export const getDeliveryPersonHistory = async ( username: string, limit?: number, offset?: number, ) => { const token = sessionStorage.getItem("admin_token"); if (!token) { throw new Error("Token admin non trouvé"); } try { let url = `${API_URL}/admin/protected/delivery-persons/${username}/history`; const params = new URLSearchParams(); if (limit) params.append("limit", limit.toString()); if (offset) params.append("offset", offset.toString()); if (params.toString()) { url += `?${params.toString()}`; } console.log("📜 [GET_DELIVERY_HISTORY] Appel:", url); const response = await fetch(url, { method: "GET", headers: { "Content-Type": "application/json", Authorization: `Bearer ${token}`, }, }); const data = await response.json(); if (!response.ok) { console.error("❌ [GET_DELIVERY_HISTORY] Erreur API:", data); throw new Error(data.error || "Erreur récupération historique"); } console.log("✅ [GET_DELIVERY_HISTORY] Réponse:", data); return { success: true, history: data.history || [], count: data.count || 0, total: data.total || 0, }; } catch (error) { console.error("❌ [GET_DELIVERY_HISTORY] Erreur fetch:", error); throw error; } }; /** * ✅ Modifier la position GPS d'un livreur (Admin) * PUT /api/v2/admin/protected/delivery-persons/:username/location */ export const updateDeliveryPersonLocationAdmin = async ( username: string, latitude: number, longitude: number, ) => { const token = sessionStorage.getItem("admin_token"); if (!token) { throw new Error("Token admin non trouvé"); } try { console.log("📍 [UPDATE_DELIVERY_LOCATION] Appel:", username, { latitude, longitude, }); const response = await fetch( `${API_URL}/admin/protected/delivery-persons/${username}/location`, { method: "PUT", headers: { "Content-Type": "application/json", Authorization: `Bearer ${token}`, }, body: JSON.stringify({ latitude, longitude }), }, ); const data = await response.json(); if (!response.ok) { console.error("❌ [UPDATE_DELIVERY_LOCATION] Erreur API:", data); throw new Error(data.error || "Erreur mise à jour position"); } console.log("✅ [UPDATE_DELIVERY_LOCATION] Réponse:", data); return { success: true, message: data.message, location: data.location, }; } catch (error) { console.error("❌ [UPDATE_DELIVERY_LOCATION] Erreur fetch:", error); throw error; } }; /** * ✅ Retirer une commande de la queue d'un livreur * DELETE /api/v2/admin/protected/delivery-persons/:username/queue/:command_id */ export const removeCommandFromQueue = async ( username: string, commandId: number, ) => { const token = sessionStorage.getItem("admin_token"); if (!token) { throw new Error("Token admin non trouvé"); } try { console.log("🗑️ [REMOVE_FROM_QUEUE] Appel:", username, commandId); const response = await fetch( `${API_URL}/admin/protected/delivery-persons/${username}/queue/${commandId}`, { method: "DELETE", headers: { "Content-Type": "application/json", Authorization: `Bearer ${token}`, }, }, ); const data = await response.json(); if (!response.ok) { console.error("❌ [REMOVE_FROM_QUEUE] Erreur API:", data); throw new Error(data.error || "Erreur suppression de la queue"); } console.log("✅ [REMOVE_FROM_QUEUE] Réponse:", data); return { success: true, message: data.message, }; } catch (error) { console.error("❌ [REMOVE_FROM_QUEUE] Erreur fetch:", error); throw error; } }; export const getDeliveryPersonMapLinks = async (username: string) => { const token = sessionStorage.getItem("admin_token"); if (!token) { throw new Error("Token admin non trouvé"); } try { console.log("🗺️ [GET_MAP_LINKS] Appel pour:", username); const response = await fetch( `${API_URL}/admin/protected/delivery-persons/${username}/map-links`, { method: "GET", headers: { "Content-Type": "application/json", Authorization: `Bearer ${token}`, }, }, ); const data = await response.json(); if (!response.ok) { console.error("❌ [GET_MAP_LINKS] Erreur API:", data); return { success: false, error: data.error || "Erreur récupération liens GPS", }; } console.log("✅ [GET_MAP_LINKS] Réponse:", data); return { success: true, deliveryman: data.deliveryman, location: data.location, map_links: data.map_links, }; } catch (error) { console.error("❌ [GET_MAP_LINKS] Erreur fetch:", error); return { success: false, error: error instanceof Error ? error.message : "Erreur réseau", }; } }; export const deleteUserAdmin = async ( userId: number, ): Promise => { const token = sessionStorage.getItem("admin_token"); if (!token) { throw new Error("Token admin non trouvé"); } try { console.log( "🗑️ [DELETE_USER_ADMIN] Tentative de suppression de l'utilisateur ID:", userId, ); const response = await fetch( `${API_URL}/admin/protected/users/${userId}`, { method: "DELETE", headers: { "Content-Type": "application/json", Authorization: `Bearer ${token}`, }, }, ); const data = await response.json(); if (!response.ok) { console.error("❌ [DELETE_USER_ADMIN] Erreur API:", data); return { success: false, error: data.error || "Erreur lors de la suppression de l'utilisateur", message: data.message, }; } console.log( "✅ [DELETE_USER_ADMIN] Utilisateur supprimé avec succès:", data, ); return { success: true, message: data.message || "Utilisateur supprimé avec succès", }; } catch (error) { console.error("❌ [DELETE_USER_ADMIN] Erreur fetch:", error); return { success: false, error: error instanceof Error ? error.message : "Erreur réseau inconnue", }; } }; export const deleteClientAdmin = async ( clientId: number, ): Promise => { const token = sessionStorage.getItem("admin_token"); if (!token) { throw new Error("Token admin non trouvé"); } try { console.log( "🗑️ [DELETE_CLIENT_ADMIN] Tentative de suppression du client ID:", clientId, ); const response = await fetch( `${API_URL}/admin/protected/clients/${clientId}`, { method: "DELETE", headers: { "Content-Type": "application/json", Authorization: `Bearer ${token}`, }, }, ); const data = await response.json(); if (!response.ok) { console.error("❌ [DELETE_CLIENT_ADMIN] Erreur API:", data); return { success: false, error: data.error || "Erreur lors de la suppression du client", message: data.message, }; } console.log( "✅ [DELETE_CLIENT_ADMIN] Client supprimé avec succès:", data, ); return { success: true, message: data.message || "Client supprimé avec succès", }; } catch (error) { console.error("❌ [DELETE_CLIENT_ADMIN] Erreur fetch:", error); return { success: false, error: error instanceof Error ? error.message : "Erreur réseau inconnue", }; } }; export const deleteAlertAdmin = async ( alertId: number, ): Promise<{ success: boolean; message?: string; error?: string; }> => { const token = sessionStorage.getItem("admin_token"); if (!token) { throw new Error("Token admin non trouvé"); } try { console.log("🗑️ [DELETE_ALERT_ADMIN] Suppression alerte:", alertId); const response = await fetch( `${API_URL}/admin/protected/delete/alerts/${alertId}`, { method: "DELETE", headers: { "Content-Type": "application/json", Authorization: `Bearer ${token}`, }, }, ); const data = await response.json(); if (!response.ok) { console.error("❌ [DELETE_ALERT_ADMIN] Erreur API:", data); return { success: false, error: data.error || "Erreur suppression alerte", }; } console.log("✅ [DELETE_ALERT_ADMIN] Alerte supprimée avec succès"); return { success: true, message: data.message || "Alerte supprimée avec succès", }; } catch (error) { console.error("❌ [DELETE_ALERT_ADMIN] Erreur fetch:", error); return { success: false, error: error instanceof Error ? error.message : "Erreur réseau inconnue", }; } }; export const CreateUser = async ( username: string, password: string, role: string, ) => { try { const token = sessionStorage.getItem("admin_token"); const response = await fetch(`${API_URL}/admin/protected/users`, { method: "POST", headers: { "Content-Type": "application/json", Authorization: `Bearer ${token}`, }, body: JSON.stringify({ username, password, role }), }); const data = await response.json(); if (!response.ok) { console.error("❌ [CREATE_USER] Erreur API:", data); return { success: false, error: data.error || "Erreur création utilisateur", }; } console.log("✅ [CREATE_USER] Utilisateur créé avec succès"); return { success: true, message: data.message || "Utilisateur créé avec succès", }; } catch (error) { console.error("❌ [CREATE_USER] Erreur fetch:", error); return { success: false, error: error instanceof Error ? error.message : "Erreur réseau inconnue", }; } }; // ============================================ // 🔄 EXPORT PAR DÉFAUT // ============================================ export default { // Auth registerAdmin, loginAdmin, logoutAdmin, CreateUser, // JWT Utils extractAdminUsernameFromToken, extractAdminRoleFromToken, syncAdminUsernameFromJWT, getAuthenticatedAdminUsername, isAdminAuthenticated, getAdminAuthToken, // Users getAllUsers, getAllClients, // Commands getAllCommands, getCommandByID, getCommandCount, getCommandCountCompleted, getCommandCountInRoute, getCommandCountByStatus, updateCommandStatus, assignDeliveryPerson, getAvailableDeliveryPersons, validateCommand, updateCommandAddress, getDeliverymanLocationForCommand, // ⭐ NOUVEAU // Utils checkAdminRole, getAdminInfo, adminAuthenticatedFetch, // Update profile updateClientByAdmin, updateUserByAdmin, // Produits getAllProductsAdmin, getProductByIdAdmin, createProductAdmin, updateProductAdmin, deleteProductAdmin, // Prix getProductPricesAdmin, addProductPriceAdmin, // Médias uploadProductMediaAdmin, deleteProductMediaAdmin, // Livreurs - ⭐ NOUVEAU getAllDeliveryPersonsWithDetails, getDeliveryPersonDetails, getDeliveryPersonStats, getDeliveryPersonHistory, updateDeliveryPersonStatusAdmin, updateDeliveryPersonLocationAdmin, removeCommandFromQueue, getDeliveryPersonMapLinks, deleteClientAdmin, deleteUserAdmin, deleteAlertAdmin, };