From 25cd6429f2f806221d7c4139a319d97e6a55b278 Mon Sep 17 00:00:00 2001 From: Xor290 Date: Mon, 30 Mar 2026 20:02:39 +0200 Subject: [PATCH] chore: update --- frontend-prep/src/App.tsx | 3 + frontend-prep/src/api/api.ts | 49 ++++ frontend-prep/src/api/api_types.ts | 4 + frontend-prep/src/components/Items.css | 60 ++--- frontend-prep/src/components/Navbar.css | 49 +++- frontend-prep/src/components/Navbar.tsx | 13 ++ frontend-prep/src/components/ProductCard.css | 42 ++-- frontend-prep/src/components/ProductModal.css | 86 +++---- frontend-prep/src/components/Toast.css | 2 +- frontend-prep/src/context/ThemeContext.tsx | 41 ++++ frontend-prep/src/index.css | 9 +- .../pages/ChangePassword/ChangePassword.css | 32 +-- frontend-prep/src/pages/Home.css | 3 +- frontend-prep/src/pages/LoginClient/Login.css | 47 ++-- frontend-prep/src/pages/LoginClient/Login.tsx | 12 +- frontend-prep/src/pages/User/Cart.css | 80 +++---- frontend-prep/src/pages/User/Checkout.css | 211 ++++++++++++------ frontend-prep/src/pages/User/Checkout.tsx | 32 ++- .../src/pages/User/ConsultationHistorique.css | 62 +++-- .../src/pages/User/ConsultationHistorique.tsx | 4 +- frontend-prep/src/pages/User/ModalSuccess.css | 6 +- frontend-prep/src/pages/User/OrderDetails.css | 82 +++---- frontend-prep/src/pages/User/OrderDetails.tsx | 18 +- frontend-prep/src/pages/User/Parrainage.css | 73 ++++-- frontend-prep/src/pages/User/Parrainage.tsx | 18 ++ .../src/pages/User/ProductDetail.css | 83 ++++--- frontend-prep/src/pages/User/ProfilePage.css | 154 +++++++++++-- frontend-prep/src/pages/User/ProfilePage.tsx | 91 +++++++- .../src/pages/User/SuiviLivraison.css | 136 +++++------ .../src/pages/User/SuiviLivraison.tsx | 2 +- frontend-prep/src/pages/User/UserAccueil.css | 34 ++- frontend-prep/src/styles/PageLayout.css | 26 +-- 32 files changed, 1024 insertions(+), 540 deletions(-) create mode 100644 frontend-prep/src/context/ThemeContext.tsx diff --git a/frontend-prep/src/App.tsx b/frontend-prep/src/App.tsx index fb44f80c..26416e15 100644 --- a/frontend-prep/src/App.tsx +++ b/frontend-prep/src/App.tsx @@ -1,5 +1,6 @@ import { BrowserRouter as Router, Routes, Route } from "react-router-dom"; import { CartProvider } from "./context/CartContext"; +import { ThemeProvider } from "./context/ThemeContext"; import "./App.css"; // Pages principales @@ -21,6 +22,7 @@ import ChangePasswordPage from "./pages/ChangePassword/ChangePassword"; function App() { return ( + {/* Routes Login - SANS CartProvider */} @@ -87,6 +89,7 @@ function App() { /> + ); } diff --git a/frontend-prep/src/api/api.ts b/frontend-prep/src/api/api.ts index ad71aa2a..6253b65e 100644 --- a/frontend-prep/src/api/api.ts +++ b/frontend-prep/src/api/api.ts @@ -1858,6 +1858,7 @@ export interface PublicSettings { points_enabled: boolean; points_separated: boolean; referral_enabled: boolean; + referral_amount: number; pool_names: string[]; crypto_payment_enabled: boolean; crypto_only: boolean; @@ -1871,6 +1872,7 @@ export const getPublicSettings = async (): Promise => { points_enabled: true, points_separated: true, referral_enabled: true, + referral_amount: 0, pool_names: ["Pool 1", "Pool 2"], crypto_payment_enabled: false, crypto_only: false, @@ -1886,6 +1888,7 @@ export const getPublicSettings = async (): Promise => { points_enabled: data.points_enabled ?? true, points_separated: data.points_separated ?? true, referral_enabled: data.referral_enabled ?? true, + referral_amount: data.referral_amount ?? 0, pool_names: Array.isArray(data.pool_names) && data.pool_names.length > 0 ? data.pool_names @@ -1903,6 +1906,7 @@ export const getPublicSettings = async (): Promise => { export interface CryptoPaymentStatus { command_id: number; + client_order_number?: number; payment_status: string; pay_address: string; pay_amount: number; @@ -2001,3 +2005,48 @@ export const updateMyProfile = async (fields: { return { success: false, message: "Erreur de connexion" }; } }; + +// ============================================ +// 🤖 TELEGRAM +// ============================================ + +export const getTelegramStatus = async (): Promise<{ linked: boolean; enabled: boolean }> => { + const token = getAuthToken(); + if (!token) return { linked: false, enabled: false }; + try { + const response = await fetch(`${API_URL}/telegram/status`, { + headers: { Authorization: `Bearer ${token}` }, + }); + return await safeJson(response); + } catch { + return { linked: false, enabled: false }; + } +}; + +export const generateTelegramLinkToken = async (): Promise<{ + link_url?: string; + error?: string; +}> => { + const token = getAuthToken(); + if (!token) return { error: "Non authentifié" }; + try { + const response = await fetch(`${API_URL}/telegram/link-token`, { + method: "POST", + headers: { Authorization: `Bearer ${token}` }, + }); + return await safeJson(response); + } catch { + return { error: "Erreur de connexion" }; + } +}; + +export const unlinkTelegram = async (): Promise => { + const token = getAuthToken(); + if (!token) return; + try { + await fetch(`${API_URL}/telegram/unlink`, { + method: "DELETE", + headers: { Authorization: `Bearer ${token}` }, + }); + } catch { /* silencieux */ } +}; diff --git a/frontend-prep/src/api/api_types.ts b/frontend-prep/src/api/api_types.ts index eede2320..7c7d9dff 100644 --- a/frontend-prep/src/api/api_types.ts +++ b/frontend-prep/src/api/api_types.ts @@ -227,6 +227,8 @@ export interface OrderItem { */ export interface OrderDetail { id: number; + client_order_number?: number; + client_order_id?: number; username: string; status: string; delivery_address?: string; @@ -565,6 +567,8 @@ export interface CheckoutFormState { export interface CompletedOrder { id: number; + client_order_number?: number; + client_order_id?: number; username: string; status: string; adresse: string; diff --git a/frontend-prep/src/components/Items.css b/frontend-prep/src/components/Items.css index cdb33d1a..358077fc 100644 --- a/frontend-prep/src/components/Items.css +++ b/frontend-prep/src/components/Items.css @@ -30,8 +30,8 @@ /* Modal Content */ .items-modal-content { - background: linear-gradient(to bottom, #1a1a1a, #0f0f0f); - border: 1px solid rgba(255, 255, 255, 0.1); + background: linear-gradient(to bottom, var(--surface), var(--bg)); + border: 1px solid var(--border); border-radius: 16px; max-width: 800px; width: 100%; @@ -62,8 +62,8 @@ justify-content: space-between; align-items: center; padding: 1.5rem 2rem; - border-bottom: 1px solid rgba(255, 255, 255, 0.08); - background: rgba(255, 255, 255, 0.02); + border-bottom: 1px solid var(--border); + background: var(--surface-2); flex-shrink: 0; } @@ -71,7 +71,7 @@ display: flex; align-items: center; gap: 0.75rem; - color: #ffffff; + color: var(--text); font-size: 1.25rem; margin: 0; font-weight: 600; @@ -102,7 +102,7 @@ .items-modal-close { background: transparent; border: none; - color: #6b7280; + color: var(--text-muted); width: 36px; height: 36px; border-radius: 8px; @@ -117,8 +117,8 @@ } .items-modal-close:hover { - background: rgba(255, 255, 255, 0.05); - color: #ffffff; + background: var(--surface-2); + color: var(--text); } /* Body */ @@ -126,7 +126,7 @@ padding: 2rem; overflow-y: auto; flex: 1; - background: #0a0a0a; + background: var(--bg); } .items-modal-body::-webkit-scrollbar { @@ -153,19 +153,19 @@ align-items: center; justify-content: center; padding: 4rem 2rem; - color: #6b7280; + color: var(--text-muted); gap: 1rem; } .items-modal-empty svg { - color: #374151; + color: var(--text-muted); opacity: 0.5; } .items-modal-empty p { font-size: 1rem; margin: 0; - color: #9ca3af; + color: var(--text-muted); } /* Summary */ @@ -181,14 +181,14 @@ flex-direction: column; gap: 0.5rem; padding: 1.25rem; - background: rgba(255, 255, 255, 0.03); - border: 1px solid rgba(255, 255, 255, 0.08); + background: var(--surface); + border: 1px solid var(--border); border-radius: 12px; transition: all 0.2s ease; } .summary-item:hover { - background: rgba(255, 255, 255, 0.05); + background: var(--surface-2); border-color: rgba(124, 58, 237, 0.3); } @@ -199,7 +199,7 @@ } .summary-item span { - color: #ffffff; + color: var(--text); font-weight: 600; font-size: 1.5rem; line-height: 1; @@ -207,7 +207,7 @@ .summary-item::before { content: attr(data-label); - color: #9ca3af; + color: var(--text-muted); font-size: 0.875rem; font-weight: 500; text-transform: uppercase; @@ -224,8 +224,8 @@ /* Item Card */ .items-modal-card { - background: rgba(255, 255, 255, 0.03); - border: 1px solid rgba(255, 255, 255, 0.08); + background: var(--surface); + border: 1px solid var(--border); border-radius: 12px; padding: 1.5rem; transition: all 0.2s ease; @@ -234,7 +234,7 @@ } .items-modal-card:hover { - background: rgba(255, 255, 255, 0.05); + background: var(--surface-2); border-color: rgba(124, 58, 237, 0.4); box-shadow: 0 8px 24px rgba(124, 58, 237, 0.15); } @@ -271,7 +271,7 @@ } .item-name { - color: #f9fafb; + color: var(--text); font-size: 1rem; font-weight: 600; line-height: 1.5; @@ -325,7 +325,7 @@ } .detail-label { - color: #9ca3af; + color: var(--text-muted); font-size: 0.8125rem; font-weight: 500; text-transform: uppercase; @@ -333,7 +333,7 @@ } .detail-value { - color: #f9fafb; + color: var(--text); font-size: 1.125rem; font-weight: 600; } @@ -350,11 +350,11 @@ gap: 0.75rem; padding-top: 0.75rem; margin-top: 0.75rem; - border-top: 1px solid rgba(255, 255, 255, 0.05); + border-top: 1px solid var(--border); } .item-id { - color: #6b7280; + color: var(--text-muted); font-size: 0.8125rem; font-family: 'SF Mono', 'Monaco', 'Courier New', monospace; } @@ -372,7 +372,7 @@ } .items-modal-total span { - color: #9ca3af; + color: var(--text-muted); font-size: 0.875rem; font-weight: 500; text-transform: uppercase; @@ -525,13 +525,13 @@ /* Touch Devices */ @media (hover: none) { .items-modal-card:hover { - background: rgba(255, 255, 255, 0.03); - border-color: rgba(255, 255, 255, 0.08); + background: var(--surface); + border-color: var(--border); box-shadow: none; } .summary-item:hover { - background: rgba(255, 255, 255, 0.03); - border-color: rgba(255, 255, 255, 0.08); + background: var(--surface); + border-color: var(--border); } } \ No newline at end of file diff --git a/frontend-prep/src/components/Navbar.css b/frontend-prep/src/components/Navbar.css index eb449904..f87e4f0f 100644 --- a/frontend-prep/src/components/Navbar.css +++ b/frontend-prep/src/components/Navbar.css @@ -20,6 +20,24 @@ --transition: 0.22s cubic-bezier(0.4, 0, 0.2, 1); } +/* ============================================================ + Light theme overrides + ============================================================ */ +[data-theme="light"] { + --bg: #f4f4f8; + --surface: #ffffff; + --surface-2: #ebebf2; + --border: rgba(0, 0, 0, 0.08); + --primary: #7c3aed; + --primary-soft: rgba(124, 58, 237, 0.08); + --primary-glow: rgba(124, 58, 237, 0.18); + --cyan: #0891b2; + --cyan-soft: rgba(8, 145, 178, 0.1); + --red: #dc2626; + --text: #18181b; + --text-muted: #52525b; +} + /* ============================================================ Body offset ============================================================ */ @@ -41,11 +59,11 @@ body { align-items: center; padding: 0 0.75rem; gap: 0.5rem; - background: rgba(9, 9, 11, 0.92); + background: color-mix(in srgb, var(--bg) 92%, transparent); backdrop-filter: blur(20px); -webkit-backdrop-filter: blur(20px); border-bottom: 1px solid var(--border); - box-shadow: 0 1px 24px rgba(0, 0, 0, 0.4); + box-shadow: 0 1px 24px rgba(0, 0, 0, 0.15); } .topbar-toggle { @@ -91,6 +109,33 @@ body { gap: 0.25rem; } +/* ── Theme toggle button ── */ +.topbar-theme-btn { + width: 40px; + height: 40px; + display: flex; + align-items: center; + justify-content: center; + background: transparent; + border: 1px solid transparent; + border-radius: var(--radius); + color: var(--text-muted); + font-size: 1rem; + cursor: pointer; + transition: all var(--transition); + -webkit-tap-highlight-color: transparent; +} + +.topbar-theme-btn:hover { + background: var(--surface-2); + border-color: var(--border); + color: var(--text); +} + +.topbar-theme-btn:active { + transform: scale(0.93); +} + /* ── Icon button (notif / cart) ── */ .topbar-icon-btn { position: relative; diff --git a/frontend-prep/src/components/Navbar.tsx b/frontend-prep/src/components/Navbar.tsx index 8e568ab9..21e8676c 100644 --- a/frontend-prep/src/components/Navbar.tsx +++ b/frontend-prep/src/components/Navbar.tsx @@ -1,6 +1,7 @@ import { useState, useEffect, useRef, useCallback } from "react"; import { useNavigate, useLocation } from "react-router-dom"; import { useCart } from "../context/useCart"; +import { useTheme } from "../context/ThemeContext"; import "./Navbar.css"; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import { @@ -15,6 +16,8 @@ import { faBell, faGift, faUserCircle, + faSun, + faMoon, } from "@fortawesome/free-solid-svg-icons"; import { faTelegram } from "@fortawesome/free-brands-svg-icons"; import type { IconDefinition } from "@fortawesome/fontawesome-svg-core"; @@ -29,6 +32,7 @@ interface MenuItem { } function Navbar() { + const { theme, toggleTheme } = useTheme(); const [isMenuOpen, setIsMenuOpen] = useState(false); const [isLoggingOut, setIsLoggingOut] = useState(false); const [notifications, setNotifications] = useState([]); @@ -155,6 +159,15 @@ function Navbar() { Milieu‑Nantais
+ {/* Theme toggle */} + + {/* Notifications */}
@@ -641,22 +655,22 @@ function Checkout() { {showZoneModal && (
setShowZoneModal(false)}>
e.stopPropagation()}> -
+

Zone non desservie

-
-

{zoneErrorMsg}

-

+

+

{zoneErrorMsg}

+

Vérifiez l'adresse saisie ou contactez-nous pour connaître les zones de livraison disponibles.

-
@@ -685,7 +699,7 @@ function Checkout() { Détails de la commande
- Numéro: #{confirmationData.command_id} + Numéro: #{confirmationData.client_order_number ?? confirmationData.command_id}
Date: {new Date().toLocaleString('fr-FR')} diff --git a/frontend-prep/src/pages/User/ConsultationHistorique.css b/frontend-prep/src/pages/User/ConsultationHistorique.css index f9e86837..dbcf9aa7 100644 --- a/frontend-prep/src/pages/User/ConsultationHistorique.css +++ b/frontend-prep/src/pages/User/ConsultationHistorique.css @@ -10,7 +10,7 @@ padding-top: calc(60px + clamp(1rem, 3vw, 2rem)); max-width: 1400px; margin: 0 auto; - background: linear-gradient(to bottom, #0a0a0a, #1a1a1a); + background: linear-gradient(to bottom, var(--bg), var(--surface)); } /* ============================================ @@ -24,18 +24,14 @@ } .history-title { - color: white; font-size: clamp(2rem, 6vw, 2.5rem); margin: 0 0 0.5rem 0; font-weight: 700; - background: #ffffff; - -webkit-background-clip: text; - -webkit-text-fill-color: transparent; - background-clip: text; + color: var(--text); } .history-subtitle { - color: #9ca3af; + color: var(--text-muted); font-size: clamp(0.9rem, 2vw, 1.1rem); margin: 0; } @@ -52,8 +48,8 @@ } .stat-card2 { - background: linear-gradient(135deg, #1a1a1a, #2a2a2a); - border: 2px solid #333; + background: linear-gradient(135deg, var(--surface), var(--surface-2)); + border: 2px solid var(--border); border-radius: 12px; padding: 1.5rem; display: flex; @@ -160,14 +156,14 @@ } .stat-label { - color: #9ca3af; + color: var(--text-muted); font-size: 0.9rem; margin: 0 0 0.5rem 0; font-weight: 500; } .stat-value { - color: white; + color: var(--text); font-size: 1.8rem; font-weight: 700; margin: 0; @@ -213,7 +209,7 @@ .penalty-limit { font-size: 1rem; - color: #6b7280; + color: var(--text-muted); font-weight: 500; } @@ -242,7 +238,7 @@ } .referral-link-hint { - color: #6b7280; + color: var(--text-muted); font-size: 0.8rem; margin: 0.25rem 0 0; } @@ -268,7 +264,7 @@ .spinner { width: 100%; height: 100%; - border: 4px solid #333; + border: 4px solid var(--border); border-top-color: #7c3aed; border-radius: 50%; animation: spin 1s linear infinite; @@ -281,7 +277,7 @@ } .loading-text { - color: #9ca3af; + color: var(--text-muted); font-size: 1.1rem; margin: 0; } @@ -318,27 +314,27 @@ .empty-history { text-align: center; padding: 4rem 2rem; - background: linear-gradient(135deg, #1a1a1a, #2a2a2a); - border: 2px solid #333; + background: linear-gradient(135deg, var(--surface), var(--surface-2)); + border: 2px solid var(--border); border-radius: 12px; margin-top: 2rem; } .empty-icon { - color: #4b5563; + color: var(--text-muted); margin-bottom: 1.5rem; opacity: 0.5; } .empty-history h2 { - color: white; + color: var(--text); font-size: 1.5rem; margin: 0 0 0.5rem 0; font-weight: 600; } .empty-history p { - color: #9ca3af; + color: var(--text-muted); font-size: 1.1rem; margin: 0 0 2rem 0; } @@ -391,8 +387,8 @@ .table-wrapper { overflow-x: auto; - background-color: #1a1a1a; - border: 2px solid #333; + background-color: var(--surface); + border: 2px solid var(--border); border-radius: 12px; box-shadow: 0 4px 15px rgba(0, 0, 0, 0.3); } @@ -404,12 +400,12 @@ } .history-table thead { - background: linear-gradient(135deg, #0a0a0a, #1a1a1a); + background: linear-gradient(135deg, var(--bg), var(--surface)); border-bottom: 2px solid #7c3aed; } .history-table th { - color: white; + color: var(--text); font-size: clamp(0.85rem, 2vw, 0.95rem); font-weight: 700; text-align: left; @@ -419,7 +415,7 @@ } .history-table tbody tr { - border-bottom: 1px solid #333; + border-bottom: 1px solid var(--border); transition: all 0.2s ease; cursor: pointer; } @@ -435,11 +431,11 @@ } .history-table tbody tr:active { - background-color: #2a2a2a; + background-color: var(--surface-2); } .history-table td { - color: #cccccc; + color: var(--text-muted); font-size: clamp(0.85rem, 2vw, 0.95rem); padding: 1.25rem 1rem; vertical-align: middle; @@ -450,7 +446,7 @@ ============================================ */ .order-id { - color: white !important; + color: var(--text) !important; font-weight: 700; font-family: "Courier New", monospace; font-size: 1rem; @@ -463,13 +459,13 @@ } .date-main { - color: white; + color: var(--text); font-weight: 600; font-size: 0.95rem; } .date-time { - color: #9ca3af; + color: var(--text-muted); font-size: 0.85rem; } @@ -491,7 +487,7 @@ } .address-text { - color: #d1d5db; + color: var(--text-muted); font-size: 0.9rem; } @@ -507,12 +503,12 @@ } .no-livreur { - color: #6b7280; + color: var(--text-muted); font-style: italic; } .products-count { - color: #9ca3af; + color: var(--text-muted); font-size: 0.9rem; } diff --git a/frontend-prep/src/pages/User/ConsultationHistorique.tsx b/frontend-prep/src/pages/User/ConsultationHistorique.tsx index e9b80f8e..1c239c8d 100644 --- a/frontend-prep/src/pages/User/ConsultationHistorique.tsx +++ b/frontend-prep/src/pages/User/ConsultationHistorique.tsx @@ -41,7 +41,7 @@ function ConsultationHistorique() { const [orders, setOrders] = useState([]); const [clientStats, setClientStats] = useState(null); const [penalties, setPenalties] = useState(null); - const [appSettings, setAppSettings] = useState({ penalties_enabled: true, show_amende_score: true, points_enabled: true, points_separated: true, referral_enabled: true, pool_names: ['Pool 1', 'Pool 2'], crypto_payment_enabled: false, crypto_only: false, nowpayments_currencies: [] }); + const [appSettings, setAppSettings] = useState({ penalties_enabled: true, show_amende_score: true, points_enabled: true, points_separated: true, referral_enabled: true, referral_amount: 0, pool_names: ['Pool 1', 'Pool 2'], crypto_payment_enabled: false, crypto_only: false, nowpayments_currencies: [] }); const [referralBalance, setReferralBalance] = useState(0); const [isLoading, setIsLoading] = useState(true); const [error, setError] = useState(''); @@ -365,7 +365,7 @@ function ConsultationHistorique() { className="clickable-row" > - #{order.id.toString().padStart(5, '0')} + #{(order.client_order_number ?? 0).toString().padStart(4, '0')}
diff --git a/frontend-prep/src/pages/User/ModalSuccess.css b/frontend-prep/src/pages/User/ModalSuccess.css index 8a88c0be..04f82c5f 100644 --- a/frontend-prep/src/pages/User/ModalSuccess.css +++ b/frontend-prep/src/pages/User/ModalSuccess.css @@ -26,7 +26,7 @@ } .modal2-success-container { - background: linear-gradient(135deg, rgba(0, 0, 0, 0.95) 0%, rgba(26, 26, 26, 0.95) 100%); + background: var(--surface); border: 1px solid rgba(124, 58, 237, 0.3); border-radius: 20px; padding: 52px 44px; @@ -156,7 +156,7 @@ align-items: center; font-size: 15px; padding: 12px 0; - border-bottom: 1px solid rgba(255, 255, 255, 0.04); + border-bottom: 1px solid var(--border); transition: all 0.3s cubic-bezier(0.25, 0.46, 0.45, 0.94); } @@ -172,7 +172,7 @@ } .detail2-label { - color: rgba(255, 255, 255, 0.6); + color: var(--text-muted); font-weight: 600; letter-spacing: 0.4px; text-transform: uppercase; diff --git a/frontend-prep/src/pages/User/OrderDetails.css b/frontend-prep/src/pages/User/OrderDetails.css index f1127f1e..92ed513d 100644 --- a/frontend-prep/src/pages/User/OrderDetails.css +++ b/frontend-prep/src/pages/User/OrderDetails.css @@ -9,7 +9,7 @@ padding-top: calc(60px + clamp(1rem, 3vw, 2rem)); max-width: 1400px; margin: 0 auto; - background: linear-gradient(to bottom, #0a0a0a, #1a1a1a); + background: linear-gradient(to bottom, var(--bg), var(--surface)); } /* ============================================ @@ -34,7 +34,7 @@ .spinner { width: 100%; height: 100%; - border: 4px solid #1a1a1a; + border: 4px solid var(--surface); border-top-color: #7c3aed; border-radius: 50%; animation: spin 1s linear infinite; @@ -47,7 +47,7 @@ } .loading-text { - color: #9ca3af; + color: var(--text-muted); font-size: 1.1rem; margin: 0; } @@ -58,14 +58,14 @@ } .error-container h2 { - color: white; + color: var(--text); font-size: 1.8rem; margin: 0; font-weight: 700; } .error-container p { - color: #9ca3af; + color: var(--text-muted); font-size: 1.1rem; margin: 0; text-align: center; @@ -88,9 +88,9 @@ align-items: center; gap: 0.5rem; padding: 0.75rem 1.5rem; - background: #1a1a1a; - color: white; - border: 1px solid #333; + background: var(--surface); + color: var(--text); + border: 1px solid var(--border); border-radius: 8px; font-size: 1rem; font-weight: 600; @@ -101,7 +101,7 @@ } .back-button:hover { - background: #2a2a2a; + background: var(--surface-2); border-color: #7c3aed; transform: translateX(-4px); } @@ -167,8 +167,8 @@ } .details-card { - background: #000000; - border: 1px solid #333; + background: var(--bg); + border: 1px solid var(--border); border-radius: 16px; overflow: hidden; transition: all 0.3s ease; @@ -186,8 +186,8 @@ align-items: center; gap: 0.75rem; padding: 1.5rem; - background: #1a1a1a; - border-bottom: 1px solid #333; + background: var(--surface); + border-bottom: 1px solid var(--border); } .card-header svg { @@ -196,7 +196,7 @@ } .card-header h2 { - color: white; + color: var(--text); font-size: 1.25rem; margin: 0; font-weight: 700; @@ -240,7 +240,7 @@ } .info-label { - color: #9ca3af; + color: var(--text-muted); font-size: 0.85rem; font-weight: 500; text-transform: uppercase; @@ -248,7 +248,7 @@ } .info-value { - color: white; + color: var(--text); font-size: 1rem; font-weight: 600; line-height: 1.4; @@ -279,8 +279,8 @@ display: flex; gap: 1rem; padding: 1rem; - background: #1a1a1a; - border: 1px solid #333; + background: var(--surface); + border: 1px solid var(--border); border-radius: 12px; transition: all 0.2s ease; } @@ -295,8 +295,8 @@ height: 80px; object-fit: cover; border-radius: 8px; - background: #0a0a0a; - border: 1px solid #333; + background: var(--bg); + border: 1px solid var(--border); flex-shrink: 0; } @@ -375,7 +375,7 @@ position: relative; width: 100%; max-width: 900px; - background: #1a1a1a; + background: var(--surface); border-radius: 16px; overflow: hidden; box-shadow: 0 20px 60px rgba(0, 0, 0, 0.5); @@ -446,7 +446,7 @@ } .product-name { - color: white; + color: var(--text); font-size: 1.1rem; margin: 0; font-weight: 600; @@ -468,13 +468,13 @@ } .product-quantity { - color: #9ca3af; + color: var(--text-muted); font-size: 0.9rem; font-weight: 500; } .product-price { - color: white; + color: var(--text); font-size: 0.9rem; font-weight: 600; } @@ -497,19 +497,19 @@ } .placeholder-icon { - color: #4b5563; + color: var(--text-muted); opacity: 0.5; } .products-placeholder p { - color: white; + color: var(--text); font-size: 1.1rem; margin: 0; font-weight: 600; } .placeholder-note { - color: #6b7280; + color: var(--text-muted); font-size: 0.9rem; font-style: italic; } @@ -519,19 +519,19 @@ ============================================ */ .summary-card { - background: linear-gradient(135deg, #1a1a1a, #0a0a0a); + background: linear-gradient(135deg, var(--surface), var(--bg)); } .summary-row { display: flex; justify-content: space-between; align-items: center; - color: #9ca3af; + color: var(--text-muted); font-size: 1rem; } .summary-row span:last-child { - color: white; + color: var(--text); font-weight: 600; } @@ -542,7 +542,7 @@ .summary-divider { height: 1px; - background: linear-gradient(to right, transparent, #333, transparent); + background: linear-gradient(to right, transparent, var(--border), transparent); margin: 0.5rem 0; } @@ -553,7 +553,7 @@ } .total-row span:first-child { - color: white; + color: var(--text); } .total-amount { @@ -570,12 +570,12 @@ } .delivery-notes { - color: #d1d5db; + color: var(--text-muted); font-size: 1rem; line-height: 1.6; margin: 0; padding: 1rem; - background: #1a1a1a; + background: var(--surface); border-left: 3px solid #7c3aed; border-radius: 8px; } @@ -585,15 +585,15 @@ ============================================ */ .delivery-timeline { - background: #000000; - border: 1px solid #333; + background: var(--bg); + border: 1px solid var(--border); border-radius: 16px; padding: 2rem; margin-top: 2rem; } .timeline-title { - color: white; + color: var(--text); font-size: 1.5rem; margin: 0 0 2rem 0; font-weight: 700; @@ -629,8 +629,8 @@ top: 0; width: 16px; height: 16px; - background: #1a1a1a; - border: 3px solid #333; + background: var(--surface); + border: 3px solid var(--border); border-radius: 50%; z-index: 1; transition: all 0.3s ease; @@ -643,14 +643,14 @@ } .timeline-content h3 { - color: white; + color: var(--text); font-size: 1.1rem; margin: 0 0 0.5rem 0; font-weight: 600; } .timeline-content p { - color: #9ca3af; + color: var(--text-muted); font-size: 0.95rem; margin: 0; } diff --git a/frontend-prep/src/pages/User/OrderDetails.tsx b/frontend-prep/src/pages/User/OrderDetails.tsx index 50bd06de..64f6eb04 100644 --- a/frontend-prep/src/pages/User/OrderDetails.tsx +++ b/frontend-prep/src/pages/User/OrderDetails.tsx @@ -1,9 +1,3 @@ -// ============================================ -// pages/OrderDetails.tsx -// ============================================ -// Page de détails d'une commande spécifique -// ✅ AJOUT: Vérification continue de l'authentification - import { useState, useEffect } from "react"; import { useParams, useNavigate } from "react-router-dom"; import Navbar from "../../components/Navbar"; @@ -251,7 +245,6 @@ function OrderDetails() { }, [order?.products]); const fetchOrderDetails = async (id: number) => { - // ✅ Vérifier l'auth avant de charger les détails if (!isUserAuthenticated()) { console.log("❌ [fetchOrderDetails] Non authentifié"); navigate("/login/client", { replace: true }); @@ -276,6 +269,8 @@ function OrderDetails() { const orderData: OrderDetailsData = { // Infos de command_info id: apiData.command_info?.id || id, + client_order_number: + apiData.command_info?.client_order_number || 0, username: apiData.client_info?.username || "", status: apiData.command_info?.command_status || "unknown", adresse: apiData.command_info?.command_address || "", @@ -302,11 +297,11 @@ function OrderDetails() { id: item.id, product_id: item.product_id, name_product: item.produit, - category: "Non spécifié", // Pas de catégorie dans command_items + category: "Non spécifié", price: item.prix, quantity: item.quantite, status: item.status, - image: item.image, // Si vous avez des images + image: item.image, })) || [], }; @@ -390,7 +385,10 @@ function OrderDetails() {

- Commande #{order.id.toString().padStart(5, "0")} + Commande # + {(order.client_order_number ?? 0) + .toString() + .padStart(4, "0")}

diff --git a/frontend-prep/src/pages/User/Parrainage.css b/frontend-prep/src/pages/User/Parrainage.css index 04dfb788..008cb1c9 100644 --- a/frontend-prep/src/pages/User/Parrainage.css +++ b/frontend-prep/src/pages/User/Parrainage.css @@ -9,7 +9,7 @@ padding-top: calc(60px + clamp(1.5rem, 4vw, 2.5rem)); max-width: 900px; margin: 0 auto; - background: linear-gradient(to bottom, #0a0a0a, #1a1a1a); + background: linear-gradient(to bottom, var(--bg), var(--surface)); display: flex; flex-direction: column; gap: 2rem; @@ -41,7 +41,7 @@ } .parrainage-subtitle { - color: #9ca3af; + color: var(--text-muted); font-size: clamp(0.95rem, 2.5vw, 1.1rem); margin: 0; } @@ -59,7 +59,7 @@ } .balance-label { - color: #9ca3af; + color: var(--text-muted); font-size: 0.875rem; text-transform: uppercase; letter-spacing: 0.08em; @@ -67,7 +67,7 @@ } .balance-amount { - color: #6b7280; + color: var(--text-muted); font-size: clamp(2.5rem, 7vw, 3.5rem); font-weight: 700; line-height: 1; @@ -79,7 +79,7 @@ } .balance-loading { - color: #6b7280; + color: var(--text-muted); font-size: 1.1rem; } @@ -89,12 +89,59 @@ margin-top: 0.5rem; } +/* ============================================ + RÉCOMPENSE PAR PARRAINAGE + ============================================ */ + +.parrainage-reward-card { + display: flex; + gap: 1rem; + background: linear-gradient(135deg, rgba(139, 92, 246, 0.12), rgba(99, 102, 241, 0.08)); + border: 1px solid rgba(139, 92, 246, 0.35); + border-radius: 14px; + padding: 1.5rem; + align-items: center; +} + +.reward-icon { + font-size: 2.2rem; + flex-shrink: 0; + color: #8b5cf6; +} + +.reward-content { + display: flex; + flex-direction: column; + gap: 0.25rem; +} + +.reward-label { + color: var(--text-muted); + font-size: 0.8rem; + text-transform: uppercase; + letter-spacing: 0.08em; +} + +.reward-amount { + color: #8b5cf6; + font-size: clamp(1.8rem, 5vw, 2.4rem); + font-weight: 700; + line-height: 1.1; +} + +.reward-hint { + color: var(--text-muted); + font-size: 0.82rem; + line-height: 1.5; + margin: 0.2rem 0 0; +} + /* ============================================ ÉTAPES ============================================ */ .parrainage-section-title { - color: #e5e7eb; + color: var(--text); font-size: 1.2rem; font-weight: 600; margin: 0 0 1.25rem; @@ -110,8 +157,8 @@ } .step-card { - background: #111115; - border: 1px solid rgba(255, 255, 255, 0.07); + background: var(--surface); + border: 1px solid var(--border); border-radius: 12px; padding: 1.25rem 1rem; text-align: center; @@ -138,14 +185,14 @@ } .step-title { - color: #f4f4f5; + color: var(--text); font-size: 0.95rem; font-weight: 600; margin: 0 0 0.5rem; } .step-desc { - color: #9ca3af; + color: var(--text-muted); font-size: 0.82rem; line-height: 1.5; margin: 0; @@ -179,7 +226,7 @@ } .warning-text { - color: #d1d5db; + color: var(--text-muted); font-size: 0.875rem; line-height: 1.55; margin: 0 0 0.75rem; @@ -190,7 +237,7 @@ border-radius: 8px; padding: 0.6rem 0.75rem; font-size: 0.85rem; - color: #e5e7eb; + color: var(--text); display: flex; flex-wrap: wrap; gap: 0.4rem; @@ -213,7 +260,7 @@ } .cta-text { - color: #9ca3af; + color: var(--text-muted); font-size: 0.9rem; margin: 0 0 1.25rem; } diff --git a/frontend-prep/src/pages/User/Parrainage.tsx b/frontend-prep/src/pages/User/Parrainage.tsx index 5e963f48..c5cf9b5e 100644 --- a/frontend-prep/src/pages/User/Parrainage.tsx +++ b/frontend-prep/src/pages/User/Parrainage.tsx @@ -2,6 +2,7 @@ import { useState, useEffect } from 'react'; import { useNavigate } from 'react-router-dom'; import Navbar from '../../components/Navbar'; import { getReferralBalance, isUserAuthenticated, getPublicSettings } from '../../api/api'; +import type { PublicSettings } from '../../api/api'; import './Parrainage.css'; const TELEGRAM_URL = 'https://t.me/'; @@ -37,6 +38,7 @@ export default function Parrainage() { const navigate = useNavigate(); const [balance, setBalance] = useState(0); const [loading, setLoading] = useState(true); + const [settings, setSettings] = useState(null); useEffect(() => { if (!isUserAuthenticated()) { @@ -48,6 +50,7 @@ export default function Parrainage() { navigate('/user/accueil', { replace: true }); return; } + setSettings(s); getReferralBalance().then((res) => { if (res.success) setBalance(res.balance); setLoading(false); @@ -85,6 +88,21 @@ export default function Parrainage() { )}
+ {/* Montant par parrainage */} + {settings && settings.referral_amount > 0 && ( +
+
+
+
Crédit par filleul parrainé
+
{settings.referral_amount.toFixed(2)} €
+

+ Vous recevrez ce montant sur votre solde dès que votre filleul + effectue sa première commande validée. +

+
+
+ )} + {/* Étapes */}

Comment ça marche ?

diff --git a/frontend-prep/src/pages/User/ProductDetail.css b/frontend-prep/src/pages/User/ProductDetail.css index 4adaa910..f41d75c6 100644 --- a/frontend-prep/src/pages/User/ProductDetail.css +++ b/frontend-prep/src/pages/User/ProductDetail.css @@ -9,14 +9,14 @@ padding: clamp(1rem, 3vw, 2rem); padding-top: calc(60px + clamp(1rem, 3vw, 2rem)); box-sizing: border-box; - background: linear-gradient(135deg, #0f0f0f 0%, #1a1a1a 100%); + background: var(--bg); } /* Back Button */ .product-detail-container > .back-button { - background: linear-gradient(135deg, rgba(255, 255, 255, 0.05) 0%, rgba(255, 255, 255, 0.02) 100%); - color: white; - border: 1px solid rgba(255, 255, 255, 0.1); + background: var(--surface-2); + color: var(--text); + border: 1px solid var(--border); border-radius: 12px; padding: clamp(0.75rem, 2vw, 1rem) clamp(1.5rem, 3vw, 2rem); font-size: clamp(0.95rem, 3vw, 1.05rem); @@ -33,10 +33,10 @@ } .product-detail-container > .back-button:hover { - background: linear-gradient(135deg, rgba(255, 255, 255, 0.08) 0%, rgba(255, 255, 255, 0.04) 100%); - border-color: rgba(255, 255, 255, 0.2); + background: var(--surface); + border-color: var(--border); transform: translateY(-2px); - box-shadow: 0 6px 20px rgba(0, 0, 0, 0.3); + box-shadow: 0 6px 20px rgba(0, 0, 0, 0.15); } .product-detail-container > .back-button:active { @@ -71,9 +71,9 @@ border-radius: 20px; overflow: hidden; display: flex; - background: linear-gradient(135deg, rgba(255, 255, 255, 0.03) 0%, rgba(255, 255, 255, 0.01) 100%); - border: 1px solid rgba(255, 255, 255, 0.08); - box-shadow: 0 8px 32px rgba(0, 0, 0, 0.3); + background: var(--surface); + border: 1px solid var(--border); + box-shadow: 0 8px 32px rgba(0, 0, 0, 0.15); backdrop-filter: blur(8px); transition: all 0.3s ease; } @@ -140,15 +140,12 @@ } .product-detail-name { - color: white; + color: var(--text); font-size: clamp(2rem, 7vw, 3.5rem); margin: 0; font-weight: 700; line-height: 1.1; - background: linear-gradient(135deg, #ffffff 0%, #e0e0e0 100%); - -webkit-background-clip: text; - -webkit-text-fill-color: transparent; - background-clip: text; + -webkit-text-fill-color: unset; letter-spacing: -1px; } @@ -175,8 +172,8 @@ /* Description */ .product-description { - background: linear-gradient(135deg, rgba(255, 255, 255, 0.03) 0%, rgba(255, 255, 255, 0.01) 100%); - border: 1px solid rgba(255, 255, 255, 0.08); + background: var(--surface); + border: 1px solid var(--border); border-left: 3px solid var(--cat-color, #7c3aed); border-radius: 12px; padding: clamp(1.25rem, 3vw, 1.75rem); @@ -191,7 +188,7 @@ } .product-description h3 { - color: rgba(255, 255, 255, 0.9); + color: var(--text); font-size: clamp(1.1rem, 4vw, 1.3rem); margin: 0 0 1rem 0; font-weight: 600; @@ -200,7 +197,7 @@ } .product-description p { - color: rgba(255, 255, 255, 0.7); + color: var(--text-muted); font-size: clamp(1rem, 3vw, 1.1rem); margin: 0; line-height: 1.7; @@ -213,9 +210,9 @@ gap: clamp(1rem, 3vw, 1.5rem); flex-wrap: wrap; padding: clamp(1rem, 3vw, 1.5rem); - background: rgba(255, 255, 255, 0.02); + background: var(--surface); border-radius: 12px; - border: 1px solid rgba(255, 255, 255, 0.05); + border: 1px solid var(--border); } /* Grams Selector */ @@ -228,7 +225,7 @@ } .grams-selector label { - color: rgba(255, 255, 255, 0.9); + color: var(--text); font-size: clamp(1rem, 3vw, 1.15rem); font-weight: 600; white-space: nowrap; @@ -236,9 +233,9 @@ .grams-dropdown { flex: 1; - background: linear-gradient(135deg, rgba(255, 255, 255, 0.05) 0%, rgba(255, 255, 255, 0.02) 100%); - color: white; - border: 1px solid rgba(255, 255, 255, 0.1); + background: var(--surface-2); + color: var(--text); + border: 1px solid var(--border); border-radius: 10px; padding: clamp(0.75rem, 2vw, 1rem) clamp(1rem, 3vw, 1.25rem); font-size: clamp(1rem, 3vw, 1.1rem); @@ -275,17 +272,17 @@ /* Styles des options - Supprimer le hover natif */ .grams-dropdown option { - background-color: #1a1a1a; - color: white; + background-color: var(--surface); + color: var(--text); padding: 1rem; font-size: 1rem; font-weight: 600; } .grams-dropdown option:hover { - background-color: #1a1a1a !important; - background: #1a1a1a !important; - color: white !important; + background-color: var(--surface) !important; + background: var(--surface) !important; + color: var(--text) !important; } .grams-dropdown option:checked { @@ -295,7 +292,7 @@ } .grams-dropdown option:focus { - background-color: #1a1a1a !important; + background-color: var(--surface) !important; outline: none !important; } @@ -303,7 +300,7 @@ .grams-dropdown option:hover, .grams-dropdown option:focus, .grams-dropdown option:active { - background-color: #1a1a1a !important; + background-color: var(--surface) !important; background-image: none !important; box-shadow: none !important; } @@ -311,7 +308,7 @@ /* Pour Firefox */ @-moz-document url-prefix() { .grams-dropdown option:hover { - background-color: #1a1a1a !important; + background-color: var(--surface) !important; } } @@ -386,8 +383,8 @@ } .add-to-cart-button.disabled { - background: linear-gradient(135deg, rgba(255, 255, 255, 0.08) 0%, rgba(255, 255, 255, 0.05) 100%); - color: rgba(255, 255, 255, 0.4); + background: var(--surface-2); + color: var(--text-muted); cursor: not-allowed; box-shadow: none; } @@ -407,7 +404,7 @@ background: linear-gradient(135deg, rgba(239, 68, 68, 0.1) 0%, rgba(239, 68, 68, 0.05) 100%); border: 1px solid rgba(239, 68, 68, 0.3); border-radius: 16px; - color: white; + color: var(--text); animation: slideUp 0.5s ease-out; } @@ -418,9 +415,9 @@ } .error-message .back-button { - background: linear-gradient(135deg, rgba(255, 255, 255, 0.05) 0%, rgba(255, 255, 255, 0.02) 100%); - color: white; - border: 1px solid rgba(255, 255, 255, 0.1); + background: var(--surface-2); + color: var(--text); + border: 1px solid var(--border); border-radius: 10px; padding: clamp(0.75rem, 2vw, 1rem) clamp(1.5rem, 3vw, 2rem); font-size: clamp(1rem, 3vw, 1.1rem); @@ -431,8 +428,8 @@ } .error-message .back-button:hover { - background: linear-gradient(135deg, rgba(255, 255, 255, 0.08) 0%, rgba(255, 255, 255, 0.04) 100%); - border-color: rgba(255, 255, 255, 0.2); + background: var(--surface); + border-color: var(--border); transform: translateY(-2px); } @@ -449,13 +446,13 @@ padding: clamp(3rem, 8vw, 5rem); text-align: center; min-height: 400px; - color: white; + color: var(--text); animation: slideUp 0.5s ease-out; } .loading-container p { font-size: clamp(1rem, 3vw, 1.2rem); - color: rgba(255, 255, 255, 0.7); + color: var(--text-muted); margin-top: 1.5rem; } diff --git a/frontend-prep/src/pages/User/ProfilePage.css b/frontend-prep/src/pages/User/ProfilePage.css index ccffbd1b..83413ae4 100644 --- a/frontend-prep/src/pages/User/ProfilePage.css +++ b/frontend-prep/src/pages/User/ProfilePage.css @@ -33,13 +33,13 @@ .profile-title { font-size: 1.6rem; font-weight: 700; - color: #f0f0f0; + color: var(--text); margin: 0; } .profile-username { font-size: 0.9rem; - color: #9ca3af; + color: var(--text-muted); margin: 0.2rem 0 0; } @@ -66,8 +66,8 @@ /* Cards */ .profile-card { - background: #1e1e2e; - border: 1px solid #2d2d40; + background: var(--surface); + border: 1px solid var(--border); border-radius: 12px; padding: 1.5rem; margin-bottom: 1.2rem; @@ -76,7 +76,7 @@ .profile-card-title { font-size: 1rem; font-weight: 600; - color: #e2e8f0; + color: var(--text); margin: 0 0 1rem; display: flex; align-items: center; @@ -86,10 +86,11 @@ .profile-card-icon { color: #7c3aed; } .profile-card-icon--address { color: #10b981; } .profile-card-icon--phone { color: #3b82f6; } +.profile-card-icon--telegram { color: #2AABEE; } .profile-hint { font-size: 0.82rem; - color: #6b7280; + color: var(--text-muted); margin: -0.5rem 0 1rem; line-height: 1.5; } @@ -108,15 +109,15 @@ .profile-group label { font-size: 0.82rem; font-weight: 500; - color: #9ca3af; + color: var(--text-muted); } .profile-group input { - background: #12121f; - border: 1px solid #2d2d40; + background: var(--bg); + border: 1px solid var(--border); border-radius: 8px; padding: 0.65rem 0.9rem; - color: #e2e8f0; + color: var(--text); font-size: 0.9rem; outline: none; transition: border-color 0.2s; @@ -125,7 +126,7 @@ } .profile-group input:focus { border-color: #7c3aed; } -.profile-group input::placeholder { color: #4b5563; } +.profile-group input::placeholder { color: var(--text-muted); } /* Buttons */ .profile-btn { @@ -145,14 +146,141 @@ } .profile-btn--secondary { - background: #1a3a5c; + background: var(--surface-2); color: #60a5fa; - border: 1px solid #2563eb44; + border: 1px solid rgba(37, 99, 235, 0.27); } .profile-btn:hover { opacity: 0.85; } .profile-btn:disabled { opacity: 0.5; cursor: not-allowed; } +.profile-btn--telegram { + background: #2AABEE; +} + +.profile-btn--danger { + background: transparent; + border: 1px solid #ef444466; + color: #ef4444; +} + +.profile-telegram-linked { + display: flex; + flex-direction: column; + gap: 0.8rem; +} + +.profile-telegram-status { + display: flex; + align-items: center; + gap: 0.5rem; + color: #10b981; + font-size: 0.88rem; + font-weight: 500; +} + @media (max-width: 480px) { .profile-row { grid-template-columns: 1fr; } } + +/* Modal confirmation */ +.profile-modal-overlay { + position: fixed; + inset: 0; + background: rgba(0, 0, 0, 0.6); + display: flex; + align-items: center; + justify-content: center; + z-index: 1000; + padding: 1rem; + backdrop-filter: blur(2px); +} + +.profile-modal { + background: var(--surface); + border: 1px solid var(--border); + border-radius: 14px; + padding: 2rem 1.8rem 1.6rem; + max-width: 400px; + width: 100%; + position: relative; + text-align: center; + box-shadow: 0 20px 60px rgba(0, 0, 0, 0.5); +} + +.profile-modal-close { + position: absolute; + top: 0.8rem; + right: 0.9rem; + background: transparent; + border: none; + color: var(--text-muted); + font-size: 1rem; + cursor: pointer; + padding: 0.3rem 0.5rem; + border-radius: 6px; + transition: color 0.2s; +} +.profile-modal-close:hover { color: var(--text); } + +.profile-modal-icon { + width: 52px; + height: 52px; + border-radius: 50%; + background: rgba(37, 99, 235, 0.15); + border: 1px solid rgba(37, 99, 235, 0.3); + display: flex; + align-items: center; + justify-content: center; + font-size: 1.3rem; + color: #60a5fa; + margin: 0 auto 1.1rem; +} + +.profile-modal-title { + font-size: 1.05rem; + font-weight: 700; + color: var(--text); + margin: 0 0 0.6rem; +} + +.profile-modal-body { + font-size: 0.85rem; + color: var(--text-muted); + line-height: 1.55; + margin: 0 0 1.5rem; +} + +.profile-modal-actions { + display: flex; + gap: 0.8rem; + justify-content: center; +} + +.profile-modal-btn { + flex: 1; + padding: 0.65rem 1rem; + border-radius: 8px; + font-size: 0.88rem; + font-weight: 600; + cursor: pointer; + border: none; + transition: opacity 0.2s; + display: flex; + align-items: center; + justify-content: center; + gap: 0.4rem; +} +.profile-modal-btn:hover { opacity: 0.85; } + +.profile-modal-btn--cancel { + background: var(--bg); + border: 1px solid var(--border); + color: var(--text-muted); +} + +.profile-modal-btn--confirm { + background: var(--surface-2); + border: 1px solid rgba(37, 99, 235, 0.27); + color: #60a5fa; +} diff --git a/frontend-prep/src/pages/User/ProfilePage.tsx b/frontend-prep/src/pages/User/ProfilePage.tsx index 1509e8c5..6df65bbd 100644 --- a/frontend-prep/src/pages/User/ProfilePage.tsx +++ b/frontend-prep/src/pages/User/ProfilePage.tsx @@ -1,11 +1,11 @@ import { useState, useEffect } from 'react'; import { useNavigate } from 'react-router-dom'; import Navbar from '../../components/Navbar'; -import { isUserAuthenticated, extractUsernameFromToken, getMyProfile, updateMyProfile } from '../../api/api'; +import { isUserAuthenticated, extractUsernameFromToken, getMyProfile, updateMyProfile, getTelegramStatus, generateTelegramLinkToken, unlinkTelegram } from '../../api/api'; import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; import { faUser, faMapMarkerAlt, faPhone, faCommentDots, - faSave, faCheckCircle, faExclamationTriangle, + faSave, faCheckCircle, faExclamationTriangle, faPaperPlane, faUnlink, faTimes, faLock, } from '@fortawesome/free-solid-svg-icons'; import './ProfilePage.css'; @@ -32,6 +32,14 @@ export default function ProfilePage() { const [successMsg, setSuccessMsg] = useState(''); const [errorMsg, setErrorMsg] = useState(''); + // Telegram + const [tgLinked, setTgLinked] = useState(false); + const [tgEnabled, setTgEnabled] = useState(false); + const [tgLoading, setTgLoading] = useState(false); + + // Modal confirmation infos par défaut + const [showSaveModal, setShowSaveModal] = useState(false); + const username = extractUsernameFromToken() ?? ''; useEffect(() => { @@ -44,6 +52,9 @@ export default function ProfilePage() { setDefaultPhone(localStorage.getItem(STORAGE_PHONE) ?? ''); setSignalPseudo(localStorage.getItem(STORAGE_SIGNAL) || username); + // Statut Telegram + getTelegramStatus().then((s) => { setTgLinked(s.linked); setTgEnabled(s.enabled); }); + // Charger depuis backend getMyProfile().then((res) => { if (res.success && res.client) { @@ -74,9 +85,28 @@ export default function ProfilePage() { localStorage.setItem(STORAGE_ADDRESS, defaultAddress.trim()); localStorage.setItem(STORAGE_PHONE, defaultPhone.trim()); localStorage.setItem(STORAGE_SIGNAL, signalPseudo.trim()); + setShowSaveModal(false); showSuccess('Informations par défaut enregistrées'); }; + const handleLinkTelegram = async () => { + setTgLoading(true); + const res = await generateTelegramLinkToken(); + setTgLoading(false); + if (res.error || !res.link_url) { + showError(res.error || 'Service Telegram non disponible'); + return; + } + window.open(res.link_url, '_blank'); + }; + + const handleUnlinkTelegram = async () => { + if (!window.confirm('Délier votre compte Telegram ? Vous ne recevrez plus de notifications.')) return; + await unlinkTelegram(); + setTgLinked(false); + showSuccess('Compte Telegram délié'); + }; + const saveContact = async () => { setSavingContact(true); const res = await updateMyProfile({ nom: nom.trim(), prenom: prenom.trim(), telephone: telephone.trim() }); @@ -165,6 +195,9 @@ export default function ProfilePage() { {savingContact ? ' Enregistrement...' : ' Enregistrer le compte'} +
{/* Section adresse par défaut */} @@ -217,11 +250,63 @@ export default function ProfilePage() { placeholder="@votre.pseudo.signal" />
-
+ {/* Section Telegram */} + {tgEnabled && ( +
+

+ + Notifications Telegram +

+

+ Recevez vos notifications sur Telegram, même quand le site est fermé. +

+ {tgLinked ? ( +
+ + Compte Telegram lié + + +
+ ) : ( + + )} +
+ )}
+ + {showSaveModal && ( +
setShowSaveModal(false)}> +
e.stopPropagation()}> + +
+ +
+

Enregistrer les infos par défaut ?

+

+ Adresse, téléphone de livraison et pseudo Signal seront sauvegardés localement et pré-remplis lors de vos prochaines commandes. +

+
+ + +
+
+
+ )} ); } diff --git a/frontend-prep/src/pages/User/SuiviLivraison.css b/frontend-prep/src/pages/User/SuiviLivraison.css index 2b1d2242..3f0cfcd5 100644 --- a/frontend-prep/src/pages/User/SuiviLivraison.css +++ b/frontend-prep/src/pages/User/SuiviLivraison.css @@ -9,8 +9,8 @@ padding-top: calc(60px + clamp(1.5rem, 4vw, 2.5rem)); max-width: 860px; margin: 0 auto; - background: #0d0d0d; - color: white; + background: var(--bg); + color: var(--text); } /* ===== HEADER ===== */ @@ -21,14 +21,14 @@ .suivi-header h1 { font-size: clamp(1.6rem, 5vw, 2.2rem); - color: #fff; + color: var(--text); margin: 0 0 0.3rem 0; letter-spacing: -0.5px; } .suivi-header p { font-size: clamp(0.85rem, 2.5vw, 0.95rem); - color: rgba(255, 255, 255, 0.4); + color: var(--text-muted); margin: 0; } @@ -88,7 +88,7 @@ } .loading-state p { - color: rgba(255, 255, 255, 0.45); + color: var(--text-muted); font-size: 0.95rem; } @@ -96,26 +96,26 @@ .empty-state { text-align: center; padding: clamp(3rem, 8vw, 5rem) 2rem; - border: 1px solid rgba(255, 255, 255, 0.06); + border: 1px solid var(--border); border-radius: 20px; - background: rgba(255, 255, 255, 0.02); + background: var(--surface); } .empty-state-icon { font-size: 3.5rem; - color: rgba(255, 255, 255, 0.15); + color: var(--text-muted); margin-bottom: 1.5rem; } .empty-state h2 { font-size: 1.3rem; font-weight: 700; - color: rgba(255, 255, 255, 0.8); + color: var(--text); margin: 0 0 0.5rem 0; } .empty-state p { - color: rgba(255, 255, 255, 0.4); + color: var(--text-muted); font-size: 0.95rem; margin: 0 0 2rem 0; } @@ -149,8 +149,8 @@ /* ===== ORDER CARD ===== */ .order-card { - background: #161616; - border: 1px solid rgba(255, 255, 255, 0.07); + background: var(--surface); + border: 1px solid var(--border); border-radius: 18px; overflow: hidden; transition: @@ -196,7 +196,7 @@ } .order-header:hover { - background: rgba(255, 255, 255, 0.02); + background: var(--surface-2); } .order-header-left { @@ -217,7 +217,7 @@ .order-id { font-size: 1rem; font-weight: 700; - color: #fff; + color: var(--text); } .status-badge { @@ -300,7 +300,7 @@ .order-date { font-size: 0.8rem; - color: rgba(255, 255, 255, 0.35); + color: var(--text-muted); } .order-header-right { @@ -323,8 +323,8 @@ width: 28px; height: 28px; border-radius: 8px; - background: rgba(255, 255, 255, 0.05); - color: rgba(255, 255, 255, 0.4); + background: var(--surface-2); + color: var(--text-muted); font-size: 0.75rem; transition: all 0.2s ease; } @@ -336,7 +336,7 @@ /* ===== EXPANDED DETAILS ===== */ .order-details { - border-top: 1px solid rgba(255, 255, 255, 0.05); + border-top: 1px solid var(--border); animation: expandDown 0.3s ease-out; overflow: hidden; } @@ -383,7 +383,7 @@ left: calc(-50% + 14px); right: calc(50% + 14px); height: 2px; - background: rgba(255, 255, 255, 0.08); + background: var(--border); z-index: 0; } @@ -404,9 +404,9 @@ align-items: center; justify-content: center; font-size: 0.7rem; - border: 2px solid rgba(255, 255, 255, 0.1); - background: #1e1e1e; - color: rgba(255, 255, 255, 0.3); + border: 2px solid var(--border); + background: var(--surface-2); + color: var(--text-muted); transition: all 0.3s ease; z-index: 1; position: relative; @@ -427,7 +427,7 @@ .timeline-label { font-size: 0.62rem; - color: rgba(255, 255, 255, 0.3); + color: var(--text-muted); margin-top: 0.4rem; text-align: center; line-height: 1.3; @@ -440,7 +440,7 @@ } .timeline-step.done .timeline-label { - color: rgba(255, 255, 255, 0.5); + color: var(--text-muted); } /* ===== DETAIL SECTIONS ===== */ @@ -452,7 +452,7 @@ .detail-section { padding: 1rem 1.4rem; - border-top: 1px solid rgba(255, 255, 255, 0.04); + border-top: 1px solid var(--border); } .detail-section:first-child { @@ -467,13 +467,13 @@ font-size: 0.72rem; text-transform: uppercase; letter-spacing: 0.8px; - color: rgba(255, 255, 255, 0.35); + color: var(--text-muted); font-weight: 700; } .detail-section p { margin: 0; - color: rgba(255, 255, 255, 0.85); + color: var(--text); font-size: 0.95rem; line-height: 1.5; } @@ -481,16 +481,16 @@ .detail-section small { display: block; margin-top: 0.35rem; - color: rgba(255, 255, 255, 0.35); + color: var(--text-muted); font-size: 0.8rem; } .address { font-weight: 600; - color: #fff; + color: var(--text); } .contact { - color: rgba(255, 255, 255, 0.5); + color: var(--text-muted); font-size: 0.85rem; margin-top: 0.25rem; } @@ -574,7 +574,7 @@ font-size: 0.75rem; text-transform: uppercase; letter-spacing: 0.7px; - color: rgba(255, 255, 255, 0.4); + color: var(--text-muted); font-weight: 700; margin: 0 0 0.2rem 0; } @@ -593,7 +593,7 @@ .eta-card-sub { font-size: 0.8rem; - color: rgba(255, 255, 255, 0.4); + color: var(--text-muted); margin: 0.2rem 0 0 0; } @@ -609,13 +609,13 @@ justify-content: space-between; align-items: center; padding: 0.6rem 0.9rem; - background: rgba(255, 255, 255, 0.025); + background: var(--surface-2); border-radius: 9px; - border: 1px solid rgba(255, 255, 255, 0.04); + border: 1px solid var(--border); } .item-name { - color: rgba(255, 255, 255, 0.8); + color: var(--text); font-size: 0.9rem; } @@ -637,8 +637,8 @@ /* ===== ACTIONS ===== */ .order-actions { padding: 1.2rem 1.4rem; - border-top: 1px solid rgba(255, 255, 255, 0.05); - background: rgba(0, 0, 0, 0.15); + border-top: 1px solid var(--border); + background: var(--surface-2); } .action-buttons { @@ -668,7 +668,7 @@ .notice-title { font-size: 1rem; font-weight: 700; - color: #fff; + color: var(--text); margin: 0 0 0.3rem 0; display: flex; align-items: center; @@ -678,7 +678,7 @@ .notice-subtitle { font-size: 0.85rem; - color: rgba(255, 255, 255, 0.5); + color: var(--text-muted); margin: 0; } @@ -727,15 +727,15 @@ .btn-secondary, .btn-cancel { - background: rgba(255, 255, 255, 0.06); - color: rgba(255, 255, 255, 0.7); - border: 1px solid rgba(255, 255, 255, 0.1); + background: var(--surface-2); + color: var(--text-muted); + border: 1px solid var(--border); } .btn-secondary:hover, .btn-cancel:hover { - background: rgba(255, 255, 255, 0.09); - color: #fff; + background: var(--surface); + color: var(--text); } .btn-danger, @@ -754,7 +754,7 @@ .btn-confirm-delivery:disabled, .btn-cancel-order:disabled, .btn-danger:disabled { - background: rgba(255, 255, 255, 0.07); + background: var(--surface-2); cursor: not-allowed; box-shadow: none; opacity: 0.5; @@ -791,10 +791,10 @@ margin-top: 2rem; text-align: center; padding: 1rem; - background: rgba(255, 255, 255, 0.02); - border: 1px solid rgba(255, 255, 255, 0.05); + background: var(--surface); + border: 1px solid var(--border); border-radius: 12px; - color: rgba(255, 255, 255, 0.3); + color: var(--text-muted); font-size: 0.8rem; } @@ -826,8 +826,8 @@ } .confirm-dialog { - background: #1a1a1a; - border: 1px solid rgba(255, 255, 255, 0.12); + background: var(--surface); + border: 1px solid var(--border); border-radius: 20px; max-width: 480px; width: 90%; @@ -856,14 +856,14 @@ .confirm-dialog-header { padding: 1.4rem 1.5rem; - border-bottom: 1px solid rgba(255, 255, 255, 0.07); + border-bottom: 1px solid var(--border); flex-shrink: 0; } .confirm-dialog-header h3 { margin: 0; font-size: 1.1rem; - color: white; + color: var(--text); font-weight: 700; display: flex; align-items: center; @@ -872,7 +872,7 @@ .confirm-dialog-body { padding: 1.5rem; - color: rgba(255, 255, 255, 0.75); + color: var(--text); overflow-y: auto; flex: 1; -webkit-overflow-scrolling: touch; @@ -932,7 +932,7 @@ display: flex; gap: 0.75rem; flex-shrink: 0; - border-top: 1px solid rgba(255, 255, 255, 0.06); + border-top: 1px solid var(--border); } .confirm-dialog-actions .btn-cancel, @@ -958,16 +958,16 @@ .cancel-form label { font-weight: 600; - color: rgba(255, 255, 255, 0.8); + color: var(--text); font-size: 0.9rem; } .cancel-form textarea { - background: rgba(255, 255, 255, 0.04); - border: 1px solid rgba(255, 255, 255, 0.1); + background: var(--bg); + border: 1px solid var(--border); border-radius: 10px; padding: 0.75rem; - color: white; + color: var(--text); font-family: inherit; resize: vertical; min-height: 90px; @@ -982,7 +982,7 @@ } .cancel-form small { - color: rgba(255, 255, 255, 0.35); + color: var(--text-muted); font-size: 0.8rem; text-align: right; } @@ -997,7 +997,7 @@ align-items: flex-start; font-size: 0.88rem; line-height: 1.5; - color: rgba(255, 255, 255, 0.6); + color: var(--text-muted); } .info-message svg { @@ -1041,15 +1041,15 @@ } .warning-details { - background: rgba(255, 255, 255, 0.04); + background: var(--surface-2); padding: 0.9rem; border-radius: 10px; - border: 1px solid rgba(255, 255, 255, 0.08); + border: 1px solid var(--border); } .warning-details p { margin: 0.4rem 0; - color: rgba(255, 255, 255, 0.7); + color: var(--text); font-size: 0.9rem; } @@ -1068,14 +1068,14 @@ } .penalty-message { - color: rgba(255, 255, 255, 0.65); + color: var(--text-muted); text-align: center; margin-bottom: 1rem; font-size: 0.9rem; } .penalty-scale { - background: rgba(255, 255, 255, 0.03); + background: var(--surface-2); padding: 0.9rem; border-radius: 8px; } @@ -1092,9 +1092,9 @@ } .penalty-scale li { padding: 0.4rem 0; - border-bottom: 1px solid rgba(255, 255, 255, 0.05); + border-bottom: 1px solid var(--border); font-size: 0.85rem; - color: rgba(255, 255, 255, 0.65); + color: var(--text-muted); } .penalty-scale li:last-child { border-bottom: none; @@ -1103,7 +1103,7 @@ .warning-question { font-size: 0.95rem; font-weight: 700; - color: rgba(255, 255, 255, 0.85); + color: var(--text); text-align: center; margin-top: 0.5rem; } diff --git a/frontend-prep/src/pages/User/SuiviLivraison.tsx b/frontend-prep/src/pages/User/SuiviLivraison.tsx index 1b38e74e..c74e0a29 100644 --- a/frontend-prep/src/pages/User/SuiviLivraison.tsx +++ b/frontend-prep/src/pages/User/SuiviLivraison.tsx @@ -675,7 +675,7 @@ function SuiviLivraison() {
- Commande #{order.id} + Commande #{order.client_order_number}