From d9ad4cb2cfb132a59250b1843dd964fd9cd3a200 Mon Sep 17 00:00:00 2001 From: Xor290 Date: Sat, 27 Jun 2026 23:54:42 +0200 Subject: [PATCH] chore: build --- frontend-prep/src/api/api.ts | 16 +- .../src/pages/User/ConsultationHistorique.css | 695 +++++------------- .../src/pages/User/ConsultationHistorique.tsx | 457 +++++++++--- 3 files changed, 534 insertions(+), 634 deletions(-) diff --git a/frontend-prep/src/api/api.ts b/frontend-prep/src/api/api.ts index f1a19bb6..bf2165ea 100644 --- a/frontend-prep/src/api/api.ts +++ b/frontend-prep/src/api/api.ts @@ -1920,8 +1920,10 @@ export const getPublicSettings = async (): Promise => { client_color_success: data.client_color_success || "#22c55e", client_color_danger: data.client_color_danger || "#ef4444", client_color_warning: data.client_color_warning || "#f59e0b", - client_title_gradient_from: data.client_title_gradient_from || "#a78bfa", - client_title_gradient_to: data.client_title_gradient_to || "#22d3ee", + client_title_gradient_from: + data.client_title_gradient_from || "#a78bfa", + client_title_gradient_to: + data.client_title_gradient_to || "#22d3ee", }; } catch { return defaults; @@ -2266,12 +2268,13 @@ export const getOrderRatingStatus = async ( export const claimMyReward = async ( poolKey: string, + productId?: number, ): Promise<{ success: boolean; description?: string; remaining_rewards?: number; product_added?: boolean; - product_name?: string; + product_names?: string[]; error?: string; }> => { const token = getAuthToken(); @@ -2283,7 +2286,10 @@ export const claimMyReward = async ( "Content-Type": "application/json", Authorization: `Bearer ${token}`, }, - body: JSON.stringify({ pool_key: poolKey }), + body: JSON.stringify({ + pool_key: poolKey, + product_id: productId ?? 0, + }), }); const data = await safeJson(response); if (!response.ok) @@ -2293,7 +2299,7 @@ export const claimMyReward = async ( description: data.description, remaining_rewards: data.remaining_rewards, product_added: data.product_added, - product_name: data.product_name, + product_names: data.product_names, }; } catch { return { success: false, error: "Erreur de connexion" }; diff --git a/frontend-prep/src/pages/User/ConsultationHistorique.css b/frontend-prep/src/pages/User/ConsultationHistorique.css index 8cc74d79..141fc8c7 100644 --- a/frontend-prep/src/pages/User/ConsultationHistorique.css +++ b/frontend-prep/src/pages/User/ConsultationHistorique.css @@ -1,579 +1,212 @@ /* ============================================ - ConsultationHistorique.css — style mobile + MODAL — CHOIX DU PRODUIT RÉCOMPENSE ============================================ */ -.history-container { - width: 100%; - min-height: 100vh; - padding: clamp(1rem, 3vw, 2rem); - padding-top: calc(60px + clamp(1rem, 3vw, 1.5rem)); - max-width: 900px; - margin: 0 auto; -} - -/* ============================================ - TITRE PAGE - ============================================ */ - -.history-title { - font-size: 1.5rem; - font-weight: 700; - color: var(--text); - margin: 0 0 1.25rem 0; - margin-top: -70px; -} - -/* ============================================ - STATS GRID — cartes centrées (style mobile) - ============================================ */ - -.stats-grid { - display: grid; - grid-template-columns: repeat(2, 1fr); - gap: 0.875rem; - margin-bottom: 1rem; -} - -.stat-card2:last-child:nth-child(odd) { - grid-column: 1 / -1; -} - -.stat-card2 { - background: var(--surface); - border: 1px solid var(--border); - border-radius: 12px; - padding: 1.25rem 1rem; - display: flex; - flex-direction: column; - align-items: center; - text-align: center; - gap: 0.35rem; - transition: - transform 0.2s, - border-color 0.2s, - box-shadow 0.2s; - cursor: default; -} - -.stat-card2:hover { - transform: translateY(-2px); - border-color: #7c3aed; - box-shadow: 0 6px 20px rgba(124, 58, 237, 0.25); -} - -.stat-card2.stat-card-danger { - border-color: rgba(239, 68, 68, 0.4); - background: rgba(239, 68, 68, 0.06); -} - -.stat-card2.stat-card-warning { - border-color: rgba(245, 158, 11, 0.4); - background: rgba(245, 158, 11, 0.06); -} - -.stat-icon { - width: 44px; - height: 44px; - border-radius: 10px; +.reward-modal-overlay { + position: fixed; + inset: 0; + background: rgba(0, 0, 0, 0.65); + backdrop-filter: blur(6px); display: flex; align-items: center; justify-content: center; - font-size: 1.1rem; - color: #fff; - margin-bottom: 0.25rem; + padding: 1rem; + z-index: 1000; + animation: fadeIn 0.2s ease; } -.stat-icon.icon-total-orders { - background: linear-gradient(135deg, #7c3aed, #6d28d9); -} -.stat-icon.icon-total { - background: linear-gradient(135deg, #f59e0b, #d97706); -} -.stat-icon.icon-penalty-ok { - background: rgba(100, 116, 139, 0.3); - color: var(--text-muted); -} -.stat-icon.icon-penalty-warning { - background: linear-gradient(135deg, #f59e0b, #d97706); -} -.stat-icon.icon-penalty-critical { - background: linear-gradient(135deg, #ef4444, #dc2626); - animation: pulse-danger 2s ease-in-out infinite; -} - -@keyframes pulse-danger { - 0%, - 100% { - box-shadow: 0 0 0 0 rgba(239, 68, 68, 0.5); - } - 50% { - box-shadow: 0 0 0 8px rgba(239, 68, 68, 0); - } -} - -.stat-value { - color: var(--text); - font-size: 1.75rem; - font-weight: 700; - margin: 0; - line-height: 1; -} - -.stat-value.value-danger { - color: #ef4444; -} -.stat-value.value-warning { - color: #f59e0b; -} - -.stat-label { - color: var(--text-muted); - font-size: 0.78rem; - margin: 0; - font-weight: 500; -} - -/* ============================================ - BOUTON PARRAINAGE - ============================================ */ - -.referral-btn-row { - display: flex; - align-items: center; - gap: 0.75rem; - background: var(--surface); - border: 1px solid rgba(124, 58, 237, 0.3); - border-radius: 10px; - padding: 0.875rem 1rem; - margin-bottom: 1.25rem; - cursor: pointer; - transition: - background 0.2s, - border-color 0.2s; -} - -.referral-btn-row:hover { - background: rgba(124, 58, 237, 0.08); - border-color: rgba(124, 58, 237, 0.6); -} - -.referral-btn-icon { - color: #7c3aed; - font-size: 1rem; - flex-shrink: 0; -} - -.referral-btn-text { - flex: 1; - color: #a78bfa; - font-size: 0.95rem; - font-weight: 600; -} - -.referral-btn-chevron { - color: var(--text-muted); - font-size: 0.85rem; -} - -/* ============================================ - RÉCOMPENSES PAR PALIER - ============================================ */ - -.rewards-section { +.reward-modal { background: var(--surface); border: 1px solid rgba(245, 158, 11, 0.3); - border-radius: 12px; - padding: 1rem; - margin-bottom: 1.25rem; -} - -.rewards-section-title { - display: flex; - align-items: center; - gap: 0.5rem; - color: #f59e0b; - font-size: 0.78rem; - font-weight: 700; - text-transform: uppercase; - letter-spacing: 0.8px; - margin: 0 0 0.4rem 0; -} - -.rewards-section-icon { - font-size: 0.85rem; -} - -.rewards-section-desc { - color: var(--text-muted); - font-size: 0.85rem; - font-style: italic; - margin: 0 0 0.875rem 0; -} - -.rewards-pools { - display: flex; - flex-direction: column; - gap: 0.75rem; -} - -.reward-pool-card { - background: rgba(245, 158, 11, 0.07); - border: 1px solid rgba(245, 158, 11, 0.2); - border-radius: 8px; - padding: 0.75rem; -} - -.reward-pool-header { - display: flex; - justify-content: space-between; - align-items: center; - margin-bottom: 0.5rem; -} - -.reward-pool-name { - font-size: 0.9rem; - font-weight: 600; - color: var(--text-primary); -} - -.reward-pool-pts { - font-size: 0.85rem; - font-weight: 700; - color: #f59e0b; -} - -.reward-eligible-cats { - display: flex; - flex-wrap: wrap; - gap: 0.35rem; - margin-bottom: 0.5rem; -} - -.reward-eligible-cat { - font-size: 0.78rem; - font-weight: 600; - color: #10b981; - background: rgba(16, 185, 129, 0.12); - border-radius: 4px; - padding: 2px 7px; -} - -.reward-progress-bar { - height: 6px; - background: rgba(245, 158, 11, 0.15); - border-radius: 3px; - overflow: hidden; - margin-bottom: 0.4rem; -} - -.reward-progress-fill { - height: 100%; - background: linear-gradient(90deg, #f59e0b, #fbbf24); - border-radius: 3px; - transition: width 0.4s ease; -} - -.reward-pool-info { - font-size: 0.8rem; - margin-bottom: 0.5rem; -} - -.reward-available { - color: #10b981; - font-weight: 600; -} - -.reward-remaining { - color: var(--text-muted); -} - -.reward-feedback { - font-size: 0.82rem; - font-style: italic; - margin: 0.25rem 0 0.4rem; -} - -.reward-feedback-success { - color: #10b981; -} - -.reward-feedback-error { - color: #ef4444; -} - -.reward-claim-btn { + border-radius: 16px; + padding: 1.5rem; + max-width: 420px; width: 100%; - background: linear-gradient(135deg, #f59e0b, #d97706); - color: #fff; - border: none; - border-radius: 6px; - padding: 0.55rem 1rem; - font-size: 0.88rem; - font-weight: 700; - cursor: pointer; - transition: opacity 0.2s; + max-height: 85vh; + overflow-y: auto; + animation: slideUp 0.25s ease; } -.reward-claim-btn:hover:not(:disabled) { - opacity: 0.88; -} - -.reward-claim-btn:disabled { - opacity: 0.5; - cursor: default; -} - -/* ============================================ - SECTION TITLE - ============================================ */ - -.section-title { - color: var(--text-muted); - font-size: 0.78rem; - font-weight: 600; - text-transform: uppercase; - letter-spacing: 1px; - margin: 0 0 0.875rem 0; -} - -/* ============================================ - ORDER CARDS - ============================================ */ - -.orders-list { - display: flex; - flex-direction: column; - gap: 0.75rem; -} - -.order-card { - background: var(--surface); - border: 1px solid var(--border); - border-radius: 12px; - padding: 1rem 1.125rem; - cursor: pointer; - transition: - transform 0.15s, - border-color 0.15s, - box-shadow 0.15s; -} - -.order-card:hover { - transform: translateY(-1px); - border-color: #7c3aed; - box-shadow: 0 4px 16px rgba(124, 58, 237, 0.2); -} - -.order-card:active { - transform: translateY(0); -} - -.order-card-header { - display: flex; - justify-content: space-between; - align-items: center; - margin-bottom: 0.75rem; -} - -.order-card-number { - color: var(--text); - font-size: 1rem; - font-weight: 600; -} - -.order-card-badge { - display: inline-flex; - align-items: center; - padding: 0.3rem 0.75rem; - border-radius: 20px; - font-size: 0.78rem; - font-weight: 700; - text-transform: uppercase; - letter-spacing: 0.5px; - color: #a78bfa; - border: 1.5px solid #7c3aed; - background: transparent; -} - -.order-card-row { +.reward-modal-header { display: flex; align-items: flex-start; - gap: 0.5rem; + justify-content: space-between; + gap: 0.75rem; margin-bottom: 0.4rem; } -.order-card-row:last-of-type { - margin-bottom: 0; -} - -.order-card-row-icon { - color: var(--text-muted); - font-size: 0.8rem; - margin-top: 0.2rem; - flex-shrink: 0; - width: 14px; -} - -.order-card-row-text { - color: var(--text-muted); - font-size: 0.88rem; - line-height: 1.4; - flex: 1; -} - -.order-card-footer { +.reward-modal-header-title { display: flex; - justify-content: space-between; align-items: center; - margin-top: 0.875rem; - padding-top: 0.875rem; - border-top: 1px solid var(--border); + gap: 0.625rem; } -.order-card-total { - color: #10b981; +.reward-modal-icon { + color: #f59e0b; + font-size: 1.1rem; +} + +.reward-modal-header h2 { + color: var(--text); font-size: 1.1rem; font-weight: 700; + margin: 0; } -.order-card-referral { - font-size: 0.72rem; - font-weight: 500; - color: #10b981; - opacity: 0.75; -} - -.order-card-chevron { - color: var(--text-muted); - font-size: 0.9rem; -} - -/* ============================================ - LOADING - ============================================ */ - -.loading-container { +.reward-modal-close { + background: transparent; + border: 1px solid var(--border); + border-radius: 8px; + width: 32px; + height: 32px; + flex-shrink: 0; display: flex; - flex-direction: column; align-items: center; justify-content: center; - min-height: 60vh; - gap: 1rem; -} - -.loading-spinner { - width: 50px; - height: 50px; -} - -.spinner { - width: 100%; - height: 100%; - border: 3px solid var(--border); - border-top-color: #7c3aed; - border-radius: 50%; - animation: spin 0.9s linear infinite; -} - -@keyframes spin { - to { - transform: rotate(360deg); - } -} - -.loading-text { color: var(--text-muted); - font-size: 1rem; - margin: 0; + cursor: pointer; + transition: all 0.15s; } -/* ============================================ - ERROR - ============================================ */ +.reward-modal-close:hover { + background: var(--surface-2); + color: var(--text); +} -.error-banner { +.reward-modal-subtitle { + color: var(--text-muted); + font-size: 0.85rem; + margin: 0 0 1.1rem 0; +} + +.reward-modal-products { display: flex; - align-items: center; - gap: 1rem; - background: rgba(109, 40, 217, 0.08); - border: 1px solid rgba(124, 58, 237, 0.4); - border-radius: 10px; - padding: 1rem 1.25rem; - margin-bottom: 1.5rem; - color: #c4b5fd; -} - -.error-banner p { - margin: 0; - font-weight: 500; -} - -/* ============================================ - EMPTY STATE - ============================================ */ - -.empty-history { - text-align: center; - padding: 4rem 2rem; - background: var(--surface); - border: 1px solid var(--border); - border-radius: 12px; - margin-top: 1rem; -} - -.empty-icon { - font-size: 4rem; - color: var(--text-muted); - opacity: 0.4; + flex-direction: column; + gap: 0.625rem; margin-bottom: 1.25rem; } -.empty-history h2 { - color: var(--text); - font-size: 1.4rem; - margin: 0 0 0.5rem 0; - font-weight: 600; -} - -.empty-history p { - color: var(--text-muted); - font-size: 0.95rem; - margin: 0 0 1.75rem 0; -} - -.browse-button { - background: linear-gradient(to right, #7c3aed, #6d28d9); - color: white; - border: none; - padding: 0.7rem 1.75rem; - border-radius: 8px; - font-size: 0.95rem; - font-weight: 600; +.reward-product-card { + display: flex; + align-items: center; + gap: 0.75rem; + width: 100%; + background: rgba(245, 158, 11, 0.05); + border: 1.5px solid rgba(245, 158, 11, 0.2); + border-radius: 10px; + padding: 0.75rem 0.875rem; cursor: pointer; - transition: - opacity 0.2s, - transform 0.2s; + transition: all 0.15s; + text-align: left; + font-family: inherit; } -.browse-button:hover { - opacity: 0.9; +.reward-product-card:hover { + border-color: rgba(245, 158, 11, 0.5); + background: rgba(245, 158, 11, 0.09); +} + +.reward-product-card.selected { + border-color: #f59e0b; + background: rgba(245, 158, 11, 0.14); +} + +.reward-product-radio { + width: 22px; + height: 22px; + border-radius: 50%; + border: 2px solid rgba(245, 158, 11, 0.5); + flex-shrink: 0; + display: flex; + align-items: center; + justify-content: center; + color: #f59e0b; + font-size: 0.7rem; + transition: all 0.15s; +} + +.reward-product-card.selected .reward-product-radio { + background: #f59e0b; + border-color: #f59e0b; + color: #fff; +} + +.reward-product-info { + flex: 1; + display: flex; + flex-direction: column; + gap: 0.15rem; + min-width: 0; +} + +.reward-product-name { + color: var(--text); + font-size: 0.92rem; + font-weight: 600; +} + +.reward-product-meta { + color: var(--text-muted); + font-size: 0.78rem; +} + +.reward-product-free { + flex-shrink: 0; + color: #10b981; + font-size: 0.78rem; + font-weight: 700; + background: rgba(16, 185, 129, 0.12); + border-radius: 6px; + padding: 3px 8px; +} + +.reward-modal-actions { + display: flex; + gap: 0.75rem; +} + +.reward-modal-btn { + flex: 1; + display: flex; + align-items: center; + justify-content: center; + gap: 0.5rem; + padding: 0.75rem; + border-radius: 10px; + font-size: 0.9rem; + font-weight: 700; + cursor: pointer; + border: none; + transition: + opacity 0.15s, + transform 0.15s; + font-family: inherit; +} + +.reward-modal-btn:hover:not(:disabled) { + opacity: 0.88; transform: translateY(-1px); } -/* ============================================ - RESPONSIVE - ============================================ */ +.reward-modal-btn-cancel { + background: transparent; + color: var(--text-muted); + border: 1px solid var(--border); +} -@media (max-width: 600px) { - .stats-grid { - gap: 0.625rem; - } +.reward-modal-btn-confirm { + background: linear-gradient(135deg, #f59e0b, #d97706); + color: #fff; +} - .stat-value { - font-size: 1.5rem; - } +.reward-modal-btn-confirm:disabled { + opacity: 0.5; + cursor: not-allowed; +} - .order-card-number { - font-size: 0.9rem; +@media (max-width: 480px) { + .reward-modal { + padding: 1.25rem; + border-radius: 14px; } } diff --git a/frontend-prep/src/pages/User/ConsultationHistorique.tsx b/frontend-prep/src/pages/User/ConsultationHistorique.tsx index f88951e2..5c3515d6 100644 --- a/frontend-prep/src/pages/User/ConsultationHistorique.tsx +++ b/frontend-prep/src/pages/User/ConsultationHistorique.tsx @@ -13,7 +13,12 @@ import { getMyPointsRewards, claimMyReward, } from "../../api/api"; -import type { PublicSettings, PointsPoolInfo, PointsRewardConfig, RewardItemConfig } from "../../api/api"; +import type { + PublicSettings, + PointsPoolInfo, + PointsRewardConfig, + RewardItemConfig, +} from "../../api/api"; import type { CompletedOrder, ClientStats, @@ -38,6 +43,8 @@ import { faChevronRight, faCheckCircle, faHistory, + faTimes, + faCheck, } from "@fortawesome/free-solid-svg-icons"; function ConsultationHistorique() { @@ -75,10 +82,21 @@ function ConsultationHistorique() { reward: PointsRewardConfig | null; } | null>(null); const [claimingPool, setClaimingPool] = useState(null); - const [claimFeedback, setClaimFeedback] = useState<{ pool: string; type: "success" | "error"; text: string } | null>(null); + const [claimFeedback, setClaimFeedback] = useState<{ + pool: string; + type: "success" | "error"; + text: string; + } | null>(null); const [isLoading, setIsLoading] = useState(true); const [error, setError] = useState(""); + // ── Modal de sélection du produit récompense ── + const [rewardModalPool, setRewardModalPool] = + useState(null); + const [selectedProductId, setSelectedProductId] = useState( + null, + ); + useEffect(() => { if (!isUserAuthenticated()) navigate("/login/client", { replace: true }); @@ -159,37 +177,51 @@ function ConsultationHistorique() { }); }; - if (isLoading) { - return ( - <> - -
-
-
-
-
-

- Chargement de l'historique... -

-
-
- - ); - } + // Ouvre la modal de choix si plusieurs produits sont éligibles, + // sinon réclame directement (1 seul produit configuré ou aucun item). + const openRewardModal = (pool: PointsPoolInfo) => { + const items = pointsRewards?.reward?.reward_items ?? []; + if (items.length > 1) { + setSelectedProductId(items[0]?.product_id ?? null); + setRewardModalPool(pool); + } else { + handleClaim(pool.key); + } + }; - const handleClaim = async (poolKey: string) => { + const closeRewardModal = () => { + setRewardModalPool(null); + setSelectedProductId(null); + }; + + const confirmRewardChoice = async () => { + if (!rewardModalPool) return; + const poolKey = rewardModalPool.key; + const productId = selectedProductId ?? undefined; + closeRewardModal(); + await handleClaim(poolKey, productId); + }; + + const handleClaim = async (poolKey: string, productId?: number) => { setClaimingPool(poolKey); setClaimFeedback(null); - const res = await claimMyReward(poolKey); + const res = await claimMyReward(poolKey, productId); setClaimingPool(null); 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.description || "Récompense réclamée !"; + const text = + res.product_added && res.product_names?.length + ? `🎁 ${res.product_names.join(", ")} ajouté${res.product_names.length > 1 ? "s" : ""} à 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) => { if (r.success) setPointsRewards(r); }); + getMyPointsRewards().then((r) => { + if (r.success) setPointsRewards(r); + }); } else { - setClaimFeedback({ pool: poolKey, type: "error", text: res.error || "Erreur" }); + setClaimFeedback({ + pool: poolKey, + type: "error", + text: res.error || "Erreur", + }); } }; @@ -209,6 +241,24 @@ function ConsultationHistorique() { "#7c3aed", ]; + if (isLoading) { + return ( + <> + +
+
+
+
+
+

