chore: build
This commit is contained in:
@@ -2168,6 +2168,7 @@ export const unlinkTelegram = async (): Promise<void> => {
|
||||
|
||||
export type RewardCategoryConfig = {
|
||||
category: string;
|
||||
type: "free_product" | "half_price_product";
|
||||
all_products: boolean;
|
||||
product_ids: number[];
|
||||
product_names: string[];
|
||||
@@ -2179,6 +2180,7 @@ export type RewardItemConfig = {
|
||||
product_name: string;
|
||||
quantity: number;
|
||||
price: number;
|
||||
type: "free_product" | "half_price_product";
|
||||
};
|
||||
|
||||
export type PointsPoolInfo = {
|
||||
@@ -2194,7 +2196,6 @@ export type PointsPoolInfo = {
|
||||
|
||||
export type PointsRewardConfig = {
|
||||
threshold: number;
|
||||
type: string;
|
||||
description: string;
|
||||
reward_items: RewardItemConfig[];
|
||||
};
|
||||
|
||||
@@ -187,8 +187,10 @@ function Cart() {
|
||||
</p>
|
||||
<p className="cart-row-qty">{item.quantity}g</p>
|
||||
<p className="cart-row-price">
|
||||
{item.is_reward ? (
|
||||
{item.is_reward && item.price === 0 ? (
|
||||
<span style={{ color: "#10b981", fontWeight: 700 }}>Offert</span>
|
||||
) : item.is_reward ? (
|
||||
<span style={{ color: "#10b981", fontWeight: 700 }}>{item.price.toFixed(2)} €</span>
|
||||
) : (
|
||||
`${item.price.toFixed(2)} €`
|
||||
)}
|
||||
|
||||
@@ -747,13 +747,18 @@ function ConsultationHistorique() {
|
||||
item.quantity !== 1
|
||||
? `×${item.quantity}`
|
||||
: ""}
|
||||
{item.price > 0
|
||||
? ` · valeur ${item.price.toFixed(2)} €`
|
||||
{item.type ===
|
||||
"half_price_product" &&
|
||||
item.price > 0
|
||||
? ` · ${item.price.toFixed(2)} €`
|
||||
: ""}
|
||||
</span>
|
||||
</div>
|
||||
<span className="reward-product-free">
|
||||
Offert
|
||||
{item.type ===
|
||||
"half_price_product"
|
||||
? "-50%"
|
||||
: "Offert"}
|
||||
</span>
|
||||
</button>
|
||||
);
|
||||
|
||||
@@ -594,4 +594,55 @@
|
||||
animation-iteration-count: 1 !important;
|
||||
transition-duration: 0.01ms !important;
|
||||
}
|
||||
}
|
||||
|
||||
/* Modal stock insuffisant */
|
||||
.stock-warning-modal {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
background: rgba(0, 0, 0, 0.75);
|
||||
backdrop-filter: blur(6px);
|
||||
z-index: 9999;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 1rem;
|
||||
}
|
||||
|
||||
.stock-warning-modal-content {
|
||||
position: relative;
|
||||
width: 100%;
|
||||
max-width: 340px;
|
||||
padding: 2rem;
|
||||
text-align: center;
|
||||
background: #1a1a1a;
|
||||
border-radius: 12px;
|
||||
box-shadow: 0 10px 40px rgba(0, 0, 0, 0.5);
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.stock-warning-close-btn {
|
||||
position: absolute;
|
||||
top: 0.75rem;
|
||||
right: 0.75rem;
|
||||
background: transparent;
|
||||
border: none;
|
||||
color: #aaa;
|
||||
cursor: pointer;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.stock-warning-ok-btn {
|
||||
background: #ef4444;
|
||||
color: #fff;
|
||||
border: none;
|
||||
border-radius: 8px;
|
||||
padding: 0.6rem 1.5rem;
|
||||
font-weight: 700;
|
||||
cursor: pointer;
|
||||
}
|
||||
@@ -1,5 +1,7 @@
|
||||
import { useParams, useNavigate } from "react-router-dom";
|
||||
import { useState, useEffect } from "react";
|
||||
import { createPortal } from "react-dom";
|
||||
import { X } from "lucide-react";
|
||||
import {
|
||||
getProductById,
|
||||
getCategories,
|
||||
@@ -25,6 +27,7 @@ function ProductDetail() {
|
||||
// floats
|
||||
const [selectedGrams, setSelectedGrams] = useState<number | null>(null);
|
||||
const [selectedPrice, setSelectedPrice] = useState<number>(0.0);
|
||||
const [stockWarning, setStockWarning] = useState<{ wanted: number; available: number } | null>(null);
|
||||
|
||||
// ✅ TOAST STATE
|
||||
const [toast, setToast] = useState<{
|
||||
@@ -171,6 +174,11 @@ function ProductDetail() {
|
||||
return;
|
||||
}
|
||||
|
||||
if (product.stock > 0 && selectedGrams > product.stock) {
|
||||
setStockWarning({ wanted: selectedGrams, available: product.stock });
|
||||
return;
|
||||
}
|
||||
|
||||
addToCart({
|
||||
product_id: product.id,
|
||||
name_product: product.name,
|
||||
@@ -363,6 +371,50 @@ function ProductDetail() {
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Modal stock insuffisant */}
|
||||
{stockWarning &&
|
||||
createPortal(
|
||||
<div
|
||||
className="stock-warning-modal"
|
||||
onClick={() => setStockWarning(null)}
|
||||
>
|
||||
<div
|
||||
className="stock-warning-modal-content"
|
||||
onClick={(e) => e.stopPropagation()}
|
||||
>
|
||||
<button
|
||||
className="stock-warning-close-btn"
|
||||
onClick={() => setStockWarning(null)}
|
||||
aria-label="Fermer"
|
||||
>
|
||||
<X size={18} />
|
||||
</button>
|
||||
<div style={{ fontSize: "2.5rem", marginBottom: "0.75rem" }}>
|
||||
⚠️
|
||||
</div>
|
||||
<p style={{ fontWeight: 700, fontSize: "1.1rem", marginBottom: "0.5rem" }}>
|
||||
Stock insuffisant
|
||||
</p>
|
||||
<p style={{ color: "#aaa", fontSize: "0.95rem", marginBottom: "1.25rem" }}>
|
||||
Vous avez sélectionné{" "}
|
||||
<strong>{stockWarning.wanted}{product.unit || "g"}</strong> mais il ne
|
||||
reste que{" "}
|
||||
<strong style={{ color: "#ef4444" }}>
|
||||
{stockWarning.available}{product.unit || "g"}
|
||||
</strong>{" "}
|
||||
disponible pour <em>{product.name}</em>.
|
||||
</p>
|
||||
<button
|
||||
className="stock-warning-ok-btn"
|
||||
onClick={() => setStockWarning(null)}
|
||||
>
|
||||
OK
|
||||
</button>
|
||||
</div>
|
||||
</div>,
|
||||
document.body,
|
||||
)}
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user