Files
projet_gestion_commande/frontend-prep/src/pages/Livreur/DeliveryDashboard.tsx
T

1343 lines
52 KiB
TypeScript

import { useState, useEffect, useRef } from "react";
import { useNavigate } from "react-router-dom";
import {
Package,
MapPin,
CheckCircle,
Navigation,
User,
AlertCircle,
MapPinned,
ShoppingBag,
BarChart3,
Shield,
} from "lucide-react";
import "./DeliveryDashboard.css";
import {
getDeliveryPersonDetails,
extractAdminUsernameFromToken,
} from "../../api/api_admin";
import {
getMyStatus,
updateMyStatus,
getMyQueue,
getMyDeliveries,
getDeliveryDetails,
startDelivery,
updateDeliveryStatus,
updateMyLocation,
isDeliveryAuthenticated,
triggerPoliceAlert,
endAlert,
getMyAlerts,
} from "../../api/api_delivery";
interface DeliveryStats {
todayDeliveries: number;
completedDeliveries: number;
pendingDeliveries: number;
approvedDeliveries: number;
totalDeliveries: number;
}
interface OrderItem {
produit: string;
prix: number;
quantite: number;
}
interface CurrentDelivery {
id: number;
orderNumber: string;
customerName: string;
customerPhone?: string;
deliveryAddress: string;
status: "assigned" | "picked_up" | "in_route";
distance: string;
items?: OrderItem[];
totalPrice?: number;
}
interface LocationState {
latitude: number | null;
longitude: number | null;
accuracy: number | null;
lastUpdate: Date | null;
permissionGranted: boolean;
error: string | null;
}
interface AlertConfig {
type: "success" | "error" | "warning" | "info";
title: string;
message: string;
onConfirm?: () => void;
}
function DeliveryDashboard() {
const navigate = useNavigate();
const [stats, setStats] = useState<DeliveryStats>({
todayDeliveries: 0,
completedDeliveries: 0,
pendingDeliveries: 0,
approvedDeliveries: 0,
totalDeliveries: 0,
});
const [currentDelivery, setCurrentDelivery] =
useState<CurrentDelivery | null>(null);
const [loading, setLoading] = useState(true);
const [deliveryPersonName, setDeliveryPersonName] = useState("");
const [isAvailable, setIsAvailable] = useState(true);
const [currentStatus, setCurrentStatus] = useState<
"available" | "busy" | "offline"
>("offline");
const [location, setLocation] = useState<LocationState>({
latitude: null,
longitude: null,
accuracy: null,
lastUpdate: null,
permissionGranted: false,
error: null,
});
const [showLocationPrompt, setShowLocationPrompt] = useState(false);
const watchIdRef = useRef<number | null>(null);
const [showAlert, setShowAlert] = useState(false);
const [alertConfig, setAlertConfig] = useState<AlertConfig>({
type: "success",
title: "",
message: "",
onConfirm: undefined,
});
const [isAlertTriggered, setIsAlertTriggered] = useState(false);
const [policeAlertLoading, setPoliceAlertLoading] = useState(false);
useEffect(() => {
const checkAuth = () => {
if (!isDeliveryAuthenticated()) {
console.log(
"❌ [UserManagement] Cabine non authentifié, redirection vers /login-delivery/delivery",
);
navigate("/login-delivery/delivery", { replace: true });
}
};
checkAuth();
}, [navigate]);
useEffect(() => {
const authInterval = setInterval(() => {
if (!isDeliveryAuthenticated()) {
console.log(
"❌ [UserManagement] Session cabine expirée, redirection vers /login-delivery/delivery",
);
navigate("/login-delivery/delivery", { replace: true });
}
}, 5000);
return () => clearInterval(authInterval);
}, [navigate]);
const showStyledAlert = (
type: "success" | "error" | "warning" | "info",
title: string,
message: string,
onConfirm?: () => void,
) => {
setAlertConfig({ type, title, message, onConfirm });
setShowAlert(true);
};
const requestLocationPermission = async () => {
if (!navigator.geolocation) {
console.error(
"❌ [GEOLOCATION] Géolocalisation non supportée par ce navigateur",
);
setLocation((prev) => ({
...prev,
error: "Géolocalisation non supportée par votre navigateur",
}));
return false;
}
try {
console.log("📍 [GEOLOCATION] Demande de permission...");
const position = await new Promise<GeolocationPosition>(
(resolve, reject) => {
navigator.geolocation.getCurrentPosition(resolve, reject, {
enableHighAccuracy: true,
timeout: 10000,
maximumAge: 0,
});
},
);
console.log("✅ [GEOLOCATION] Permission accordée!", {
lat: position.coords.latitude,
lng: position.coords.longitude,
accuracy: position.coords.accuracy,
});
setLocation({
latitude: position.coords.latitude,
longitude: position.coords.longitude,
accuracy: position.coords.accuracy,
lastUpdate: new Date(),
permissionGranted: true,
error: null,
});
setShowLocationPrompt(false);
await updateLocationOnServer(
position.coords.latitude,
position.coords.longitude,
);
startLocationTracking();
return true;
} catch (error: any) {
console.error("❌ [GEOLOCATION] Erreur permission:", error);
let errorMessage = "Impossible d'accéder à votre position";
if (error.code === 1) {
errorMessage = "Permission de géolocalisation refusée";
} else if (error.code === 2) {
errorMessage = "Position indisponible";
} else if (error.code === 3) {
errorMessage = "Délai d'attente dépassé";
}
setLocation((prev) => ({
...prev,
error: errorMessage,
permissionGranted: false,
}));
return false;
}
};
const updateLocationOnServer = async (
latitude: number,
longitude: number,
) => {
try {
console.log("📡 [GEOLOCATION] Envoi position au serveur:", {
latitude,
longitude,
});
const result = await updateMyLocation(latitude, longitude);
if (!result.success) {
console.error("❌ [GEOLOCATION] Erreur:", result.error);
return;
}
console.log("✅ [GEOLOCATION] Position mise à jour sur le serveur");
if (result.status) {
setCurrentStatus(result.status as any);
setIsAvailable(result.status === "available");
}
} catch (error) {
console.error(
"❌ [GEOLOCATION] Erreur mise à jour serveur:",
error,
);
}
};
const startLocationTracking = () => {
if (!navigator.geolocation) return;
console.log("🎯 [GEOLOCATION] Démarrage du suivi continu...");
if (watchIdRef.current !== null) {
navigator.geolocation.clearWatch(watchIdRef.current);
}
watchIdRef.current = navigator.geolocation.watchPosition(
(position) => {
console.log("📍 [GEOLOCATION] Position mise à jour:", {
lat: position.coords.latitude,
lng: position.coords.longitude,
accuracy: position.coords.accuracy,
});
setLocation({
latitude: position.coords.latitude,
longitude: position.coords.longitude,
accuracy: position.coords.accuracy,
lastUpdate: new Date(),
permissionGranted: true,
error: null,
});
updateLocationOnServer(
position.coords.latitude,
position.coords.longitude,
);
},
(error) => {
console.error("❌ [GEOLOCATION] Erreur suivi:", error);
},
{
enableHighAccuracy: true,
timeout: 30000,
maximumAge: 0,
},
);
};
const stopLocationTracking = () => {
if (watchIdRef.current !== null) {
console.log("🛑 [GEOLOCATION] Arrêt du suivi");
navigator.geolocation.clearWatch(watchIdRef.current);
watchIdRef.current = null;
}
};
useEffect(() => {
const fetchDeliveryData = async () => {
try {
const username = extractAdminUsernameFromToken();
if (!username) {
console.error(
"❌ [DELIVERY_DASHBOARD] Username non trouvé",
);
setLoading(false);
return;
}
console.log(
"🔍 [DELIVERY_DASHBOARD] Chargement données pour:",
username,
);
const statusResult = await getMyStatus();
if (statusResult.success && statusResult.status) {
const status = statusResult.status.status;
setCurrentStatus(status);
setIsAvailable(status === "available");
console.log("✅ [DELIVERY_DASHBOARD] Statut:", status);
}
const queueResult = await getMyQueue();
// ✅ FIX: Vérification de queue_info avec optional chaining
if (queueResult.success && queueResult.queue_info) {
setStats((prev) => ({
...prev,
pendingDeliveries:
queueResult.queue_info?.queue_size || 0,
}));
console.log(
"✅ [DELIVERY_DASHBOARD] Queue:",
queueResult.queue_info.queue_size,
);
}
const deliveriesResult = await getMyDeliveries();
if (deliveriesResult.success && deliveriesResult.deliveries) {
const deliveries = deliveriesResult.deliveries;
console.log(
"✅ [DELIVERY_DASHBOARD] Livraisons:",
deliveries,
);
const completedToday = deliveries.filter((d: any) => {
const isToday =
new Date(d.updated_at).toDateString() ===
new Date().toDateString();
return d.status === "livre" && isToday;
}).length;
const totalCompleted = deliveries.filter(
(d: any) => d.status === "livre",
).length;
const approvedCount = deliveries.filter(
(d: any) => d.status === "approved",
).length;
console.log(
`📊 [DELIVERY_DASHBOARD] Commandes approved: ${approvedCount}`,
);
setStats((prev) => ({
...prev,
todayDeliveries: completedToday,
completedDeliveries: totalCompleted,
approvedDeliveries: approvedCount,
totalDeliveries: deliveries.length,
}));
let currentDeliveryData = deliveries.find(
(d: any) => d.status === "en_route",
);
if (!currentDeliveryData) {
currentDeliveryData = deliveries.find(
(d: any) => d.status === "assigned",
);
}
if (currentDeliveryData) {
console.log(
"📦 [DELIVERY_DASHBOARD] Livraison en cours:",
currentDeliveryData,
);
const detailsResult = await getDeliveryDetails(
currentDeliveryData.id,
);
if (detailsResult.success && detailsResult.delivery) {
const delivery = detailsResult.delivery.delivery;
const clientInfo =
detailsResult.delivery.client_info;
setCurrentDelivery({
id: delivery.id,
orderNumber: `CMD-${delivery.id}`,
customerName: clientInfo?.nom
? `${clientInfo.nom} ${clientInfo.prenom || ""}`.trim()
: clientInfo?.username || "Client",
customerPhone:
clientInfo?.telephone ||
"+33 X XX XX XX XX",
deliveryAddress:
delivery.adresse || "Adresse de livraison",
status:
delivery.status === "assigned"
? "assigned"
: "in_route",
distance: "N/A",
// ✅ FIX: Cast to any pour accéder à items qui peut exister mais n'est pas typé
items: (currentDeliveryData as any).items || [],
totalPrice: currentDeliveryData.total_prix || 0,
});
}
} else {
setCurrentDelivery(null);
}
}
const details = await getDeliveryPersonDetails(username);
// ✅ FIX: Cast to any car 'nom' n'existe pas dans le type mais peut exister dans la réponse
setDeliveryPersonName((details as any).nom || details.username);
// ✅ Vérifier les alertes actives au chargement
const alertsResult = await getMyAlerts();
if (alertsResult.success && alertsResult.alerts) {
const hasActiveAlert = alertsResult.alerts.some(
(alert) => alert.status === "true",
);
setIsAlertTriggered(hasActiveAlert);
console.log(
"📋 [DELIVERY_DASHBOARD] Alertes actives:",
hasActiveAlert,
);
}
} catch (error) {
console.error(
"❌ [DELIVERY_DASHBOARD] Erreur chargement données:",
error,
);
} finally {
setLoading(false);
setTimeout(() => {
setShowLocationPrompt(true);
}, 1000);
}
};
fetchDeliveryData();
const refreshInterval = setInterval(() => {
console.log(
"🔄 [DELIVERY_DASHBOARD] Rafraîchissement automatique...",
);
fetchDeliveryData();
}, 10000);
return () => {
stopLocationTracking();
clearInterval(refreshInterval);
};
}, []);
const handleStartDelivery = async () => {
if (!currentDelivery) return;
if (!location.latitude || !location.longitude) {
showStyledAlert(
"warning",
"Position GPS requise",
"Votre position GPS n'est pas disponible. Veuillez activer la géolocalisation pour démarrer la livraison.",
);
return;
}
try {
console.log(
"📦 [DELIVERY_DASHBOARD] Démarrage de la livraison:",
currentDelivery.id,
);
const result = await startDelivery(
currentDelivery.id,
location.latitude,
location.longitude,
);
if (!result.success) {
console.error("❌ [DELIVERY_DASHBOARD] Erreur:", result.error);
showStyledAlert(
"error",
"Erreur",
result.error ||
"Une erreur est survenue lors du démarrage de la livraison.",
);
return;
}
console.log("✅ [DELIVERY_DASHBOARD] Livraison démarrée");
showStyledAlert(
"success",
"Livraison démarrée",
"La livraison a été démarrée avec succès !",
() => window.location.reload(),
);
} catch (error) {
console.error(
"❌ [DELIVERY_DASHBOARD] Erreur démarrage livraison:",
error,
);
showStyledAlert(
"error",
"Erreur",
"Une erreur est survenue lors du démarrage de la livraison.",
);
}
};
const handleCompleteDelivery = async () => {
if (!currentDelivery) return;
if (!location.latitude || !location.longitude) {
showStyledAlert(
"warning",
"Position GPS requise",
"Votre position GPS n'est pas disponible. Veuillez activer la géolocalisation pour valider la livraison.",
);
return;
}
try {
console.log(
"✅ [DELIVERY_DASHBOARD] Marquage livraison comme terminée:",
currentDelivery.id,
);
const result = await updateDeliveryStatus(
currentDelivery.id,
"livre",
location.latitude,
location.longitude,
);
if (!result.success) {
console.error("❌ [DELIVERY_DASHBOARD] Erreur:", result.error);
showStyledAlert(
"error",
"Erreur",
result.error ||
"Une erreur est survenue lors de la validation de la livraison.",
);
return;
}
console.log("✅ [DELIVERY_DASHBOARD] Livraison terminée");
showStyledAlert(
"success",
"Livraison terminée",
"La livraison a été marquée comme terminée avec succès !",
() => window.location.reload(),
);
} catch (error) {
console.error(
"❌ [DELIVERY_DASHBOARD] Erreur complétion livraison:",
error,
);
showStyledAlert(
"error",
"Erreur",
"Une erreur est survenue lors de la validation de la livraison.",
);
}
};
const handleNavigate = () => {
if (currentDelivery) {
const address = encodeURIComponent(currentDelivery.deliveryAddress);
window.open(
`https://www.google.com/maps/search/?api=1&query=${address}`,
"_blank",
);
}
};
const toggleAvailability = async () => {
try {
const newStatus = isAvailable ? "offline" : "available";
console.log(
`🔄 [DELIVERY_DASHBOARD] Changement statut: ${currentStatus} -> ${newStatus}`,
);
const result = await updateMyStatus(newStatus);
if (!result.success) {
console.error("❌ [DELIVERY_DASHBOARD] Erreur:", result.error);
showStyledAlert(
"error",
"Erreur",
result.error ||
"Une erreur est survenue lors du changement de statut.",
);
return;
}
console.log("✅ [DELIVERY_DASHBOARD] Statut mis à jour");
setIsAvailable(!isAvailable);
setCurrentStatus(newStatus);
} catch (error) {
console.error(
"❌ [DELIVERY_DASHBOARD] Erreur changement statut:",
error,
);
showStyledAlert(
"error",
"Erreur",
"Une erreur est survenue lors du changement de statut.",
);
}
};
const handlePoliceAlert = async () => {
if (policeAlertLoading) return;
setPoliceAlertLoading(true);
try {
console.log("🚨 [POLICE_ALERT] Déclenchement alerte police...");
const result = await triggerPoliceAlert();
if (!result.success) {
console.error("❌ [POLICE_ALERT] Erreur:", result.error);
showStyledAlert(
"error",
"Erreur",
result.error || "Impossible de déclencher l'alerte police.",
);
return;
}
console.log("✅ [POLICE_ALERT] Alerte créée:", result);
setIsAlertTriggered(true);
showStyledAlert(
"success",
"Alerte Police Activée",
"Votre alerte police a été transmise avec succès. Les autorités et votre équipe ont été notifiées.",
);
// Vérifier les alertes actives pour mettre à jour l'état
const alertsResult = await getMyAlerts();
if (alertsResult.success && alertsResult.alerts) {
const hasActiveAlert = alertsResult.alerts.some(
(alert) => alert.status === "true",
);
setIsAlertTriggered(hasActiveAlert);
}
} catch (error) {
console.error("❌ [POLICE_ALERT] Erreur:", error);
showStyledAlert(
"error",
"Erreur",
"Une erreur est survenue lors du déclenchement de l'alerte police.",
);
} finally {
setPoliceAlertLoading(false);
}
};
const handleEndPoliceAlert = async () => {
if (policeAlertLoading) return;
// Trouver l'alerte active
const alertsResult = await getMyAlerts();
if (!alertsResult.success || !alertsResult.alerts) {
showStyledAlert(
"error",
"Erreur",
"Impossible de récupérer les alertes actives.",
);
return;
}
const activeAlert = alertsResult.alerts.find(
(alert) => alert.status === "true",
);
if (!activeAlert) {
showStyledAlert(
"warning",
"Aucune alerte active",
"Il n'y a pas d'alerte police active à terminer.",
);
return;
}
setPoliceAlertLoading(true);
try {
console.log(
"🔚 [END_POLICE_ALERT] Fin d'alerte police:",
activeAlert.id,
);
const result = await endAlert(activeAlert.id);
if (!result.success) {
console.error("❌ [END_POLICE_ALERT] Erreur:", result.error);
showStyledAlert(
"error",
"Erreur",
result.error || "Impossible de terminer l'alerte police.",
);
return;
}
console.log("✅ [END_POLICE_ALERT] Alerte terminée:", result);
setIsAlertTriggered(false);
showStyledAlert(
"success",
"Alerte Police Terminée",
"Votre alerte police a été désactivée avec succès.",
);
} catch (error) {
console.error("❌ [END_POLICE_ALERT] Erreur:", error);
showStyledAlert(
"error",
"Erreur",
"Une erreur est survenue lors de la désactivation de l'alerte police.",
);
} finally {
setPoliceAlertLoading(false);
}
};
if (loading) {
return (
<div className="delivery-container">
<div className="loading-delivery">
<p>Chargement de votre tableau de bord...</p>
</div>
</div>
);
}
return (
<div className="delivery-container">
{/* Modal de demande de géolocalisation */}
{showLocationPrompt && !location.permissionGranted && (
<div className="location-modal-overlay">
<div className="location-modal">
<div className="location-modal-icon">
<MapPinned size={48} color="#10b981" />
</div>
<h2 className="location-modal-title">
Activer la localisation
</h2>
<p className="location-modal-text">
Pour vous assigner des livraisons et suivre vos
déplacements en temps réel, nous avons besoin
d'accéder à votre position GPS.
</p>
<div className="location-modal-buttons">
<button
className="location-button primary"
onClick={requestLocationPermission}
>
<MapPin size={20} />
Autoriser la localisation
</button>
<button
className="location-button secondary"
onClick={() => setShowLocationPrompt(false)}
>
Plus tard
</button>
</div>
{location.error && (
<p className="location-error">
⚠️ {location.error}
</p>
)}
</div>
</div>
)}
{/* Modal d'alerte stylisée */}
{showAlert && (
<div
className="alert-modal-overlay"
onClick={() => setShowAlert(false)}
>
<div
className="alert-modal"
onClick={(e) => e.stopPropagation()}
>
<div className={`alert-modal-icon ${alertConfig.type}`}>
{alertConfig.type === "success" && (
<CheckCircle size={48} />
)}
{alertConfig.type === "error" && (
<AlertCircle size={48} />
)}
{alertConfig.type === "warning" && (
<AlertCircle size={48} />
)}
{alertConfig.type === "info" && (
<Package size={48} />
)}
</div>
<h2 className="alert-modal-title">
{alertConfig.title}
</h2>
<p className="alert-modal-text">
{alertConfig.message}
</p>
<div className="alert-modal-buttons">
<button
className={`alert-button primary ${alertConfig.type}`}
onClick={() => {
setShowAlert(false);
if (alertConfig.onConfirm) {
alertConfig.onConfirm();
}
}}
>
OK
</button>
</div>
</div>
</div>
)}
{/* Header */}
<div className="delivery-header">
<h1>Tableau de Bord Livreur</h1>
<p className="delivery-subtitle">
Gérez vos livraisons efficacement
</p>
<div className="welcome-message">
<User size={20} />
<span>Bonjour, {deliveryPersonName}!</span>
</div>
<div className="header-badges">
<div
className={`status-badge ${isAvailable ? "available" : "offline"}`}
onClick={toggleAvailability}
style={{ cursor: "pointer" }}
>
<div
style={{
width: 8,
height: 8,
borderRadius: "50%",
backgroundColor: isAvailable
? "#10b981"
: "#6b7280",
}}
/>
{isAvailable ? "Disponible" : "Hors ligne"}
</div>
{location.permissionGranted &&
location.latitude &&
location.longitude && (
<div
className="status-badge"
style={{
background:
"linear-gradient(135deg, rgba(59, 130, 246, 0.2), rgba(37, 99, 235, 0.1))",
color: "#3b82f6",
border: "1px solid rgba(59, 130, 246, 0.3)",
cursor: "default",
}}
>
<MapPin size={14} />
GPS actif
</div>
)}
{!location.permissionGranted && (
<div
className="status-badge"
style={{
background:
"linear-gradient(135deg, rgba(239, 68, 68, 0.2), rgba(220, 38, 38, 0.1))",
color: "#ef4444",
border: "1px solid rgba(239, 68, 68, 0.3)",
cursor: "pointer",
}}
onClick={() => setShowLocationPrompt(true)}
>
<MapPin size={14} />
GPS désactivé
</div>
)}
{/* Bouton Alerte Police - Un seul bouton à la fois */}
{!isAlertTriggered ? (
<button
className="police-alert-btn"
onClick={handlePoliceAlert}
disabled={policeAlertLoading}
style={{
background:
"linear-gradient(135deg, #dc2626, #991b1b)",
color: "white",
border: "none",
borderRadius: "12px",
padding: "0.75rem 1rem",
fontSize: "0.9rem",
fontWeight: "600",
cursor: policeAlertLoading
? "not-allowed"
: "pointer",
display: "flex",
alignItems: "center",
gap: "0.5rem",
transition: "all 0.3s ease",
boxShadow: "0 4px 12px rgba(220, 38, 38, 0.3)",
opacity: policeAlertLoading ? 0.7 : 1,
}}
onMouseEnter={(e) => {
if (!policeAlertLoading) {
e.currentTarget.style.transform =
"translateY(-2px)";
e.currentTarget.style.boxShadow =
"0 6px 16px rgba(220, 38, 38, 0.4)";
}
}}
onMouseLeave={(e) => {
if (!policeAlertLoading) {
e.currentTarget.style.transform =
"translateY(0)";
e.currentTarget.style.boxShadow =
"0 4px 12px rgba(220, 38, 38, 0.3)";
}
}}
>
<Shield size={16} />
{policeAlertLoading ? "Envoi..." : "Alerte Police"}
</button>
) : (
<button
className="end-alert-btn"
onClick={handleEndPoliceAlert}
disabled={policeAlertLoading}
style={{
background:
"linear-gradient(135deg, #10b981, #059669)",
color: "white",
border: "none",
borderRadius: "12px",
padding: "0.75rem 1rem",
fontSize: "0.9rem",
fontWeight: "600",
cursor: policeAlertLoading
? "not-allowed"
: "pointer",
display: "flex",
alignItems: "center",
gap: "0.5rem",
transition: "all 0.3s ease",
boxShadow: "0 4px 12px rgba(16, 185, 129, 0.3)",
opacity: policeAlertLoading ? 0.7 : 1,
animation: "pulse 2s infinite",
}}
onMouseEnter={(e) => {
if (!policeAlertLoading) {
e.currentTarget.style.transform =
"translateY(-2px)";
e.currentTarget.style.boxShadow =
"0 6px 16px rgba(16, 185, 129, 0.4)";
}
}}
onMouseLeave={(e) => {
if (!policeAlertLoading) {
e.currentTarget.style.transform =
"translateY(0)";
e.currentTarget.style.boxShadow =
"0 4px 12px rgba(16, 185, 129, 0.3)";
}
}}
>
<CheckCircle size={16} />
{policeAlertLoading ? "Arrêt..." : "Fin d'Alerte"}
</button>
)}
</div>
</div>
{/* Stats Cards */}
<div className="delivery-stats-grid">
<div className="delivery-stat-card">
<div className="delivery-stat-icon blue">
<CheckCircle size={24} />
</div>
<div className="delivery-stat-content">
<p className="delivery-stat-label">Complétées</p>
<h3 className="delivery-stat-value">
{stats.completedDeliveries}
</h3>
</div>
</div>
<div className="delivery-stat-card">
<div className="delivery-stat-icon orange">
<AlertCircle size={24} />
</div>
<div className="delivery-stat-content">
<p className="delivery-stat-label">Approved</p>
<h3 className="delivery-stat-value">
{stats.approvedDeliveries}
</h3>
</div>
</div>
<div
className="delivery-stat-card stats-card"
onClick={() => navigate("/delivery/stats")}
style={{ cursor: "pointer" }}
>
<div className="delivery-stat-icon purple">
<BarChart3 size={24} />
</div>
<div className="delivery-stat-content">
<p className="delivery-stat-label">Voir Statistiques</p>
<h3 className="delivery-stat-value">Détails</h3>
</div>
</div>
<div
className="delivery-stat-card stats-card"
onClick={() => navigate("/delivery/alerts")}
style={{ cursor: "pointer" }}
>
<div className="delivery-stat-icon red">
<AlertCircle size={24} />
</div>
<div className="delivery-stat-content">
<p className="delivery-stat-label">Alertes</p>
<h3 className="delivery-stat-value">Détails</h3>
</div>
</div>
</div>
{/* Current Delivery Section */}
<div className="current-delivery-section">
<div
style={{
display: "flex",
justifyContent: "space-between",
alignItems: "center",
marginBottom: "1rem",
}}
>
<h2>Livraison en Cours</h2>
{stats.pendingDeliveries > 0 && (
<div
style={{
background:
"linear-gradient(135deg, rgba(59, 130, 246, 0.2), rgba(37, 99, 235, 0.1))",
padding: "0.5rem 1rem",
borderRadius: "12px",
border: "1px solid rgba(59, 130, 246, 0.3)",
color: "#3b82f6",
fontSize: "0.9rem",
fontWeight: "600",
display: "flex",
alignItems: "center",
gap: "0.5rem",
}}
>
<i
className="fas fa-box"
style={{ fontSize: "1rem" }}
></i>
{stats.pendingDeliveries} livraison
{stats.pendingDeliveries > 1 ? "s" : ""} en file
d'attente
</div>
)}
</div>
{currentDelivery ? (
<div className="current-delivery-card">
<div
style={{
display: "flex",
justifyContent: "space-between",
alignItems: "center",
marginBottom: "1rem",
paddingBottom: "1rem",
borderBottom: "1px solid #e5e7eb",
}}
>
<h3 style={{ margin: 0, color: "#fff" }}>
Détails de la Livraison
</h3>
<span
style={{
padding: "0.5rem 1rem",
borderRadius: "20px",
fontSize: "0.875rem",
fontWeight: "600",
display: "flex",
alignItems: "center",
gap: "0.5rem",
background:
currentDelivery.status === "assigned"
? "linear-gradient(135deg, rgba(59, 130, 246, 0.2), rgba(37, 99, 235, 0.1))"
: "linear-gradient(135deg, rgba(251, 146, 60, 0.2), rgba(249, 115, 22, 0.1))",
color:
currentDelivery.status === "assigned"
? "#3b82f6"
: "#fb923c",
border: `1px solid ${
currentDelivery.status === "assigned"
? "#3b82f6"
: "#fb923c"
}`,
}}
>
{currentDelivery.status === "assigned" && (
<>
<i
className="fas fa-box"
style={{ fontSize: "0.875rem" }}
></i>
Assignée
</>
)}
{currentDelivery.status === "in_route" && (
<>
<i
className="fas fa-truck"
style={{ fontSize: "0.875rem" }}
></i>
En Route
</>
)}
</span>
</div>
<div className="delivery-info-grid">
<div className="delivery-info-item">
<div className="delivery-info-icon">
<Package size={20} />
</div>
<div className="delivery-info-content">
<h4>N° Commande</h4>
<p>{currentDelivery.orderNumber}</p>
</div>
</div>
<div className="delivery-info-item">
<div className="delivery-info-icon">
<User size={20} />
</div>
<div className="delivery-info-content">
<h4>Client</h4>
<p>{currentDelivery.customerName}</p>
</div>
</div>
<div className="delivery-info-item">
<div className="delivery-info-icon">
<MapPin size={20} />
</div>
<div className="delivery-info-content">
<h4>Adresse</h4>
<p>{currentDelivery.deliveryAddress}</p>
</div>
</div>
</div>
{/* Section Produits */}
{currentDelivery.items &&
currentDelivery.items.length > 0 && (
<div className="order-items-section">
<div
style={{
display: "flex",
alignItems: "center",
gap: "0.5rem",
marginBottom: "1rem",
paddingTop: "1.5rem",
borderTop:
"1px solid rgba(255, 255, 255, 0.1)",
}}
>
<ShoppingBag
size={20}
color="#10b981"
/>
<h4
style={{
margin: 0,
color: "#fff",
fontSize: "1.1rem",
fontWeight: "600",
}}
>
Produits à livrer
</h4>
</div>
<div className="order-items-list">
{currentDelivery.items.map(
(item, index) => (
<div
key={index}
className="order-item"
>
<div style={{ flex: 1 }}>
<p className="order-item-name">
{item.produit}
</p>
<p className="order-item-quantity">
Quantité:{" "}
{item.quantite}
</p>
</div>
<p className="order-item-price">
{item.prix.toFixed(2)}
</p>
</div>
),
)}
</div>
<div className="order-total">
<span>Total de la commande</span>
<span className="order-total-amount">
{currentDelivery.totalPrice?.toFixed(
2,
)}{" "}
</span>
</div>
</div>
)}
<div className="delivery-actions">
{currentDelivery.status === "assigned" && (
<button
className="delivery-button primary"
onClick={handleStartDelivery}
>
<Package size={20} />
Démarrer la Livraison
</button>
)}
{currentDelivery.status === "in_route" && (
<button
className="delivery-button primary"
onClick={handleCompleteDelivery}
>
<CheckCircle size={20} />
Marquer comme Livrée
</button>
)}
<button
className="delivery-button secondary"
onClick={handleNavigate}
>
<Navigation size={20} />
Navigation GPS
</button>
</div>
</div>
) : (
<div className="current-delivery-card no-delivery-card">
<Package size={48} color="#888" />
<p>Aucune livraison en cours</p>
<div
style={{
display: "flex",
flexDirection: "column",
gap: "0.5rem",
marginTop: "1rem",
fontSize: "0.9rem",
}}
>
<div
style={{
display: "flex",
alignItems: "center",
gap: "0.5rem",
color: "#10b981",
marginLeft: "30px",
}}
></div>
<div
style={{
display: "flex",
alignItems: "center",
gap: "0.5rem",
color: "#6b7280",
}}
>
<span>
Les commandes vous seront assignées
automatiquement selon votre proximité
</span>
</div>
</div>
</div>
)}
</div>
</div>
);
}
export default DeliveryDashboard;