chore: build
Backend - Build & Lint / build (push) Failing after 28m7s
Frontend Admin - EAS Build / build (push) Failing after 1h39m13s
Frontend Client - EAS Build / build (push) Failing after 1h38m12s
Frontend Web - Build & Lint / build (push) Failing after 10m56s

This commit is contained in:
Xor290
2026-08-19 17:49:04 +02:00
parent 1623a9eafd
commit 901d013830
33 changed files with 1459 additions and 263 deletions
+2 -1
View File
@@ -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[];
};
+3 -1
View File
@@ -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,
)}
</>
);
}