From 8fc1335588f5078872a741c71e6aa82837c7d937 Mon Sep 17 00:00:00 2001 From: Xor290 Date: Tue, 31 Mar 2026 19:50:44 +0200 Subject: [PATCH] chore: fix ui --- frontend-prep/src/pages/User/Cart.css | 841 +++++++----------- frontend-prep/src/pages/User/Cart.tsx | 514 +++-------- frontend-prep/src/pages/User/Checkout.css | 9 + frontend-prep/src/pages/User/Checkout.tsx | 30 +- .../src/pages/User/ConsultationHistorique.css | 742 +++++---------- .../src/pages/User/ConsultationHistorique.tsx | 459 ++++------ frontend-prep/src/pages/User/OrderDetails.tsx | 10 +- 7 files changed, 860 insertions(+), 1745 deletions(-) diff --git a/frontend-prep/src/pages/User/Cart.css b/frontend-prep/src/pages/User/Cart.css index 49865797..2f26667b 100644 --- a/frontend-prep/src/pages/User/Cart.css +++ b/frontend-prep/src/pages/User/Cart.css @@ -1,361 +1,228 @@ /* ============================================ - Cart.css - STYLES ADAPTÉS + Cart.css — style app mobile ============================================ */ .cart-container { width: 100%; min-height: 100vh; - padding: clamp(1rem, 3vw, 2rem); - padding-top: calc(60px + clamp(1rem, 3vw, 2rem)); - max-width: 1200px; + padding-top: 60px; + max-width: 640px; margin: 0 auto; - background: linear-gradient(135deg, var(--bg) 0%, var(--surface) 100%); + padding-bottom: 140px; + background: var(--bg); } +/* ============================================ + HEADER + ============================================ */ + .cart-header { display: flex; justify-content: space-between; align-items: center; - margin-bottom: clamp(1.5rem, 4vw, 2rem); - flex-wrap: wrap; - gap: 1rem; - margin-top: -65px; + padding: 1rem 1.25rem; + border-bottom: 1px solid var(--border); } -.cart-header h1 { - font-size: clamp(1.8rem, 5vw, 2.5rem); - margin: 0; - font-weight: bold; - letter-spacing: -0.5px; +.cart-header-left { + display: flex; + flex-direction: column; + gap: 0.1rem; +} + +.cart-title { color: var(--text); + font-size: 1.4rem; + font-weight: 700; + margin: 0; +} + +.cart-header-count { + color: var(--text-muted); + font-size: 0.85rem; + font-weight: 400; } .clear-all-button { - display: flex; - align-items: center; - gap: 0.5rem; - padding: clamp(0.6rem, 2vw, 0.8rem) clamp(1rem, 3vw, 1.5rem); - background-color: rgba(239, 68, 68, 0.1); + background: none; + border: none; color: #ef4444; - border: 1px solid rgba(239, 68, 68, 0.3); - border-radius: 8px; - font-size: clamp(0.9rem, 3vw, 1rem); - font-weight: 600; + font-size: 0.9rem; + font-weight: 500; cursor: pointer; - transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1); - -webkit-tap-highlight-color: transparent; + padding: 0.25rem 0; + transition: opacity 0.2s; } .clear-all-button:hover:not(:disabled) { - background-color: rgba(239, 68, 68, 0.15); - border-color: rgba(239, 68, 68, 0.5); - transform: translateY(-1px); + opacity: 0.75; } -.clear-all-button:active:not(:disabled) { - transform: scale(0.98); -} - -.trash-icon { - width: 16px; - height: 16px; - opacity: 0.8; - flex-shrink: 0; -} - -/* Empty Cart */ -.empty-cart { - text-align: center; - padding: 4rem 2rem; - display: flex; - flex-direction: column; - align-items: center; - justify-content: center; -} - -.empty-cart-icon { - margin-bottom: 1.5rem; +.clear-all-button:disabled { opacity: 0.4; - color: var(--text-muted); -} - -.empty-cart p { - font-size: 1.2rem; - color: var(--text-muted); - margin-bottom: 2rem; -} - -.continue-shopping { - background: linear-gradient(to right, #7c3aed, #6d28d9); - color: white; - padding: 1rem 2rem; - border: none; - border-radius: 8px; - font-size: 1rem; - font-weight: 600; - cursor: pointer; - transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1); - -webkit-tap-highlight-color: transparent; -} - -.continue-shopping:hover { - transform: translateY(-2px); - box-shadow: 0 8px 20px rgba(124, 58, 237, 0.3); -} - -.continue-shopping:active { - transform: scale(0.98); -} - -/* Cart Items - Grid Layout */ -.cart-items { - display: grid; - grid-template-columns: repeat(auto-fill, minmax(350px, 1fr)); - gap: clamp(1.5rem, 4vw, 2rem); - margin-bottom: clamp(1.5rem, 4vw, 2rem); -} - -.cart-item { - position: relative; - background: var(--surface); - border: 1px solid var(--border); - border-radius: 16px; - padding: clamp(1.5rem, 4vw, 2rem); - display: flex; - flex-direction: column; - gap: clamp(1rem, 3vw, 1.5rem); - transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1); - overflow: hidden; - backdrop-filter: blur(8px); - box-shadow: 0 4px 16px rgba(0, 0, 0, 0.2); -} - -.cart-item::before { - content: ""; - position: absolute; - top: 0; - left: -100%; - width: 100%; - height: 100%; - background: linear-gradient( - 90deg, - transparent, - rgba(124, 58, 237, 0.1), - transparent - ); - transition: left 0.6s cubic-bezier(0.4, 0, 0.2, 1); - pointer-events: none; -} - -.cart-item:hover { - background: var(--surface-2); - border-color: rgba(124, 58, 237, 0.3); - box-shadow: 0 12px 32px rgba(124, 58, 237, 0.15); - transform: translateY(-4px); -} - -.cart-item:hover::before { - left: 100%; -} - -/* ✨ CONTAINER MÉDIA - Photo + Badge Vidéo */ -.cart-item-media-container { - position: relative; - width: 100%; - height: 220px; - border-radius: 12px; - overflow: hidden; - background: var(--surface-2); - border: 1px solid var(--border); -} - -.cart-item-image { - width: 100%; - height: 100%; - object-fit: cover; - transition: transform 0.3s cubic-bezier(0.4, 0, 0.2, 1); -} - -.cart-item:hover .cart-item-image { - transform: scale(1.03); -} - -/* ✨ BADGE VIDÉO - Style bouton cliquable */ -.cart-video-badge { - position: absolute; - top: 12px; - right: 12px; - background: rgba(124, 58, 237, 0.95); - color: white; - padding: 0.5rem 0.75rem; - border-radius: 8px; - display: flex; - align-items: center; - gap: 0.5rem; - font-size: 0.85rem; - font-weight: 600; - backdrop-filter: blur(8px); - box-shadow: 0 4px 12px rgba(124, 58, 237, 0.4); - z-index: 2; - transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1); - border: none; - cursor: pointer; -} - -.cart-video-badge:hover { - transform: scale(1.05); - background: rgba(124, 58, 237, 1); - box-shadow: 0 6px 16px rgba(124, 58, 237, 0.5); -} - -.cart-video-badge i { - font-size: 1rem; - filter: drop-shadow(0 2px 4px rgba(0, 0, 0, 0.2)); -} - -.cart-item:hover .cart-video-badge { - transform: scale(1.1); -} - -.cart-item-info { - flex: 1; - display: flex; - flex-direction: column; - gap: 0.5rem; -} - -.cart-item-info h3 { - color: var(--text); - font-size: clamp(1.2rem, 4vw, 1.4rem); - margin: 0; - font-weight: 600; - line-height: 1.3; - letter-spacing: -0.3px; -} - -.cart-item-category { - font-size: 0.85rem; - color: #7c3aed; - margin: 0; - font-weight: 500; - text-transform: uppercase; - letter-spacing: 0.5px; - opacity: 0.9; -} - -/* ✨ NOUVEAU: Affichage de la quantité en grammes */ -.cart-item-quantity { - font-size: 1rem; - color: var(--text-muted); - margin: 0.5rem 0; - padding: 0.5rem 1rem; - background: rgba(124, 58, 237, 0.1); - border: 1px solid rgba(124, 58, 237, 0.2); - border-radius: 8px; - display: inline-block; - width: fit-content; -} - -.cart-item-quantity strong { - color: #7c3aed; - font-weight: 700; - font-size: 1.1em; -} - -.cart-item-price { - color: #10b981; - font-size: clamp(1.2rem, 3vw, 1.4rem); - margin: 0; - font-weight: 700; - display: flex; - align-items: center; - gap: 0.3rem; -} - -/* ✅ SIMPLIFIÉ: Controls sans boutons +/- */ -.cart-item-controls { - display: flex; - align-items: center; - justify-content: flex-end; - padding-top: 1rem; - border-top: 1px solid var(--border); -} - -.remove-item-button { - background-color: rgba(239, 68, 68, 0.1); - color: #ef4444; - border: 1px solid rgba(239, 68, 68, 0.3); - border-radius: 10px; - padding: 0.75rem 1.5rem; - cursor: pointer; - transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1); - -webkit-tap-highlight-color: transparent; - display: flex; - align-items: center; - justify-content: center; - gap: 0.5rem; - font-size: 0.95rem; - font-weight: 600; -} - -.remove-item-button:hover:not(:disabled) { - background-color: rgba(239, 68, 68, 0.2); - border-color: rgba(239, 68, 68, 0.5); - transform: translateY(-2px); -} - -.remove-item-button:active:not(:disabled) { - transform: scale(0.98); -} - -.remove-item-button:disabled { - opacity: 0.5; cursor: not-allowed; } -.remove-item-button .trash-icon { - width: 16px; - height: 16px; +/* ============================================ + LISTE DES ARTICLES + ============================================ */ + +.cart-items-list { + padding: 0.75rem 1rem; } -/* Cart Summary */ -.cart-summary { - background-color: var(--surface); +.cart-row { + display: flex; + align-items: center; + gap: 0.875rem; + background: var(--surface); border: 1px solid var(--border); border-radius: 12px; - padding: clamp(1.5rem, 4vw, 2rem); - position: sticky; - bottom: clamp(1rem, 3vw, 2rem); - backdrop-filter: blur(10px); + padding: 0.875rem; + margin-bottom: 0.625rem; + transition: border-color 0.15s; } -.summary-row { +.cart-row:hover { + border-color: rgba(124, 58, 237, 0.35); +} + +/* Vignette */ +.cart-row-thumb { + position: relative; + flex-shrink: 0; +} + +.cart-row-img { + width: 68px; + height: 68px; + border-radius: 8px; + object-fit: cover; + background: var(--surface-2); + display: block; +} + +.cart-row-img-placeholder { + width: 68px; + height: 68px; + border-radius: 8px; + background: var(--surface-2); + display: flex; + align-items: center; + justify-content: center; + color: var(--text-muted); + font-size: 1.4rem; +} + +.cart-row-video-badge { + position: absolute; + bottom: 4px; + right: 4px; + background: rgba(124, 58, 237, 0.9); + color: #fff; + border: none; + border-radius: 5px; + width: 24px; + height: 24px; + display: flex; + align-items: center; + justify-content: center; + font-size: 0.65rem; + cursor: pointer; + transition: background 0.15s; +} + +.cart-row-video-badge:hover { + background: rgba(124, 58, 237, 1); +} + +/* Infos */ +.cart-row-info { + flex: 1; + min-width: 0; +} + +.cart-row-name { + color: var(--text); + font-size: 0.95rem; + font-weight: 600; + margin: 0 0 0.2rem 0; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; +} + +.cart-row-qty { + color: var(--text-muted); + font-size: 0.82rem; + margin: 0 0 0.25rem 0; +} + +.cart-row-price { + color: #10b981; + font-size: 0.95rem; + font-weight: 700; + margin: 0; +} + +/* Bouton supprimer */ +.cart-row-remove { + background: none; + border: none; + color: #ef4444; + cursor: pointer; + padding: 0.5rem; + border-radius: 8px; + display: flex; + align-items: center; + justify-content: center; + opacity: 0.7; + transition: opacity 0.15s, background 0.15s; + flex-shrink: 0; +} + +.cart-row-remove:hover:not(:disabled) { + opacity: 1; + background: rgba(239, 68, 68, 0.1); +} + +.cart-row-remove:disabled { + opacity: 0.3; + cursor: not-allowed; +} + +/* ============================================ + FOOTER FIXE + ============================================ */ + +.cart-footer { + position: fixed; + bottom: 0; + left: 50%; + transform: translateX(-50%); + width: 100%; + max-width: 640px; + background: var(--surface); + border-top: 1px solid var(--border); + padding: 1rem 1.25rem 1.25rem; + z-index: 100; +} + +.cart-footer-total { display: flex; justify-content: space-between; align-items: center; - margin-bottom: 1rem; + margin-bottom: 0.875rem; +} + +.cart-footer-total-label { color: var(--text-muted); - font-size: clamp(0.95rem, 3vw, 1.05rem); + font-size: 1rem; } -.summary-row span:last-child { - font-weight: 600; - color: var(--text); -} - -.total-row { - color: var(--text); - font-size: clamp(1.2rem, 4vw, 1.4rem); - padding-top: 1rem; - border-top: 1px solid var(--border); - margin-top: 1rem; - margin-bottom: 1.5rem; - font-weight: 700; -} - -.total-amount { +.cart-footer-total-value { color: #10b981; - font-size: clamp(1.4rem, 5vw, 1.8rem); + font-size: 1.4rem; font-weight: 700; } @@ -364,287 +231,251 @@ background: linear-gradient(to right, #7c3aed, #6d28d9); color: white; border: none; - border-radius: 12px; - padding: clamp(1rem, 3vw, 1.3rem); - font-size: clamp(1rem, 4vw, 1.1rem); + border-radius: 10px; + padding: 0.9rem; + font-size: 1rem; font-weight: 700; cursor: pointer; - transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1); - touch-action: manipulation; - -webkit-tap-highlight-color: transparent; - text-transform: uppercase; - letter-spacing: 1px; - position: relative; - overflow: hidden; -} - -.checkout-button::before { - content: ""; - position: absolute; - top: 0; - left: -100%; - width: 100%; - height: 100%; - background: rgba(255, 255, 255, 0.1); - transition: left 0.4s cubic-bezier(0.4, 0, 0.2, 1); -} - -.checkout-button:hover:not(:disabled)::before { - left: 100%; + transition: opacity 0.2s, transform 0.15s; + letter-spacing: 0.3px; } .checkout-button:hover:not(:disabled) { - transform: translateY(-2px); - box-shadow: 0 12px 32px rgba(124, 58, 237, 0.4); + opacity: 0.92; + transform: translateY(-1px); } .checkout-button:active:not(:disabled) { - transform: scale(0.98); + transform: translateY(0); } .checkout-button:disabled { - opacity: 0.6; + opacity: 0.55; cursor: not-allowed; } -/* Loading */ +/* ============================================ + EMPTY STATE + ============================================ */ + +.empty-cart { + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + text-align: center; + padding: 5rem 2rem; + min-height: calc(100vh - 60px); +} + +.empty-cart-icon { + color: var(--text-muted); + opacity: 0.35; + margin-bottom: 1.25rem; +} + +.empty-cart h2 { + color: var(--text); + font-size: 1.4rem; + font-weight: 700; + margin: 0 0 0.4rem 0; +} + +.empty-cart p { + color: var(--text-muted); + font-size: 0.95rem; + margin: 0 0 1.75rem 0; +} + +.continue-shopping { + background: linear-gradient(to right, #7c3aed, #6d28d9); + color: white; + border: none; + border-radius: 8px; + padding: 0.75rem 1.75rem; + font-size: 0.95rem; + font-weight: 600; + cursor: pointer; + transition: opacity 0.2s, transform 0.15s; +} + +.continue-shopping:hover { + opacity: 0.9; + transform: translateY(-1px); +} + +/* ============================================ + LOADING + ============================================ */ + .loading-cart { text-align: center; - padding: 4rem 2rem; + padding: 3rem 2rem; } .loading-cart p { - font-size: 1.2rem; color: var(--text-muted); + font-size: 0.95rem; animation: pulse 1.5s ease-in-out infinite; } @keyframes pulse { - 0%, - 100% { - opacity: 0.6; - } - 50% { - opacity: 1; - } + 0%, 100% { opacity: 0.5; } + 50% { opacity: 1; } } -/* Modal de confirmation */ +/* ============================================ + MODAL CONFIRMATION + ============================================ */ + .modal-overlay { position: fixed; - top: 0; - left: 0; - right: 0; - bottom: 0; - background-color: rgba(0, 0, 0, 0.75); - backdrop-filter: blur(8px); + inset: 0; + background: rgba(0, 0, 0, 0.65); + backdrop-filter: blur(6px); display: flex; align-items: center; justify-content: center; padding: 1rem; - animation: fadeIn 0.2s cubic-bezier(0.4, 0, 0.2, 1); + z-index: 1000; + animation: fadeIn 0.2s ease; } @keyframes fadeIn { - from { - opacity: 0; - } - to { - opacity: 1; - } + from { opacity: 0; } + to { opacity: 1; } } .modal-content2 { background: var(--surface); - border: 1px solid rgba(239, 68, 68, 0.3); - border-radius: 20px; - padding: clamp(2rem, 5vw, 3rem); - max-width: 480px; + border: 1px solid var(--border); + border-radius: 16px; + padding: 1.75rem; + max-width: 360px; width: 100%; - position: relative; - box-shadow: 0 20px 60px rgba(239, 68, 68, 0.3); - animation: slideUp 0.3s cubic-bezier(0.4, 0, 0.2, 1); + animation: slideUp 0.25s ease; } @keyframes slideUp { - from { - opacity: 0; - transform: translateY(20px) scale(0.95); - } - to { - opacity: 1; - transform: translateY(0) scale(1); - } + from { opacity: 0; transform: translateY(16px); } + to { opacity: 1; transform: translateY(0); } } .modal-icon-container { display: flex; - align-items: center; justify-content: center; - width: 80px; - height: 80px; - margin: 0 auto 1.5rem; - background: radial-gradient( - circle, - rgba(239, 68, 68, 0.15) 0%, - transparent 70% - ); - border-radius: 50%; - position: relative; + margin-bottom: 1rem; } .modal-icon { color: #ef4444; - filter: drop-shadow(0 4px 12px rgba(239, 68, 68, 0.4)); - animation: shake 0.5s cubic-bezier(0.4, 0, 0.2, 1); -} - -@keyframes shake { - 0%, - 100% { - transform: translateX(0); - } - 25% { - transform: translateX(-4px) rotate(-5deg); - } - 75% { - transform: translateX(4px) rotate(5deg); - } } .modal-title { color: var(--text); - font-size: clamp(1.5rem, 4vw, 1.8rem); + font-size: 1.1rem; font-weight: 700; text-align: center; - margin: 0 0 1rem 0; - letter-spacing: -0.5px; + margin: 0 0 0.5rem 0; } .modal-message { color: var(--text-muted); - font-size: clamp(0.95rem, 3vw, 1.05rem); + font-size: 0.9rem; text-align: center; - line-height: 1.6; - margin: 0 0 2rem 0; + margin: 0 0 1.25rem 0; + line-height: 1.5; } .modal-actions { display: flex; - gap: 1rem; - flex-wrap: wrap; + gap: 0.75rem; } .modal-button { flex: 1; - min-width: 140px; - padding: clamp(0.9rem, 3vw, 1.1rem) clamp(1.5rem, 4vw, 2rem); - border-radius: 12px; - font-size: clamp(0.95rem, 3vw, 1rem); + padding: 0.75rem; + border-radius: 8px; + font-size: 0.9rem; font-weight: 600; cursor: pointer; - transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1); - -webkit-tap-highlight-color: transparent; border: none; display: flex; align-items: center; justify-content: center; - gap: 0.5rem; - letter-spacing: 0.3px; + gap: 0.4rem; + transition: opacity 0.15s, transform 0.15s; +} + +.modal-button:hover:not(:disabled) { + opacity: 0.85; + transform: translateY(-1px); } .modal-cancel { - background: var(--bg); + background: transparent; color: var(--text-muted); border: 1px solid var(--border); } -.modal-cancel:hover { - background: var(--surface-2); - color: var(--text); - border-color: var(--text-muted); - transform: translateY(-2px); -} - -.modal-cancel:active { - transform: scale(0.98); -} - .modal-confirm { - background: linear-gradient(135deg, #ef4444 0%, #dc2626 100%); + background: linear-gradient(135deg, #ef4444, #dc2626); color: white; - border: 1px solid rgba(239, 68, 68, 0.5); - box-shadow: 0 4px 16px rgba(239, 68, 68, 0.3); -} - -.modal-confirm:hover:not(:disabled) { - box-shadow: 0 8px 24px rgba(239, 68, 68, 0.4); - transform: translateY(-2px); -} - -.modal-confirm:active:not(:disabled) { - transform: scale(0.98); } .modal-confirm:disabled { - opacity: 0.6; + opacity: 0.5; cursor: not-allowed; } -/* ✨ MODAL VIDÉO */ +/* ============================================ + MODAL VIDÉO + ============================================ */ + .video-modal { position: fixed; - top: 0; - left: 0; - right: 0; - bottom: 0; - background-color: rgba(0, 0, 0, 0.9); - backdrop-filter: blur(8px); + inset: 0; + background: rgba(0, 0, 0, 0.92); + backdrop-filter: blur(6px); display: flex; align-items: center; justify-content: center; z-index: 10000; - padding: 2rem; - animation: fadeIn 0.3s cubic-bezier(0.4, 0, 0.2, 1); + padding: 1.5rem; + animation: fadeIn 0.2s ease; } .video-modal-content { position: relative; width: 100%; - max-width: 900px; + max-width: 860px; background: var(--surface); - border-radius: 16px; + border-radius: 12px; overflow: hidden; - box-shadow: 0 20px 60px rgba(0, 0, 0, 0.5); - animation: slideUp 0.3s cubic-bezier(0.4, 0, 0.2, 1); } .video-close-btn { position: absolute; - top: 1rem; - right: 1rem; + top: 0.75rem; + right: 0.75rem; background: rgba(239, 68, 68, 0.9); color: white; border: none; border-radius: 50%; - width: 40px; - height: 40px; + width: 36px; + height: 36px; display: flex; align-items: center; justify-content: center; cursor: pointer; z-index: 10; - font-size: 1.5rem; - font-weight: bold; - transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1); - backdrop-filter: blur(8px); - box-shadow: 0 4px 12px rgba(239, 68, 68, 0.4); + font-size: 1.1rem; + transition: background 0.2s, transform 0.2s; } .video-close-btn:hover { - background: rgba(239, 68, 68, 1); + background: #ef4444; transform: scale(1.1) rotate(90deg); - box-shadow: 0 6px 16px rgba(239, 68, 68, 0.6); } .video-player { @@ -655,60 +486,18 @@ background: #000; } -/* État de chargement des médias */ -.cart-item-media-container.loading { - background: linear-gradient( - 90deg, - var(--surface) 0%, - var(--surface-2) 50%, - var(--surface) 100% - ); - background-size: 200% 100%; - animation: shimmer 1.5s infinite; -} +/* ============================================ + RESPONSIVE + ============================================ */ -@keyframes shimmer { - 0% { - background-position: -200% 0; +@media (max-width: 480px) { + .cart-row-img, + .cart-row-img-placeholder { + width: 58px; + height: 58px; } - 100% { - background-position: 200% 0; - } -} -/* Responsive */ -@media (max-width: 768px) { - .cart-items { - grid-template-columns: 1fr; - } - - .modal-actions { - flex-direction: column; - } - - .modal-button { - width: 100%; - } -} - -@media (max-width: 600px) { - .cart-item-media-container { - height: 180px; - } - - .cart-items { - gap: 1rem; - } - - .cart-video-badge { - padding: 0.4rem 0.6rem; - font-size: 0.75rem; - top: 8px; - right: 8px; - } - - .cart-video-badge svg { - width: 14px; - height: 14px; + .cart-footer { + padding: 0.875rem 1rem 1rem; } } diff --git a/frontend-prep/src/pages/User/Cart.tsx b/frontend-prep/src/pages/User/Cart.tsx index 1bd6437c..4423db76 100644 --- a/frontend-prep/src/pages/User/Cart.tsx +++ b/frontend-prep/src/pages/User/Cart.tsx @@ -1,19 +1,10 @@ -// ============================================ -// pages/Cart/Cart.tsx - MÊME LOGIQUE QUE PRODUCTCARD -// ============================================ -// ✅ Affiche quantity en grammes (5g, 10g, etc.) -// ✅ Affiche photos et vidéos SANS appels API supplémentaires -// ❌ Pas de boutons +/- (on ne modifie pas les grammes) -// ✅ Pour acheter 2× le même produit, l'ajouter 2 fois -// ✅ Vérification continue de l'authentification - import { useCart } from "../../context/useCart"; import Navbar from "../../components/Navbar"; import { useEffect, useState } from "react"; import { useNavigate } from "react-router-dom"; import { isUserAuthenticated, getProductById, getMediaUrl } from "../../api/api"; import type { Product } from "../../api/api"; -import { Trash2, ShoppingBag, AlertTriangle } from "lucide-react"; +import { Trash2, ShoppingCart, AlertTriangle } from "lucide-react"; import "./Cart.css"; interface CartItemWithMedia { @@ -30,258 +21,89 @@ interface CartItemWithMedia { function Cart() { const navigate = useNavigate(); - const { cartItems, removeFromCart, clearCart, loading, refreshCart } = - useCart(); + const { cartItems, removeFromCart, clearCart, loading, refreshCart } = useCart(); const [showClearConfirm, setShowClearConfirm] = useState(false); - const [showVideo, setShowVideo] = useState(false); // ✨ État pour afficher/masquer la vidéo + const [showVideo, setShowVideo] = useState(false); const [currentVideoUrl, setCurrentVideoUrl] = useState(""); const [enrichedItems, setEnrichedItems] = useState([]); const [loadingMedia, setLoadingMedia] = useState(false); - // ============================================ - // HANDLERS VIDÉO (COMME PRODUCTCARD) - // ============================================ - - const handleVideoToggle = (videoUrl: string) => { - setCurrentVideoUrl(videoUrl); - setShowVideo(true); - }; - - const handleCloseVideo = () => { - setShowVideo(false); - setCurrentVideoUrl(""); - }; - - // ============================================ - // VÉRIFICATION AUTHENTIFICATION - // ============================================ useEffect(() => { - const checkAuth = () => { - if (!isUserAuthenticated()) { - console.log( - "❌ [Cart] Utilisateur non authentifié, redirection vers /login/client", - ); - navigate("/login/client", { replace: true }); - } - }; - - checkAuth(); + if (!isUserAuthenticated()) navigate("/login/client", { replace: true }); }, [navigate]); - // ✅ VÉRIFICATION CONTINUE (toutes les 5 secondes) useEffect(() => { - const authInterval = setInterval(() => { - if (!isUserAuthenticated()) { - console.log( - "❌ [Cart] Session expirée, redirection vers /login/client", - ); - navigate("/login/client", { replace: true }); - } + const interval = setInterval(() => { + if (!isUserAuthenticated()) navigate("/login/client", { replace: true }); }, 5000); - - return () => clearInterval(authInterval); + return () => clearInterval(interval); }, [navigate]); useEffect(() => { - // ✅ Vérifier l'auth avant de rafraîchir le panier - if (!isUserAuthenticated()) { - console.log("❌ [refreshCart] Non authentifié"); - navigate("/login/client", { replace: true }); - return; - } - + if (!isUserAuthenticated()) { navigate("/login/client", { replace: true }); return; } refreshCart(); }, []); - // ============================================ - // CALCULS - // ============================================ - const total = cartItems.reduce((sum, item) => sum + item.price, 0); - // ============================================ - // FONCTIONS POUR PHOTOS ET VIDÉOS (COMME PRODUCTCARD) - // ============================================ - const getProductImage = (product: Product): string => { - if (!product.media || product.media.length === 0) { - return "https://via.placeholder.com/400x400/1a1a1a/ffffff?text=No+Image"; + if (!product.media || product.media.length === 0) + return "https://via.placeholder.com/120x120/1a1a1a/ffffff?text=No+Image"; + for (const m of product.media) { + if (m && m.type === "image" && m.url) return getMediaUrl(m.url); } - - for (let i = 0; i < product.media.length; i++) { - const mediaItem = product.media[i]; - if (mediaItem && mediaItem.type === "image" && mediaItem.url) { - return getMediaUrl(mediaItem.url); - } - } - - return "https://via.placeholder.com/400x400/1a1a1a/ffffff?text=No+Image"; + return "https://via.placeholder.com/120x120/1a1a1a/ffffff?text=No+Image"; }; - const hasProductVideo = (product: Product): boolean => { - if (!product.media || product.media.length === 0) { - return false; - } - return product.media.some( - (mediaItem) => mediaItem && mediaItem.type === "video", - ); - }; - - const getProductVideoUrl = (product: Product): string | undefined => { - if (!product.media || product.media.length === 0) { - return undefined; - } - - const videoMedia = product.media.find( - (mediaItem) => mediaItem && mediaItem.type === "video", - ); - - return videoMedia?.url ? getMediaUrl(videoMedia.url) : undefined; - }; - - // ============================================ - // ENRICHIR LES ITEMS AVEC MÉDIAS - // ============================================ - useEffect(() => { - const enrichCartItems = async () => { - if (cartItems.length === 0) { - console.log("🔄 [CART] Panier vide, pas d'enrichissement"); - setEnrichedItems([]); - setLoadingMedia(false); - return; - } - - // ✅ Guard: ne pas re-enrichir si déjà fait pour ces mêmes items - const sameItems = + const enrich = async () => { + if (cartItems.length === 0) { setEnrichedItems([]); return; } + const same = enrichedItems.length === cartItems.length && - enrichedItems.every( - (enriched, index) => - enriched.id === cartItems[index]?.id && - enriched.product_id === cartItems[index]?.product_id, - ); - - if (sameItems) { - console.log("🔄 [CART] Items déjà enrichis, skip"); - return; - } + enrichedItems.every((e, i) => e.id === cartItems[i]?.id && e.product_id === cartItems[i]?.product_id); + if (same) return; setLoadingMedia(true); - console.log( - "🔄 [CART] Enrichissement de", - cartItems.length, - "items...", - ); - try { - const enrichedPromises = cartItems.map(async (item) => { - try { - console.log( - `📦 [CART] Récupération médias pour produit ${item.product_id}...`, - ); - const productResponse = await getProductById( - item.product_id, - ); - - if (productResponse.success && productResponse.data) { - const product = productResponse.data; - const enriched = { - ...item, - image: getProductImage(product), - hasVideo: hasProductVideo(product), - videoUrl: getProductVideoUrl(product), - }; - console.log(`✅ [CART] Item enrichi:`, { - name: item.name_product, - image: enriched.image, - hasVideo: enriched.hasVideo, - }); - return enriched; - } else { - console.warn( - `⚠️ [CART] Produit ${item.product_id} non trouvé`, - ); - return { - ...item, - image: "https://via.placeholder.com/400x400/1a1a1a/ffffff?text=No+Image", - hasVideo: false, - videoUrl: undefined, - }; - } - } catch (error) { - console.error( - `❌ [CART] Erreur pour produit ${item.product_id}:`, - error, - ); - return { - ...item, - image: "https://via.placeholder.com/400x400/1a1a1a/ffffff?text=No+Image", - hasVideo: false, - videoUrl: undefined, - }; - } - }); - - const enriched = await Promise.all(enrichedPromises); + const enriched = await Promise.all( + cartItems.map(async (item) => { + try { + const res = await getProductById(item.product_id); + if (res.success && res.data) { + const p = res.data; + const videoMedia = p.media?.find((m: any) => m && m.type === "video"); + return { + ...item, + image: getProductImage(p), + hasVideo: !!videoMedia, + videoUrl: videoMedia?.url ? getMediaUrl(videoMedia.url) : undefined, + }; + } + } catch { /* ignore */ } + return { ...item, image: "https://via.placeholder.com/120x120/1a1a1a/ffffff?text=No+Image", hasVideo: false }; + }) + ); setEnrichedItems(enriched); - console.log( - "✅ [CART] Enrichissement terminé:", - enriched.length, - "items", - ); - } catch (error) { - console.error("❌ [CART] Erreur enrichissement:", error); - setEnrichedItems( - cartItems.map((item) => ({ - ...item, - image: "https://via.placeholder.com/400x400/1a1a1a/ffffff?text=No+Image", - hasVideo: false, - videoUrl: undefined, - })), - ); } finally { setLoadingMedia(false); } }; - - enrichCartItems(); - }, [cartItems]); // ✅ Dépendance sur cartItems complet - - // ============================================ - // HANDLERS - // ============================================ + enrich(); + }, [cartItems]); const handleClearCart = async () => { - // ✅ Vérifier l'auth avant de vider le panier - if (!isUserAuthenticated()) { - console.log("❌ [handleClearCart] Non authentifié"); - navigate("/login/client", { replace: true }); - return; - } - + if (!isUserAuthenticated()) { navigate("/login/client", { replace: true }); return; } setShowClearConfirm(false); await clearCart(); }; const handleRemoveItem = async (itemId: number) => { - // ✅ Vérifier l'auth avant de supprimer un item - if (!isUserAuthenticated()) { - console.log("❌ [handleRemoveItem] Non authentifié"); - navigate("/login/client", { replace: true }); - return; - } - + if (!isUserAuthenticated()) { navigate("/login/client", { replace: true }); return; } await removeFromCart(itemId); }; const handleCheckout = () => { - // ✅ Vérifier l'auth avant de passer commande - if (!isUserAuthenticated()) { - console.log("❌ [handleCheckout] Non authentifié"); - navigate("/login/client", { replace: true }); - return; - } - + if (!isUserAuthenticated()) { navigate("/login/client", { replace: true }); return; } navigate("/user/checkout"); }; @@ -290,231 +112,145 @@ function Cart() { <>
-
-

Votre Panier

-
-
-

Chargement de votre panier...

-
+

Chargement du panier...

); } + const displayItems = enrichedItems.length > 0 ? enrichedItems : cartItems.map(i => ({ ...i, image: "", hasVideo: false })); + return ( <>
-
-

Votre Panier

- {cartItems.length > 0 && ( - - )} -
{cartItems.length === 0 ? (
- -

Votre panier est vide

-
) : ( <> - {loadingMedia && enrichedItems.length === 0 ? ( -
-

Chargement des médias...

+ {/* Header */} +
+
+

Mon panier

+ + {cartItems.length} article{cartItems.length > 1 ? "s" : ""} +
- ) : ( -
- {enrichedItems.map((item, index) => ( -
- {/* ✨ MEDIA CONTAINER - Photo + Badge Vidéo */} -
- {item.name_product} { - console.warn( - `❌ Erreur chargement image pour ${item.name_product}:`, - item.image, - ); - e.currentTarget.src = - "https://via.placeholder.com/400x400/7c3aed/ffffff?text=" + - encodeURIComponent( - item.name_product.substring( - 0, - 10, - ), - ); - }} - /> + +
- {/* ✨ Badge vidéo si disponible */} - {item.hasVideo && item.videoUrl && ( + {/* Liste des articles */} +
+ {loadingMedia && enrichedItems.length === 0 ? ( +

Chargement des médias...

+ ) : ( + displayItems.map((item, index) => ( +
+ + {/* Vignette image */} +
+ {item.image ? ( + {item.name_product} { e.currentTarget.src = "https://via.placeholder.com/72x72/2d2d2d/ffffff?text=?"; }} + /> + ) : ( +
+ +
+ )} + {(item as CartItemWithMedia).hasVideo && (item as CartItemWithMedia).videoUrl && ( )}
-
-

{item.name_product}

-

- Catégorie: {item.category} -

- - {/* Quantité en grammes */} -

- Quantité:{" "} - - {item.quantity}g - -

- -

- {item.price.toFixed(2)} € -

+ {/* Infos */} +
+

{item.name_product}

+

{item.quantity}g

+

{item.price.toFixed(2)} €

{/* Bouton supprimer */} -
- -
+
- ))} -
- )} + )) + )} +
-
-
- Nombre d'articles: - {cartItems.length} + {/* Footer fixe */} +
+
+ Total + {total.toFixed(2)} €
- -
- Total: - - {total.toFixed(2)} € - -
- -
)}
- {/* Modal de confirmation vidage */} + {/* Modal vider le panier */} {showClearConfirm && ( -
setShowClearConfirm(false)} - > -
e.stopPropagation()} - > +
setShowClearConfirm(false)}> +
e.stopPropagation()}>
- +
-

