@@ -90,6 +90,28 @@ func GetLivreurRatings(c *gin.Context) {
|
||||
})
|
||||
}
|
||||
|
||||
// GetMyRatings retourne les avis reçus par le livreur connecté (uniquement les siens).
|
||||
func GetMyRatings(c *gin.Context) {
|
||||
username := c.GetString("username")
|
||||
if username == "" || c.GetString("role") != "livreur" {
|
||||
c.JSON(http.StatusForbidden, gin.H{"error": "Accès réservé aux livreurs"})
|
||||
return
|
||||
}
|
||||
|
||||
database := c.MustGet("database").(*db.Database)
|
||||
ratings, avg, err := database.GetLivreurRatings(username)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": "Erreur récupération avis"})
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"ratings": ratings,
|
||||
"average": avg,
|
||||
"count": len(ratings),
|
||||
})
|
||||
}
|
||||
|
||||
func GetOrderRatingStatus(c *gin.Context) {
|
||||
clientUsername := c.GetString("username")
|
||||
if clientUsername == "" {
|
||||
|
||||
@@ -5,23 +5,108 @@ import (
|
||||
"gestion/db"
|
||||
"gestion/models"
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
var weekdayNames = []string{"Dimanche", "Lundi", "Mardi", "Mercredi", "Jeudi", "Vendredi", "Samedi"}
|
||||
|
||||
// sections valides pour le reset
|
||||
var validStatsSections = map[string]string{
|
||||
"commandes": "stats_reset_commandes_at",
|
||||
"revenus": "stats_reset_revenus_at",
|
||||
"produits": "stats_reset_produits_at",
|
||||
}
|
||||
|
||||
// ResetAdminStats réinitialise une section précise des statistiques.
|
||||
func ResetAdminStats(c *gin.Context) {
|
||||
section := c.Param("section")
|
||||
key, ok := validStatsSections[section]
|
||||
if !ok {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": fmt.Sprintf("section invalide : %s", section)})
|
||||
return
|
||||
}
|
||||
database := c.MustGet("database").(*db.Database)
|
||||
now := time.Now().UTC().Format(time.RFC3339)
|
||||
upsert := `INSERT INTO app_settings (key, value) VALUES (?, ?)
|
||||
ON CONFLICT (key) DO UPDATE SET value = EXCLUDED.value`
|
||||
if err := database.GDB.Exec(upsert, key, now).Error; err != nil {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"success": false, "error": "Erreur reset statistiques"})
|
||||
return
|
||||
}
|
||||
c.JSON(http.StatusOK, gin.H{"success": true, "section": section, "reset_at": now})
|
||||
}
|
||||
|
||||
// readResetAt lit une date de reset depuis app_settings (zero value si absente).
|
||||
func readResetAt(database *db.Database, key string) time.Time {
|
||||
var row struct{ Value string }
|
||||
database.GDB.Table("app_settings").Select("value").Where("key = ?", key).Scan(&row)
|
||||
if row.Value != "" {
|
||||
if t, err := time.Parse(time.RFC3339, row.Value); err == nil {
|
||||
return t
|
||||
}
|
||||
}
|
||||
return time.Time{}
|
||||
}
|
||||
|
||||
func dateFilter(t time.Time) string {
|
||||
if t.IsZero() {
|
||||
return ""
|
||||
}
|
||||
return t.Format(time.RFC3339)
|
||||
}
|
||||
|
||||
// GetAdminStats returns aggregated order & product statistics for the admin dashboard.
|
||||
func GetAdminStats(c *gin.Context) {
|
||||
database := c.MustGet("database").(*db.Database)
|
||||
gdb := database.GDB
|
||||
|
||||
// ── Commandes par jour de la semaine (all time, non annulées) ──────────────
|
||||
resetCmd := readResetAt(database, "stats_reset_commandes_at")
|
||||
resetRev := readResetAt(database, "stats_reset_revenus_at")
|
||||
resetProd := readResetAt(database, "stats_reset_produits_at")
|
||||
|
||||
// whereCmd : filtre commandes non annulées depuis resetCmd
|
||||
whereCmd := func() string {
|
||||
base := "status != 'cancelled'"
|
||||
if !resetCmd.IsZero() {
|
||||
base += " AND created_at >= '" + resetCmd.Format(time.RFC3339) + "'"
|
||||
}
|
||||
return base
|
||||
}
|
||||
// whereRev : filtre commandes approuvées depuis resetRev
|
||||
whereRev := func() string {
|
||||
base := "status = 'approved'"
|
||||
if !resetRev.IsZero() {
|
||||
base += " AND created_at >= '" + resetRev.Format(time.RFC3339) + "'"
|
||||
}
|
||||
return base
|
||||
}
|
||||
// whereProd : filtre commandes non annulées depuis resetProd (pour les jointures sur c.)
|
||||
whereProd := func() string {
|
||||
base := "status != 'cancelled'"
|
||||
if !resetProd.IsZero() {
|
||||
base += " AND created_at >= '" + resetProd.Format(time.RFC3339) + "'"
|
||||
}
|
||||
return base
|
||||
}
|
||||
|
||||
// compatibilité : les anciennes clauses whereBase/whereApproved pointent sur commandes/revenus
|
||||
whereBase := func(extra string) string {
|
||||
base := whereCmd()
|
||||
if extra != "" {
|
||||
return base + " AND " + extra
|
||||
}
|
||||
return base
|
||||
}
|
||||
whereApproved := whereRev
|
||||
|
||||
// ── Commandes par jour de la semaine (non annulées) ────────────────────────
|
||||
var wdRows []models.WeekdayRow
|
||||
gdb.Raw(`
|
||||
SELECT EXTRACT(DOW FROM created_at)::int AS dow, COUNT(*) AS count
|
||||
FROM commandes
|
||||
WHERE status != 'cancelled'
|
||||
WHERE ` + whereBase("") + `
|
||||
GROUP BY dow
|
||||
ORDER BY dow
|
||||
`).Scan(&wdRows)
|
||||
@@ -47,7 +132,7 @@ func GetAdminStats(c *gin.Context) {
|
||||
SELECT DATE(created_at) AS day, COUNT(*) AS count
|
||||
FROM commandes
|
||||
WHERE created_at >= NOW() - INTERVAL '30 days'
|
||||
AND status != 'cancelled'
|
||||
AND ` + whereBase("") + `
|
||||
GROUP BY DATE(created_at)
|
||||
ORDER BY day
|
||||
`).Scan(&dayRows)
|
||||
@@ -67,7 +152,7 @@ func GetAdminStats(c *gin.Context) {
|
||||
SELECT DATE(created_at) AS day, COALESCE(SUM(total_prix - COALESCE(referral_used, 0)), 0) AS revenue
|
||||
FROM commandes
|
||||
WHERE created_at >= NOW() - INTERVAL '30 days'
|
||||
AND status = 'approved'
|
||||
AND ` + whereApproved() + `
|
||||
GROUP BY DATE(created_at)
|
||||
ORDER BY day
|
||||
`).Scan(&dayRevRows)
|
||||
@@ -81,7 +166,7 @@ func GetAdminStats(c *gin.Context) {
|
||||
}
|
||||
}
|
||||
|
||||
// ── Commandes & revenus par heure (all time, non annulées) ───────────────
|
||||
// ── Commandes & revenus par heure (non annulées) ─────────────────────────
|
||||
var hourRows []models.HourRow
|
||||
gdb.Raw(`
|
||||
SELECT
|
||||
@@ -89,7 +174,7 @@ func GetAdminStats(c *gin.Context) {
|
||||
COUNT(*) AS count,
|
||||
COALESCE(SUM(total_prix - COALESCE(referral_used, 0)), 0) AS revenue
|
||||
FROM commandes
|
||||
WHERE status != 'cancelled'
|
||||
WHERE ` + whereBase("") + `
|
||||
GROUP BY hour
|
||||
ORDER BY hour
|
||||
`).Scan(&hourRows)
|
||||
@@ -124,7 +209,7 @@ func GetAdminStats(c *gin.Context) {
|
||||
JOIN commandes c ON c.id = ci.command_id
|
||||
LEFT JOIN products p ON p.id = ci.product_id
|
||||
LEFT JOIN categories cat ON cat.name = p.category
|
||||
WHERE c.status != 'cancelled'
|
||||
WHERE c.` + whereProd() + `
|
||||
GROUP BY ci.product_id, ci.produit, p.category, cat.color
|
||||
ORDER BY total_quantity DESC
|
||||
LIMIT 15
|
||||
@@ -162,7 +247,7 @@ func GetAdminStats(c *gin.Context) {
|
||||
JOIN commandes c ON c.id = ci.command_id
|
||||
LEFT JOIN products p ON p.id = ci.product_id
|
||||
LEFT JOIN categories cat ON cat.name = p.category
|
||||
WHERE c.status != 'cancelled'
|
||||
WHERE c.` + whereProd() + `
|
||||
GROUP BY ci.product_id, ci.produit, ci.quantite, cat.color
|
||||
ORDER BY ci.product_id, COUNT(DISTINCT ci.command_id) DESC
|
||||
`).Scan(&qtyRows)
|
||||
@@ -220,24 +305,21 @@ func GetAdminStats(c *gin.Context) {
|
||||
// ── Résumé global ─────────────────────────────────────────────────────────
|
||||
var totalOrders int64
|
||||
var totalRevenue float64
|
||||
gdb.Raw(`SELECT COUNT(*) FROM commandes WHERE status != 'cancelled'`).Scan(&totalOrders)
|
||||
gdb.Raw(`SELECT COALESCE(SUM(total_prix - COALESCE(referral_used, 0)), 0) FROM commandes WHERE status = 'approved'`).Scan(&totalRevenue)
|
||||
gdb.Raw(`SELECT COUNT(*) FROM commandes WHERE ` + whereBase("")).Scan(&totalOrders)
|
||||
gdb.Raw(`SELECT COALESCE(SUM(total_prix - COALESCE(referral_used, 0)), 0) FROM commandes WHERE ` + whereApproved()).Scan(&totalRevenue)
|
||||
|
||||
avgPerDay := 0.0
|
||||
if totalOrders > 0 {
|
||||
// average over the last 30 days with data
|
||||
var activeDays int64
|
||||
gdb.Raw(`
|
||||
SELECT COUNT(DISTINCT DATE(created_at))
|
||||
FROM commandes
|
||||
WHERE created_at >= NOW() - INTERVAL '30 days' AND status != 'cancelled'
|
||||
`).Scan(&activeDays)
|
||||
WHERE created_at >= NOW() - INTERVAL '30 days' AND ` + whereBase("")).Scan(&activeDays)
|
||||
if activeDays > 0 {
|
||||
var last30Count int64
|
||||
gdb.Raw(`
|
||||
SELECT COUNT(*) FROM commandes
|
||||
WHERE created_at >= NOW() - INTERVAL '30 days' AND status != 'cancelled'
|
||||
`).Scan(&last30Count)
|
||||
WHERE created_at >= NOW() - INTERVAL '30 days' AND ` + whereBase("")).Scan(&last30Count)
|
||||
avgPerDay = float64(last30Count) / float64(activeDays)
|
||||
}
|
||||
}
|
||||
@@ -250,11 +332,14 @@ func GetAdminStats(c *gin.Context) {
|
||||
"top_product": topProductName,
|
||||
"avg_per_day": avgPerDay,
|
||||
},
|
||||
"by_weekday": byWeekday,
|
||||
"by_day_30": byDay,
|
||||
"by_day_revenue": byDayRevenue,
|
||||
"by_hour": byHour,
|
||||
"top_products": topProducts,
|
||||
"by_quantity": byQuantity,
|
||||
"reset_at_commandes": dateFilter(resetCmd),
|
||||
"reset_at_revenus": dateFilter(resetRev),
|
||||
"reset_at_produits": dateFilter(resetProd),
|
||||
"by_weekday": byWeekday,
|
||||
"by_day_30": byDay,
|
||||
"by_day_revenue": byDayRevenue,
|
||||
"by_hour": byHour,
|
||||
"top_products": topProducts,
|
||||
"by_quantity": byQuantity,
|
||||
})
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user