chore: update

This commit is contained in:
2026-03-30 20:02:39 +02:00
parent 32e129da31
commit 25cd6429f2
32 changed files with 1024 additions and 540 deletions
+3
View File
@@ -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 (
<ThemeProvider>
<Router>
<Routes>
{/* Routes Login - SANS CartProvider */}
@@ -87,6 +89,7 @@ function App() {
/>
</Routes>
</Router>
</ThemeProvider>
);
}
+49
View File
@@ -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<PublicSettings> => {
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<PublicSettings> => {
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<PublicSettings> => {
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<void> => {
const token = getAuthToken();
if (!token) return;
try {
await fetch(`${API_URL}/telegram/unlink`, {
method: "DELETE",
headers: { Authorization: `Bearer ${token}` },
});
} catch { /* silencieux */ }
};
+4
View File
@@ -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;
+30 -30
View File
@@ -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);
}
}
+47 -2
View File
@@ -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;
+13
View File
@@ -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<boolean>(false);
const [isLoggingOut, setIsLoggingOut] = useState<boolean>(false);
const [notifications, setNotifications] = useState<ClientNotification[]>([]);
@@ -155,6 +159,15 @@ function Navbar() {
<span className="topbar-brand">MilieuNantais</span>
<div className="topbar-actions">
{/* Theme toggle */}
<button
className="topbar-theme-btn"
onClick={toggleTheme}
aria-label={theme === "dark" ? "Passer en mode clair" : "Passer en mode sombre"}
>
<FontAwesomeIcon icon={theme === "dark" ? faSun : faMoon} />
</button>
{/* Notifications */}
<div className="notif-wrapper" ref={notifPanelRef}>
<button
+21 -21
View File
@@ -1,5 +1,5 @@
.product-card {
background-color: #1a1a1a;
background-color: var(--surface);
border: 2px solid var(--category-color, white);
border-radius: 15px;
overflow: hidden;
@@ -20,7 +20,7 @@
.product-card:active {
transform: scale(0.97);
background-color: #2a2a2a;
background-color: var(--surface-2);
}
.product-card-content {
@@ -30,7 +30,7 @@
.product-image-container {
width: 100%;
aspect-ratio: 1;
background-color: #2a2a2a;
background-color: var(--surface-2);
display: flex;
align-items: center;
justify-content: center;
@@ -78,12 +78,12 @@
.product-info {
padding: clamp(0.8rem, 3vw, 1.2rem);
background-color: #1a1a1a;
border-top: 1px solid #333;
background-color: var(--surface);
border-top: 1px solid var(--border);
}
.product-name {
color: white;
color: var(--text);
margin: 0 0 0.5rem 0;
font-size: clamp(1rem, 3.5vw, 1.2rem);
font-weight: 600;
@@ -99,18 +99,18 @@
}
.product-stock {
color: #cccccc;
color: var(--text-muted);
font-size: clamp(0.85rem, 2.5vw, 1rem);
text-align: center;
padding: clamp(0.6rem, 2vw, 0.8rem);
background-color: #0a0a0a;
border-top: 1px solid #333;
background-color: var(--bg);
border-top: 1px solid var(--border);
}
.quick-add-section {
padding: clamp(0.8rem, 3vw, 1rem);
background-color: #0a0a0a;
border-top: 1px solid #333;
background-color: var(--bg);
border-top: 1px solid var(--border);
display: flex;
flex-direction: column;
gap: 0.6rem;
@@ -224,8 +224,8 @@ div[data-category="gros&semi"] .quick-add-btn:active {
.quantity-select {
width: 100%;
padding: clamp(0.7rem, 2.5vw, 0.9rem);
background-color: #2a2a2a;
color: white;
background-color: var(--surface-2);
color: var(--text);
border: 2px solid #4ade80;
border-radius: 8px;
font-size: clamp(0.9rem, 3vw, 1.05rem);
@@ -241,7 +241,7 @@ div[data-category="gros&semi"] .quick-add-btn:active {
.quantity-select:hover {
border-color: #22c55e;
background-color: #333;
background-color: var(--surface);
}
.quantity-select:focus {
@@ -260,7 +260,7 @@ div[data-category="weed&hash"] .quantity-select {
div[data-category="weed&hash"] .quantity-select:hover {
border-color: #059669;
background-color: #333;
background-color: var(--surface);
}
div[data-category="weed&hash"] .quantity-select:focus {
@@ -276,7 +276,7 @@ div[data-category="tous"] .quantity-select {
div[data-category="tous"] .quantity-select:hover {
border-color: #7e22ce;
background-color: #333;
background-color: var(--surface);
}
div[data-category="tous"] .quantity-select:focus {
@@ -292,7 +292,7 @@ div[data-category="zipette&co"] .quantity-select {
div[data-category="zipette&co"] .quantity-select:hover {
border-color: #e8e8e0;
background-color: #333;
background-color: var(--surface);
}
div[data-category="zipette&co"] .quantity-select:focus {
@@ -308,7 +308,7 @@ div[data-category="gros&semi"] .quantity-select {
div[data-category="gros&semi"] .quantity-select:hover {
border-color: #0ea5e9;
background-color: #333;
background-color: var(--surface);
}
div[data-category="gros&semi"] .quantity-select:focus {
@@ -317,8 +317,8 @@ div[data-category="gros&semi"] .quantity-select:focus {
}
.quantity-select option {
background-color: #2a2a2a;
color: white;
background-color: var(--surface-2);
color: var(--text);
padding: 0.5rem;
}
@@ -706,7 +706,7 @@ div[data-category="gros&semi"] .details-button:hover {
@media (hover: none) {
.product-card:hover {
transform: none;
background-color: #1a1a1a;
background-color: var(--surface);
}
.quick-add-btn:hover {
+43 -43
View File
@@ -37,7 +37,7 @@
width: 90%;
max-width: 900px;
max-height: 90vh;
background: linear-gradient(135deg, rgba(20, 20, 25, 0.98), rgba(15, 15, 20, 0.98));
background: linear-gradient(135deg, var(--surface), var(--bg));
border: 1px solid rgba(124, 58, 237, 0.3);
border-radius: 20px;
box-shadow: 0 20px 60px rgba(124, 58, 237, 0.3), 0 0 0 1px rgba(255, 255, 255, 0.05);
@@ -67,8 +67,8 @@
justify-content: space-between;
align-items: center;
padding: 1.8rem 2rem;
border-bottom: 1px solid rgba(255, 255, 255, 0.08);
background: linear-gradient(135deg, rgba(124, 58, 237, 0.08), rgba(109, 40, 217, 0.04));
border-bottom: 1px solid var(--border);
background: var(--primary-soft);
flex-shrink: 0;
}
@@ -84,7 +84,7 @@
}
.modal-header h2 {
color: white;
color: var(--text);
font-size: 1.6rem;
font-weight: 700;
margin: 0;
@@ -92,7 +92,7 @@
}
.modal-subtitle {
color: #888;
color: var(--text-muted);
font-size: 0.9rem;
margin: 0.3rem 0 0 0;
font-weight: 500;
@@ -102,12 +102,12 @@
width: 40px;
height: 40px;
border-radius: 10px;
background: linear-gradient(135deg, rgba(255, 255, 255, 0.05), rgba(255, 255, 255, 0.02));
border: 1px solid rgba(255, 255, 255, 0.08);
background: var(--surface-2);
border: 1px solid var(--border);
display: flex;
align-items: center;
justify-content: center;
color: #888;
color: var(--text-muted);
cursor: pointer;
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
flex-shrink: 0;
@@ -128,7 +128,7 @@
overflow-y: auto;
padding: 2rem;
scrollbar-width: thin;
scrollbar-color: rgba(124, 58, 237, 0.4) rgba(255, 255, 255, 0.05);
scrollbar-color: rgba(124, 58, 237, 0.4) var(--surface-2);
}
.modal-content::-webkit-scrollbar {
@@ -136,7 +136,7 @@
}
.modal-content::-webkit-scrollbar-track {
background: rgba(255, 255, 255, 0.05);
background: var(--surface-2);
border-radius: 4px;
}
@@ -331,8 +331,8 @@
}
.detail-section {
background: linear-gradient(135deg, rgba(255, 255, 255, 0.03), rgba(255, 255, 255, 0.01));
border: 1px solid rgba(255, 255, 255, 0.08);
background: var(--surface);
border: 1px solid var(--border);
border-radius: 12px;
padding: 1.5rem;
}
@@ -345,7 +345,7 @@
display: flex;
align-items: center;
gap: 0.6rem;
color: white;
color: var(--text);
font-size: 0.95rem;
font-weight: 600;
text-transform: uppercase;
@@ -358,7 +358,7 @@
}
.detail-description {
color: #aaa;
color: var(--text-muted);
font-size: 1rem;
line-height: 1.6;
margin: 0;
@@ -403,8 +403,8 @@
align-items: center;
gap: 1rem;
padding: 0.8rem 1.2rem;
background: rgba(0, 0, 0, 0.2);
border: 1px solid rgba(255, 255, 255, 0.05);
background: var(--bg);
border: 1px solid var(--border);
border-radius: 8px;
}
@@ -415,7 +415,7 @@
.quantity-value,
.amount-value {
color: white;
color: var(--text);
font-size: 1.1rem;
font-weight: 600;
}
@@ -435,7 +435,7 @@
display: flex;
align-items: center;
gap: 0.6rem;
color: #aaa;
color: var(--text-muted);
font-size: 0.95rem;
font-weight: 500;
}
@@ -445,13 +445,13 @@
}
.date-text {
color: #aaa;
color: var(--text-muted);
font-size: 0.95rem;
margin: 0;
}
.no-data {
color: #666;
color: var(--text-muted);
font-style: italic;
margin: 0;
}
@@ -460,8 +460,8 @@
FORM SECTIONS (Create & Edit Modals)
============================================ */
.form-section {
background: linear-gradient(135deg, rgba(255, 255, 255, 0.03), rgba(255, 255, 255, 0.01));
border: 1px solid rgba(255, 255, 255, 0.08);
background: var(--surface);
border: 1px solid var(--border);
border-radius: 12px;
padding: 1.5rem;
margin-bottom: 1.5rem;
@@ -472,7 +472,7 @@
}
.form-section h3 {
color: white;
color: var(--text);
font-size: 1.1rem;
font-weight: 600;
margin: 0 0 1.2rem 0;
@@ -526,7 +526,7 @@
.form-group label {
display: block;
color: white;
color: var(--text);
font-size: 0.9rem;
font-weight: 600;
margin-bottom: 0.6rem;
@@ -539,10 +539,10 @@
.form-group textarea {
width: 100%;
padding: 0.9rem 1.2rem;
background: linear-gradient(135deg, rgba(255, 255, 255, 0.05), rgba(255, 255, 255, 0.02));
border: 1px solid rgba(255, 255, 255, 0.08);
background: var(--surface-2);
border: 1px solid var(--border);
border-radius: 10px;
color: white;
color: var(--text);
font-size: 0.95rem;
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
font-family: inherit;
@@ -552,7 +552,7 @@
.form-group select:focus,
.form-group textarea:focus {
outline: none;
background: linear-gradient(135deg, rgba(255, 255, 255, 0.08), rgba(255, 255, 255, 0.04));
background: var(--surface);
border-color: rgba(124, 58, 237, 0.5);
box-shadow: 0 0 0 3px rgba(124, 58, 237, 0.1);
}
@@ -602,7 +602,7 @@
}
.price-input-group label {
color: #888;
color: var(--text-muted);
font-size: 0.85rem;
font-weight: 600;
text-transform: uppercase;
@@ -708,8 +708,8 @@
justify-content: space-between;
align-items: center;
padding: 0.8rem 1.2rem;
background: rgba(0, 0, 0, 0.2);
border: 1px solid rgba(255, 255, 255, 0.05);
background: var(--bg);
border: 1px solid var(--border);
border-radius: 8px;
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
}
@@ -727,13 +727,13 @@
}
.file-name {
color: white;
color: var(--text);
font-size: 0.9rem;
font-weight: 500;
}
.file-size {
color: #888;
color: var(--text-muted);
font-size: 0.8rem;
}
@@ -772,8 +772,8 @@
aspect-ratio: 1;
border-radius: 8px;
overflow: hidden;
background: rgba(0, 0, 0, 0.3);
border: 1px solid rgba(255, 255, 255, 0.08);
background: var(--surface-2);
border: 1px solid var(--border);
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
}
@@ -859,8 +859,8 @@
display: flex;
gap: 1rem;
padding: 1.5rem 2rem;
border-top: 1px solid rgba(255, 255, 255, 0.08);
background: linear-gradient(135deg, rgba(255, 255, 255, 0.03), rgba(255, 255, 255, 0.01));
border-top: 1px solid var(--border);
background: var(--surface);
flex-shrink: 0;
}
@@ -888,15 +888,15 @@
}
.action-button.secondary {
background: linear-gradient(135deg, rgba(255, 255, 255, 0.05), rgba(255, 255, 255, 0.02));
border-color: rgba(255, 255, 255, 0.08);
color: #888;
background: var(--surface-2);
border-color: var(--border);
color: var(--text-muted);
}
.action-button.secondary:hover:not(:disabled) {
background: linear-gradient(135deg, rgba(255, 255, 255, 0.08), rgba(255, 255, 255, 0.04));
border-color: rgba(255, 255, 255, 0.15);
color: white;
background: var(--surface);
border-color: var(--border);
color: var(--text);
}
.action-button:disabled {
+1 -1
View File
@@ -128,7 +128,7 @@
line-height: 1.5;
flex: 1;
letter-spacing: 0.1px;
color: rgba(255, 255, 255, 0.85);
color: var(--text);
}
.toast-progress {
@@ -0,0 +1,41 @@
import { createContext, useContext, useEffect, useState } from "react";
import type { ReactNode } from "react";
type Theme = "dark" | "light";
interface ThemeContextValue {
theme: Theme;
toggleTheme: () => void;
}
const ThemeContext = createContext<ThemeContextValue>({
theme: "dark",
toggleTheme: () => {},
});
export function ThemeProvider({ children }: { children: ReactNode }) {
const [theme, setTheme] = useState<Theme>(() => {
const stored = localStorage.getItem("theme");
if (stored === "light" || stored === "dark") return stored;
return "dark";
});
useEffect(() => {
document.documentElement.setAttribute("data-theme", theme);
localStorage.setItem("theme", theme);
}, [theme]);
const toggleTheme = () => {
setTheme((prev) => (prev === "dark" ? "light" : "dark"));
};
return (
<ThemeContext.Provider value={{ theme, toggleTheme }}>
{children}
</ThemeContext.Provider>
);
}
export function useTheme(): ThemeContextValue {
return useContext(ThemeContext);
}
+7 -2
View File
@@ -12,7 +12,6 @@
line-height: 1.5;
font-weight: 400;
color-scheme: dark;
color: rgba(255, 255, 255, 0.87);
font-synthesis: none;
text-rendering: optimizeLegibility;
-webkit-font-smoothing: antialiased;
@@ -21,6 +20,10 @@
font-size: 16px;
}
[data-theme="light"] {
color-scheme: light;
}
html, body {
margin: 0;
padding: 0;
@@ -36,6 +39,8 @@ body {
position: fixed;
width: 100%;
height: 100%;
background-color: var(--bg);
color: var(--text);
}
#root {
@@ -75,7 +80,7 @@ button {
font-size: 1em;
font-weight: 500;
font-family: inherit;
background-color: #1a1a1a;
background-color: var(--surface);
cursor: pointer;
transition: all 0.2s;
-webkit-tap-highlight-color: transparent;
@@ -4,7 +4,7 @@
left: 0;
right: 0;
bottom: 0;
background-color: #09090b;
background-color: var(--bg);
display: flex;
align-items: center;
justify-content: center;
@@ -37,21 +37,21 @@
.cp-title {
font-size: 1.6rem;
font-weight: 700;
color: #f4f4f5;
color: var(--text);
margin-bottom: 0.5rem;
}
.cp-subtitle {
color: #71717a;
color: var(--text-muted);
font-size: 0.9rem;
line-height: 1.5;
padding: 0 0.5rem;
}
.cp-card {
background-color: #111115;
background-color: var(--surface);
border-radius: 1rem;
border: 1px solid rgba(255, 255, 255, 0.07);
border: 1px solid var(--border);
box-shadow: 0 25px 50px rgba(0, 0, 0, 0.5);
padding: 2rem;
}
@@ -71,7 +71,7 @@
.cp-label {
font-size: 0.875rem;
font-weight: 500;
color: #d1d5db;
color: var(--text);
}
.cp-input-wrapper {
@@ -84,23 +84,23 @@
left: 1rem;
transform: translateY(-50%);
pointer-events: none;
color: #52525b;
color: var(--text-muted);
}
.cp-input {
width: 100%;
padding: 0.75rem 3rem 0.75rem 3rem;
background-color: #09090b;
border: 1px solid rgba(255, 255, 255, 0.1);
background-color: var(--bg);
border: 1px solid var(--border);
border-radius: 0.5rem;
color: #f4f4f5;
color: var(--text);
font-size: 1rem;
transition: border-color 0.2s, box-shadow 0.2s;
box-sizing: border-box;
}
.cp-input::placeholder {
color: #52525b;
color: var(--text-muted);
}
.cp-input:focus {
@@ -120,7 +120,7 @@
transform: translateY(-50%);
background: transparent;
border: none;
color: #52525b;
color: var(--text-muted);
cursor: pointer;
padding: 0.25rem;
display: flex;
@@ -129,12 +129,12 @@
}
.cp-toggle:hover {
color: #a1a1aa;
color: var(--text);
}
.cp-hint {
font-size: 0.78rem;
color: #71717a;
color: var(--text-muted);
margin: 0;
}
@@ -189,7 +189,7 @@
gap: 0.75rem;
padding: 1.5rem 0;
text-align: center;
color: #f4f4f5;
color: var(--text);
}
.cp-success-icon {
@@ -212,7 +212,7 @@
}
.cp-success-sub {
color: #71717a;
color: var(--text-muted);
font-size: 0.85rem;
font-weight: 400 !important;
}
+1 -2
View File
@@ -10,7 +10,6 @@
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen', 'Ubuntu', 'Cantarell', sans-serif;
line-height: 1.5;
font-weight: 400;
color-scheme: dark;
color: rgba(255, 255, 255, 0.87);
font-synthesis: none;
text-rendering: optimizeLegibility;
@@ -145,7 +144,7 @@ button {
font-size: 1em;
font-weight: 500;
font-family: inherit;
background-color: #1a1a1a;
background-color: var(--surface);
cursor: pointer;
transition: all 0.2s;
-webkit-tap-highlight-color: transparent;
+29 -18
View File
@@ -4,7 +4,7 @@
left: 0;
right: 0;
bottom: 0;
background-color: #1a1a1a;
background-color: var(--bg);
display: flex;
align-items: center;
justify-content: center;
@@ -38,20 +38,20 @@
.login-title {
font-size: 1.875rem;
font-weight: bold;
color: white;
color: var(--text);
margin-bottom: 0.5rem;
}
.login-subtitle {
color: #9ca3af;
color: var(--text-muted);
}
.login-card {
background-color: #000000;
background-color: var(--surface);
backdrop-filter: blur(16px);
border-radius: 1rem;
box-shadow: 0 25px 50px -12px rgba(0, 0, 0, 0.25);
border: 1px solid rgba(75, 85, 99, 0.5);
border: 1px solid var(--border);
padding: 2rem;
}
@@ -70,7 +70,7 @@
display: block;
font-size: 0.875rem;
font-weight: 500;
color: #d1d5db;
color: var(--text);
margin-bottom: 0.5rem;
}
@@ -84,22 +84,22 @@
left: 1rem;
transform: translateY(-50%);
pointer-events: none;
color: #6b7280;
color: var(--text-muted);
}
.form-input {
width: 100%;
padding: 0.75rem 1rem 0.75rem 3rem;
background-color: #000000;
border: 1px solid #4b5563;
background-color: var(--bg);
border: 1px solid var(--border);
border-radius: 0.5rem;
color: white;
color: var(--text);
font-size: 1rem;
transition: all 0.3s;
}
.form-input::placeholder {
color: #6b7280;
color: var(--text-muted);
}
.form-input:focus {
@@ -121,7 +121,7 @@
transform: translateY(-50%);
background: transparent;
border: none;
color: #6b7280;
color: var(--text-muted);
cursor: pointer;
padding: 0.25rem;
display: flex;
@@ -130,7 +130,7 @@
}
.password-toggle:hover {
color: #d1d5db;
color: var(--text);
}
.error-message {
@@ -139,6 +139,17 @@
color: #f87171;
}
.error-banner {
padding: 0.75rem 1rem;
margin-bottom: 1rem;
background: rgba(239, 68, 68, 0.1);
border-left: 4px solid #ef4444;
border-radius: 6px;
color: #f87171;
font-size: 0.875rem;
font-weight: 500;
}
.form-options {
display: flex;
align-items: center;
@@ -154,8 +165,8 @@
.remember-me input {
width: 1rem;
height: 1rem;
background-color: #111827;
border: 1px solid #4b5563;
background-color: var(--surface-2);
border: 1px solid var(--border);
border-radius: 0.25rem;
cursor: pointer;
accent-color: #7c3aed;
@@ -164,7 +175,7 @@
.remember-me span {
margin-left: 0.5rem;
font-size: 0.875rem;
color: #9ca3af;
color: var(--text-muted);
}
.forgot-password {
@@ -236,7 +247,7 @@
}
.signup-text {
color: #9ca3af;
color: var(--text-muted);
font-size: 0.875rem;
}
@@ -257,6 +268,6 @@
.login-footer {
margin-top: 2rem;
text-align: center;
color: #6b7280;
color: var(--text-muted);
font-size: 0.875rem;
}
+1 -11
View File
@@ -158,17 +158,7 @@ const LoginClient = () => {
<form className="login-form" onSubmit={handleSubmit}>
{/* ✅ Erreur API globale */}
{apiError && (
<div
className="error-banner"
style={{
padding: "12px",
marginBottom: "16px",
backgroundColor: "#fee",
borderLeft: "4px solid #f44",
borderRadius: "4px",
color: "#c33",
}}
>
<div className="error-banner">
{apiError}
</div>
)}
+32 -48
View File
@@ -9,7 +9,7 @@
padding-top: calc(60px + clamp(1rem, 3vw, 2rem));
max-width: 1200px;
margin: 0 auto;
background: linear-gradient(135deg, #0f0f0f 0%, #1a1a1a 100%);
background: linear-gradient(135deg, var(--bg) 0%, var(--surface) 100%);
}
.cart-header {
@@ -23,15 +23,11 @@
}
.cart-header h1 {
color: white;
font-size: clamp(1.8rem, 5vw, 2.5rem);
margin: 0;
font-weight: bold;
letter-spacing: -0.5px;
background: #ffffff;
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
background-clip: text;
color: var(--text);
}
.clear-all-button {
@@ -80,12 +76,12 @@
.empty-cart-icon {
margin-bottom: 1.5rem;
opacity: 0.4;
color: rgba(255, 255, 255, 0.5);
color: var(--text-muted);
}
.empty-cart p {
font-size: 1.2rem;
color: #888;
color: var(--text-muted);
margin-bottom: 2rem;
}
@@ -121,12 +117,8 @@
.cart-item {
position: relative;
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-radius: 16px;
padding: clamp(1.5rem, 4vw, 2rem);
display: flex;
@@ -156,11 +148,7 @@
}
.cart-item:hover {
background: linear-gradient(
135deg,
rgba(255, 255, 255, 0.05) 0%,
rgba(255, 255, 255, 0.02) 100%
);
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);
@@ -177,12 +165,8 @@
height: 220px;
border-radius: 12px;
overflow: hidden;
background: linear-gradient(
135deg,
rgba(255, 255, 255, 0.08) 0%,
rgba(255, 255, 255, 0.04) 100%
);
border: 1px solid rgba(255, 255, 255, 0.1);
background: var(--surface-2);
border: 1px solid var(--border);
}
.cart-item-image {
@@ -241,7 +225,7 @@
}
.cart-item-info h3 {
color: white;
color: var(--text);
font-size: clamp(1.2rem, 4vw, 1.4rem);
margin: 0;
font-weight: 600;
@@ -262,7 +246,7 @@
/* ✨ NOUVEAU: Affichage de la quantité en grammes */
.cart-item-quantity {
font-size: 1rem;
color: rgba(255, 255, 255, 0.8);
color: var(--text-muted);
margin: 0.5rem 0;
padding: 0.5rem 1rem;
background: rgba(124, 58, 237, 0.1);
@@ -294,7 +278,7 @@
align-items: center;
justify-content: flex-end;
padding-top: 1rem;
border-top: 1px solid rgba(255, 255, 255, 0.08);
border-top: 1px solid var(--border);
}
.remove-item-button {
@@ -336,8 +320,8 @@
/* Cart Summary */
.cart-summary {
background-color: rgba(255, 255, 255, 0.02);
border: 1px solid rgba(255, 255, 255, 0.08);
background-color: var(--surface);
border: 1px solid var(--border);
border-radius: 12px;
padding: clamp(1.5rem, 4vw, 2rem);
position: sticky;
@@ -350,20 +334,20 @@
justify-content: space-between;
align-items: center;
margin-bottom: 1rem;
color: rgba(255, 255, 255, 0.7);
color: var(--text-muted);
font-size: clamp(0.95rem, 3vw, 1.05rem);
}
.summary-row span:last-child {
font-weight: 600;
color: white;
color: var(--text);
}
.total-row {
color: white;
color: var(--text);
font-size: clamp(1.2rem, 4vw, 1.4rem);
padding-top: 1rem;
border-top: 1px solid rgba(255, 255, 255, 0.1);
border-top: 1px solid var(--border);
margin-top: 1rem;
margin-bottom: 1.5rem;
font-weight: 700;
@@ -431,7 +415,7 @@
.loading-cart p {
font-size: 1.2rem;
color: #666;
color: var(--text-muted);
animation: pulse 1.5s ease-in-out infinite;
}
@@ -471,7 +455,7 @@
}
.modal-content2 {
background: #1a1a1a;
background: var(--surface);
border: 1px solid rgba(239, 68, 68, 0.3);
border-radius: 20px;
padding: clamp(2rem, 5vw, 3rem);
@@ -529,7 +513,7 @@
}
.modal-title {
color: white;
color: var(--text);
font-size: clamp(1.5rem, 4vw, 1.8rem);
font-weight: 700;
text-align: center;
@@ -538,7 +522,7 @@
}
.modal-message {
color: rgba(255, 255, 255, 0.7);
color: var(--text-muted);
font-size: clamp(0.95rem, 3vw, 1.05rem);
text-align: center;
line-height: 1.6;
@@ -570,15 +554,15 @@
}
.modal-cancel {
background: #0f0f0f;
color: rgba(255, 255, 255, 0.8);
border: 1px solid rgba(255, 255, 255, 0.15);
background: var(--bg);
color: var(--text-muted);
border: 1px solid var(--border);
}
.modal-cancel:hover {
background: rgba(255, 255, 255, 0.1);
color: white;
border-color: rgba(255, 255, 255, 0.3);
background: var(--surface-2);
color: var(--text);
border-color: var(--text-muted);
transform: translateY(-2px);
}
@@ -628,7 +612,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);
@@ -675,9 +659,9 @@
.cart-item-media-container.loading {
background: linear-gradient(
90deg,
rgba(255, 255, 255, 0.05) 0%,
rgba(255, 255, 255, 0.1) 50%,
rgba(255, 255, 255, 0.05) 100%
var(--surface) 0%,
var(--surface-2) 50%,
var(--surface) 100%
);
background-size: 200% 100%;
animation: shimmer 1.5s infinite;
+138 -73
View File
@@ -1,3 +1,40 @@
.checkout-telegram-note {
display: flex;
gap: 0.875rem;
align-items: flex-start;
background: rgba(41, 182, 246, 0.08);
border: 1px solid rgba(41, 182, 246, 0.3);
border-radius: 10px;
padding: 0.9rem 1rem;
margin-bottom: 1.25rem;
}
.checkout-telegram-note .fa-telegram {
font-size: 1.4rem;
color: #229ed9;
flex-shrink: 0;
margin-top: 0.1rem;
}
.checkout-telegram-note strong {
display: block;
color: #29b6f6;
font-size: 0.875rem;
margin-bottom: 0.2rem;
}
.checkout-telegram-note p {
color: var(--text-muted);
font-size: 0.82rem;
line-height: 1.5;
margin: 0;
}
.checkout-telegram-note a {
color: #29b6f6;
text-decoration: underline;
}
.form-prefill-hint {
font-size: 0.75rem;
color: #6ee7b7;
@@ -12,15 +49,12 @@
padding-top: calc(60px + clamp(1rem, 3vw, 2rem));
max-width: 1200px;
margin: 0 auto;
background: linear-gradient(135deg, #0f0f0f 0%, #1a1a1a 100%);
background: linear-gradient(135deg, var(--bg) 0%, var(--surface) 100%);
}
.checkout-container h1 {
font-size: clamp(1.8rem, 5vw, 2.5rem);
background: linear-gradient(135deg, #ffffff 0%, #b0b0b0 100%);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
background-clip: text;
color: var(--text);
text-align: center;
margin-bottom: clamp(1.5rem, 4vw, 2rem);
font-weight: 300;
@@ -36,8 +70,8 @@
/* Résumé de la commande */
.order-summary-box {
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-radius: 16px;
padding: clamp(1.5rem, 4vw, 2rem);
height: fit-content;
@@ -47,17 +81,17 @@
}
.order-summary-box:hover {
background: linear-gradient(135deg, rgba(255, 255, 255, 0.05) 0%, rgba(255, 255, 255, 0.02) 100%);
background: var(--surface-2);
border-color: rgba(16, 185, 129, 0.3);
box-shadow: 0 12px 32px rgba(16, 185, 129, 0.15);
}
.order-summary-box h2 {
font-size: clamp(1.2rem, 4vw, 1.3rem);
color: white;
color: var(--text);
margin: 0 0 clamp(1rem, 3vw, 1.5rem) 0;
padding-bottom: 1rem;
border-bottom: 1px solid rgba(255, 255, 255, 0.1);
border-bottom: 1px solid var(--border);
font-weight: 600;
letter-spacing: -0.3px;
}
@@ -74,7 +108,7 @@
gap: clamp(1rem, 3vw, 1.5rem);
margin-bottom: 1rem;
padding-bottom: 1rem;
border-bottom: 1px solid rgba(255, 255, 255, 0.08);
border-bottom: 1px solid var(--border);
transition: all 0.2s ease;
}
@@ -93,10 +127,10 @@
height: 80px;
object-fit: cover;
border-radius: 12px;
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.3);
border: 1px solid rgba(255, 255, 255, 0.1);
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
border: 1px solid var(--border);
flex-shrink: 0;
background: linear-gradient(135deg, rgba(255, 255, 255, 0.08) 0%, rgba(255, 255, 255, 0.04) 100%);
background: var(--surface-2);
transition: transform 0.3s ease;
}
@@ -114,7 +148,7 @@
.summary-item-name {
margin: 0;
color: white;
color: var(--text);
font-weight: 600;
font-size: clamp(0.9rem, 3vw, 1rem);
line-height: 1.3;
@@ -122,7 +156,7 @@
.summary-item-details {
margin: 0;
color: rgba(255, 255, 255, 0.6);
color: var(--text-muted);
font-size: clamp(0.8rem, 2vw, 0.9rem);
}
@@ -131,10 +165,10 @@
justify-content: space-between;
align-items: center;
padding-top: clamp(1rem, 3vw, 1.5rem);
border-top: 1px solid rgba(255, 255, 255, 0.1);
border-top: 1px solid var(--border);
font-size: clamp(1.1rem, 4vw, 1.3rem);
font-weight: 700;
color: white;
color: var(--text);
margin-top: clamp(1rem, 3vw, 1.5rem);
}
@@ -146,8 +180,8 @@
/* Formulaire */
.checkout-form {
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-radius: 16px;
padding: clamp(1.5rem, 4vw, 2rem);
box-shadow: 0 4px 16px rgba(0, 0, 0, 0.2);
@@ -160,7 +194,7 @@
padding: clamp(1rem, 3vw, 1.5rem);
margin-bottom: clamp(1rem, 3vw, 1.5rem);
border-radius: 8px;
color: #f472b6;
color: #f87171;
font-weight: 500;
box-shadow: 0 0 15px rgba(239, 68, 68, 0.1);
}
@@ -175,10 +209,10 @@
.form-section h3 {
font-size: clamp(1.1rem, 3vw, 1.2rem);
color: white;
color: var(--text);
margin: 0 0 clamp(1rem, 3vw, 1.5rem) 0;
padding-bottom: 0.75rem;
border-bottom: 1px solid rgba(255, 255, 255, 0.1);
border-bottom: 1px solid var(--border);
font-weight: 600;
letter-spacing: -0.3px;
}
@@ -197,25 +231,25 @@
}
.form-group label {
color: rgba(255, 255, 255, 0.9);
color: var(--text);
font-weight: 600;
font-size: clamp(0.9rem, 3vw, 1rem);
}
.form-group input {
padding: clamp(0.75rem, 2vw, 1rem);
border: 1px solid rgba(255, 255, 255, 0.1);
border: 1px solid var(--border);
border-radius: 10px;
font-size: clamp(0.9rem, 3vw, 1rem);
color: white;
background-color: rgba(255, 255, 255, 0.03);
color: var(--text);
background-color: var(--surface-2);
font-family: inherit;
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
-webkit-tap-highlight-color: transparent;
}
.form-group input::placeholder {
color: rgba(255, 255, 255, 0.3);
color: var(--text-muted);
}
.form-group input:focus {
@@ -226,10 +260,10 @@
}
.form-group input:disabled {
background-color: rgba(255, 255, 255, 0.02);
background-color: var(--surface);
cursor: not-allowed;
opacity: 0.5;
border-color: rgba(255, 255, 255, 0.05);
border-color: var(--border);
}
/* Actions */
@@ -237,7 +271,7 @@
display: flex;
gap: 1rem;
padding-top: clamp(1.5rem, 4vw, 2rem);
border-top: 1px solid rgba(255, 255, 255, 0.1);
border-top: 1px solid var(--border);
margin-top: clamp(1.5rem, 4vw, 2rem);
}
@@ -261,15 +295,15 @@
}
.back-button {
background-color: #1a1a1a;
color: rgba(255, 255, 255, 0.9);
border: 1px solid rgba(255, 255, 255, 0.15);
background-color: var(--surface);
color: var(--text);
border: 1px solid var(--border);
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.3);
}
.back-button:hover:not(:disabled) {
background-color: #252525;
border-color: rgba(255, 255, 255, 0.25);
background-color: var(--surface-2);
border-color: var(--text-muted);
transform: translateY(-2px);
box-shadow: 0 8px 20px rgba(0, 0, 0, 0.4);
}
@@ -353,7 +387,7 @@
}
.confirmation-modal {
background: #1a1a1a;
background: var(--surface);
border: 2px solid #5b21b6;
border-radius: 20px;
max-width: 600px;
@@ -376,7 +410,7 @@
}
.confirmation-modal-header {
background: #0f0f0f;
background: var(--bg);
padding: 2rem;
border-bottom: 2px solid #5b21b6;
display: flex;
@@ -404,20 +438,20 @@
.confirmation-modal-header h2 {
margin: 0;
font-size: clamp(1.5rem, 4vw, 2rem);
color: #ffffff;
color: var(--text);
font-weight: 700;
letter-spacing: -0.5px;
}
.confirmation-modal-body {
padding: 2rem;
background: #1a1a1a;
background: var(--surface);
}
.confirmation-section {
margin-bottom: 1.5rem;
padding: 1.25rem;
background: #0f0f0f;
background: var(--bg);
border-radius: 12px;
border: 1px solid #5b21b6;
}
@@ -431,7 +465,7 @@
align-items: center;
gap: 0.75rem;
font-size: 1.1rem;
color: #ffffff;
color: var(--text);
font-weight: 600;
margin-bottom: 1rem;
}
@@ -445,7 +479,7 @@
}
.confirmation-detail {
color: rgba(255, 255, 255, 0.8);
color: var(--text-muted);
font-size: 0.95rem;
line-height: 1.8;
margin: 0.5rem 0;
@@ -453,7 +487,7 @@
}
.confirmation-detail strong {
color: rgba(255, 255, 255, 0.95);
color: var(--text);
font-weight: 600;
}
@@ -494,19 +528,19 @@
}
.confirmation-footer {
color: rgba(255, 255, 255, 0.75);
color: var(--text-muted);
font-size: 0.9rem;
line-height: 1.8;
text-align: center;
padding: 1.25rem;
background: #0f0f0f;
background: var(--bg);
border-radius: 12px;
border: 1px solid #5b21b6;
margin-top: 1.5rem;
}
.confirmation-footer-highlight {
color: rgba(255, 255, 255, 0.95);
color: var(--text);
font-weight: 600;
display: inline-flex;
align-items: center;
@@ -532,7 +566,7 @@
.confirmation-modal-actions {
padding: 1.5rem 2rem 2rem;
background: #1a1a1a;
background: var(--surface);
border-radius: 0 0 18px 18px;
}
@@ -568,6 +602,37 @@
transform: translateY(-1px) scale(0.98);
}
.confirmation-modal-header--error {
background: linear-gradient(135deg, #dc2626 0%, #991b1b 100%);
}
.confirmation-modal-header--crypto {
background: linear-gradient(135deg, #f7931a 0%, #c2620a 100%);
}
.confirmation-button--neutral {
background: var(--surface-2);
border-color: var(--border);
color: var(--text);
box-shadow: none;
}
.confirmation-button--neutral:hover {
background: var(--surface);
box-shadow: none;
transform: translateY(-1px);
}
.confirmation-detail--centered {
text-align: center;
padding: 1rem 0;
}
.confirmation-detail--muted {
color: var(--text-muted);
font-size: 0.875rem;
}
/* Scrollbar personnalisée pour le modal */
.confirmation-modal::-webkit-scrollbar {
width: 8px;
@@ -879,7 +944,7 @@
}
.back-button:hover {
background-color: #1a1a1a;
background-color: var(--surface);
transform: none;
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.3);
}
@@ -920,7 +985,7 @@
}
.referral-toggle-label {
color: #e5e7eb;
color: var(--text);
font-size: 0.9rem;
font-weight: 600;
margin: 0 0 0.2rem;
@@ -950,7 +1015,7 @@
.referral-switch-slider {
position: absolute;
inset: 0;
background: #374151;
background: var(--surface-2);
border-radius: 26px;
cursor: pointer;
transition: background 0.2s;
@@ -988,10 +1053,10 @@
.payment-method-btn {
flex: 1;
padding: 0.75rem 1rem;
border: 2px solid #2d2d2d;
border: 2px solid var(--border);
border-radius: 10px;
background: #1a1a1a;
color: #9ca3af;
background: var(--surface);
color: var(--text-muted);
font-size: 0.95rem;
cursor: pointer;
transition: all 0.2s;
@@ -1002,8 +1067,8 @@
}
.payment-method-btn:hover {
border-color: #4b5563;
color: #fff;
border-color: var(--text-muted);
color: var(--text);
}
.payment-method-btn.active {
@@ -1022,7 +1087,7 @@
.crypto-currency-selector label {
display: block;
font-size: 0.85rem;
color: #9ca3af;
color: var(--text-muted);
margin-bottom: 0.5rem;
}
@@ -1034,10 +1099,10 @@
.crypto-currency-btn {
padding: 0.4rem 0.9rem;
border: 2px solid #2d2d2d;
border: 2px solid var(--border);
border-radius: 8px;
background: #1a1a1a;
color: #9ca3af;
background: var(--surface);
color: var(--text-muted);
font-size: 0.8rem;
font-weight: 600;
cursor: pointer;
@@ -1046,8 +1111,8 @@
}
.crypto-currency-btn:hover {
border-color: #4b5563;
color: #fff;
border-color: var(--text-muted);
color: var(--text);
}
.crypto-currency-btn.active {
@@ -1058,7 +1123,7 @@
.crypto-info-hint {
font-size: 0.78rem;
color: #6b7280;
color: var(--text-muted);
margin-top: 0.6rem;
}
@@ -1069,8 +1134,8 @@
display: flex;
align-items: center;
gap: 0.75rem;
background: #111;
border: 1px solid #2d2d2d;
background: var(--bg);
border: 1px solid var(--border);
border-radius: 8px;
padding: 0.75rem 1rem;
margin-top: 0.5rem;
@@ -1080,17 +1145,17 @@
.crypto-address {
font-family: monospace;
font-size: 0.78rem;
color: #d1d5db;
color: var(--text-muted);
word-break: break-all;
flex: 1;
}
.crypto-copy-btn {
padding: 0.35rem 0.75rem;
border: 1px solid #374151;
border: 1px solid var(--border);
border-radius: 6px;
background: #1f2937;
color: #9ca3af;
background: var(--surface-2);
color: var(--text-muted);
font-size: 0.8rem;
cursor: pointer;
white-space: nowrap;
@@ -1098,12 +1163,12 @@
}
.crypto-copy-btn:hover {
background: #374151;
color: #fff;
background: var(--surface);
color: var(--text);
}
.crypto-equiv {
color: #6b7280;
color: var(--text-muted);
font-size: 0.85rem;
margin-left: 0.5rem;
}
@@ -1117,7 +1182,7 @@
.crypto-polling-info {
text-align: center;
color: #6b7280;
color: var(--text-muted);
font-size: 0.82rem;
margin-top: 0.5rem;
}
+23 -9
View File
@@ -11,6 +11,7 @@ import './Checkout.css';
// ============================================
interface ConfirmationData {
command_id: number;
client_order_number?: number;
assigned_to?: {
username: string;
distance_km?: number;
@@ -267,6 +268,7 @@ function Checkout() {
if (response.success && response.payment_method === 'crypto') {
setCryptoPaymentData({
command_id: response.command_id!,
client_order_number: (response as any).client_order_number,
payment_status: response.payment_status!,
pay_address: response.pay_address!,
pay_amount: response.pay_amount!,
@@ -325,6 +327,7 @@ function Checkout() {
// ✅ Préparer les données pour le modal
setConfirmationData({
command_id,
client_order_number: (response as any).client_order_number,
assigned_to,
queue_info,
delivery_address: delivery_address || address,
@@ -537,6 +540,17 @@ function Checkout() {
</div>
)}
<div className="checkout-telegram-note">
<i className="fab fa-telegram" />
<div>
<strong>Compte Telegram requis</strong>
<p>
Votre commande ne pourra être validée que si votre compte Telegram est lié.
Rendez-vous dans votre <a href="/user/profil">profil</a> pour le lier avant de confirmer.
</p>
</div>
</div>
<div className="form-actions">
<button
type="button"
@@ -565,14 +579,14 @@ function Checkout() {
{showCryptoModal && cryptoPaymentData && (
<div className="confirmation-modal-overlay">
<div className="confirmation-modal">
<div className="confirmation-modal-header" style={{ background: 'linear-gradient(135deg, #f7931a, #c2620a)' }}>
<div className="confirmation-modal-header confirmation-modal-header--crypto">
<i className="fas fa-coins confirmation-icon" />
<h2>Paiement Crypto</h2>
</div>
<div className="confirmation-modal-body">
<div className="confirmation-section">
<div className="confirmation-section-title">
<i className="fas fa-receipt icon" /> Commande #{cryptoPaymentData.command_id}
<i className="fas fa-receipt icon" /> Commande #{cryptoPaymentData.client_order_number ?? cryptoPaymentData.command_id}
</div>
<div className="confirmation-detail">
<strong>Statut :</strong>{' '}
@@ -627,7 +641,7 @@ function Checkout() {
)}
</div>
<div className="confirmation-modal-actions">
<button className="confirmation-button" style={{ background: '#374151' }} onClick={handleCryptoModalClose}>
<button className="confirmation-button confirmation-button--neutral" onClick={handleCryptoModalClose}>
<i className="fas fa-location-arrow" /> Suivre ma commande
</button>
</div>
@@ -641,22 +655,22 @@ function Checkout() {
{showZoneModal && (
<div className="confirmation-modal-overlay" onClick={() => setShowZoneModal(false)}>
<div className="confirmation-modal" onClick={(e) => e.stopPropagation()}>
<div className="confirmation-modal-header" style={{ background: 'linear-gradient(135deg, #dc2626, #991b1b)' }}>
<div className="confirmation-modal-header confirmation-modal-header--error">
<i className="fas fa-map-marker-alt confirmation-icon"></i>
<h2>Zone non desservie</h2>
</div>
<div className="confirmation-modal-body">
<div className="confirmation-section">
<div className="confirmation-detail" style={{ textAlign: 'center', padding: '1rem 0' }}>
<p style={{ fontSize: '1rem', marginBottom: '0.75rem' }}>{zoneErrorMsg}</p>
<p style={{ color: '#9ca3af', fontSize: '0.875rem' }}>
<div className="confirmation-detail confirmation-detail--centered">
<p style={{ marginBottom: '0.75rem' }}>{zoneErrorMsg}</p>
<p className="confirmation-detail--muted">
Vérifiez l'adresse saisie ou contactez-nous pour connaître les zones de livraison disponibles.
</p>
</div>
</div>
</div>
<div className="confirmation-modal-actions">
<button className="confirmation-button" style={{ background: '#374151' }} onClick={() => setShowZoneModal(false)}>
<button className="confirmation-button confirmation-button--neutral" onClick={() => setShowZoneModal(false)}>
<i className="fas fa-arrow-left"></i> Modifier l'adresse
</button>
</div>
@@ -685,7 +699,7 @@ function Checkout() {
Détails de la commande
</div>
<div className="confirmation-detail">
<strong>Numéro:</strong> #{confirmationData.command_id}
<strong>Numéro:</strong> #{confirmationData.client_order_number ?? confirmationData.command_id}
</div>
<div className="confirmation-detail">
<strong>Date:</strong> {new Date().toLocaleString('fr-FR')}
@@ -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;
}
@@ -41,7 +41,7 @@ function ConsultationHistorique() {
const [orders, setOrders] = useState<CompletedOrder[]>([]);
const [clientStats, setClientStats] = useState<ClientStats | null>(null);
const [penalties, setPenalties] = useState<PenaltyInfo | null>(null);
const [appSettings, setAppSettings] = useState<PublicSettings>({ 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<PublicSettings>({ 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<number>(0);
const [isLoading, setIsLoading] = useState(true);
const [error, setError] = useState<string>('');
@@ -365,7 +365,7 @@ function ConsultationHistorique() {
className="clickable-row"
>
<td className="order-id">
#{order.id.toString().padStart(5, '0')}
#{(order.client_order_number ?? 0).toString().padStart(4, '0')}
</td>
<td>
<div className="date-cell">
@@ -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;
+41 -41
View File
@@ -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;
}
+8 -10
View File
@@ -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() {
</button>
<div className="header-info">
<h1 className="order-number">
Commande #{order.id.toString().padStart(5, "0")}
Commande #
{(order.client_order_number ?? 0)
.toString()
.padStart(4, "0")}
</h1>
<div className="status-container">
<span className="status-badge delivered">
+60 -13
View File
@@ -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;
}
@@ -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<number>(0);
const [loading, setLoading] = useState(true);
const [settings, setSettings] = useState<PublicSettings | null>(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() {
)}
</div>
{/* Montant par parrainage */}
{settings && settings.referral_amount > 0 && (
<div className="parrainage-reward-card">
<div className="reward-icon"><i className="fas fa-coins"></i></div>
<div className="reward-content">
<div className="reward-label">Crédit par filleul parrainé</div>
<div className="reward-amount">{settings.referral_amount.toFixed(2)} </div>
<p className="reward-hint">
Vous recevrez ce montant sur votre solde dès que votre filleul
effectue sa première commande validée.
</p>
</div>
</div>
)}
{/* Étapes */}
<div className="parrainage-steps-section">
<h2 className="parrainage-section-title">Comment ça marche ?</h2>
+40 -43
View File
@@ -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;
}
+141 -13
View File
@@ -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;
}
+88 -3
View File
@@ -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() {
<FontAwesomeIcon icon={faSave} />
{savingContact ? ' Enregistrement...' : ' Enregistrer le compte'}
</button>
<button className="profile-btn profile-btn--secondary" onClick={() => navigate('/user/change-password')} style={{ marginTop: '0.6rem' }}>
<FontAwesomeIcon icon={faLock} /> Changer le mot de passe
</button>
</div>
{/* Section adresse par défaut */}
@@ -217,11 +250,63 @@ export default function ProfilePage() {
placeholder="@votre.pseudo.signal"
/>
</div>
<button className="profile-btn profile-btn--secondary" onClick={saveLocal}>
<button className="profile-btn profile-btn--secondary" onClick={() => setShowSaveModal(true)}>
<FontAwesomeIcon icon={faSave} /> Enregistrer les infos par défaut
</button>
</div>
{/* Section Telegram */}
{tgEnabled && (
<div className="profile-card">
<h2 className="profile-card-title">
<FontAwesomeIcon icon={faPaperPlane} className="profile-card-icon profile-card-icon--telegram" />
Notifications Telegram
</h2>
<p className="profile-hint">
Recevez vos notifications sur Telegram, même quand le site est fermé.
</p>
{tgLinked ? (
<div className="profile-telegram-linked">
<span className="profile-telegram-status">
<FontAwesomeIcon icon={faCheckCircle} /> Compte Telegram lié
</span>
<button className="profile-btn profile-btn--danger" onClick={handleUnlinkTelegram}>
<FontAwesomeIcon icon={faUnlink} /> Délier Telegram
</button>
</div>
) : (
<button className="profile-btn profile-btn--telegram" onClick={handleLinkTelegram} disabled={tgLoading}>
<FontAwesomeIcon icon={faPaperPlane} />
{tgLoading ? ' Génération du lien...' : ' Lier mon compte Telegram'}
</button>
)}
</div>
)}
</div>
{showSaveModal && (
<div className="profile-modal-overlay" onClick={() => setShowSaveModal(false)}>
<div className="profile-modal" onClick={(e) => e.stopPropagation()}>
<button className="profile-modal-close" onClick={() => setShowSaveModal(false)}>
<FontAwesomeIcon icon={faTimes} />
</button>
<div className="profile-modal-icon">
<FontAwesomeIcon icon={faSave} />
</div>
<h3 className="profile-modal-title">Enregistrer les infos par défaut ?</h3>
<p className="profile-modal-body">
Adresse, téléphone de livraison et pseudo Signal seront sauvegardés localement et pré-remplis lors de vos prochaines commandes.
</p>
<div className="profile-modal-actions">
<button className="profile-modal-btn profile-modal-btn--cancel" onClick={() => setShowSaveModal(false)}>
Annuler
</button>
<button className="profile-modal-btn profile-modal-btn--confirm" onClick={saveLocal}>
<FontAwesomeIcon icon={faCheckCircle} /> Confirmer
</button>
</div>
</div>
</div>
)}
</>
);
}
+68 -68
View File
@@ -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;
}
@@ -675,7 +675,7 @@ function SuiviLivraison() {
<div className="order-header-left">
<div className="order-id-row">
<span className="order-id">
Commande #{order.id}
Commande #{order.client_order_number}
</span>
<div
className="status-badge"
+13 -21
View File
@@ -15,7 +15,7 @@
box-sizing: border-box;
overflow-x: hidden;
margin-top: 0;
background-color: #1a1a1a;
background-color: var(--bg);
position: relative;
}
@@ -77,8 +77,8 @@
.category-button {
background-color: transparent;
color: white;
border: 2px solid rgba(255, 255, 255, 0.3);
color: var(--text);
border: 2px solid var(--border);
border-radius: 25px;
padding: clamp(0.6rem, 2vw, 0.8rem) clamp(1.2rem, 3vw, 1.5rem);
font-size: clamp(0.9rem, 3vw, 1rem);
@@ -95,9 +95,9 @@
}
.category-button.active {
background-color: white;
color: black;
border-color: white;
background-color: var(--text);
color: var(--bg);
border-color: var(--text);
}
/* ===== CATEGORY HEADER ===== */
@@ -121,22 +121,14 @@
.category-title {
font-size: clamp(1.8rem, 5vw, 2.8rem);
font-weight: 700;
color: white;
color: var(--text);
margin: 0 0 0.8rem 0;
letter-spacing: -0.5px;
background: linear-gradient(
135deg,
#ffffff 0%,
rgba(255, 255, 255, 0.8) 100%
);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
background-clip: text;
}
.category-subtitle {
font-size: clamp(0.95rem, 3vw, 1.1rem);
color: rgba(255, 255, 255, 0.65);
color: var(--text-muted);
margin: 0;
font-weight: 400;
letter-spacing: 0.3px;
@@ -226,12 +218,12 @@
@media (hover: none) {
.category-button:hover {
background-color: transparent;
color: white;
color: var(--text);
}
.category-button.active:hover {
background-color: white;
color: black;
background-color: var(--text);
color: var(--bg);
}
}
@@ -245,13 +237,13 @@
padding: 3rem;
text-align: center;
min-height: 300px;
color: white;
color: var(--text);
}
.loading-container p,
.empty-container p {
font-size: 1.1rem;
color: rgba(255, 255, 255, 0.8);
color: var(--text-muted);
}
.error-message {
+13 -13
View File
@@ -11,7 +11,7 @@
}
.page-container h1 {
color: white;
color: var(--text);
font-size: clamp(1.8rem, 6vw, 2.5rem);
margin-bottom: clamp(1.5rem, 4vh, 2rem);
text-align: center;
@@ -29,9 +29,9 @@
}
.page-link {
background-color: #1a1a1a;
border: 2px solid white;
color: white;
background-color: var(--surface);
border: 2px solid var(--text);
color: var(--text);
padding: clamp(1rem, 3vw, 1.5rem);
text-decoration: none;
border-radius: 12px;
@@ -45,16 +45,16 @@
.page-link:active {
transform: scale(0.97);
background-color: #2a2a2a;
background-color: var(--surface-2);
}
.back-link {
color: white;
color: var(--text);
text-decoration: none;
margin-top: clamp(1rem, 3vh, 2rem);
font-size: clamp(0.9rem, 3vw, 1.1rem);
padding: clamp(0.6rem, 2vw, 0.8rem) clamp(1rem, 3vw, 1.5rem);
border: 1.5px solid white;
border: 1.5px solid var(--text);
border-radius: 8px;
transition: all 0.2s ease;
touch-action: manipulation;
@@ -62,19 +62,19 @@
}
.back-link:active {
background-color: #1a1a1a;
background-color: var(--surface);
transform: scale(0.95);
}
.content {
color: white;
color: var(--text);
font-size: clamp(1rem, 3.5vw, 1.2rem);
text-align: center;
width: 100%;
max-width: 500px;
padding: clamp(1.5rem, 4vw, 2rem);
background-color: #1a1a1a;
border: 2px solid white;
background-color: var(--surface);
border: 2px solid var(--text);
border-radius: 15px;
margin-bottom: clamp(1.5rem, 4vh, 2rem);
box-sizing: border-box;
@@ -111,9 +111,9 @@
@media (hover: none) {
.page-link:hover {
transform: none;
background-color: #1a1a1a;
background-color: var(--surface);
}
.back-link:hover {
background-color: transparent;
}