import { useState, useEffect } from "react"; import { useParams, useNavigate, useLocation } from "react-router-dom"; import Navbar from "../../components/Navbar"; import "./OrderDetails.css"; import { formatPrice, getOrderAge, getCommandItemsWithDetails, isUserAuthenticated, getProductById, getMediaUrl, } from "../../api/api"; import type { CompletedOrder, Product } from "../../api/api_types"; import { Package, MapPin, User, Calendar, Clock, ArrowLeft, CheckCircle, Truck, Phone, Mail, CreditCard, ShoppingBag, Camera, X, } from "lucide-react"; interface OrderProduct { id: number; product_id: number; name_product: string; category: string; price: number; quantity: number; image?: string; } interface OrderProductWithMedia extends OrderProduct { hasVideo: boolean; videoUrl?: string; } interface OrderDetailsData extends CompletedOrder { products?: OrderProduct[]; phone?: string; email?: string; nom?: string; prenom?: string; payment_method?: string; delivery_time?: string; delivery_notes?: string; } function OrderDetails() { const { orderId } = useParams<{ orderId: string }>(); const navigate = useNavigate(); const location = useLocation(); // commandId = ID global pour l'API (passé en state depuis l'historique) // fallback sur orderId si navigation directe via URL const commandId = (location.state as { commandId?: number } | null)?.commandId ?? parseInt(orderId ?? "0"); const [order, setOrder] = useState(null); const [enrichedProducts, setEnrichedProducts] = useState< OrderProductWithMedia[] >([]); const [isLoading, setIsLoading] = useState(true); const [loadingMedia, setLoadingMedia] = useState(false); const [error, setError] = useState(""); const [showVideo, setShowVideo] = useState(false); const [currentVideoUrl, setCurrentVideoUrl] = useState(""); // ✅ VÉRIFICATION INITIALE DE L'AUTHENTIFICATION useEffect(() => { const checkAuth = () => { if (!isUserAuthenticated()) { console.log( "❌ [OrderDetails] Utilisateur non authentifié, redirection vers /login/client", ); navigate("/login/client", { replace: true }); } }; checkAuth(); }, [navigate]); // ✅ VÉRIFICATION CONTINUE (toutes les 5 secondes) useEffect(() => { const authInterval = setInterval(() => { if (!isUserAuthenticated()) { console.log( "❌ [OrderDetails] Session expirée, redirection vers /login/client", ); navigate("/login/client", { replace: true }); } }, 5000); return () => clearInterval(authInterval); }, [navigate]); useEffect(() => { if (commandId) { fetchOrderDetails(commandId); } }, [orderId]); // ============================================ // FONCTIONS POUR PHOTOS ET VIDÉOS (COMME PRODUCTCARD) // ============================================ const getProductImage = (product: Product): string => { if (!product.media || product.media.length === 0) { return "https://via.placeholder.com/400x400/1a1a1a/ffffff?text=No+Image"; } for (let i = 0; i < product.media.length; i++) { const mediaItem = product.media[i]; if (mediaItem && mediaItem.type === "image" && mediaItem.url) { return getMediaUrl(mediaItem.url); } } return "https://via.placeholder.com/400x400/1a1a1a/ffffff?text=No+Image"; }; const hasProductVideo = (product: Product): boolean => { if (!product.media || product.media.length === 0) { return false; } return product.media.some( (mediaItem) => mediaItem && mediaItem.type === "video", ); }; const getProductVideoUrl = (product: Product): string | undefined => { if (!product.media || product.media.length === 0) { return undefined; } const videoMedia = product.media.find( (mediaItem) => mediaItem && mediaItem.type === "video", ); return videoMedia?.url ? getMediaUrl(videoMedia.url) : undefined; }; // HANDLERS VIDÉO const handleVideoToggle = (videoUrl: string) => { setCurrentVideoUrl(videoUrl); setShowVideo(true); }; const handleCloseVideo = () => { setShowVideo(false); setCurrentVideoUrl(""); }; // ENRICHIR LES PRODUITS AVEC MÉDIAS useEffect(() => { const enrichOrderProducts = async () => { if (!order?.products || order.products.length === 0) { setEnrichedProducts([]); setLoadingMedia(false); return; } setLoadingMedia(true); console.log( "🔄 [ORDER DETAILS] Enrichissement de", order.products.length, "produits...", ); try { const enrichedPromises = order.products.map(async (product) => { try { console.log( `📦 [ORDER DETAILS] Récupération médias pour produit ${product.product_id}...`, ); const productResponse = await getProductById( product.product_id, ); if (productResponse.success && productResponse.data) { const fullProduct = productResponse.data; const enriched: OrderProductWithMedia = { ...product, image: getProductImage(fullProduct), hasVideo: hasProductVideo(fullProduct), videoUrl: getProductVideoUrl(fullProduct), }; console.log(`✅ [ORDER DETAILS] Produit enrichi:`, { name: product.name_product, image: enriched.image, hasVideo: enriched.hasVideo, }); return enriched; } else { console.warn( `⚠️ [ORDER DETAILS] Produit ${product.product_id} non trouvé`, ); return { ...product, image: "https://via.placeholder.com/400x400/1a1a1a/ffffff?text=No+Image", hasVideo: false, videoUrl: undefined, }; } } catch (error) { console.error( `❌ [ORDER DETAILS] Erreur pour produit ${product.product_id}:`, error, ); return { ...product, image: "https://via.placeholder.com/400x400/1a1a1a/ffffff?text=No+Image", hasVideo: false, videoUrl: undefined, }; } }); const enriched = await Promise.all(enrichedPromises); setEnrichedProducts(enriched); console.log( "✅ [ORDER DETAILS] Enrichissement terminé:", enriched.length, "produits", ); } catch (error) { console.error( "❌ [ORDER DETAILS] Erreur enrichissement:", error, ); setEnrichedProducts( order.products.map((product) => ({ ...product, image: "https://via.placeholder.com/400x400/1a1a1a/ffffff?text=No+Image", hasVideo: false, videoUrl: undefined, })), ); } finally { setLoadingMedia(false); } }; enrichOrderProducts(); }, [order?.products]); const fetchOrderDetails = async (id: number) => { if (!isUserAuthenticated()) { console.log("❌ [fetchOrderDetails] Non authentifié"); navigate("/login/client", { replace: true }); return; } setIsLoading(true); setError(""); try { console.log("📦 [ORDER DETAILS] Chargement commande:", id); // ✅ Appeler getCommandItemsWithDetails pour récupérer les items const result = await getCommandItemsWithDetails(id); if (result.success && result.data) { console.log("✅ [ORDER DETAILS] Données reçues:", result.data); const apiData = result.data; // ✅ Mapper les données depuis la structure de GetCommandItemsWithDetails const orderData: OrderDetailsData = { // Infos de command_info id: apiData.command_info?.id || id, client_order_number: apiData.command_info?.client_order_number || 0, username: apiData.client_info?.username || "", status: apiData.command_info?.command_status || "unknown", adresse: apiData.command_info?.command_address || "", total_prix: apiData.command_info?.total_prix || apiData.total_price || 0, livreur_assign: apiData.command_info?.livreur_assign || "", created_at: apiData.command_info?.command_created_at || new Date().toISOString(), updated_at: apiData.command_info?.command_created_at || new Date().toISOString(), // ✅ Infos client depuis client_info nom: apiData.client_info?.nom || "", prenom: apiData.client_info?.prenom || "", phone: apiData.client_info?.telephone || "", // ✅ Produits depuis items (command_items) products: apiData.items?.map((item: any) => ({ id: item.id, product_id: item.product_id, name_product: item.produit, category: "Non spécifié", price: item.prix, quantity: item.quantite, status: item.status, image: item.image, })) || [], }; console.log("✅ [ORDER DETAILS] Données mappées:", orderData); setOrder(orderData); } else { console.error("❌ [ORDER DETAILS] Erreur:", result.message); setError(result.message || "Erreur lors du chargement"); } } catch (err) { console.error("❌ [ORDER DETAILS] Erreur catch:", err); setError("Erreur de connexion"); } finally { setIsLoading(false); } }; const getProductCount = (totalPrix: number): number => { return Math.max(1, Math.round(totalPrix / 25)); }; if (isLoading) { return ( <>

Chargement des détails...

); } if (error || !order) { return ( <>

Commande introuvable

{error || "Cette commande n'existe pas ou vous n'y avez pas accès"}

); } return ( <>
{/* Header avec bouton retour */}

Commande # {(order.client_order_number ?? 0) .toString() .padStart(4, "0")}

Livrée
{/* Grille principale */}
{/* Section Informations de livraison */}

Informations de livraison

Adresse de livraison {order.adresse}
{order.livreur_assign && (
Livreur {order.livreur_assign}
)}
Date de commande {new Date( order.created_at, ).toLocaleDateString("fr-FR", { weekday: "long", day: "numeric", month: "long", year: "numeric", })}
Heure {new Date( order.created_at, ).toLocaleTimeString("fr-FR", { hour: "2-digit", minute: "2-digit", })} {getOrderAge(order.created_at)}
{order.delivery_time && (
Livrée le {order.delivery_time}
)}
{/* Section Contact Client */}

Informations de contact

Client {order.prenom && order.nom ? `${order.prenom} ${order.nom}` : order.username}
{order.phone && (
Téléphone {order.phone}
)} {order.email && (
Email {order.email}
)}
Nom d'utilisateur {order.username}
{/* Section Produits */}

Produits commandés

{loadingMedia && enrichedProducts.length === 0 ? (

Chargement des médias...

) : enrichedProducts.length > 0 ? (
{enrichedProducts.map((product, index) => (
{product.name_product} { console.warn( `❌ Erreur chargement image pour ${product.name_product}:`, product.image, ); e.currentTarget.src = "https://via.placeholder.com/400x400/7c3aed/ffffff?text=" + encodeURIComponent( product.name_product.substring( 0, 10, ), ); }} /> {/* Badge vidéo si disponible */} {product.hasVideo && product.videoUrl && ( )}

{product.name_product}

{product.category}

Qté: {product.quantity}g {formatPrice( product.price, )}
{/* ✅ FIX: Ne pas multiplier, price contient déjà le total */}
{formatPrice(product.price)}
))}
) : (

~{getProductCount(order.total_prix)}{" "} produit {getProductCount(order.total_prix) > 1 ? "s" : ""}

Détails non disponibles
)}
{/* Section Récapitulatif */}

Récapitulatif

Sous-total {formatPrice(order.total_prix)}
Total {formatPrice(order.total_prix)}
{/* Notes de livraison */} {order.delivery_notes && (

Notes de livraison

{order.delivery_notes}

)}
{/* Timeline de livraison */}

Suivi de la commande

Commande confirmée

{new Date(order.created_at).toLocaleString( "fr-FR", )}

En préparation

Votre commande a été préparée

En cours de livraison

{order.livreur_assign ? `Livrée par ${order.livreur_assign}` : "En route"}

Livrée

{order.delivery_time || "Commande livrée avec succès"}

{/* Modal vidéo */} {showVideo && currentVideoUrl && (
e.stopPropagation()} >
)}
); } export default OrderDetails;