From 0043646cff5262f352b98103061158f645c99bee Mon Sep 17 00:00:00 2001 From: Xor290 Date: Sat, 14 Mar 2026 15:28:40 +0100 Subject: [PATCH] chore: fix --- backend/gestion/db/db_clients.go | 70 ++++++++++++++--- backend/gestion/db/db_init.go | 5 ++ backend/gestion/handlers/history.go | 20 +++++ backend/gestion/handlers/redis_services.go | 17 ++-- backend/gestion/handlers/settings.go | 9 +++ backend/gestion/models/client.go | 5 +- mobile/src/api/api.ts | 7 +- mobile/src/api/api_types.ts | 3 +- .../src/screens/client/OrderHistoryScreen.tsx | 77 ++++++------------- .../screens/client/OrderTrackingScreen.tsx | 20 ++++- 10 files changed, 156 insertions(+), 77 deletions(-) diff --git a/backend/gestion/db/db_clients.go b/backend/gestion/db/db_clients.go index 97ad7ce7..aff49376 100644 --- a/backend/gestion/db/db_clients.go +++ b/backend/gestion/db/db_clients.go @@ -2,6 +2,7 @@ package db import ( "database/sql" + "encoding/json" "fmt" "gestion/models" "log" @@ -57,7 +58,7 @@ func (d *Database) GetClientByID(id int) (*models.Client, error) { // GetAllClients récupère tous les clients func (d *Database) GetAllClients() ([]*models.Client, error) { - query := `SELECT id, username, password, nom, prenom, telephone, command, point, point_zipette, amende, referral_balance, created_at + query := `SELECT id, username, password, nom, prenom, telephone, command, point, point_zipette, amende, referral_balance, COALESCE(points_extra, '{}'::jsonb), created_at FROM clients ORDER BY created_at DESC` rows, err := d.Query(query) @@ -69,6 +70,7 @@ func (d *Database) GetAllClients() ([]*models.Client, error) { var clients []*models.Client for rows.Next() { client := &models.Client{} + var pointsExtraJSON []byte err := rows.Scan( &client.ID, &client.Username, @@ -81,11 +83,16 @@ func (d *Database) GetAllClients() ([]*models.Client, error) { &client.PointZipette, &client.Amende, &client.ReferralBalance, + &pointsExtraJSON, &client.CreatedAt, ) if err != nil { return nil, fmt.Errorf("erreur lors du scan du client: %w", err) } + client.PointsExtra = map[string]int{} + if len(pointsExtraJSON) > 0 { + json.Unmarshal(pointsExtraJSON, &client.PointsExtra) + } clients = append(clients, client) } @@ -470,12 +477,24 @@ func (d *Database) CheckClientCanOrder(username string) (bool, float64, error) { return true, 0, nil } -func (d *Database) ResetClientPoint(username string, resetCancellationsPoint bool) error { +// ResetClientPoint réinitialise les points d'un client. +// poolIdx=0 → point, poolIdx=1 → point_zipette, poolIdx=-1 → tous +// poolIdx>=2 → points_extra[extraPoolKey] +func (d *Database) ResetClientPoint(username string, poolIdx int, extraPoolKey string) error { var query string - if resetCancellationsPoint { - query = `UPDATE clients SET point = 0, point_zipette = 0, updated_at = CURRENT_TIMESTAMP WHERE username = $1` - } else { + switch { + case poolIdx == 0: query = `UPDATE clients SET point = 0, updated_at = CURRENT_TIMESTAMP WHERE username = $1` + case poolIdx == 1: + query = `UPDATE clients SET point_zipette = 0, updated_at = CURRENT_TIMESTAMP WHERE username = $1` + case poolIdx >= 2 && extraPoolKey != "": + _, err := d.Exec( + `UPDATE clients SET points_extra = points_extra - $2, updated_at = CURRENT_TIMESTAMP WHERE username = $1`, + username, extraPoolKey, + ) + return err + default: // -1 → reset total + query = `UPDATE clients SET point = 0, point_zipette = 0, points_extra = '{}'::jsonb, updated_at = CURRENT_TIMESTAMP WHERE username = $1` } result, err := d.Exec(query, username) @@ -943,18 +962,47 @@ func (d *Database) CalculateAndAddPointsForCommandTx(tx *sql.Tx, commandID int, } totalPoints = pts0 + pts1 - log.Printf("💰 [CalcPointsTx] pool[0]=%d pts, pool[1]=%d pts", pts0, pts1) + // Pools supplémentaires (index 2+) → points_extra JSONB + for i := 2; i < len(pools); i++ { + ptsExtra := CalcPointsFromTiers(poolTotals[i], pools[i].Tiers) + if ptsExtra == 0 { + continue + } + totalPoints += ptsExtra + poolKey := pools[i].Key + _, err = tx.Exec(` + UPDATE clients + SET points_extra = jsonb_set( + COALESCE(points_extra, '{}'::jsonb), + ARRAY[$2], + to_jsonb(COALESCE((points_extra->>$2)::int, 0) + $3) + ), updated_at = CURRENT_TIMESTAMP + WHERE username = $1 + `, username, poolKey, ptsExtra) + if err != nil { + log.Printf("❌ [CalcPointsTx] Erreur UPDATE points_extra pool[%d]: %v", i, err) + return 0, "", fmt.Errorf("erreur mise à jour points pool[%d]: %w", i, err) + } + log.Printf("💰 [CalcPointsTx] pool[%d] (%s): +%d pts → points_extra", i, pools[i].Name, ptsExtra) + } + + log.Printf("💰 [CalcPointsTx] pool[0]=%d pts, pool[1]=%d pts, total=%d pts", pts0, pts1, totalPoints) if totalPoints == 0 { return 0, "", nil } - if pts0 > 0 && pts1 > 0 { - pointCategory = pools[0].Name + " & " + pools[1].Name - } else if pts1 > 0 { - pointCategory = pools[1].Name + var categoryParts []string + if pts0 > 0 { + categoryParts = append(categoryParts, pools[0].Name) + } + if pts1 > 0 { + categoryParts = append(categoryParts, pools[1].Name) + } + if len(categoryParts) > 0 { + pointCategory = strings.Join(categoryParts, " & ") } else { - pointCategory = pools[0].Name + pointCategory = "points" } if len(pools) == 1 || pts1 == 0 { diff --git a/backend/gestion/db/db_init.go b/backend/gestion/db/db_init.go index a7bd723c..b7b1f21a 100644 --- a/backend/gestion/db/db_init.go +++ b/backend/gestion/db/db_init.go @@ -161,6 +161,11 @@ func InitDB() *Database { log.Fatalf("❌ Erreur migration commandes.referral_used: %v", err) } + // Migration: points extra pour les pools de points supplémentaires (pool[2+]) + if _, err = database.Exec(`ALTER TABLE clients ADD COLUMN IF NOT EXISTS points_extra JSONB NOT NULL DEFAULT '{}'::jsonb`); err != nil { + log.Fatalf("❌ Erreur migration clients.points_extra: %v", err) + } + // Lancer le nettoyage périodique des tokens expirés go database.cleanExpiredTokensPeriodically() diff --git a/backend/gestion/handlers/history.go b/backend/gestion/handlers/history.go index 31c302f5..3e1a425d 100644 --- a/backend/gestion/handlers/history.go +++ b/backend/gestion/handlers/history.go @@ -50,6 +50,15 @@ func GetMyCompletedOrders(c *gin.Context) { // ✅ Récupérer les infos client pour statistiques client, err := database.GetClientByUsername(usernameStr) + // ✅ Récupérer les noms des pools de points + poolNames := []string{"Pool 1", "Pool 2"} + if settings, sErr := database.GetSettings(); sErr == nil && len(settings.PointsPools) > 0 { + poolNames = make([]string, len(settings.PointsPools)) + for i, p := range settings.PointsPools { + poolNames[i] = p.Name + } + } + response := gin.H{ "success": true, "commands": commands, @@ -57,10 +66,21 @@ func GetMyCompletedOrders(c *gin.Context) { } if err == nil && client != nil { + // Construire le tableau générique des valeurs par pool + poolPoints := make([]int, len(poolNames)) + if len(poolPoints) > 0 { + poolPoints[0] = client.Point + } + if len(poolPoints) > 1 { + poolPoints[1] = client.PointZipette + } + response["client_stats"] = gin.H{ "username": client.Username, "total_commands": client.Command, "points": client.Point, + "pool_points": poolPoints, + "pool_names": poolNames, "penalties": client.Amende, } } diff --git a/backend/gestion/handlers/redis_services.go b/backend/gestion/handlers/redis_services.go index 75ed0814..237f250e 100644 --- a/backend/gestion/handlers/redis_services.go +++ b/backend/gestion/handlers/redis_services.go @@ -952,16 +952,23 @@ func ResetClientPointAdmin(c *gin.Context) { return } var req struct { - ResetCancellationsPoints bool `json:"reset_cancellations_points"` + Pool int `json:"pool"` // 0=pool principal, 1=pool secondaire, -1=tous (défaut) } - - if err := c.ShouldBindJSON(&req); err != nil { - req.ResetCancellationsPoints = false + req.Pool = -1 // défaut : reset total + if err := c.ShouldBindJSON(&req); err == nil { + // valeur reçue correctement } database := c.MustGet("database").(*db.Database) - err := database.ResetClientPoint(username, req.ResetCancellationsPoints) + extraPoolKey := "" + if req.Pool >= 2 { + if settings, err := database.GetSettings(); err == nil && req.Pool < len(settings.PointsPools) { + extraPoolKey = settings.PointsPools[req.Pool].Key + } + } + + err := database.ResetClientPoint(username, req.Pool, extraPoolKey) if err != nil { c.JSON(http.StatusInternalServerError, gin.H{ "error": "Erreur lors de la réinitialisation", diff --git a/backend/gestion/handlers/settings.go b/backend/gestion/handlers/settings.go index 3d35ab87..9c171975 100644 --- a/backend/gestion/handlers/settings.go +++ b/backend/gestion/handlers/settings.go @@ -19,12 +19,21 @@ func GetPublicSettings(c *gin.Context) { settings = db.DefaultSettings() } + poolNames := make([]string, len(settings.PointsPools)) + poolKeys := make([]string, len(settings.PointsPools)) + for i, p := range settings.PointsPools { + poolNames[i] = p.Name + poolKeys[i] = p.Key + } + c.JSON(http.StatusOK, gin.H{ "success": true, "penalties_enabled": settings.PenaltiesEnabled, "show_amende_score": settings.ShowAmendeScore, "points_enabled": settings.PointsEnabled, "points_separated": len(settings.PointsPools) > 1, + "pool_names": poolNames, + "pool_keys": poolKeys, "referral_enabled": settings.ReferralEnabled, "delivery_schedule": settings.DeliverySchedule, }) diff --git a/backend/gestion/models/client.go b/backend/gestion/models/client.go index a6a8fbdf..dfeca26a 100644 --- a/backend/gestion/models/client.go +++ b/backend/gestion/models/client.go @@ -11,8 +11,9 @@ type Client struct { Prenom string `json:"prenom"` Telephone string `json:"telephone"` Command int `json:"command"` - Point int `json:"point"` - PointZipette int `json:"points_zipette"` + Point int `json:"point"` + PointZipette int `json:"points_zipette"` + PointsExtra map[string]int `json:"points_extra"` // pools[2+] Amende float64 `json:"amende"` CancellationsCount int `json:"cancellations_count"` LastPenaltyReason string `json:"last_penalty_reason"` diff --git a/mobile/src/api/api.ts b/mobile/src/api/api.ts index bc940037..be1983c1 100644 --- a/mobile/src/api/api.ts +++ b/mobile/src/api/api.ts @@ -730,9 +730,11 @@ export interface PublicSettings { points_enabled: boolean; points_separated: boolean; referral_enabled: boolean; + pool_names: string[]; } export const getPublicSettings = async (): Promise => { + const defaults: PublicSettings = { penalties_enabled: true, show_amende_score: true, points_enabled: true, points_separated: true, referral_enabled: true, pool_names: ['Pool 1', 'Pool 2'] }; try { const { data } = await apiClient.get(`${V1}/app-settings`); return { @@ -741,9 +743,12 @@ export const getPublicSettings = async (): Promise => { points_enabled: data.points_enabled ?? true, points_separated: data.points_separated ?? true, referral_enabled: data.referral_enabled ?? true, + pool_names: Array.isArray(data.pool_names) && data.pool_names.length > 0 + ? data.pool_names + : defaults.pool_names, }; } catch { - return { penalties_enabled: true, show_amende_score: true, points_enabled: true, points_separated: true, referral_enabled: true }; + return defaults; } }; diff --git a/mobile/src/api/api_types.ts b/mobile/src/api/api_types.ts index a12e0783..984ef0e5 100644 --- a/mobile/src/api/api_types.ts +++ b/mobile/src/api/api_types.ts @@ -585,7 +585,8 @@ export interface ClientStats { telephone?: string; total_commands: number; points: number; - points_zipette: number; // ✅ AJOUTER CETTE LIGNE + pool_points: number[]; + pool_names: string[]; penalties: number; } diff --git a/mobile/src/screens/client/OrderHistoryScreen.tsx b/mobile/src/screens/client/OrderHistoryScreen.tsx index 79202b2c..012d97bd 100644 --- a/mobile/src/screens/client/OrderHistoryScreen.tsx +++ b/mobile/src/screens/client/OrderHistoryScreen.tsx @@ -45,7 +45,7 @@ export default function OrderHistoryScreen() { const [orders, setOrders] = useState([]); const [stats, setStats] = useState(null); const [penalties, setPenalties] = useState(null); - const [appSettings, setAppSettings] = useState({ penalties_enabled: true, show_amende_score: true, points_enabled: true, points_separated: true, referral_enabled: false }); + const [appSettings, setAppSettings] = useState({ penalties_enabled: true, show_amende_score: true, points_enabled: true, points_separated: true, referral_enabled: false, pool_names: ['Pool 1', 'Pool 2'] }); const [referralBalance, setReferralBalance] = useState(0); const [loading, setLoading] = useState(true); const [refreshing, setRefreshing] = useState(false); @@ -216,7 +216,9 @@ export default function OrderHistoryScreen() { if (loading && !refreshing) return ; - const totalPoints = (stats?.points || 0) + (stats?.points_zipette || 0); + const poolNames = stats?.pool_names?.length ? stats.pool_names : appSettings.pool_names; + const poolPoints = stats?.pool_points ?? [stats?.points ?? 0]; + const totalPoints = poolPoints.reduce((s, v) => s + (v || 0), 0); const penaltyCount = penalties?.total_penalty || stats?.penalties || 0; return ( @@ -247,64 +249,33 @@ export default function OrderHistoryScreen() { Commandes {appSettings.points_enabled && ( - appSettings.points_separated ? ( + poolNames.length <= 1 ? ( + + + {poolPoints[0] || 0} + Pts {poolNames[0] ?? 'Points'} + + ) : ( <> - - - - {stats?.points || 0} - - - Pts Weed/Hash - - - - - - {stats?.points_zipette || 0} - - - Pts Zipette - - - {(stats?.points || 0) > 0 && (stats?.points_zipette || 0) > 0 && ( - + {poolNames.map((name, i) => ( + - - {totalPoints} - - - Total Points - + {poolPoints[i] || 0} + Pts {name} + + ))} + {totalPoints > 0 && ( + + + {totalPoints} + Total Points )} - ) : ( - - - - {stats?.points || 0} - - - Points - - ) )} {appSettings.show_amende_score && ( diff --git a/mobile/src/screens/client/OrderTrackingScreen.tsx b/mobile/src/screens/client/OrderTrackingScreen.tsx index 32d5a3de..dc6db74b 100644 --- a/mobile/src/screens/client/OrderTrackingScreen.tsx +++ b/mobile/src/screens/client/OrderTrackingScreen.tsx @@ -537,8 +537,9 @@ export default function OrderTrackingScreen() { )} - {(order.status === "en_route" || - order.status === "arrived") && ( + {order.status === "en_route" && + eta?.eta_minutes != null && + eta.eta_minutes > 0 && ( - Temps de livraison estimé : - ~ + Temps de livraison estimé : ~{eta.eta_minutes} min + + + )} + {order.status === "arrived" && ( + + + + Temps de livraison estimé : ~ {eta?.eta_minutes != null && eta.eta_minutes > 0 && eta.eta_minutes < 5