+ Chargement de l'historique... +

+
+
+ + ); + } + return ( <> @@ -305,81 +355,178 @@ function ConsultationHistorique() { {/* Section récompenses par palier */} - {pointsRewards?.enabled && pointsRewards.reward && pointsRewards.pools.length > 0 && ( -
-

- - Récompenses -

- {pointsRewards.reward.description && ( -

{pointsRewards.reward.description}

- )} - {(pointsRewards.reward.reward_items ?? []).filter((it: RewardItemConfig) => it.product_name).length > 0 && ( -
- {(pointsRewards.reward.reward_items ?? []).map((it: RewardItemConfig, idx: number) => ( - - - {it.product_name}{it.quantity > 0 && it.quantity !== 1 ? ` ×${it.quantity}` : ""}{it.price > 0 ? ` — ${it.price}€` : ""} - - ))} -
- )} -
- {pointsRewards.pools.map((pool) => { - const threshold = pointsRewards.reward!.threshold; - const progress = Math.min(100, Math.round((pool.points % threshold) / threshold * 100)); - const remaining = threshold - (pool.points % threshold); - const isClaiming = claimingPool === pool.key; - const feedback = claimFeedback?.pool === pool.key ? claimFeedback : null; - return ( -
-
- {pool.name} - {pool.points} pts -
- {pool.eligible_configs.length > 0 && ( -
- {pool.eligible_configs.flatMap((cfg) => - cfg.all_products - ? [ - {cfg.category} - ] - : (cfg.product_names ?? []).map((name) => ( - - {name} - - )) + {pointsRewards?.enabled && + pointsRewards.reward && + pointsRewards.pools.length > 0 && ( +
+

+ + Récompenses +

+ {pointsRewards.reward.description && ( +

+ {pointsRewards.reward.description} +

+ )} + {(pointsRewards.reward.reward_items ?? []).filter( + (it: RewardItemConfig) => it.product_name, + ).length > 0 && ( +
+ {( + pointsRewards.reward.reward_items ?? [] + ).map( + (it: RewardItemConfig, idx: number) => ( + + + {it.product_name} + {it.quantity > 0 && + it.quantity !== 1 + ? ` ×${it.quantity}` + : ""} + {it.price > 0 + ? ` — ${it.price}€` + : ""} + + ), + )} +
+ )} +
+ {pointsRewards.pools.map((pool) => { + const threshold = + pointsRewards.reward!.threshold; + const progress = Math.min( + 100, + Math.round( + ((pool.points % threshold) / + threshold) * + 100, + ), + ); + const remaining = + threshold - (pool.points % threshold); + const isClaiming = + claimingPool === pool.key; + const feedback = + claimFeedback?.pool === pool.key + ? claimFeedback + : null; + return ( +
+
+ + {pool.name} + + + {pool.points} pts + +
+ {pool.eligible_configs.length > + 0 && ( +
+ {pool.eligible_configs.flatMap( + (cfg) => + cfg.all_products + ? [ + + { + cfg.category + } + , + ] + : ( + cfg.product_names ?? + [] + ).map( + ( + name, + ) => ( + + { + name + } + + ), + ), + )} +
+ )} +
+
+
+
+ {pool.rewards_available > 0 ? ( + + {pool.rewards_available}{" "} + récompense + {pool.rewards_available > + 1 + ? "s" + : ""}{" "} + disponible + {pool.rewards_available > + 1 + ? "s" + : ""} + + ) : ( + + Encore {remaining} pts + pour une récompense + )}
- )} -
-
-
-
- {pool.rewards_available > 0 ? ( - {pool.rewards_available} récompense{pool.rewards_available > 1 ? "s" : ""} disponible{pool.rewards_available > 1 ? "s" : ""} - ) : ( - Encore {remaining} pts pour une récompense + {feedback && ( +

+ {feedback.text} +

+ )} + {pool.rewards_available > 0 && ( + )}
- {feedback && ( -

{feedback.text}

- )} - {pool.rewards_available > 0 && ( - - )} -
- ); - })} + ); + })} +
-
- )} + )} {/* Bouton parrainage */} {appSettings.referral_enabled && ( @@ -501,10 +648,19 @@ function ConsultationHistorique() {
- {formatPrice((order.total_prix || 0) - (order.referral_used || 0))} + {formatPrice( + (order.total_prix || 0) - + (order.referral_used || 0), + )} {(order.referral_used || 0) > 0 && ( - {" "}— dont {formatPrice(order.referral_used ?? 0)} parrainage + {" "} + — dont{" "} + {formatPrice( + order.referral_used ?? + 0, + )}{" "} + parrainage )} @@ -519,6 +675,111 @@ function ConsultationHistorique() { )}
+ + {/* ============================================ */} + {/* MODAL — CHOIX DU PRODUIT RÉCOMPENSE */} + {/* ============================================ */} + {rewardModalPool && pointsRewards?.reward && ( +
+
e.stopPropagation()} + > +
+
+ +

Choisissez votre récompense

+
+ +
+ +

+ {rewardModalPool.name} —{" "} + {rewardModalPool.rewards_available} récompense + {rewardModalPool.rewards_available > 1 + ? "s" + : ""}{" "} + disponible + {rewardModalPool.rewards_available > 1 ? "s" : ""} +

+ +
+ {(pointsRewards.reward.reward_items ?? []).map( + (item) => { + const isSelected = + selectedProductId === item.product_id; + return ( + + ); + }, + )} +
+ +
+ + +
+
+
+ )} ); }