chore: update
This commit is contained in:
@@ -399,7 +399,8 @@ func (d *Database) GetClientByTelephone(telephone string) (*models.Client, error
|
|||||||
// GetClientByUsername récupère un client par son username
|
// GetClientByUsername récupère un client par son username
|
||||||
func (d *Database) GetClientByUsername(username string) (*models.Client, error) {
|
func (d *Database) GetClientByUsername(username string) (*models.Client, error) {
|
||||||
client := &models.Client{}
|
client := &models.Client{}
|
||||||
query := `SELECT id, username, password, nom, prenom, telephone, command, point, point_zipette, amende, must_change_password, created_at
|
var pointsExtraJSON []byte
|
||||||
|
query := `SELECT id, username, password, nom, prenom, telephone, command, point, point_zipette, amende, must_change_password, COALESCE(points_extra, '{}'::jsonb), created_at
|
||||||
FROM clients WHERE username = $1`
|
FROM clients WHERE username = $1`
|
||||||
|
|
||||||
err := d.QueryRow(query, username).Scan(
|
err := d.QueryRow(query, username).Scan(
|
||||||
@@ -414,6 +415,7 @@ func (d *Database) GetClientByUsername(username string) (*models.Client, error)
|
|||||||
&client.PointZipette,
|
&client.PointZipette,
|
||||||
&client.Amende,
|
&client.Amende,
|
||||||
&client.MustChangePassword,
|
&client.MustChangePassword,
|
||||||
|
&pointsExtraJSON,
|
||||||
&client.CreatedAt,
|
&client.CreatedAt,
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -424,6 +426,11 @@ func (d *Database) GetClientByUsername(username string) (*models.Client, error)
|
|||||||
return nil, fmt.Errorf("erreur lors de la récupération du client: %w", err)
|
return nil, fmt.Errorf("erreur lors de la récupération du client: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
client.PointsExtra = map[string]int{}
|
||||||
|
if len(pointsExtraJSON) > 0 {
|
||||||
|
json.Unmarshal(pointsExtraJSON, &client.PointsExtra)
|
||||||
|
}
|
||||||
|
|
||||||
return client, nil
|
return client, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -949,27 +956,38 @@ func (d *Database) CalculateAndAddPointsForCommandTx(tx *sql.Tx, commandID int,
|
|||||||
log.Printf("📊 [CalcPointsTx] Pool[%d] (%s): %.2f€", i, pools[i].Name, t)
|
log.Printf("📊 [CalcPointsTx] Pool[%d] (%s): %.2f€", i, pools[i].Name, t)
|
||||||
}
|
}
|
||||||
|
|
||||||
// ✅ ÉTAPE 2: Calculer les points par pool et mettre à jour les colonnes DB
|
// ✅ ÉTAPE 2: Calculer les points pour tous les pools
|
||||||
// Pool[0] → colonne `point`, Pool[1] → colonne `point_zipette`
|
|
||||||
var totalPoints int
|
var totalPoints int
|
||||||
var pointCategory string
|
var pointCategory string
|
||||||
var result sql.Result
|
|
||||||
|
|
||||||
pts0 := CalcPointsFromTiers(poolTotals[0], pools[0].Tiers)
|
poolPts := make([]int, len(pools))
|
||||||
pts1 := 0
|
var categoryParts []string
|
||||||
if len(pools) >= 2 {
|
for i, pool := range pools {
|
||||||
pts1 = CalcPointsFromTiers(poolTotals[1], pools[1].Tiers)
|
poolPts[i] = CalcPointsFromTiers(poolTotals[i], pool.Tiers)
|
||||||
|
totalPoints += poolPts[i]
|
||||||
|
if poolPts[i] > 0 {
|
||||||
|
categoryParts = append(categoryParts, pool.Name)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
totalPoints = pts0 + pts1
|
|
||||||
|
|
||||||
// Pools supplémentaires (index 2+) → points_extra JSONB
|
log.Printf("💰 [CalcPointsTx] points par pool: %v, total=%d", poolPts, totalPoints)
|
||||||
for i := 2; i < len(pools); i++ {
|
|
||||||
ptsExtra := CalcPointsFromTiers(poolTotals[i], pools[i].Tiers)
|
if totalPoints == 0 {
|
||||||
if ptsExtra == 0 {
|
return 0, "", nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(categoryParts) > 0 {
|
||||||
|
pointCategory = strings.Join(categoryParts, " & ")
|
||||||
|
} else {
|
||||||
|
pointCategory = "points"
|
||||||
|
}
|
||||||
|
|
||||||
|
// Écrire tous les pools dans points_extra[pool.Key] (stockage dynamique)
|
||||||
|
// + maintenir les colonnes legacy point/point_zipette pour la compatibilité admin
|
||||||
|
for i, pool := range pools {
|
||||||
|
if poolPts[i] == 0 {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
totalPoints += ptsExtra
|
|
||||||
poolKey := pools[i].Key
|
|
||||||
_, err = tx.Exec(`
|
_, err = tx.Exec(`
|
||||||
UPDATE clients
|
UPDATE clients
|
||||||
SET points_extra = jsonb_set(
|
SET points_extra = jsonb_set(
|
||||||
@@ -978,50 +996,30 @@ func (d *Database) CalculateAndAddPointsForCommandTx(tx *sql.Tx, commandID int,
|
|||||||
to_jsonb(COALESCE((points_extra->>$2)::int, 0) + $3)
|
to_jsonb(COALESCE((points_extra->>$2)::int, 0) + $3)
|
||||||
), updated_at = CURRENT_TIMESTAMP
|
), updated_at = CURRENT_TIMESTAMP
|
||||||
WHERE username = $1
|
WHERE username = $1
|
||||||
`, username, poolKey, ptsExtra)
|
`, username, pool.Key, poolPts[i])
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Printf("❌ [CalcPointsTx] Erreur UPDATE points_extra pool[%d]: %v", i, err)
|
log.Printf("❌ [CalcPointsTx] Erreur UPDATE points_extra pool[%d] (%s): %v", i, pool.Key, err)
|
||||||
return 0, "", fmt.Errorf("erreur mise à jour points pool[%d]: %w", 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[%d] (%s / key=%s): +%d pts", i, pool.Name, pool.Key, poolPts[i])
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Printf("💰 [CalcPointsTx] pool[0]=%d pts, pool[1]=%d pts, total=%d pts", pts0, pts1, totalPoints)
|
// Maintenir colonnes legacy pour affichage admin
|
||||||
|
pts0 := poolPts[0]
|
||||||
if totalPoints == 0 {
|
pts1 := 0
|
||||||
return 0, "", nil
|
if len(pools) >= 2 {
|
||||||
|
pts1 = poolPts[1]
|
||||||
}
|
}
|
||||||
|
var legacyErr error
|
||||||
var categoryParts []string
|
var result sql.Result
|
||||||
if pts0 > 0 {
|
if pts1 == 0 {
|
||||||
categoryParts = append(categoryParts, pools[0].Name)
|
result, legacyErr = tx.Exec(`UPDATE clients SET point = point + $1, updated_at = CURRENT_TIMESTAMP WHERE username = $2`, pts0, username)
|
||||||
}
|
|
||||||
if pts1 > 0 {
|
|
||||||
categoryParts = append(categoryParts, pools[1].Name)
|
|
||||||
}
|
|
||||||
if len(categoryParts) > 0 {
|
|
||||||
pointCategory = strings.Join(categoryParts, " & ")
|
|
||||||
} else {
|
} else {
|
||||||
pointCategory = "points"
|
result, legacyErr = tx.Exec(`UPDATE clients SET point = point + $1, point_zipette = point_zipette + $2, updated_at = CURRENT_TIMESTAMP WHERE username = $3`, pts0, pts1, username)
|
||||||
}
|
}
|
||||||
|
if legacyErr != nil {
|
||||||
if len(pools) == 1 || pts1 == 0 {
|
log.Printf("❌ [CalcPointsTx] Erreur UPDATE colonnes legacy: %v", legacyErr)
|
||||||
result, err = tx.Exec(`
|
return 0, "", fmt.Errorf("erreur mise à jour points: %w", legacyErr)
|
||||||
UPDATE clients
|
|
||||||
SET point = point + $1, updated_at = CURRENT_TIMESTAMP
|
|
||||||
WHERE username = $2
|
|
||||||
`, pts0, username)
|
|
||||||
} else {
|
|
||||||
result, err = tx.Exec(`
|
|
||||||
UPDATE clients
|
|
||||||
SET point = point + $1, point_zipette = point_zipette + $2, updated_at = CURRENT_TIMESTAMP
|
|
||||||
WHERE username = $3
|
|
||||||
`, pts0, pts1, username)
|
|
||||||
}
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
log.Printf("❌ [CalcPointsTx] Erreur UPDATE points: %v", err)
|
|
||||||
return 0, "", fmt.Errorf("erreur mise à jour points: %w", err)
|
|
||||||
}
|
}
|
||||||
affected, _ := result.RowsAffected()
|
affected, _ := result.RowsAffected()
|
||||||
if affected == 0 {
|
if affected == 0 {
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ package db
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"database/sql"
|
"database/sql"
|
||||||
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"log"
|
"log"
|
||||||
"os"
|
"os"
|
||||||
@@ -161,11 +162,42 @@ func InitDB() *Database {
|
|||||||
log.Fatalf("❌ Erreur migration commandes.referral_used: %v", err)
|
log.Fatalf("❌ Erreur migration commandes.referral_used: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Migration: points extra pour les pools de points supplémentaires (pool[2+])
|
// Migration: points extra pour tous les pools de points (stockage dynamique par clé)
|
||||||
if _, err = database.Exec(`ALTER TABLE clients ADD COLUMN IF NOT EXISTS points_extra JSONB NOT NULL DEFAULT '{}'::jsonb`); err != nil {
|
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)
|
log.Fatalf("❌ Erreur migration clients.points_extra: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Migration: copier point/point_zipette → points_extra[pool.Key] selon les clés admin
|
||||||
|
{
|
||||||
|
var poolsJSON string
|
||||||
|
_ = database.QueryRow(`SELECT value FROM app_settings WHERE key = 'points_pools'`).Scan(&poolsJSON)
|
||||||
|
if poolsJSON != "" {
|
||||||
|
var pools []struct {
|
||||||
|
Key string `json:"key"`
|
||||||
|
}
|
||||||
|
if jsonErr := json.Unmarshal([]byte(poolsJSON), &pools); jsonErr == nil {
|
||||||
|
if len(pools) > 0 && pools[0].Key != "" {
|
||||||
|
k0 := pools[0].Key
|
||||||
|
if _, mErr := database.Exec(`UPDATE clients SET points_extra = points_extra || jsonb_build_object($1::text, point) WHERE point > 0 AND NOT (points_extra ? $1)`, k0); mErr != nil {
|
||||||
|
log.Printf("⚠️ Migration points_extra pool[0] (%s): %v", k0, mErr)
|
||||||
|
} else {
|
||||||
|
log.Printf("✅ Migration points_extra pool[0] key=%s", k0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if len(pools) > 1 && pools[1].Key != "" {
|
||||||
|
k1 := pools[1].Key
|
||||||
|
if _, mErr := database.Exec(`UPDATE clients SET points_extra = points_extra || jsonb_build_object($1::text, point_zipette) WHERE point_zipette > 0 AND NOT (points_extra ? $1)`, k1); mErr != nil {
|
||||||
|
log.Printf("⚠️ Migration points_extra pool[1] (%s): %v", k1, mErr)
|
||||||
|
} else {
|
||||||
|
log.Printf("✅ Migration points_extra pool[1] key=%s", k1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
log.Printf("ℹ️ Migration points_extra: aucun pool configuré (app_settings vide)")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Lancer le nettoyage périodique des tokens expirés
|
// Lancer le nettoyage périodique des tokens expirés
|
||||||
go database.cleanExpiredTokensPeriodically()
|
go database.cleanExpiredTokensPeriodically()
|
||||||
|
|
||||||
|
|||||||
@@ -735,7 +735,25 @@ func GetClientCommandsHistory(c *gin.Context) {
|
|||||||
"count": len(commands),
|
"count": len(commands),
|
||||||
}
|
}
|
||||||
|
|
||||||
if err == nil && client != nil { // ✅ VÉRIFIE AUSSI que client != nil
|
if err == nil && client != nil {
|
||||||
|
// Récupérer les pools configurés par l'admin
|
||||||
|
poolNames := []string{}
|
||||||
|
poolKeys := []string{}
|
||||||
|
if settings, sErr := database.GetSettings(); sErr == nil && len(settings.PointsPools) > 0 {
|
||||||
|
for _, p := range settings.PointsPools {
|
||||||
|
poolNames = append(poolNames, p.Name)
|
||||||
|
poolKeys = append(poolKeys, p.Key)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Construire pool_points depuis points_extra[pool.Key] (stockage dynamique)
|
||||||
|
poolPoints := make([]int, len(poolKeys))
|
||||||
|
for i, key := range poolKeys {
|
||||||
|
if key != "" {
|
||||||
|
poolPoints[i] = client.PointsExtra[key]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
resp["client_stats"] = gin.H{
|
resp["client_stats"] = gin.H{
|
||||||
"username": client.Username,
|
"username": client.Username,
|
||||||
"nom": client.Nom,
|
"nom": client.Nom,
|
||||||
@@ -743,12 +761,13 @@ func GetClientCommandsHistory(c *gin.Context) {
|
|||||||
"telephone": client.Telephone,
|
"telephone": client.Telephone,
|
||||||
"total_commands": client.Command,
|
"total_commands": client.Command,
|
||||||
"points": client.Point,
|
"points": client.Point,
|
||||||
"points_zipette": client.PointZipette,
|
"pool_points": poolPoints,
|
||||||
|
"pool_names": poolNames,
|
||||||
"penalties": client.Amende,
|
"penalties": client.Amende,
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Printf("✅ [HISTORY] Stats client: point=%d, point_zipette=%d",
|
log.Printf("📊 [HISTORY] pool_names=%v pool_keys=%v pool_points=%v extra=%v",
|
||||||
client.Point, client.PointZipette)
|
poolNames, poolKeys, poolPoints, client.PointsExtra)
|
||||||
} else {
|
} else {
|
||||||
log.Printf("⚠️ [HISTORY] client_stats NON ajouté - err=%v, client=%v", err, client)
|
log.Printf("⚠️ [HISTORY] client_stats NON ajouté - err=%v, client=%v", err, client)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -50,12 +50,15 @@ func GetMyCompletedOrders(c *gin.Context) {
|
|||||||
// ✅ Récupérer les infos client pour statistiques
|
// ✅ Récupérer les infos client pour statistiques
|
||||||
client, err := database.GetClientByUsername(usernameStr)
|
client, err := database.GetClientByUsername(usernameStr)
|
||||||
|
|
||||||
// ✅ Récupérer les noms des pools de points
|
// ✅ Récupérer les noms et clés des pools de points
|
||||||
poolNames := []string{"Pool 1", "Pool 2"}
|
poolNames := []string{"Pool 1", "Pool 2"}
|
||||||
|
var poolKeys []string
|
||||||
if settings, sErr := database.GetSettings(); sErr == nil && len(settings.PointsPools) > 0 {
|
if settings, sErr := database.GetSettings(); sErr == nil && len(settings.PointsPools) > 0 {
|
||||||
poolNames = make([]string, len(settings.PointsPools))
|
poolNames = make([]string, len(settings.PointsPools))
|
||||||
|
poolKeys = make([]string, len(settings.PointsPools))
|
||||||
for i, p := range settings.PointsPools {
|
for i, p := range settings.PointsPools {
|
||||||
poolNames[i] = p.Name
|
poolNames[i] = p.Name
|
||||||
|
poolKeys[i] = p.Key
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -66,15 +69,17 @@ func GetMyCompletedOrders(c *gin.Context) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if err == nil && client != nil {
|
if err == nil && client != nil {
|
||||||
// Construire le tableau générique des valeurs par pool
|
// Construire le tableau depuis points_extra[poolKey] pour tous les pools (stockage dynamique)
|
||||||
poolPoints := make([]int, len(poolNames))
|
poolPoints := make([]int, len(poolKeys))
|
||||||
if len(poolPoints) > 0 {
|
for i, key := range poolKeys {
|
||||||
poolPoints[0] = client.Point
|
if key != "" {
|
||||||
}
|
poolPoints[i] = client.PointsExtra[key]
|
||||||
if len(poolPoints) > 1 {
|
}
|
||||||
poolPoints[1] = client.PointZipette
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
log.Printf("📊 [HISTORY] pool_names=%v pool_keys=%v pool_points=%v extra=%v",
|
||||||
|
poolNames, poolKeys, poolPoints, client.PointsExtra)
|
||||||
|
|
||||||
response["client_stats"] = gin.H{
|
response["client_stats"] = gin.H{
|
||||||
"username": client.Username,
|
"username": client.Username,
|
||||||
"total_commands": client.Command,
|
"total_commands": client.Command,
|
||||||
|
|||||||
@@ -814,29 +814,21 @@ func UploadMedia(c *gin.Context) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// ✅ VÉRIFIER LE TYPE MIME RÉEL
|
// ✅ VÉRIFIER LE TYPE MIME RÉEL
|
||||||
fileHeader, err := file.Open()
|
detectedMime, err := validateFileMimeType(file)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
c.JSON(http.StatusInternalServerError, gin.H{"error": "Erreur lecture fichier"})
|
log.Printf("❌ [UploadMedia] Type MIME invalide: %v", err)
|
||||||
return
|
c.JSON(http.StatusBadRequest, gin.H{"error": "Type de fichier non autorisé"})
|
||||||
}
|
|
||||||
defer fileHeader.Close()
|
|
||||||
|
|
||||||
buffer := make([]byte, 512)
|
|
||||||
_, err = fileHeader.Read(buffer)
|
|
||||||
if err != nil {
|
|
||||||
c.JSON(http.StatusInternalServerError, gin.H{"error": "Erreur lecture fichier"})
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
mimeType := http.DetectContentType(buffer)
|
log.Printf("📋 [UploadMedia] Type MIME détecté: %s", detectedMime)
|
||||||
log.Printf("📋 [UploadMedia] Type MIME détecté: %s", mimeType)
|
|
||||||
|
|
||||||
// Vérifier que le MIME correspond au type déclaré
|
// Vérifier que le MIME correspond au type déclaré
|
||||||
if fileType == "image" && !strings.HasPrefix(mimeType, "image/") {
|
if fileType == "image" && !strings.HasPrefix(detectedMime, "image/") {
|
||||||
c.JSON(http.StatusBadRequest, gin.H{"error": "Le fichier n'est pas une image valide"})
|
c.JSON(http.StatusBadRequest, gin.H{"error": "Le fichier n'est pas une image valide"})
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if fileType == "video" && !strings.HasPrefix(mimeType, "video/") {
|
if fileType == "video" && !strings.HasPrefix(detectedMime, "video/") {
|
||||||
c.JSON(http.StatusBadRequest, gin.H{"error": "Le fichier n'est pas une vidéo valide"})
|
c.JSON(http.StatusBadRequest, gin.H{"error": "Le fichier n'est pas une vidéo valide"})
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -37,7 +37,7 @@ server {
|
|||||||
ssl_protocols TLSv1.2 TLSv1.3;
|
ssl_protocols TLSv1.2 TLSv1.3;
|
||||||
ssl_ciphers HIGH:!aNULL:!MD5;
|
ssl_ciphers HIGH:!aNULL:!MD5;
|
||||||
|
|
||||||
client_max_body_size 10M;
|
client_max_body_size 100M;
|
||||||
|
|
||||||
modsecurity on;
|
modsecurity on;
|
||||||
modsecurity_rules_file /etc/nginx/modsec/custom-rules.conf;
|
modsecurity_rules_file /etc/nginx/modsec/custom-rules.conf;
|
||||||
@@ -67,8 +67,8 @@ server {
|
|||||||
proxy_set_header Connection "";
|
proxy_set_header Connection "";
|
||||||
|
|
||||||
proxy_connect_timeout 60s;
|
proxy_connect_timeout 60s;
|
||||||
proxy_send_timeout 60s;
|
proxy_send_timeout 300s;
|
||||||
proxy_read_timeout 60s;
|
proxy_read_timeout 300s;
|
||||||
}
|
}
|
||||||
|
|
||||||
add_header X-Frame-Options "SAMEORIGIN" always;
|
add_header X-Frame-Options "SAMEORIGIN" always;
|
||||||
|
|||||||
@@ -352,6 +352,7 @@ export default function ProductsScreen() {
|
|||||||
// 3) Upload new media pour les produits existants (mode edit)
|
// 3) Upload new media pour les produits existants (mode edit)
|
||||||
if (editingProduct && pendingMedia.length > 0) {
|
if (editingProduct && pendingMedia.length > 0) {
|
||||||
setUploadingMedia(true);
|
setUploadingMedia(true);
|
||||||
|
const uploadErrors: string[] = [];
|
||||||
for (const m of pendingMedia) {
|
for (const m of pendingMedia) {
|
||||||
try {
|
try {
|
||||||
await uploadProductMediaAdmin(
|
await uploadProductMediaAdmin(
|
||||||
@@ -361,11 +362,17 @@ export default function ProductsScreen() {
|
|||||||
m.mimeType,
|
m.mimeType,
|
||||||
m.mediaType,
|
m.mediaType,
|
||||||
);
|
);
|
||||||
} catch {
|
} catch (uploadErr: any) {
|
||||||
/* ignore individual failures */
|
const msg = uploadErr?.response?.data?.error || uploadErr?.message || `Erreur upload ${m.mediaType}`;
|
||||||
|
uploadErrors.push(msg);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
setUploadingMedia(false);
|
setUploadingMedia(false);
|
||||||
|
if (uploadErrors.length > 0) {
|
||||||
|
setFormError(`Erreur upload média: ${uploadErrors.join(", ")}`);
|
||||||
|
await loadData();
|
||||||
|
return;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
showSuccess(
|
showSuccess(
|
||||||
|
|||||||
@@ -792,15 +792,9 @@ export default function UsersScreen() {
|
|||||||
<Text style={styles.statLabel}>Cmd</Text>
|
<Text style={styles.statLabel}>Cmd</Text>
|
||||||
</View>
|
</View>
|
||||||
{pointsEnabled && poolNames.map((name, i) => {
|
{pointsEnabled && poolNames.map((name, i) => {
|
||||||
let value: number;
|
const key = poolKeys[i] ?? "";
|
||||||
let color: string;
|
const value = key ? (item.clientData!.points_extra?.[key] ?? 0) : 0;
|
||||||
if (i === 0) { value = item.clientData!.point; color = colors.success; }
|
const color = i === 0 ? colors.success : i === 1 ? colors.info : colors.warning;
|
||||||
else if (i === 1) { value = item.clientData!.points_zipette; color = colors.info; }
|
|
||||||
else {
|
|
||||||
const key = poolKeys[i] ?? "";
|
|
||||||
value = item.clientData!.points_extra?.[key] ?? 0;
|
|
||||||
color = colors.warning;
|
|
||||||
}
|
|
||||||
return (
|
return (
|
||||||
<View key={i} style={styles.stat}>
|
<View key={i} style={styles.stat}>
|
||||||
<Text style={[styles.statValue, { color }]}>{value}</Text>
|
<Text style={[styles.statValue, { color }]}>{value}</Text>
|
||||||
|
|||||||
@@ -137,15 +137,9 @@ export default function UsersScreen() {
|
|||||||
</Text>
|
</Text>
|
||||||
<View style={styles.statsRow}>
|
<View style={styles.statsRow}>
|
||||||
{appSettings.points_enabled && appSettings.pool_names.map((name, i) => {
|
{appSettings.points_enabled && appSettings.pool_names.map((name, i) => {
|
||||||
let value: number;
|
const key = appSettings.pool_keys[i] ?? "";
|
||||||
let color: string;
|
const value = key ? (item.points_extra?.[key] ?? 0) : 0;
|
||||||
if (i === 0) { value = item.point; color = colors.success; }
|
const color = i === 0 ? colors.success : i === 1 ? colors.info : colors.warning;
|
||||||
else if (i === 1) { value = item.points_zipette; color = colors.info; }
|
|
||||||
else {
|
|
||||||
const key = appSettings.pool_keys[i] ?? "";
|
|
||||||
value = item.points_extra?.[key] ?? 0;
|
|
||||||
color = colors.warning;
|
|
||||||
}
|
|
||||||
return (
|
return (
|
||||||
<View key={i} style={styles.stat}>
|
<View key={i} style={styles.stat}>
|
||||||
<Text style={[styles.statValue, { color }]}>{value}</Text>
|
<Text style={[styles.statValue, { color }]}>{value}</Text>
|
||||||
|
|||||||
@@ -28,6 +28,11 @@ export async function getExpoPushToken(): Promise<string | null> {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
const tokenData = await Notifications.getExpoPushTokenAsync();
|
try {
|
||||||
return tokenData.data;
|
const tokenData = await Notifications.getExpoPushTokenAsync();
|
||||||
|
return tokenData.data;
|
||||||
|
} catch {
|
||||||
|
// Google Play Services absent (ex: GrapheneOS) — push non disponible
|
||||||
|
return null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user