From 5b3c30143d9eee89a1c5cd60615acceb89eec28b Mon Sep 17 00:00:00 2001 From: Xor290 Date: Tue, 25 Aug 2026 15:15:45 +0200 Subject: [PATCH] chore: build --- omnex-plateform-app/src/api/api_admin.ts | 20 ++-- omnex-plateform-app/src/api/api_cabine.ts | 12 +- omnex-plateform-app/src/api/api_delivery.ts | 3 +- .../src/screens/admin/SettingsScreen.tsx | 103 +++++++++--------- .../src/screens/delivery/DashboardScreen.tsx | 8 +- omnex-plateform-client/src/api/api.ts | 19 ++-- omnex-plateform-client/src/api/api_types.ts | 2 +- .../src/screens/client/OrderHistoryScreen.tsx | 49 +++++++-- .../src/screens/client/ProductsScreen.tsx | 2 + 9 files changed, 124 insertions(+), 94 deletions(-) diff --git a/omnex-plateform-app/src/api/api_admin.ts b/omnex-plateform-app/src/api/api_admin.ts index 2cf5b8d..c442501 100644 --- a/omnex-plateform-app/src/api/api_admin.ts +++ b/omnex-plateform-app/src/api/api_admin.ts @@ -1,4 +1,5 @@ import apiClient from "./client"; +import { API_BASE_URL } from "./client"; import type { AuthResponse, ClientResponse, @@ -9,9 +10,9 @@ import type { Alert, } from "./types"; -const V2 = "/api/v2"; -const CABINE_URL = "/api/v1/cabine"; -const V1_PUBLIC = "/api/v1"; +const V2 = `${API_BASE_URL}/api/v2`; +const CABINE_URL = `${API_BASE_URL}/api/v1/cabine`; +const V1_PUBLIC = `${API_BASE_URL}/api/v1`; export const loginAdmin = async ( username: string, @@ -172,10 +173,12 @@ export const resetAdminStats = async ( ); return data; }; - +// ============================================ +// STATISTIQUES MENSUELLES (jour par jour) +// ============================================ export interface MonthlyDayStat { - day: string; - label: string; + day: string; // "2026-06-05" + label: string; // "05/06" count: number; revenue: number; quantity: number; @@ -186,7 +189,7 @@ export interface MonthlyStatsSummary { total_quantity: number; } export interface MonthlyStats { - month: string; + month: string; // "2026-06" summary: MonthlyStatsSummary; by_day: MonthlyDayStat[]; } @@ -798,6 +801,7 @@ export const getClientCancelledOrders = async ( } }; +// ============================================ // AMENDES & POINTS CLIENT (Admin) // ============================================ @@ -1105,6 +1109,7 @@ export interface PointsTier { export interface RewardCategoryConfig { category: string; + type: "free_product" | "half_price_product"; all_products: boolean; product_ids: number[]; } @@ -1117,7 +1122,6 @@ export interface RewardItem { export interface PointsReward { threshold: number; - type: "free_product" | "half_price_product" | "custom"; description: string; category_configs: RewardCategoryConfig[]; reward_items: RewardItem[]; diff --git a/omnex-plateform-app/src/api/api_cabine.ts b/omnex-plateform-app/src/api/api_cabine.ts index b198dde..6905af6 100644 --- a/omnex-plateform-app/src/api/api_cabine.ts +++ b/omnex-plateform-app/src/api/api_cabine.ts @@ -1,4 +1,5 @@ import apiClient from "./client"; +import { API_BASE_URL } from "./client"; //@ts import type { @@ -8,8 +9,8 @@ import type { Alert, } from "./types"; -const API = "/api/v1/cabine"; -const V2 = "/api/v2"; +const API = `${API_BASE_URL}/api/v1/cabine`; +const V2 = `${API_BASE_URL}/api/v2`; export const getCommandItems = async (commandId: number) => { const { data } = await apiClient.get(`${API}/commands/${commandId}/items`); @@ -163,7 +164,6 @@ export const getDeliverymanLocationForCommand = async (commandId: number) => { // ============================================ // LIVREURS -// ============================================ const parseStatus = (status: any): "available" | "busy" | "offline" => { if (!status) return "offline"; @@ -365,7 +365,9 @@ export const markCabineNotificationsRead = async (): Promise => { export const getPublicSettings = async (): Promise => { try { - const { data } = await apiClient.get("/api/v1/app-settings"); + const { data } = await apiClient.get( + `${API_BASE_URL}/api/v1/app-settings`, + ); return { penalties_enabled: data.penalties_enabled ?? true, show_amende_score: data.show_amende_score ?? true, @@ -458,7 +460,7 @@ export const getCabineAllClients = async (): Promise => { // TELEGRAM — CABINE // ============================================ -const CABINE_API = "/api/v1/cabine"; +const CABINE_API = `${API_BASE_URL}/api/v1/cabine`; export const getCabineTelegramStatus = async (): Promise<{ linked: boolean; diff --git a/omnex-plateform-app/src/api/api_delivery.ts b/omnex-plateform-app/src/api/api_delivery.ts index de91cfe..30aac8c 100644 --- a/omnex-plateform-app/src/api/api_delivery.ts +++ b/omnex-plateform-app/src/api/api_delivery.ts @@ -1,4 +1,5 @@ import apiClient from "./client"; +import { API_BASE_URL } from "./client"; import type { DeliveryStatus, QueueInfo, @@ -7,7 +8,7 @@ import type { Alert, } from "./types"; -const API = "/api/v1/livreur"; +const API = `${API_BASE_URL}/api/v1/livreur`; export const getMyStatus = async (): Promise<{ success: boolean; diff --git a/omnex-plateform-app/src/screens/admin/SettingsScreen.tsx b/omnex-plateform-app/src/screens/admin/SettingsScreen.tsx index bca1526..e58d03a 100644 --- a/omnex-plateform-app/src/screens/admin/SettingsScreen.tsx +++ b/omnex-plateform-app/src/screens/admin/SettingsScreen.tsx @@ -686,14 +686,13 @@ function TiersSection({ const REWARD_ACCENT = "#f59e0b"; -const REWARD_TYPES: { value: PointsReward["type"]; label: string; icon: string }[] = [ +const REWARD_TYPES: { value: RewardCategoryConfig["type"]; label: string; icon: string }[] = [ { value: "free_product", label: "Produit offert", icon: "gift-outline" }, { value: "half_price_product", label: "Produit à -50%", icon: "pricetag-outline" }, ]; const EMPTY_REWARD: PointsReward = { threshold: 20, - type: "free_product", description: "", category_configs: [], reward_items: [], @@ -822,7 +821,7 @@ function CentralRewardSection({ const getCatConfig = (catName: string): RewardCategoryConfig => r.category_configs.find((c) => c.category === catName) ?? - { category: catName, all_products: true, product_ids: [] }; + { category: catName, type: "free_product", all_products: true, product_ids: [] }; const isCatSelected = (catName: string) => r.category_configs.some((c) => c.category === catName); @@ -831,7 +830,7 @@ function CentralRewardSection({ if (isCatSelected(catName)) { update({ category_configs: r.category_configs.filter((c) => c.category !== catName) }); } else { - update({ category_configs: [...r.category_configs, { category: catName, all_products: true, product_ids: [] }] }); + update({ category_configs: [...r.category_configs, { category: catName, type: "free_product", all_products: true, product_ids: [] }] }); } }; @@ -894,34 +893,6 @@ function CentralRewardSection({ - {/* Type */} - - Type de récompense - - {REWARD_TYPES.map((rt) => { - const sel = r.type === rt.value; - return ( - update({ type: rt.value })} - style={{ - flexDirection: "row", alignItems: "center", gap: spacing.xs, - paddingHorizontal: spacing.m, paddingVertical: spacing.s, - borderRadius: borderRadius.sm, borderWidth: 1.5, - borderColor: sel ? REWARD_ACCENT : colors.border, - backgroundColor: sel ? REWARD_ACCENT + "20" : "transparent", - }} - > - - - {rt.label} - - - ); - })} - - - {/* Description */} Description affichée au client @@ -935,12 +906,12 @@ function CentralRewardSection({ /> - {/* Produits récompense — uniquement pour le type "Produit offert" */} - {r.type === "free_product" && ( + {/* Produits récompense — proposés au client selon le type choisi + pour la catégorie de chaque produit (voir "Catégories éligibles" ci-dessous) */} Produits ajoutés au panier - Quand le client réclame sa récompense, ces produits sont automatiquement ajoutés à son panier (gratuits). Il doit commander au moins un produit normal. + Quand le client réclame sa récompense, ces produits sont automatiquement ajoutés à son panier — gratuits ou à -50% selon le type configuré pour la catégorie du produit. Il doit commander au moins un produit normal. {r.reward_items.map((item, idx) => { @@ -1062,14 +1033,14 @@ function CentralRewardSection({ )} - )} - {/* Catégories éligibles — uniquement pour le type "Produit à -50%" */} - {r.type === "half_price_product" && ( + {/* Catégories éligibles — chaque catégorie choisit son propre type + (produit offert ou -50%), qui s'applique aux produits récompense + de cette catégorie configurés ci-dessus */} - Catégories éligibles à -50% + Catégories éligibles - Sélectionnez les catégories, puis pour chacune choisissez tous les produits ou une sélection. + Sélectionnez les catégories, choisissez le type de récompense pour chacune, puis tous les produits ou une sélection. {allCategories.length === 0 ? ( Aucune catégorie disponible @@ -1103,15 +1074,41 @@ function CentralRewardSection({ /> - {/* Sélecteur produits (visible si catégorie sélectionnée) */} + {/* Type + sélecteur produits (visible si catégorie sélectionnée) */} {selected && ( - + + + {REWARD_TYPES.map((rt) => { + const cfg = getCatConfig(cat.name); + const sel = (cfg.type || "free_product") === rt.value; + return ( + updateCatConfig({ ...cfg, type: rt.value })} + style={{ + flexDirection: "row", alignItems: "center", gap: 4, + paddingHorizontal: spacing.s, paddingVertical: 4, + borderRadius: borderRadius.sm, borderWidth: 1.5, + borderColor: sel ? REWARD_ACCENT : colors.border, + backgroundColor: sel ? REWARD_ACCENT + "22" : "transparent", + }} + > + + + {rt.label} + + + ); + })} + + + )} ); @@ -1119,25 +1116,23 @@ function CentralRewardSection({ )} - )} {/* Récapitulatif */} {(r.category_configs.length > 0 || r.reward_items.filter((it) => it.product_id > 0).length > 0) && ( Récapitulatif - Dès {r.threshold} pts par type →{" "} - {REWARD_TYPES.find((x) => x.value === r.type)?.label} + Dès {r.threshold} pts par type de points → récompense débloquée {r.description !== "" && ( "{r.description}" )} - {r.type === "half_price_product" && r.category_configs.map((cfg) => ( + {r.category_configs.map((cfg) => ( - • Éligible à -50% : {cfg.category} — {cfg.all_products ? "tous les produits" : `${cfg.product_ids.length} produit(s)`} + • {REWARD_TYPES.find((x) => x.value === (cfg.type || "free_product"))?.label} : {cfg.category} — {cfg.all_products ? "tous les produits" : `${cfg.product_ids.length} produit(s)`} ))} - {r.type === "free_product" && r.reward_items.filter((it) => it.product_id > 0).map((it, idx) => { + {r.reward_items.filter((it) => it.product_id > 0).map((it, idx) => { const prod = Object.values(productsByCategory).flat().find((p) => p.id === it.product_id); return ( diff --git a/omnex-plateform-app/src/screens/delivery/DashboardScreen.tsx b/omnex-plateform-app/src/screens/delivery/DashboardScreen.tsx index 734ad1a..626da74 100644 --- a/omnex-plateform-app/src/screens/delivery/DashboardScreen.tsx +++ b/omnex-plateform-app/src/screens/delivery/DashboardScreen.tsx @@ -715,7 +715,7 @@ export default function DashboardScreen() { {prod.is_reward && ( - Offert + Récompense )} @@ -724,7 +724,7 @@ export default function DashboardScreen() { - {prod.is_reward ? "Offert" : `${(prod.prix ?? 0).toFixed(2)}€`} + {prod.is_reward && (prod.prix ?? 0) === 0 ? "Offert" : `${(prod.prix ?? 0).toFixed(2)}€`} ))} @@ -2018,7 +2018,7 @@ export default function DashboardScreen() { {prod.is_reward && ( - Offert + Récompense )} @@ -2027,7 +2027,7 @@ export default function DashboardScreen() { - {prod.is_reward ? "Offert" : `${(prod.prix ?? 0).toFixed(2)}€`} + {prod.is_reward && (prod.prix ?? 0) === 0 ? "Offert" : `${(prod.prix ?? 0).toFixed(2)}€`} ))} diff --git a/omnex-plateform-client/src/api/api.ts b/omnex-plateform-client/src/api/api.ts index 20a2af6..58afaa1 100644 --- a/omnex-plateform-client/src/api/api.ts +++ b/omnex-plateform-client/src/api/api.ts @@ -1,4 +1,5 @@ import apiClient from "./client"; +import { API_BASE_URL } from "./client"; import type { ConfirmReceptionResponse, CheckoutCartResponse, @@ -12,7 +13,7 @@ import type { import { getToken } from "../auth/tokenStorage"; import { extractUsernameFromToken } from "../auth/jwtUtils"; -const V1 = "/api/v1"; +const V1 = `${API_BASE_URL}/api/v1`; export const getJwtUsername = async (): Promise => { const token = await getToken(); @@ -359,19 +360,18 @@ export const checkoutCart = async ( price_currency: data.price_currency, }; } catch (error: any) { - const errMsg: string = error.response?.data?.error || "Erreur serveur"; - if (errMsg.startsWith("Adresse invalide ")) { - const suggested = errMsg.replace("Adresse invalide ", "").trim(); + const data = error.response?.data; + if (data?.corrected_address) { return { success: false, invalid_address: true, - suggested_address: suggested, - message: errMsg, + suggested_address: data.corrected_address, + message: data.error || "Adresse non reconnue", }; } return { success: false, - message: errMsg, + message: data?.error || "Erreur serveur", }; } }; @@ -529,7 +529,6 @@ export const getOrdersWithTracking = async () => { // ============================================ // HISTORY -// ============================================ export const getMyCompletedOrders = async (): Promise => { try { @@ -661,6 +660,7 @@ export const updateMyProfile = async (fields: { } }; +// ============================================ // ORDER DETAILS // ============================================ @@ -963,6 +963,7 @@ export const toggle2FA = async ( export type RewardCategoryConfig = { category: string; + type: "free_product" | "half_price_product"; all_products: boolean; product_ids: number[]; product_names: string[]; @@ -974,6 +975,7 @@ export type RewardItemConfig = { product_name: string; quantity: number; price: number; + type: "free_product" | "half_price_product"; }; export type PointsPoolInfo = { @@ -989,7 +991,6 @@ export type PointsPoolInfo = { export type PointsRewardConfig = { threshold: number; - type: string; description: string; reward_items: RewardItemConfig[]; }; diff --git a/omnex-plateform-client/src/api/api_types.ts b/omnex-plateform-client/src/api/api_types.ts index 75408ee..b66244c 100644 --- a/omnex-plateform-client/src/api/api_types.ts +++ b/omnex-plateform-client/src/api/api_types.ts @@ -10,7 +10,7 @@ export interface ApiResponse { token_type?: string; expires_in?: number; user?: UserResponse; - [key: string]: any; + [key: string]: any; // Pour les champs additionnels } /** diff --git a/omnex-plateform-client/src/screens/client/OrderHistoryScreen.tsx b/omnex-plateform-client/src/screens/client/OrderHistoryScreen.tsx index a163ccf..f1dc37a 100644 --- a/omnex-plateform-client/src/screens/client/OrderHistoryScreen.tsx +++ b/omnex-plateform-client/src/screens/client/OrderHistoryScreen.tsx @@ -146,7 +146,7 @@ export default function OrderHistoryScreen() { if (res.success) { const text = res.product_added && res.product_name - ? `🎁 ${res.product_name} ajouté à votre panier ! Commandez au moins un produit pour en profiter.` + ? `${res.product_name} ajouté à votre panier ! Commandez au moins un produit pour en profiter.` : res.description || "Récompense réclamée !"; setClaimFeedback({ pool: poolKey, type: "success", text }); getMyPointsRewards().then((r) => { @@ -882,16 +882,34 @@ export default function OrderHistoryScreen() { : `Encore ${remaining} pts pour une récompense`} {feedback && ( - - {feedback.text} - + {feedback.type === + "success" && ( + + )} + + {feedback.text} + + )} {pool.rewards_available > 0 && ( )} - {item.price > 0 && ( + {item.type === + "half_price_product" ? ( - {item.price}€ + -50% · {item.price}€ + + ) : ( + + Offert )}