Vider le panier

- Êtes-vous sûr de vouloir supprimer tous les articles - de votre panier ? Cette action est irréversible. + Supprimer les {cartItems.length} article{cartItems.length > 1 ? "s" : ""} du panier ?

-
- -
)} - {/* ✨ Modal vidéo (COMME PRODUCTCARD) */} + {/* Modal vidéo */} {showVideo && currentVideoUrl && ( -
-
e.stopPropagation()} - > - -
diff --git a/frontend-prep/src/pages/User/Checkout.css b/frontend-prep/src/pages/User/Checkout.css index 987cd88b..a8e2cd0e 100644 --- a/frontend-prep/src/pages/User/Checkout.css +++ b/frontend-prep/src/pages/User/Checkout.css @@ -138,6 +138,15 @@ transform: scale(1.05); } +.summary-item-image--placeholder { + display: flex; + align-items: center; + justify-content: center; + color: var(--text-muted); + font-size: 1.5rem; + object-fit: unset; +} + .summary-item-info { flex: 1; display: flex; diff --git a/frontend-prep/src/pages/User/Checkout.tsx b/frontend-prep/src/pages/User/Checkout.tsx index 3557874b..bf923c8b 100644 --- a/frontend-prep/src/pages/User/Checkout.tsx +++ b/frontend-prep/src/pages/User/Checkout.tsx @@ -1,8 +1,9 @@ import { useState, useEffect, useRef } from 'react'; import { useNavigate } from 'react-router-dom'; import { useCart } from '../../context/useCart'; -import { createCheckout, clearCart, extractUsernameFromToken, isUserAuthenticated, getReferralBalance, getPublicSettings, getMyProfile, getCryptoPaymentStatus } from '../../api/api'; +import { createCheckout, clearCart, extractUsernameFromToken, isUserAuthenticated, getReferralBalance, getPublicSettings, getMyProfile, getCryptoPaymentStatus, getProductById, getMediaUrl } from '../../api/api'; import type { CheckoutData, CryptoPaymentStatus } from '../../api/api'; +import type { Product } from '../../api/api'; import Navbar from '../../components/Navbar'; import './Checkout.css'; @@ -55,6 +56,25 @@ function Checkout() { const total = cartTotal; + // Images enrichies + const [itemImages, setItemImages] = useState>({}); + + useEffect(() => { + if (cartItems.length === 0) return; + cartItems.forEach(async (item) => { + try { + const res = await getProductById(item.product_id); + if (res.success && res.data) { + const p: Product = res.data; + const img = p.media?.find((m: any) => m && m.type === 'image'); + if (img?.url) { + setItemImages((prev) => ({ ...prev, [item.id]: getMediaUrl(img.url) })); + } + } + } catch { /* ignore */ } + }); + }, [cartItems]); + // Parrainage const [referralBalance, setReferralBalance] = useState(0); const [referralEnabled, setReferralEnabled] = useState(false); @@ -378,7 +398,13 @@ function Checkout() {
{cartItems.map((item, index) => (
- {item.name_product + {itemImages[item.id] ? ( + {item.name_product + ) : ( +
+ +
+ )}

{item.name_product}

diff --git a/frontend-prep/src/pages/User/ConsultationHistorique.css b/frontend-prep/src/pages/User/ConsultationHistorique.css index dbcf9aa7..2675b9b6 100644 --- a/frontend-prep/src/pages/User/ConsultationHistorique.css +++ b/frontend-prep/src/pages/User/ConsultationHistorique.css @@ -1,246 +1,269 @@ /* ============================================ - ConsultationHistorique.css - VERSION VIOLET SOMBRE - ============================================ - Styles pour 4 compteurs de points distincts */ + ConsultationHistorique.css — style mobile + ============================================ */ .history-container { width: 100%; min-height: 100vh; padding: clamp(1rem, 3vw, 2rem); - padding-top: calc(60px + clamp(1rem, 3vw, 2rem)); - max-width: 1400px; + padding-top: calc(60px + clamp(1rem, 3vw, 1.5rem)); + max-width: 900px; margin: 0 auto; - background: linear-gradient(to bottom, var(--bg), var(--surface)); } /* ============================================ - HEADER + TITRE PAGE ============================================ */ -.history-header { - text-align: center; - margin-bottom: 2rem; - margin-top: -65px; -} - .history-title { - font-size: clamp(2rem, 6vw, 2.5rem); - margin: 0 0 0.5rem 0; + font-size: 1.5rem; font-weight: 700; color: var(--text); -} - -.history-subtitle { - color: var(--text-muted); - font-size: clamp(0.9rem, 2vw, 1.1rem); - margin: 0; + margin: 0 0 1.25rem 0; } /* ============================================ - STATISTIQUES - GRID ADAPTÉ POUR 6 CARTES + STATS GRID — cartes centrées (style mobile) ============================================ */ .stats-grid { display: grid; - grid-template-columns: repeat(auto-fit, minmax(220px, 1fr)); - gap: 1.5rem; - margin-bottom: 2rem; + 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: linear-gradient(135deg, var(--surface), var(--surface-2)); - border: 2px solid var(--border); + background: var(--surface); + border: 1px solid var(--border); border-radius: 12px; - padding: 1.5rem; + padding: 1.25rem 1rem; display: flex; + flex-direction: column; align-items: center; - gap: 1rem; - transition: all 0.3s ease; - box-shadow: 0 4px 15px rgba(0, 0, 0, 0.3); + text-align: center; + gap: 0.35rem; + transition: transform 0.2s, border-color 0.2s, box-shadow 0.2s; + cursor: default; } -/* ============================================ - ✅ HOVER VIOLET SOMBRE POUR TOUTES LES CARTES - ============================================ */ - .stat-card2:hover { - border-color: #6d28d9; transform: translateY(-2px); - box-shadow: 0 8px 25px rgba(109, 40, 217, 0.4); + 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: 48px; - height: 48px; - background: linear-gradient(135deg, #7c3aed, #6d28d9); - border-radius: 12px; + width: 44px; + height: 44px; + border-radius: 10px; display: flex; align-items: center; justify-content: center; - color: white; - flex-shrink: 0; + font-size: 1.1rem; + color: #fff; + margin-bottom: 0.25rem; } -/* ============================================ - ✅ ICÔNES POUR COMMANDES - VIOLET SOMBRE - ============================================ */ - -.stat-icon.icon-total-orders { - background: linear-gradient(135deg, #6d28d9, #5b21b6); +.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; } -.stat-icon.icon-completed-orders { - background: linear-gradient(135deg, #8b5cf6, #7c3aed); -} - -/* ============================================ - ✅ STYLES SPÉCIFIQUES POUR LES POINTS - ============================================ */ - -/* Total Commandes - Violet Très Sombre */ -.stat-card2.total-orders:hover { - border-color: #5b21b6; - box-shadow: 0 8px 25px rgba(91, 33, 182, 0.5); -} - -.stat-icon.icon-total-orders { - background: linear-gradient(135deg, #6d28d9, #5b21b6); -} - -/* Commandes Livrées - Violet Sombre Clair */ -.stat-card2.completed-orders:hover { - border-color: #7c3aed; - box-shadow: 0 8px 25px rgba(124, 58, 237, 0.5); -} - -.stat-icon.icon-completed-orders { - background: linear-gradient(135deg, #8b5cf6, #7c3aed); -} - -/* Points Weed/Hash - Vert (couleur d'origine) */ -.stat-card2.points-weed:hover { - border-color: #5b21b6; - box-shadow: 0 8px 25px rgba(91, 33, 182, 0.5); -} - -.stat-icon.icon-weed { - background: linear-gradient(135deg, #10b981, #059669); -} - -/* Points Zipette - Bleu (couleur d'origine) */ -.stat-card2.points-zipette:hover { - border-color: #6d28d9; - box-shadow: 0 8px 25px rgba(109, 40, 217, 0.5); -} - -.stat-icon.icon-zipette { - background: linear-gradient(135deg, #3b82f6, #2563eb); -} - -/* Points Total - Orange (couleur d'origine) */ -.stat-card2.points-total:hover { - border-color: #4c1d95; - box-shadow: 0 8px 25px rgba(76, 29, 149, 0.5); -} - -.stat-icon.icon-total { - background: linear-gradient(135deg, #f59e0b, #d97706); -} - -/* ============================================ - STAT CONTENT - ============================================ */ - -.stat-content { - flex: 1; -} - -.stat-label { - color: var(--text-muted); - font-size: 0.9rem; - margin: 0 0 0.5rem 0; - font-weight: 500; +@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.8rem; + font-size: 1.75rem; font-weight: 700; margin: 0; + line-height: 1; } -/* ============================================ - PENALTY STAT CARD - VIOLET SOMBRE - ============================================ */ +.stat-value.value-danger { color: #ef4444; } +.stat-value.value-warning { color: #f59e0b; } -.penalty-stat { - position: relative; -} - -.penalty-stat.has-penalty:hover { - border-color: #7c3aed; - box-shadow: 0 8px 25px rgba(124, 58, 237, 0.5); -} - -.stat-icon.penalty-warning { - background: linear-gradient(135deg, #8b5cf6, #7c3aed); -} - -.stat-icon.penalty-critical { - background: linear-gradient(135deg, #6d28d9, #5b21b6); - animation: pulse-penalty 2s ease-in-out infinite; -} - -@keyframes pulse-penalty { - 0%, - 100% { - box-shadow: 0 0 0 0 rgba(109, 40, 217, 0.7); - } - 50% { - box-shadow: 0 0 0 10px rgba(109, 40, 217, 0); - } -} - -.penalty-value { - display: inline-flex; - align-items: baseline; - gap: 0.25rem; -} - -.penalty-limit { - font-size: 1rem; +.stat-label { color: var(--text-muted); + font-size: 0.78rem; + margin: 0; font-weight: 500; } -.penalty-warning-text { +/* ============================================ + 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.85rem; - margin: 0.5rem 0 0 0; + font-size: 0.95rem; font-weight: 600; } +.referral-btn-chevron { + color: var(--text-muted); + font-size: 0.85rem; +} + /* ============================================ - PARRAINAGE STAT CARD + SECTION TITLE ============================================ */ -.stat-card2.referral-stat:hover { - border-color: #8b5cf6; - box-shadow: 0 8px 25px rgba(139, 92, 246, 0.4); +.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; } -.stat-icon.icon-referral { - background: linear-gradient(135deg, #8b5cf6, #6d28d9); +/* ============================================ + ORDER CARDS + ============================================ */ + +.orders-list { + display: flex; + flex-direction: column; + gap: 0.75rem; } -.referral-value-active { - color: #8b5cf6 !important; +.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; } -.referral-link-hint { +.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 { + display: flex; + align-items: flex-start; + gap: 0.5rem; + 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: 0.25rem 0 0; + 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 { + display: flex; + justify-content: space-between; + align-items: center; + margin-top: 0.875rem; + padding-top: 0.875rem; + border-top: 1px solid var(--border); +} + +.order-card-total { + color: #10b981; + font-size: 1.1rem; + font-weight: 700; +} + +.order-card-chevron { + color: var(--text-muted); + font-size: 0.9rem; } /* ============================================ @@ -257,28 +280,26 @@ } .loading-spinner { - width: 60px; - height: 60px; + width: 50px; + height: 50px; } .spinner { width: 100%; height: 100%; - border: 4px solid var(--border); + border: 3px solid var(--border); border-top-color: #7c3aed; border-radius: 50%; - animation: spin 1s linear infinite; + animation: spin 0.9s linear infinite; } @keyframes spin { - to { - transform: rotate(360deg); - } + to { transform: rotate(360deg); } } .loading-text { color: var(--text-muted); - font-size: 1.1rem; + font-size: 1rem; margin: 0; } @@ -290,19 +311,15 @@ display: flex; align-items: center; gap: 1rem; - background: rgba(109, 40, 217, 0.1); - border: 2px solid #7c3aed; - border-radius: 12px; - padding: 1rem 1.5rem; - margin-bottom: 2rem; -} - -.error-banner span { - font-size: 1.5rem; + 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 { - color: #c4b5fd; margin: 0; font-weight: 500; } @@ -314,372 +331,63 @@ .empty-history { text-align: center; padding: 4rem 2rem; - background: linear-gradient(135deg, var(--surface), var(--surface-2)); - border: 2px solid var(--border); + background: var(--surface); + border: 1px solid var(--border); border-radius: 12px; - margin-top: 2rem; + margin-top: 1rem; } .empty-icon { + font-size: 4rem; color: var(--text-muted); - margin-bottom: 1.5rem; - opacity: 0.5; + opacity: 0.4; + margin-bottom: 1.25rem; } .empty-history h2 { color: var(--text); - font-size: 1.5rem; + font-size: 1.4rem; margin: 0 0 0.5rem 0; font-weight: 600; } .empty-history p { color: var(--text-muted); - font-size: 1.1rem; - margin: 0 0 2rem 0; + 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.75rem 2rem; + padding: 0.7rem 1.75rem; border-radius: 8px; - font-size: 1rem; + font-size: 0.95rem; font-weight: 600; cursor: pointer; - transition: all 0.2s; - box-shadow: 0 4px 15px rgba(124, 58, 237, 0.3); + transition: opacity 0.2s, transform 0.2s; } .browse-button:hover { - background: linear-gradient(to right, #6d28d9, #5b21b6); - transform: translateY(-2px); - box-shadow: 0 6px 20px rgba(109, 40, 217, 0.5); -} - -.browse-button:active { - transform: translateY(0); -} - -/* ============================================ - SUMMARY - ============================================ */ - -.orders-summary { - margin-bottom: 1rem; - padding: 0.75rem 1rem; - background: rgba(124, 58, 237, 0.1); - border: 1px solid rgba(124, 58, 237, 0.3); - border-radius: 8px; -} - -.orders-summary p { - color: #a78bfa; - margin: 0; - font-weight: 600; - font-size: 0.95rem; -} - -/* ============================================ - TABLE - HOVER VIOLET SOMBRE - ============================================ */ - -.table-wrapper { - overflow-x: auto; - background-color: var(--surface); - border: 2px solid var(--border); - border-radius: 12px; - box-shadow: 0 4px 15px rgba(0, 0, 0, 0.3); -} - -.history-table { - width: 100%; - border-collapse: collapse; - min-width: 900px; -} - -.history-table thead { - background: linear-gradient(135deg, var(--bg), var(--surface)); - border-bottom: 2px solid #7c3aed; -} - -.history-table th { - color: var(--text); - font-size: clamp(0.85rem, 2vw, 0.95rem); - font-weight: 700; - text-align: left; - padding: 1.25rem 1rem; - text-transform: uppercase; - letter-spacing: 0.5px; -} - -.history-table tbody tr { - border-bottom: 1px solid var(--border); - transition: all 0.2s ease; - cursor: pointer; -} - -.history-table tbody tr:last-child { - border-bottom: none; -} - -/* ✅ HOVER VIOLET SOMBRE SUR LES LIGNES */ -.history-table tbody tr:hover { - background: linear-gradient(90deg, rgba(109, 40, 217, 0.15), transparent); - border-left: 3px solid #6d28d9; -} - -.history-table tbody tr:active { - background-color: var(--surface-2); -} - -.history-table td { - color: var(--text-muted); - font-size: clamp(0.85rem, 2vw, 0.95rem); - padding: 1.25rem 1rem; - vertical-align: middle; -} - -/* ============================================ - TABLE CELLS - SPECIFIC STYLES - ============================================ */ - -.order-id { - color: var(--text) !important; - font-weight: 700; - font-family: "Courier New", monospace; - font-size: 1rem; -} - -.date-cell { - display: flex; - flex-direction: column; - gap: 0.25rem; -} - -.date-main { - color: var(--text); - font-weight: 600; - font-size: 0.95rem; -} - -.date-time { - color: var(--text-muted); - font-size: 0.85rem; -} - -.date-age { - color: #7c3aed; - font-size: 0.8rem; - font-weight: 500; -} - -.address-cell { - display: flex; - align-items: center; - gap: 0.5rem; -} - -.address-icon { - color: #7c3aed; - flex-shrink: 0; -} - -.address-text { - color: var(--text-muted); - font-size: 0.9rem; -} - -.livreur-cell { - display: flex; - align-items: center; - gap: 0.5rem; -} - -.livreur-icon { - color: #7c3aed; - flex-shrink: 0; -} - -.no-livreur { - color: var(--text-muted); - font-style: italic; -} - -.products-count { - color: var(--text-muted); - font-size: 0.9rem; -} - -.order-total2 { - color: #7c3aed !important; - font-weight: 700 !important; - font-size: 1.1rem !important; -} - -/* ============================================ - STATUS BADGE - VIOLET SOMBRE - ============================================ */ - -.status-badge { - display: inline-flex; - align-items: center; - gap: 0.4rem; - padding: 0.5rem 1rem; - border-radius: 20px; - font-size: 0.85rem; - font-weight: 700; - text-transform: uppercase; - letter-spacing: 0.5px; - white-space: nowrap; -} - -.status-badge.delivered { - background: transparent; - color: #a78bfa; - border: 2px solid #7c3aed; - box-shadow: 0 0 30px rgba(124, 58, 237, 0.5); -} - -.status-badge.in-progress { - background: rgba(139, 92, 246, 0.15); - color: #a78bfa; - border: 2px solid #8b5cf6; - box-shadow: 0 0 10px rgba(139, 92, 246, 0.3); -} - -.status-badge.pending { - background: rgba(124, 58, 237, 0.15); - color: #c4b5fd; - border: 2px solid #7c3aed; + opacity: 0.9; + transform: translateY(-1px); } /* ============================================ RESPONSIVE ============================================ */ -@media (max-width: 1024px) { - .history-table { - min-width: 800px; - } - - .stats-grid { - grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); - } -} - -@media (max-width: 768px) { - .stats-grid { - grid-template-columns: 1fr 1fr; - gap: 1rem; - } - - .stat-card2 { - padding: 1.25rem; - } - - .history-table th, - .history-table td { - padding: 1rem 0.75rem; - font-size: 0.85rem; - } - - .order-id { - font-size: 0.9rem; - } - - .order-total { - font-size: 1rem !important; - } - - .address-text { - font-size: 0.85rem; - } -} - @media (max-width: 600px) { - .history-container { - padding: 1rem; - padding-top: calc(60px + 1rem); - } - .stats-grid { - grid-template-columns: 1fr; - } - - .table-wrapper { - border-radius: 8px; - } - - .history-table { - min-width: 700px; - font-size: 0.8rem; - } - - .history-table th, - .history-table td { - padding: 0.875rem 0.625rem; + gap: 0.625rem; } .stat-value { font-size: 1.5rem; } - .empty-history { - padding: 3rem 1.5rem; - } -} - -/* ============================================ - HOVER EFFECTS (Desktop only) - VIOLET SOMBRE - ============================================ */ - -@media (hover: hover) { - .clickable-row { - position: relative; - } - - .clickable-row::before { - content: "→"; - position: absolute; - right: 1rem; - color: #a78bfa; - font-size: 1.5rem; - opacity: 0; - transform: translateX(-10px); - transition: all 0.2s ease; - } - - .clickable-row:hover::before { - opacity: 1; - transform: translateX(0); - } -} - -/* ============================================ - PRINT STYLES - ============================================ */ - -@media print { - .history-container { - background: white; - padding: 1rem; - } - - .stats-grid, - .browse-button, - .status-badge { - display: none; - } - - .history-table { - border: 1px solid #000; - } - - .history-table th, - .history-table td { - color: #000; - border: 1px solid #ccc; + .order-card-number { + font-size: 0.9rem; } } diff --git a/frontend-prep/src/pages/User/ConsultationHistorique.tsx b/frontend-prep/src/pages/User/ConsultationHistorique.tsx index 1c239c8d..b561e231 100644 --- a/frontend-prep/src/pages/User/ConsultationHistorique.tsx +++ b/frontend-prep/src/pages/User/ConsultationHistorique.tsx @@ -1,9 +1,3 @@ -// ============================================ -// pages/ConsultationHistorique.tsx - VERSION AVEC FONT AWESOME -// ============================================ -// Page d'historique avec 4 compteurs de points distincts -// ✅ AJOUT: Vérification continue de l'authentification - import { useState, useEffect } from 'react'; import { useNavigate } from 'react-router-dom'; import Navbar from '../../components/Navbar'; @@ -19,9 +13,7 @@ import { } from '../../api/api'; import type { PublicSettings } from '../../api/api'; import type { CompletedOrder, ClientStats, PenaltyInfo } from "../../api/api_types"; -import { Package, MapPin, User, TrendingUp } from 'lucide-react'; -// ✅ Import Font Awesome import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; import { faCannabis, @@ -31,13 +23,20 @@ import { faStar, faTrophy, faExclamationTriangle, - faCheckCircle, + faShieldAlt, faGift, + faReceipt, + faMapMarkerAlt, + faClock, + faBicycle, + faChevronRight, + faCheckCircle, + faHistory, } from '@fortawesome/free-solid-svg-icons'; function ConsultationHistorique() { const navigate = useNavigate(); - + const [orders, setOrders] = useState([]); const [clientStats, setClientStats] = useState(null); const [penalties, setPenalties] = useState(null); @@ -46,28 +45,15 @@ function ConsultationHistorique() { const [isLoading, setIsLoading] = useState(true); const [error, setError] = useState(''); - // ✅ VÉRIFICATION INITIALE DE L'AUTHENTIFICATION useEffect(() => { - const checkAuth = () => { - if (!isUserAuthenticated()) { - console.log('❌ [ConsultationHistorique] Utilisateur non authentifié, redirection vers /login/client'); - navigate('/login/client', { replace: true }); - } - }; - - checkAuth(); + if (!isUserAuthenticated()) navigate('/login/client', { replace: true }); }, [navigate]); - // ✅ VÉRIFICATION CONTINUE (toutes les 5 secondes) useEffect(() => { - const authInterval = setInterval(() => { - if (!isUserAuthenticated()) { - console.log('❌ [ConsultationHistorique] Session expirée, redirection vers /login/client'); - navigate('/login/client', { replace: true }); - } + const interval = setInterval(() => { + if (!isUserAuthenticated()) navigate('/login/client', { replace: true }); }, 5000); - - return () => clearInterval(authInterval); + return () => clearInterval(interval); }, [navigate]); useEffect(() => { @@ -82,34 +68,18 @@ function ConsultationHistorique() { }, []); const fetchHistory = async () => { - // ✅ Vérifier l'auth avant de charger l'historique - if (!isUserAuthenticated()) { - console.log('❌ [fetchHistory] Non authentifié'); - navigate('/login/client', { replace: true }); - return; - } - + if (!isUserAuthenticated()) { navigate('/login/client', { replace: true }); return; } setIsLoading(true); setError(''); - try { - console.log('📚 [HISTORY] Chargement historique...'); const result = await getMyCompletedOrders(); - if (result.success) { - console.log('✅ [HISTORY] Historique chargé:', result.count, 'commandes'); - - console.log('🔍 [DEBUG] result.client_stats:', result.client_stats); - console.log('🔍 [DEBUG] points:', result.client_stats?.points); - setOrders(result.commands); setClientStats(result.client_stats || null); } else { - console.error('❌ [HISTORY] Erreur:', result.message); setError(result.message || 'Erreur lors du chargement'); } - } catch (err) { - console.error('❌ [HISTORY] Erreur catch:', err); + } catch { setError('Erreur de connexion'); } finally { setIsLoading(false); @@ -117,39 +87,23 @@ function ConsultationHistorique() { }; const fetchPenalties = async () => { - // ✅ Vérifier l'auth avant de charger les pénalités - if (!isUserAuthenticated()) { - console.log('❌ [fetchPenalties] Non authentifié'); - navigate('/login/client', { replace: true }); - return; - } - + if (!isUserAuthenticated()) return; try { - console.log('🚨 [PENALTIES] Chargement pénalités...'); const result = await getMyPenalties(); - - if (result.success && result.data) { - console.log('✅ [PENALTIES] Pénalités chargées:', result.data); - setPenalties(result.data); - } - } catch (err) { - console.error('❌ [PENALTIES] Erreur:', err); - } + if (result.success && result.data) setPenalties(result.data); + } catch { /* ignore */ } }; - const getProductCount = (totalPrix: number): number => { - return Math.max(1, Math.round(totalPrix / 25)); + const viewOrderDetails = (order: CompletedOrder) => { + if (!isUserAuthenticated()) { navigate('/login/client', { replace: true }); return; } + navigate(`/user/commande/${order.client_order_number ?? order.id}`, { + state: { commandId: order.id }, + }); }; - const viewOrderDetails = (orderId: number) => { - // ✅ Vérifier l'auth avant de naviguer - if (!isUserAuthenticated()) { - console.log('❌ [viewOrderDetails] Non authentifié'); - navigate('/login/client', { replace: true }); - return; - } - - navigate(`/user/commande/${orderId}`); + const formatDate = (dateStr: string) => { + const d = new Date(dateStr); + return d.toLocaleDateString('fr-FR', { day: '2-digit', month: 'short', year: 'numeric' }); }; if (isLoading) { @@ -158,9 +112,7 @@ function ConsultationHistorique() {

-
-
-
+

Chargement de l'historique...

@@ -168,155 +120,86 @@ function ConsultationHistorique() { ); } + const poolNames = clientStats?.pool_names?.length ? clientStats.pool_names : appSettings.pool_names; + const poolPoints = clientStats?.pool_points ?? [clientStats?.points ?? 0]; + const totalPoints = poolPoints.reduce((s, v) => s + (v || 0), 0); + const penaltyCount = penalties?.total_penalty || clientStats?.penalties || 0; + const poolIcons = [faCannabis, faPills, faFlask, faMortarPestle, faStar]; + const poolIconColors = ['#10b981', '#e879f9', '#fb923c', '#38bdf8', '#7c3aed']; + return ( <>
- -
-

Historique des commandes

+ +

Historique des commandes

+ + {/* Stats grid */} +
+ + {/* Total commandes */} +
+
+ +
+

{clientStats?.total_commands ?? orders.length}

+

Commandes

+
+ + {/* Points */} + {appSettings.points_enabled && ( + poolNames.length <= 1 ? ( +
+
+ +
+

{poolPoints[0] || 0}

+

Pts {poolNames[0] ?? 'Points'}

+
+ ) : ( + <> + {poolNames.map((name, i) => ( +
+
+ +
+

{poolPoints[i] || 0}

+

Pts {name}

+
+ ))} + {totalPoints > 0 && ( +
+
+ +
+

{totalPoints}

+

Total Points

+
+ )} + + ) + )} + + {/* Score amendes */} + {appSettings.show_amende_score && ( +
= 3 ? ' stat-card-danger' : penaltyCount > 0 ? ' stat-card-warning' : ''}`}> +
= 3 ? 'icon-penalty-critical' : penaltyCount > 0 ? 'icon-penalty-warning' : 'icon-penalty-ok'}`}> + 0 ? faExclamationTriangle : faShieldAlt} /> +
+

= 3 ? 'value-danger' : penaltyCount > 0 ? 'value-warning' : ''}`}>{penaltyCount}

+

Score amendes

+
+ )}
- {/* ✅ STATISTIQUES CLIENT - 6 CARTES AVEC ICÔNES FONT AWESOME */} - {clientStats && ( -
- {/* Carte 1: Total Commandes */} -
-
- -
-
-

Total commandes

-

{clientStats.total_commands}

-
-
- - {/* Cartes points - affichées uniquement si le système de points est activé */} - {appSettings.points_enabled && (() => { - const poolNames = clientStats.pool_names?.length ? clientStats.pool_names : appSettings.pool_names; - const poolPoints = clientStats.pool_points ?? [clientStats.points]; - const poolIcons = [faCannabis, faPills, faFlask, faMortarPestle, faStar]; - const poolClasses = poolNames.map((_, i) => `points-pool-${i}`); - const poolIconClasses = poolNames.map((_, i) => `icon-pool-${i}`); - - if (poolNames.length <= 1) { - return ( -
-
- -
-
-

- - Points {poolNames[0] ?? 'Points'} -

-

{poolPoints[0] || 0}

-
-
- ); - } - - const total = poolPoints.reduce((s, v) => s + (v || 0), 0); - return ( - <> - {poolNames.map((name, i) => ( -
-
- -
-
-

- - Points {name} -

-

{poolPoints[i] || 0}

-
-
- ))} - {total > 0 && poolNames.length > 1 && ( -
-
- -
-
-

- - Total Points -

-

{total}

-
-
- )} - - ); - })()} - - {/* Carte 5: Commandes Livrées */} -
-
- -
-
-

Commandes livrées

-

{orders.length}

-
-
- - {/* Carte pénalités - affichée uniquement si le score amendes est activé */} - {appSettings.show_amende_score && penalties && ( -
0 ? 'has-penalty' : ''}`}> -
= 100 - ? 'penalty-critical' - : penalties.total_penalty > 0 - ? 'penalty-warning' - : '' - }`} - > - -
- -
-

Points de pénalité

- -

- {penalties.total_penalty} -

- - {penalties.total_penalty > 0 && ( -

- {penalties.total_penalty >= 100 - ? 'Commandes bloquées' - : `${penalties.cancellations_count} annulation${ - penalties.cancellations_count > 1 ? 's' : '' - }` - } -

- )} -
-
- )} - - {/* Carte parrainage - affichée si le parrainage est activé */} - {appSettings.referral_enabled && ( -
navigate('/user/parrainage')} - style={{ cursor: 'pointer' }} - > -
- -
-
-

Solde parrainage

-

0 ? 'referral-value-active' : ''}`}> - {referralBalance.toFixed(2)} € -

-

Voir le programme →

-
-
- )} + {/* Bouton parrainage */} + {appSettings.referral_enabled && ( +
navigate('/user/parrainage')}> + + + Parrainage{referralBalance > 0 ? ` — ${referralBalance.toFixed(2)} €` : ''} + +
)} @@ -329,107 +212,67 @@ function ConsultationHistorique() { {orders.length === 0 ? (
- -

Aucune commande terminée

-
) : ( <> -
-

{orders.length} commande{orders.length > 1 ? 's' : ''} terminée{orders.length > 1 ? 's' : ''}

-
+

Historique des commandes

+
+ {orders.map((order) => ( +
viewOrderDetails(order)} + > +
+ + Commande #{(order.client_order_number ?? 0).toString().padStart(4, '0')} + + + + Livrée + +
-
- - - - - - - - - - - - - - {orders.map((order) => ( - viewOrderDetails(order.id)} - className="clickable-row" - > - - - - - - - - - ))} - -
N° CommandeDateAdresseLivreurProduitsTotalStatut
- #{(order.client_order_number ?? 0).toString().padStart(4, '0')} - -
- - {new Date(order.created_at).toLocaleDateString('fr-FR', { - day: '2-digit', - month: 'short', - year: 'numeric' - })} - - - {new Date(order.created_at).toLocaleTimeString('fr-FR', { - hour: '2-digit', - minute: '2-digit' - })} - - {getOrderAge(order.created_at)} -
-
-
- - - {order.adresse.length > 40 - ? order.adresse.substring(0, 40) + '...' - : order.adresse - } - -
-
-
- {order.livreur_assign ? ( - <> - - {order.livreur_assign} - - ) : ( - Non assigné - )} -
-
- ~{getProductCount(order.total_prix)} produit{getProductCount(order.total_prix) > 1 ? 's' : ''} - - {formatPrice(order.total_prix)} - - - - Livrée - -
+
+ + + {order.adresse ? (order.adresse.length > 50 ? order.adresse.substring(0, 50) + '…' : order.adresse) : 'N/A'} + +
+ +
+ + + {formatDate(order.created_at)} · {getOrderAge(order.created_at)} + +
+ + {order.livreur_assign && ( +
+ + {order.livreur_assign} +
+ )} + +
+ {formatPrice(order.total_prix || 0)} + +
+
+ ))}
)} +
); } -export default ConsultationHistorique; \ No newline at end of file +export default ConsultationHistorique; diff --git a/frontend-prep/src/pages/User/OrderDetails.tsx b/frontend-prep/src/pages/User/OrderDetails.tsx index 64f6eb04..e8d4e2c3 100644 --- a/frontend-prep/src/pages/User/OrderDetails.tsx +++ b/frontend-prep/src/pages/User/OrderDetails.tsx @@ -1,5 +1,5 @@ import { useState, useEffect } from "react"; -import { useParams, useNavigate } from "react-router-dom"; +import { useParams, useNavigate, useLocation } from "react-router-dom"; import Navbar from "../../components/Navbar"; import "./OrderDetails.css"; import { @@ -55,6 +55,10 @@ interface OrderDetailsData extends CompletedOrder { function OrderDetails() { const { orderId } = useParams<{ orderId: string }>(); const navigate = useNavigate(); + const location = useLocation(); + // commandId = ID global pour l'API (passé en state depuis l'historique) + // fallback sur orderId si navigation directe via URL + const commandId = (location.state as { commandId?: number } | null)?.commandId ?? parseInt(orderId ?? "0"); const [order, setOrder] = useState(null); const [enrichedProducts, setEnrichedProducts] = useState< @@ -95,8 +99,8 @@ function OrderDetails() { }, [navigate]); useEffect(() => { - if (orderId) { - fetchOrderDetails(parseInt(orderId)); + if (commandId) { + fetchOrderDetails(commandId); } }, [orderId]);