chore: add new features
This commit is contained in:
Binary file not shown.
|
After Width: | Height: | Size: 848 KiB |
@@ -6,6 +6,13 @@
|
||||
// ✅ sessionStorage (pas localStorage)
|
||||
|
||||
const API_URL = "https://uber-stup.club/api/v1";
|
||||
const BACKEND_URL = "https://uber-stup.club";
|
||||
|
||||
export function getMediaUrl(url: string): string {
|
||||
if (!url) return "";
|
||||
if (url.startsWith("http")) return url;
|
||||
return `${BACKEND_URL}${url}`;
|
||||
}
|
||||
import type {
|
||||
ConfirmReceptionResponse,
|
||||
CheckoutCartResponse,
|
||||
@@ -291,14 +298,21 @@ export const changePassword = async (
|
||||
if (!response.ok) {
|
||||
return {
|
||||
success: false,
|
||||
message: data.error || data.message || "Erreur lors du changement de mot de passe",
|
||||
message:
|
||||
data.error ||
|
||||
data.message ||
|
||||
"Erreur lors du changement de mot de passe",
|
||||
};
|
||||
}
|
||||
return { success: true, message: data.message || "Mot de passe mis à jour" };
|
||||
return {
|
||||
success: true,
|
||||
message: data.message || "Mot de passe mis à jour",
|
||||
};
|
||||
} catch (error) {
|
||||
return {
|
||||
success: false,
|
||||
message: error instanceof Error ? error.message : "Erreur de connexion",
|
||||
message:
|
||||
error instanceof Error ? error.message : "Erreur de connexion",
|
||||
};
|
||||
}
|
||||
};
|
||||
@@ -763,6 +777,7 @@ export interface Product {
|
||||
name: string;
|
||||
description?: string;
|
||||
category: string;
|
||||
unit?: string;
|
||||
stock: number;
|
||||
prices?: Array<{ quantity: number; price: number }>;
|
||||
media?: MediaItem[]; // ✅ CHANGÉ: string[] → MediaItem[]
|
||||
|
||||
@@ -365,6 +365,7 @@ export interface Product {
|
||||
description?: string;
|
||||
category: string;
|
||||
stock: number;
|
||||
unit?: string;
|
||||
prices?: ProductPrice[];
|
||||
media?: Array<{
|
||||
// ✅ CHANGÉ
|
||||
|
||||
@@ -20,6 +20,7 @@ function ProductCard({
|
||||
id,
|
||||
name,
|
||||
price,
|
||||
unit = "g",
|
||||
image,
|
||||
stock,
|
||||
category = "autre",
|
||||
@@ -180,7 +181,7 @@ function ProductCard({
|
||||
key={priceOption.quantity}
|
||||
value={priceOption.quantity}
|
||||
>
|
||||
{priceOption.quantity}g -{" "}
|
||||
{priceOption.quantity}{unit} -{" "}
|
||||
{priceOption.price.toFixed(2)} €
|
||||
</option>
|
||||
))}
|
||||
|
||||
@@ -19,6 +19,7 @@ import {
|
||||
Play,
|
||||
} from "lucide-react";
|
||||
import type { Product, ProductPrice } from "../api/api_types";
|
||||
import { getMediaUrl } from "../api/api";
|
||||
import "./ProductModal.css";
|
||||
|
||||
interface ProductDetailsModalProps {
|
||||
@@ -114,14 +115,14 @@ const ProductDetailsModal: React.FC<ProductDetailsModalProps> = ({
|
||||
<div className="media-viewer">
|
||||
{currentMedia?.type === "image" ? (
|
||||
<img
|
||||
src={`${currentMedia.url}`}
|
||||
src={getMediaUrl(currentMedia.url)}
|
||||
alt={product.name}
|
||||
className="media-display"
|
||||
/>
|
||||
) : currentMedia?.type === "video" ? (
|
||||
<div className="video-container">
|
||||
<video
|
||||
src={`${currentMedia.url}`}
|
||||
src={getMediaUrl(currentMedia.url)}
|
||||
controls
|
||||
className="media-display"
|
||||
>
|
||||
@@ -179,7 +180,7 @@ const ProductDetailsModal: React.FC<ProductDetailsModalProps> = ({
|
||||
>
|
||||
{media.type === "image" ? (
|
||||
<img
|
||||
src={`{product.media[0].url}${media.url}`}
|
||||
src={getMediaUrl(media.url)}
|
||||
alt={`Media ${index + 1}`}
|
||||
/>
|
||||
) : (
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { useState, useEffect } from "react";
|
||||
import ProductCard from "../../components/ProductCard";
|
||||
import Navbar from "../../components/Navbar";
|
||||
import { getAllProducts, getProductsByCategory } from "../../api/api";
|
||||
import { getAllProducts, getProductsByCategory, getMediaUrl } from "../../api/api";
|
||||
import type { Product } from "../../api/api";
|
||||
import "./UserAccueil.css";
|
||||
|
||||
@@ -85,7 +85,7 @@ function UserAccueil() {
|
||||
(mediaItem) => mediaItem && mediaItem.type === "video",
|
||||
);
|
||||
|
||||
return videoMedia?.url ? `${videoMedia.url}` : undefined;
|
||||
return videoMedia?.url ? getMediaUrl(videoMedia.url) : undefined;
|
||||
};
|
||||
const getProductImage = (product: Product): string => {
|
||||
// Pas de media ? Image placeholder
|
||||
@@ -99,7 +99,7 @@ function UserAccueil() {
|
||||
|
||||
// Vérifier si c'est un objet avec type "image" et url
|
||||
if (mediaItem && mediaItem.type === "image" && mediaItem.url) {
|
||||
return `${mediaItem.url}`;
|
||||
return getMediaUrl(mediaItem.url);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -107,10 +107,21 @@ function UserAccueil() {
|
||||
return "https://via.placeholder.com/400x400/1a1a1a/ffffff?text=No+Image";
|
||||
};
|
||||
|
||||
const grosSemiStyle =
|
||||
selectedCategory === "gros&semi"
|
||||
? {
|
||||
backgroundImage: `url('/logo-gros-semi.png')`,
|
||||
backgroundRepeat: "no-repeat",
|
||||
backgroundPosition: "center center",
|
||||
backgroundSize: "contain",
|
||||
backgroundAttachment: "local",
|
||||
}
|
||||
: {};
|
||||
|
||||
return (
|
||||
<>
|
||||
<Navbar />
|
||||
<div className="user-page-container">
|
||||
<div className="user-page-container" style={grosSemiStyle}>
|
||||
<div className="category-filter">
|
||||
{categories.map((category) => (
|
||||
<button
|
||||
@@ -136,18 +147,6 @@ function UserAccueil() {
|
||||
</div>
|
||||
)}
|
||||
|
||||
{error && (
|
||||
<div className="error-container">
|
||||
<p className="error-message">{error}</p>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{!loading && !error && products.length === 0 && (
|
||||
<div className="empty-container">
|
||||
<p>Aucun produit disponible dans cette catégorie.</p>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{!loading && !error && products.length > 0 && (
|
||||
<div className="products-grid">
|
||||
{products.map((product) => (
|
||||
@@ -159,7 +158,7 @@ function UserAccueil() {
|
||||
id={product.id}
|
||||
name={product.name}
|
||||
price={getProductPrice(product)}
|
||||
unit="g"
|
||||
unit={product.unit || "g"}
|
||||
image={getProductImage(product)}
|
||||
stock={product.stock}
|
||||
category={product.category}
|
||||
@@ -171,6 +170,11 @@ function UserAccueil() {
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
{selectedCategory === "gros&semi" && (
|
||||
<div className="coming-soon-overlay">
|
||||
<span className="coming-soon-text">Prochainement</span>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
|
||||
@@ -11,7 +11,7 @@ import { useCart } from "../../context/CartContext";
|
||||
import Navbar from "../../components/Navbar";
|
||||
import { useEffect, useState } from "react";
|
||||
import { useNavigate } from "react-router-dom";
|
||||
import { isUserAuthenticated, getProductById } from "../../api/api";
|
||||
import { isUserAuthenticated, getProductById, getMediaUrl } from "../../api/api";
|
||||
import type { Product } from "../../api/api";
|
||||
import { Trash2, ShoppingBag, AlertTriangle } from "lucide-react";
|
||||
import "./Cart.css";
|
||||
@@ -111,7 +111,7 @@ function Cart() {
|
||||
for (let i = 0; i < product.media.length; i++) {
|
||||
const mediaItem = product.media[i];
|
||||
if (mediaItem && mediaItem.type === "image" && mediaItem.url) {
|
||||
return `${mediaItem.url}`;
|
||||
return getMediaUrl(mediaItem.url);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -136,7 +136,7 @@ function Cart() {
|
||||
(mediaItem) => mediaItem && mediaItem.type === "video",
|
||||
);
|
||||
|
||||
return videoMedia?.url ? `${videoMedia.url}` : undefined;
|
||||
return videoMedia?.url ? getMediaUrl(videoMedia.url) : undefined;
|
||||
};
|
||||
|
||||
// ============================================
|
||||
|
||||
@@ -14,6 +14,7 @@ import {
|
||||
getCommandItemsWithDetails,
|
||||
isUserAuthenticated,
|
||||
getProductById,
|
||||
getMediaUrl,
|
||||
} from "../../api/api";
|
||||
import type { CompletedOrder, Product } from "../../api/api_types";
|
||||
import {
|
||||
@@ -117,7 +118,7 @@ function OrderDetails() {
|
||||
for (let i = 0; i < product.media.length; i++) {
|
||||
const mediaItem = product.media[i];
|
||||
if (mediaItem && mediaItem.type === "image" && mediaItem.url) {
|
||||
return `${mediaItem.url}`;
|
||||
return getMediaUrl(mediaItem.url);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -142,7 +143,7 @@ function OrderDetails() {
|
||||
(mediaItem) => mediaItem && mediaItem.type === "video",
|
||||
);
|
||||
|
||||
return videoMedia?.url ? `${videoMedia.url}` : undefined;
|
||||
return videoMedia?.url ? getMediaUrl(videoMedia.url) : undefined;
|
||||
};
|
||||
|
||||
// HANDLERS VIDÉO
|
||||
|
||||
@@ -148,7 +148,7 @@ function ProductDetail() {
|
||||
// ✅ Toast de succès
|
||||
setToast({
|
||||
show: true,
|
||||
message: `${product.name} (${selectedGrams}g) ajouté au panier !`,
|
||||
message: `${product.name} (${selectedGrams}${product.unit || "g"}) ajouté au panier !`,
|
||||
type: "success",
|
||||
});
|
||||
};
|
||||
@@ -214,7 +214,7 @@ function ProductDetail() {
|
||||
{selectedPrice > 0 && (
|
||||
<p className="product-detail-price">
|
||||
{selectedPrice.toFixed(2)} €{" "}
|
||||
{selectedGrams && `pour ${selectedGrams}g`}
|
||||
{selectedGrams && `pour ${selectedGrams}${product.unit || "g"}`}
|
||||
</p>
|
||||
)}
|
||||
|
||||
@@ -252,7 +252,7 @@ function ProductDetail() {
|
||||
key={p.quantity}
|
||||
value={p.quantity}
|
||||
>
|
||||
{p.quantity}g -{" "}
|
||||
{p.quantity}{product.unit || "g"} -{" "}
|
||||
{p.price.toFixed(2)} €
|
||||
</option>
|
||||
))}
|
||||
|
||||
@@ -1,319 +1,369 @@
|
||||
.user-page-container {
|
||||
width: 100%;
|
||||
min-height: 100vh;
|
||||
padding: clamp(1rem, 3vw, 1.5rem);
|
||||
padding-top: calc(60px + clamp(1rem, 3vw, 1.5rem));
|
||||
box-sizing: border-box;
|
||||
overflow-x: hidden;
|
||||
margin-top: 0;
|
||||
background-color: #1a1a1a;
|
||||
@font-face {
|
||||
font-family: "Reach fill & Outline";
|
||||
src:
|
||||
url("/fonts/reach-fill-outline.woff2") format("woff2"),
|
||||
url("/fonts/reach-fill-outline.woff") format("woff");
|
||||
font-weight: normal;
|
||||
font-style: normal;
|
||||
}
|
||||
|
||||
.user-page-container {
|
||||
width: 100%;
|
||||
min-height: 100vh;
|
||||
padding: clamp(1rem, 3vw, 1.5rem);
|
||||
padding-top: calc(60px + clamp(1rem, 3vw, 1.5rem));
|
||||
box-sizing: border-box;
|
||||
overflow-x: hidden;
|
||||
margin-top: 0;
|
||||
background-color: #1a1a1a;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
/* ===== COMING SOON OVERLAY (Gros&Semi) ===== */
|
||||
.coming-soon-overlay {
|
||||
position: fixed;
|
||||
inset: 0;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: flex-end;
|
||||
padding-bottom: 12vh;
|
||||
pointer-events: none;
|
||||
z-index: 10;
|
||||
}
|
||||
|
||||
.coming-soon-text {
|
||||
font-family: "Reach fill & Outline", sans-serif;
|
||||
font-size: clamp(1.5rem, 8vw, 0rem);
|
||||
font-weight: 400;
|
||||
color: #8e8fe8;
|
||||
letter-spacing: 4px;
|
||||
text-align: center;
|
||||
text-transform: uppercase;
|
||||
text-shadow:
|
||||
0 0 10px rgba(142, 143, 232, 0.9),
|
||||
0 0 25px rgba(142, 143, 232, 0.6),
|
||||
0 0 50px rgba(142, 143, 232, 0.3);
|
||||
animation: pulse-scale 2s ease-in-out infinite;
|
||||
}
|
||||
|
||||
@keyframes pulse-scale {
|
||||
0%,
|
||||
100% {
|
||||
transform: scale(1);
|
||||
}
|
||||
50% {
|
||||
transform: scale(1.35);
|
||||
}
|
||||
}
|
||||
|
||||
.category-filter {
|
||||
display: flex;
|
||||
gap: clamp(0.5rem, 2vw, 0.8rem);
|
||||
overflow-x: auto;
|
||||
padding-bottom: clamp(1rem, 3vw, 1.5rem);
|
||||
margin-bottom: clamp(1rem, 3vw, 1.5rem);
|
||||
scrollbar-width: none;
|
||||
-ms-overflow-style: none;
|
||||
display: flex;
|
||||
gap: clamp(0.5rem, 2vw, 0.8rem);
|
||||
overflow-x: auto;
|
||||
padding-bottom: clamp(1rem, 3vw, 1.5rem);
|
||||
margin-bottom: clamp(1rem, 3vw, 1.5rem);
|
||||
scrollbar-width: none;
|
||||
-ms-overflow-style: none;
|
||||
}
|
||||
|
||||
.category-filter::-webkit-scrollbar {
|
||||
display: none;
|
||||
display: none;
|
||||
}
|
||||
|
||||
.category-button {
|
||||
background-color: transparent;
|
||||
color: white;
|
||||
border: 2px solid rgba(255, 255, 255, 0.3);
|
||||
border-radius: 25px;
|
||||
padding: clamp(0.6rem, 2vw, 0.8rem) clamp(1.2rem, 3vw, 1.5rem);
|
||||
font-size: clamp(0.9rem, 3vw, 1rem);
|
||||
font-weight: 600;
|
||||
cursor: pointer;
|
||||
transition: all 0.3s ease;
|
||||
white-space: nowrap;
|
||||
-webkit-tap-highlight-color: transparent;
|
||||
flex-shrink: 0;
|
||||
background-color: transparent;
|
||||
color: white;
|
||||
border: 2px solid rgba(255, 255, 255, 0.3);
|
||||
border-radius: 25px;
|
||||
padding: clamp(0.6rem, 2vw, 0.8rem) clamp(1.2rem, 3vw, 1.5rem);
|
||||
font-size: clamp(0.9rem, 3vw, 1rem);
|
||||
font-weight: 600;
|
||||
cursor: pointer;
|
||||
transition: all 0.3s ease;
|
||||
white-space: nowrap;
|
||||
-webkit-tap-highlight-color: transparent;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.category-button:active {
|
||||
transform: scale(0.95);
|
||||
transform: scale(0.95);
|
||||
}
|
||||
|
||||
.category-button.active {
|
||||
background-color: white;
|
||||
color: black;
|
||||
border-color: white;
|
||||
background-color: white;
|
||||
color: black;
|
||||
border-color: white;
|
||||
}
|
||||
|
||||
/* Effets néon par catégorie - BOUTONS */
|
||||
.category-button.active[data-category="tous"] {
|
||||
background-color: #9333ea;
|
||||
color: white;
|
||||
border-color: #9333ea;
|
||||
background-color: #9333ea;
|
||||
color: white;
|
||||
border-color: #9333ea;
|
||||
}
|
||||
|
||||
.category-button.active[data-category="weed&hash"] {
|
||||
background-color: #10b981;
|
||||
color: white;
|
||||
border-color: #10b981;
|
||||
background-color: #10b981;
|
||||
color: white;
|
||||
border-color: #10b981;
|
||||
}
|
||||
|
||||
.category-button.active[data-category="zipette&co"] {
|
||||
background-color: #F5F5F0;
|
||||
color: black;
|
||||
border-color: #F5F5F0;
|
||||
background-color: #f5f5f0;
|
||||
color: black;
|
||||
border-color: #f5f5f0;
|
||||
}
|
||||
|
||||
.category-button.active[data-category="gros&semi"] {
|
||||
background-color: #3dc2f7;
|
||||
color: white;
|
||||
border-color: #3dc2f7;
|
||||
|
||||
background-color: #3dc2f7;
|
||||
color: white;
|
||||
border-color: #3dc2f7;
|
||||
}
|
||||
|
||||
/* ===== CATEGORY HEADER ===== */
|
||||
.category-header {
|
||||
margin-bottom: clamp(2rem, 5vw, 3rem);
|
||||
animation: headerFadeIn 0.5s cubic-bezier(0.25, 0.46, 0.45, 0.94);
|
||||
margin-bottom: clamp(2rem, 5vw, 3rem);
|
||||
animation: headerFadeIn 0.5s cubic-bezier(0.25, 0.46, 0.45, 0.94);
|
||||
}
|
||||
|
||||
@keyframes headerFadeIn {
|
||||
from {
|
||||
opacity: 0;
|
||||
transform: translateY(-10px);
|
||||
}
|
||||
to {
|
||||
opacity: 1;
|
||||
transform: translateY(0);
|
||||
}
|
||||
from {
|
||||
opacity: 0;
|
||||
transform: translateY(-10px);
|
||||
}
|
||||
to {
|
||||
opacity: 1;
|
||||
transform: translateY(0);
|
||||
}
|
||||
}
|
||||
|
||||
.category-title {
|
||||
font-size: clamp(1.8rem, 5vw, 2.8rem);
|
||||
font-weight: 700;
|
||||
color: white;
|
||||
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;
|
||||
font-size: clamp(1.8rem, 5vw, 2.8rem);
|
||||
font-weight: 700;
|
||||
color: white;
|
||||
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);
|
||||
margin: 0;
|
||||
font-weight: 400;
|
||||
letter-spacing: 0.3px;
|
||||
line-height: 1.5;
|
||||
font-size: clamp(0.95rem, 3vw, 1.1rem);
|
||||
color: rgba(255, 255, 255, 0.65);
|
||||
margin: 0;
|
||||
font-weight: 400;
|
||||
letter-spacing: 0.3px;
|
||||
line-height: 1.5;
|
||||
}
|
||||
|
||||
/* Carrousel de produits - un produit à la fois */
|
||||
.products-grid {
|
||||
display: flex;
|
||||
overflow-x: auto;
|
||||
scroll-snap-type: x mandatory;
|
||||
gap: clamp(1rem, 3vw, 1.5rem);
|
||||
width: 100%;
|
||||
max-width: 1200px;
|
||||
padding: 0 clamp(1rem, 3vw, 1.5rem);
|
||||
padding-bottom: 1rem;
|
||||
scrollbar-width: none;
|
||||
-ms-overflow-style: none;
|
||||
-webkit-overflow-scrolling: touch;
|
||||
display: flex;
|
||||
overflow-x: auto;
|
||||
scroll-snap-type: x mandatory;
|
||||
gap: clamp(1rem, 3vw, 1.5rem);
|
||||
width: 100%;
|
||||
max-width: 1200px;
|
||||
padding: 0 clamp(1rem, 3vw, 1.5rem);
|
||||
padding-bottom: 1rem;
|
||||
scrollbar-width: none;
|
||||
-ms-overflow-style: none;
|
||||
-webkit-overflow-scrolling: touch;
|
||||
}
|
||||
|
||||
.products-grid::-webkit-scrollbar {
|
||||
display: none;
|
||||
display: none;
|
||||
}
|
||||
|
||||
.products-grid > * {
|
||||
flex: 0 0 calc(100% - clamp(2rem, 6vw, 3rem));
|
||||
scroll-snap-align: center;
|
||||
scroll-snap-stop: always;
|
||||
flex: 0 0 calc(100% - clamp(2rem, 6vw, 3rem));
|
||||
scroll-snap-align: center;
|
||||
scroll-snap-stop: always;
|
||||
}
|
||||
|
||||
/* Effets néon par catégorie - CONTAINERS DE PRODUITS */
|
||||
|
||||
/* Catégorie: tous - VIOLET - Effet néon amélioré */
|
||||
.products-grid > div[data-category="tous"] .product-card {
|
||||
border: 2px solid #9333ea;
|
||||
box-shadow:
|
||||
0 0 20px rgba(147, 51, 234, 0.6),
|
||||
0 0 40px rgba(147, 51, 234, 0.4),
|
||||
0 0 60px rgba(147, 51, 234, 0.2),
|
||||
0 0 80px rgba(147, 51, 234, 0.1);
|
||||
border: 2px solid #9333ea;
|
||||
box-shadow:
|
||||
0 0 20px rgba(147, 51, 234, 0.6),
|
||||
0 0 40px rgba(147, 51, 234, 0.4),
|
||||
0 0 60px rgba(147, 51, 234, 0.2),
|
||||
0 0 80px rgba(147, 51, 234, 0.1);
|
||||
}
|
||||
|
||||
/* Catégorie: weed&hash - VERT - Effet néon amélioré */
|
||||
.products-grid > div[data-category="weed&hash"] .product-card {
|
||||
border: 2px solid #10b981;
|
||||
box-shadow:
|
||||
0 0 20px rgba(16, 185, 129, 0.6),
|
||||
0 0 40px rgba(16, 185, 129, 0.4),
|
||||
0 0 60px rgba(16, 185, 129, 0.2),
|
||||
0 0 80px rgba(16, 185, 129, 0.1);
|
||||
border: 2px solid #10b981;
|
||||
box-shadow:
|
||||
0 0 20px rgba(16, 185, 129, 0.6),
|
||||
0 0 40px rgba(16, 185, 129, 0.4),
|
||||
0 0 60px rgba(16, 185, 129, 0.2),
|
||||
0 0 80px rgba(16, 185, 129, 0.1);
|
||||
}
|
||||
|
||||
/* Catégorie: zipette&co - BLANC CASSÉ - Effet néon amélioré */
|
||||
.products-grid > div[data-category="zipette&co"] .product-card {
|
||||
border: 2px solid #F5F5F0;
|
||||
box-shadow:
|
||||
0 0 20px rgba(245, 245, 240, 0.6),
|
||||
0 0 40px rgba(245, 245, 240, 0.4),
|
||||
0 0 60px rgba(245, 245, 240, 0.2),
|
||||
0 0 80px rgba(245, 245, 240, 0.1);
|
||||
border: 2px solid #f5f5f0;
|
||||
box-shadow:
|
||||
0 0 20px rgba(245, 245, 240, 0.6),
|
||||
0 0 40px rgba(245, 245, 240, 0.4),
|
||||
0 0 60px rgba(245, 245, 240, 0.2),
|
||||
0 0 80px rgba(245, 245, 240, 0.1);
|
||||
}
|
||||
|
||||
/* Catégorie: gros&semi - BLEU CIEL - Effet néon amélioré */
|
||||
.products-grid > div[data-category="gros&semi"] .product-card {
|
||||
border: 2px solid #3dc2f7;
|
||||
box-shadow:
|
||||
0 0 20px rgba(61, 194, 247, 0.6),
|
||||
0 0 40px rgba(61, 194, 247, 0.4),
|
||||
0 0 60px rgba(61, 194, 247, 0.2),
|
||||
0 0 80px rgba(61, 194, 247, 0.1);
|
||||
border: 2px solid #3dc2f7;
|
||||
box-shadow:
|
||||
0 0 20px rgba(61, 194, 247, 0.6),
|
||||
0 0 40px rgba(61, 194, 247, 0.4),
|
||||
0 0 60px rgba(61, 194, 247, 0.2),
|
||||
0 0 80px rgba(61, 194, 247, 0.1);
|
||||
}
|
||||
|
||||
/* Petits téléphones */
|
||||
@media (max-width: 360px) {
|
||||
.products-grid {
|
||||
padding: 0 0.8rem;
|
||||
padding-bottom: 1rem;
|
||||
}
|
||||
|
||||
.products-grid > * {
|
||||
flex: 0 0 calc(100% - 1.6rem);
|
||||
}
|
||||
|
||||
.user-page-container {
|
||||
padding: 0.8rem;
|
||||
padding-top: calc(60px + 0.8rem);
|
||||
}
|
||||
.products-grid {
|
||||
padding: 0 0.8rem;
|
||||
padding-bottom: 1rem;
|
||||
}
|
||||
|
||||
.category-title {
|
||||
font-size: 1.6rem;
|
||||
}
|
||||
.products-grid > * {
|
||||
flex: 0 0 calc(100% - 1.6rem);
|
||||
}
|
||||
|
||||
.category-subtitle {
|
||||
font-size: 0.9rem;
|
||||
}
|
||||
.user-page-container {
|
||||
padding: 0.8rem;
|
||||
padding-top: calc(60px + 0.8rem);
|
||||
}
|
||||
|
||||
.category-title {
|
||||
font-size: 1.6rem;
|
||||
}
|
||||
|
||||
.category-subtitle {
|
||||
font-size: 0.9rem;
|
||||
}
|
||||
}
|
||||
|
||||
/* Tablettes portrait */
|
||||
@media (min-width: 600px) {
|
||||
.products-grid {
|
||||
padding: 0 2rem;
|
||||
padding-bottom: 1rem;
|
||||
}
|
||||
|
||||
.products-grid > * {
|
||||
flex: 0 0 calc(100% - 4rem);
|
||||
}
|
||||
|
||||
.user-page-container {
|
||||
padding: 2rem;
|
||||
padding-top: calc(60px + 2rem);
|
||||
}
|
||||
.products-grid {
|
||||
padding: 0 2rem;
|
||||
padding-bottom: 1rem;
|
||||
}
|
||||
|
||||
.products-grid > * {
|
||||
flex: 0 0 calc(100% - 4rem);
|
||||
}
|
||||
|
||||
.user-page-container {
|
||||
padding: 2rem;
|
||||
padding-top: calc(60px + 2rem);
|
||||
}
|
||||
}
|
||||
|
||||
/* Tablettes paysage et desktop */
|
||||
@media (min-width: 900px) {
|
||||
.products-grid {
|
||||
padding: 0 2rem;
|
||||
padding-bottom: 1rem;
|
||||
}
|
||||
|
||||
.products-grid > * {
|
||||
flex: 0 0 calc(50% - 2rem);
|
||||
}
|
||||
.products-grid {
|
||||
padding: 0 2rem;
|
||||
padding-bottom: 1rem;
|
||||
}
|
||||
|
||||
.products-grid > * {
|
||||
flex: 0 0 calc(50% - 2rem);
|
||||
}
|
||||
}
|
||||
|
||||
/* Désactiver hover sur tactile */
|
||||
@media (hover: none) {
|
||||
.category-button:hover {
|
||||
background-color: transparent;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.category-button.active:hover {
|
||||
background-color: white;
|
||||
color: black;
|
||||
}
|
||||
|
||||
.category-button.active[data-category="tous"]:hover {
|
||||
background-color: #9333ea;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.category-button.active[data-category="weed&hash"]:hover {
|
||||
background-color: #10b981;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.category-button.active[data-category="zipette&co"]:hover {
|
||||
background-color: #F5F5F0;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.category-button.active[data-category="gros&semi"]:hover {
|
||||
background-color: #3dc2f7;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.category-button.active[data-category="festif"]:hover {
|
||||
background-color: #9333ea;
|
||||
color: white;
|
||||
}
|
||||
.category-button:hover {
|
||||
background-color: transparent;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.category-button.active:hover {
|
||||
background-color: white;
|
||||
color: black;
|
||||
}
|
||||
|
||||
.category-button.active[data-category="tous"]:hover {
|
||||
background-color: #9333ea;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.category-button.active[data-category="weed&hash"]:hover {
|
||||
background-color: #10b981;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.category-button.active[data-category="zipette&co"]:hover {
|
||||
background-color: #f5f5f0;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.category-button.active[data-category="gros&semi"]:hover {
|
||||
background-color: #3dc2f7;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.category-button.active[data-category="festif"]:hover {
|
||||
background-color: #9333ea;
|
||||
color: white;
|
||||
}
|
||||
}
|
||||
|
||||
.loading-container,
|
||||
.error-container,
|
||||
.empty-container {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 3rem;
|
||||
text-align: center;
|
||||
min-height: 300px;
|
||||
color: white;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 3rem;
|
||||
text-align: center;
|
||||
min-height: 300px;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.loading-container p,
|
||||
.empty-container p {
|
||||
font-size: 1.1rem;
|
||||
color: rgba(255, 255, 255, 0.8);
|
||||
font-size: 1.1rem;
|
||||
color: rgba(255, 255, 255, 0.8);
|
||||
}
|
||||
|
||||
.error-message {
|
||||
color: #ff4444;
|
||||
margin-bottom: 1rem;
|
||||
font-size: 1.1rem;
|
||||
font-weight: 500;
|
||||
color: #ff4444;
|
||||
margin-bottom: 1rem;
|
||||
font-size: 1.1rem;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.error-container button {
|
||||
padding: 0.75rem 1.5rem;
|
||||
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||||
color: white;
|
||||
border: none;
|
||||
border-radius: 8px;
|
||||
cursor: pointer;
|
||||
font-size: 1rem;
|
||||
font-weight: 600;
|
||||
transition: all 0.3s ease;
|
||||
padding: 0.75rem 1.5rem;
|
||||
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||||
color: white;
|
||||
border: none;
|
||||
border-radius: 8px;
|
||||
cursor: pointer;
|
||||
font-size: 1rem;
|
||||
font-weight: 600;
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
.error-container button:hover {
|
||||
transform: translateY(-2px);
|
||||
box-shadow: 0 4px 12px rgba(102, 126, 234, 0.4);
|
||||
transform: translateY(-2px);
|
||||
box-shadow: 0 4px 12px rgba(102, 126, 234, 0.4);
|
||||
}
|
||||
|
||||
.error-container button:active {
|
||||
transform: translateY(0);
|
||||
}
|
||||
transform: translateY(0);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user