265 lines
6.8 KiB
Go
265 lines
6.8 KiB
Go
// ============================================
|
||
// handlers/client_tracking.go - NOUVEAU FICHIER
|
||
// ➕ SUIVI COMMANDE POUR CLIENTS
|
||
// ============================================
|
||
|
||
package handlers
|
||
|
||
import (
|
||
"gestion/db"
|
||
"log"
|
||
"net/http"
|
||
"strconv"
|
||
|
||
"github.com/gin-gonic/gin"
|
||
)
|
||
|
||
// GetCommandStatus - Statut temps réel d'une commande
|
||
// GET /api/v1/commands/:id/status
|
||
func GetCommandStatus(c *gin.Context) {
|
||
database := c.MustGet("database").(*db.Database)
|
||
|
||
username, exists := c.Get("username")
|
||
if !exists {
|
||
c.JSON(http.StatusUnauthorized, gin.H{"error": "Authentification requise"})
|
||
return
|
||
}
|
||
|
||
usernameStr := username.(string)
|
||
|
||
commandID, err := strconv.Atoi(c.Param("id"))
|
||
if err != nil {
|
||
c.JSON(http.StatusBadRequest, gin.H{"error": "ID invalide"})
|
||
return
|
||
}
|
||
|
||
log.Printf("📊 [STATUS] Client %s demande statut cmd %d", usernameStr, commandID)
|
||
|
||
// Récupérer la commande
|
||
command, err := database.GetCommandByID(commandID)
|
||
if err != nil {
|
||
c.JSON(http.StatusNotFound, gin.H{"error": "Commande non trouvée"})
|
||
return
|
||
}
|
||
|
||
// ✅ VÉRIFIER PROPRIÉTÉ
|
||
cmdUsername, _ := command["username"].(string)
|
||
if cmdUsername != usernameStr {
|
||
log.Printf("❌ [STATUS] Accès refusé - cmd de %s demandée par %s", cmdUsername, usernameStr)
|
||
c.JSON(http.StatusForbidden, gin.H{
|
||
"error": "Cette commande ne vous appartient pas",
|
||
})
|
||
return
|
||
}
|
||
|
||
// Récupérer ETA
|
||
etaData, _ := database.GetCommandETA(commandID)
|
||
|
||
// Récupérer infos livreur (si assigné)
|
||
livreurInfo := gin.H{
|
||
"assigned": false,
|
||
}
|
||
if livreurAssign, ok := command["livreur_assign"].(string); ok && livreurAssign != "" {
|
||
queueInfo, _ := database.GetDeliverymanQueueInfo(livreurAssign)
|
||
livreurInfo = gin.H{
|
||
"assigned": true,
|
||
"livreur_name": livreurAssign,
|
||
"queue_position": queueInfo["queue_size"],
|
||
}
|
||
}
|
||
|
||
// Mapper le statut en message lisible
|
||
statusMessage := getStatusMessage(command["status"].(string))
|
||
|
||
c.JSON(http.StatusOK, gin.H{
|
||
"success": true,
|
||
"command_id": commandID,
|
||
"status": command["status"],
|
||
"status_message": statusMessage,
|
||
"adresse": command["adresse"],
|
||
"total_prix": command["total_prix"],
|
||
"created_at": command["created_at"],
|
||
"livreur": livreurInfo,
|
||
"eta": etaData,
|
||
})
|
||
}
|
||
|
||
// GetMyCommandsWithTracking - Liste des commandes avec suivi
|
||
// GET /api/v1/my-commands
|
||
func GetMyCommandsWithTracking(c *gin.Context) {
|
||
database := c.MustGet("database").(*db.Database)
|
||
|
||
username, exists := c.Get("username")
|
||
if !exists {
|
||
c.JSON(http.StatusUnauthorized, gin.H{"error": "Authentification requise"})
|
||
return
|
||
}
|
||
|
||
usernameStr := username.(string)
|
||
status := c.Query("status")
|
||
|
||
log.Printf("📋 [MY_CMDS] Client %s demande ses commandes (status=%s)", usernameStr, status)
|
||
|
||
commands, err := database.GetAllCommands(status, usernameStr)
|
||
if err != nil {
|
||
c.JSON(http.StatusInternalServerError, gin.H{
|
||
"error": "Erreur récupération",
|
||
"details": err.Error(),
|
||
})
|
||
return
|
||
}
|
||
|
||
// Enrichir avec tracking
|
||
enrichedCommands := make([]gin.H, len(commands))
|
||
for i, cmd := range commands {
|
||
commandID, _ := cmd["id"].(int)
|
||
|
||
// ETA
|
||
etaData, _ := database.GetCommandETA(commandID)
|
||
|
||
// Infos livreur
|
||
livreurInfo := gin.H{
|
||
"assigned": false,
|
||
}
|
||
if livreurAssign, ok := cmd["livreur_assign"].(string); ok && livreurAssign != "" {
|
||
queueInfo, _ := database.GetDeliverymanQueueInfo(livreurAssign)
|
||
livreurInfo = gin.H{
|
||
"assigned": true,
|
||
"livreur_name": livreurAssign,
|
||
"queue_position": queueInfo["queue_size"],
|
||
}
|
||
}
|
||
|
||
enrichedCommands[i] = gin.H{
|
||
"id": cmd["id"],
|
||
"status": cmd["status"],
|
||
"status_message": getStatusMessage(cmd["status"].(string)),
|
||
"adresse": cmd["adresse"],
|
||
"total_prix": cmd["total_prix"],
|
||
"created_at": cmd["created_at"],
|
||
"livreur": livreurInfo,
|
||
"eta": etaData,
|
||
}
|
||
}
|
||
|
||
c.JSON(http.StatusOK, gin.H{
|
||
"success": true,
|
||
"commands": enrichedCommands,
|
||
"count": len(enrichedCommands),
|
||
})
|
||
}
|
||
|
||
// GetCommandTracking - Suivi détaillé d'une commande
|
||
// GET /api/v1/commands/:id/tracking
|
||
func GetCommandTracking(c *gin.Context) {
|
||
database := c.MustGet("database").(*db.Database)
|
||
|
||
username := c.GetString("username")
|
||
commandID, err := strconv.Atoi(c.Param("id"))
|
||
if err != nil {
|
||
c.JSON(http.StatusBadRequest, gin.H{"error": "ID invalide"})
|
||
return
|
||
}
|
||
|
||
// Récupérer commande
|
||
command, err := database.GetCommandByID(commandID)
|
||
if err != nil {
|
||
c.JSON(http.StatusNotFound, gin.H{"error": "Commande non trouvée"})
|
||
return
|
||
}
|
||
|
||
// Vérifier propriété
|
||
cmdUsername, _ := command["username"].(string)
|
||
if cmdUsername != username {
|
||
c.JSON(http.StatusForbidden, gin.H{
|
||
"error": "Cette commande ne vous appartient pas",
|
||
})
|
||
return
|
||
}
|
||
|
||
// Récupérer logs
|
||
logs, _ := database.GetCommandLogs(commandID)
|
||
|
||
// ETA
|
||
etaData, _ := database.GetCommandETA(commandID)
|
||
|
||
// Timeline (basé sur les logs)
|
||
timeline := buildTimeline(logs)
|
||
|
||
c.JSON(http.StatusOK, gin.H{
|
||
"success": true,
|
||
"command_id": commandID,
|
||
"status": command["status"],
|
||
"status_message": getStatusMessage(command["status"].(string)),
|
||
"eta": etaData,
|
||
"timeline": timeline,
|
||
"logs": logs,
|
||
})
|
||
}
|
||
|
||
// ============================================
|
||
// HELPERS
|
||
// ============================================
|
||
|
||
// getStatusMessage retourne un message lisible pour le client
|
||
func getStatusMessage(status string) string {
|
||
messages := map[string]string{
|
||
"pending": "⏳ En attente d'assignation",
|
||
"assigned": "✅ Livreur assigné",
|
||
"support": "👨💼 En préparation",
|
||
"en_route": "🚗 En cours de livraison",
|
||
"arrived": "📍 Livreur arrivé",
|
||
"livre": "📦 Livré - En attente de confirmation",
|
||
"delivered": "✅ Livré",
|
||
"approved": "🎉 Livraison confirmée",
|
||
"failed": "❌ Échec de livraison",
|
||
"cancelled": "🚫 Annulée",
|
||
"disabled": "⚠️ Désactivée",
|
||
}
|
||
|
||
if msg, ok := messages[status]; ok {
|
||
return msg
|
||
}
|
||
return "📋 " + status
|
||
}
|
||
|
||
// buildTimeline construit une timeline depuis les logs
|
||
func buildTimeline(logs []map[string]interface{}) []gin.H {
|
||
timeline := make([]gin.H, 0)
|
||
|
||
for _, logEntry := range logs {
|
||
status, _ := logEntry["status"].(string)
|
||
message, _ := logEntry["message"].(string)
|
||
createdAt, _ := logEntry["created_at"]
|
||
|
||
timeline = append(timeline, gin.H{
|
||
"status": status,
|
||
"message": message,
|
||
"icon": getStatusIcon(status),
|
||
"created_at": createdAt,
|
||
})
|
||
}
|
||
|
||
return timeline
|
||
}
|
||
|
||
// getStatusIcon retourne une icône pour la timeline
|
||
func getStatusIcon(status string) string {
|
||
icons := map[string]string{
|
||
"created": "🛒",
|
||
"assigned": "👤",
|
||
"support": "📦",
|
||
"en_route": "🚗",
|
||
"arrived": "📍",
|
||
"livre": "✅",
|
||
"approved": "🎉",
|
||
"failed": "❌",
|
||
"cancelled": "🚫",
|
||
}
|
||
|
||
if icon, ok := icons[status]; ok {
|
||
return icon
|
||
}
|
||
return "📋"
|
||
}
|