802 lines
37 KiB
TypeScript
802 lines
37 KiB
TypeScript
import { useState, useEffect } from "react";
|
||
import { useNavigate } from "react-router-dom";
|
||
import Navbar from "../../components/Navbar";
|
||
import "./ConsultationHistorique.css";
|
||
import {
|
||
getMyCompletedOrders,
|
||
formatPrice,
|
||
getOrderAge,
|
||
getMyPenalties,
|
||
isUserAuthenticated,
|
||
getPublicSettings,
|
||
getReferralBalance,
|
||
getMyPointsRewards,
|
||
claimMyReward,
|
||
} from "../../api/api";
|
||
import type {
|
||
PublicSettings,
|
||
PointsPoolInfo,
|
||
PointsRewardConfig,
|
||
RewardItemConfig,
|
||
} from "../../api/api";
|
||
import type {
|
||
CompletedOrder,
|
||
ClientStats,
|
||
PenaltyInfo,
|
||
} from "../../api/api_types";
|
||
|
||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||
import {
|
||
faCannabis,
|
||
faPills,
|
||
faFlask,
|
||
faMortarPestle,
|
||
faStar,
|
||
faTrophy,
|
||
faExclamationTriangle,
|
||
faShieldAlt,
|
||
faGift,
|
||
faReceipt,
|
||
faMapMarkerAlt,
|
||
faClock,
|
||
faBicycle,
|
||
faChevronRight,
|
||
faCheckCircle,
|
||
faHistory,
|
||
faTimes,
|
||
faCheck,
|
||
} from "@fortawesome/free-solid-svg-icons";
|
||
|
||
function ConsultationHistorique() {
|
||
const navigate = useNavigate();
|
||
|
||
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,
|
||
referral_amount: 0,
|
||
pool_names: ["Pool 1", "Pool 2"],
|
||
crypto_payment_enabled: false,
|
||
crypto_only: false,
|
||
nowpayments_currencies: [],
|
||
shop_name: "Milieu-Nantais",
|
||
two_fa_enabled: false,
|
||
contact_telegram: "",
|
||
client_color_primary: "",
|
||
client_color_secondary: "",
|
||
client_color_success: "",
|
||
client_color_danger: "",
|
||
client_color_warning: "",
|
||
client_title_gradient_from: "",
|
||
client_title_gradient_to: "",
|
||
});
|
||
const [referralBalance, setReferralBalance] = useState<number>(0);
|
||
const [pointsRewards, setPointsRewards] = useState<{
|
||
enabled: boolean;
|
||
pools: PointsPoolInfo[];
|
||
reward: PointsRewardConfig | null;
|
||
} | null>(null);
|
||
const [claimingPool, setClaimingPool] = useState<string | null>(null);
|
||
const [claimFeedback, setClaimFeedback] = useState<{
|
||
pool: string;
|
||
type: "success" | "error";
|
||
text: string;
|
||
} | null>(null);
|
||
const [isLoading, setIsLoading] = useState(true);
|
||
const [error, setError] = useState<string>("");
|
||
|
||
// ── Modal de sélection du produit récompense ──
|
||
const [rewardModalPool, setRewardModalPool] =
|
||
useState<PointsPoolInfo | null>(null);
|
||
const [selectedProductId, setSelectedProductId] = useState<number | null>(
|
||
null,
|
||
);
|
||
|
||
useEffect(() => {
|
||
if (!isUserAuthenticated())
|
||
navigate("/login/client", { replace: true });
|
||
}, [navigate]);
|
||
|
||
useEffect(() => {
|
||
const interval = setInterval(() => {
|
||
if (!isUserAuthenticated())
|
||
navigate("/login/client", { replace: true });
|
||
}, 5000);
|
||
return () => clearInterval(interval);
|
||
}, [navigate]);
|
||
|
||
useEffect(() => {
|
||
fetchHistory();
|
||
fetchPenalties();
|
||
getPublicSettings().then((s) => {
|
||
setAppSettings(s);
|
||
if (s.referral_enabled) {
|
||
getReferralBalance().then((r) => {
|
||
if (r.success) setReferralBalance(r.balance);
|
||
});
|
||
}
|
||
});
|
||
getMyPointsRewards().then((r) => {
|
||
if (r.success && r.enabled) setPointsRewards(r);
|
||
});
|
||
}, []); // eslint-disable-line react-hooks/exhaustive-deps
|
||
|
||
const fetchHistory = async () => {
|
||
if (!isUserAuthenticated()) {
|
||
navigate("/login/client", { replace: true });
|
||
return;
|
||
}
|
||
setIsLoading(true);
|
||
setError("");
|
||
try {
|
||
const result = await getMyCompletedOrders();
|
||
if (result.success) {
|
||
setOrders(result.commands);
|
||
setClientStats(result.client_stats || null);
|
||
} else {
|
||
setError(result.message || "Erreur lors du chargement");
|
||
}
|
||
} catch {
|
||
setError("Erreur de connexion");
|
||
} finally {
|
||
setIsLoading(false);
|
||
}
|
||
};
|
||
|
||
const fetchPenalties = async () => {
|
||
if (!isUserAuthenticated()) return;
|
||
try {
|
||
const result = await getMyPenalties();
|
||
if (result.success && result.data) setPenalties(result.data);
|
||
} catch {
|
||
/* ignore */
|
||
}
|
||
};
|
||
|
||
const viewOrderDetails = (order: CompletedOrder) => {
|
||
if (!isUserAuthenticated()) {
|
||
navigate("/login/client", { replace: true });
|
||
return;
|
||
}
|
||
navigate(`/user/commande/${order.client_order_number ?? order.id}`, {
|
||
state: { commandId: order.id },
|
||
});
|
||
};
|
||
|
||
const formatDate = (dateStr: string) => {
|
||
const d = new Date(dateStr);
|
||
return d.toLocaleDateString("fr-FR", {
|
||
day: "2-digit",
|
||
month: "short",
|
||
year: "numeric",
|
||
});
|
||
};
|
||
|
||
// Ouvre la modal de choix si plusieurs produits sont éligibles,
|
||
// sinon réclame directement (1 seul produit configuré ou aucun item).
|
||
const openRewardModal = (pool: PointsPoolInfo) => {
|
||
const items = pool.eligible_reward_items ?? [];
|
||
if (items.length > 1) {
|
||
setSelectedProductId(items[0]?.product_id ?? null);
|
||
setRewardModalPool(pool);
|
||
} else {
|
||
handleClaim(pool.key);
|
||
}
|
||
};
|
||
|
||
const closeRewardModal = () => {
|
||
setRewardModalPool(null);
|
||
setSelectedProductId(null);
|
||
};
|
||
|
||
const confirmRewardChoice = async () => {
|
||
if (!rewardModalPool) return;
|
||
const poolKey = rewardModalPool.key;
|
||
const productId = selectedProductId ?? undefined;
|
||
closeRewardModal();
|
||
await handleClaim(poolKey, productId);
|
||
};
|
||
|
||
const handleClaim = async (poolKey: string, productId?: number) => {
|
||
setClaimingPool(poolKey);
|
||
setClaimFeedback(null);
|
||
const res = await claimMyReward(poolKey, productId);
|
||
setClaimingPool(null);
|
||
if (res.success) {
|
||
const text =
|
||
res.product_added && res.product_names?.length
|
||
? `${res.product_names.join(", ")} ajouté${res.product_names.length > 1 ? "s" : ""} à votre panier ! Commandez au moins un produit pour en profiter.`
|
||
: res.description || "Récompense réclamée !";
|
||
setClaimFeedback({ pool: poolKey, type: "success", text });
|
||
getMyPointsRewards().then((r) => {
|
||
if (r.success) setPointsRewards(r);
|
||
});
|
||
} else {
|
||
setClaimFeedback({
|
||
pool: poolKey,
|
||
type: "error",
|
||
text: res.error || "Erreur",
|
||
});
|
||
}
|
||
};
|
||
|
||
const poolNames = clientStats?.pool_names?.length
|
||
? clientStats.pool_names
|
||
: appSettings.pool_names;
|
||
const poolPoints = clientStats?.pool_points ?? [clientStats?.points ?? 0];
|
||
const totalPoints = poolPoints.reduce((s, v) => s + (v || 0), 0);
|
||
const penaltyCount =
|
||
penalties?.total_penalty || clientStats?.penalties || 0;
|
||
const poolIcons = [faCannabis, faPills, faFlask, faMortarPestle, faStar];
|
||
const poolIconColors = [
|
||
"#10b981",
|
||
"#e879f9",
|
||
"#fb923c",
|
||
"#38bdf8",
|
||
"#7c3aed",
|
||
];
|
||
|
||
if (isLoading) {
|
||
return (
|
||
<>
|
||
<Navbar />
|
||
<div className="history-container">
|
||
<div className="loading-container">
|
||
<div className="loading-spinner">
|
||
<div className="spinner"></div>
|
||
</div>
|
||
<p className="loading-text">
|
||
Chargement de l'historique...
|
||
</p>
|
||
</div>
|
||
</div>
|
||
</>
|
||
);
|
||
}
|
||
|
||
return (
|
||
<>
|
||
<Navbar />
|
||
<div className="history-container">
|
||
<h1 className="history-title">Historique des commandes</h1>
|
||
|
||
{/* Stats grid */}
|
||
<div className="stats-grid">
|
||
{/* Total commandes */}
|
||
<div className="stat-card2">
|
||
<div className="stat-icon icon-total-orders">
|
||
<FontAwesomeIcon icon={faReceipt} />
|
||
</div>
|
||
<p className="stat-value">
|
||
{clientStats?.total_commands ?? orders.length}
|
||
</p>
|
||
<p className="stat-label">Commandes</p>
|
||
</div>
|
||
|
||
{/* Points */}
|
||
{appSettings.points_enabled &&
|
||
(poolNames.length <= 1 ? (
|
||
<div className="stat-card2">
|
||
<div className="stat-icon icon-total">
|
||
<FontAwesomeIcon icon={faTrophy} />
|
||
</div>
|
||
<p className="stat-value">
|
||
{poolPoints[0] || 0}
|
||
</p>
|
||
<p className="stat-label">
|
||
Pts {poolNames[0] ?? "Points"}
|
||
</p>
|
||
</div>
|
||
) : (
|
||
<>
|
||
{poolNames.map((name, i) => (
|
||
<div key={i} className="stat-card2">
|
||
<div
|
||
className="stat-icon"
|
||
style={{
|
||
background: `linear-gradient(135deg, ${poolIconColors[i] ?? "#7c3aed"}cc, ${poolIconColors[i] ?? "#7c3aed"})`,
|
||
}}
|
||
>
|
||
<FontAwesomeIcon
|
||
icon={poolIcons[i] ?? faStar}
|
||
/>
|
||
</div>
|
||
<p className="stat-value">
|
||
{poolPoints[i] || 0}
|
||
</p>
|
||
<p className="stat-label">Pts {name}</p>
|
||
</div>
|
||
))}
|
||
{totalPoints > 0 && (
|
||
<div className="stat-card2">
|
||
<div className="stat-icon icon-total">
|
||
<FontAwesomeIcon icon={faTrophy} />
|
||
</div>
|
||
<p className="stat-value">
|
||
{totalPoints}
|
||
</p>
|
||
<p className="stat-label">
|
||
Total Points
|
||
</p>
|
||
</div>
|
||
)}
|
||
</>
|
||
))}
|
||
|
||
{/* Score amendes */}
|
||
{appSettings.show_amende_score && (
|
||
<div
|
||
className={`stat-card2${penaltyCount >= 3 ? " stat-card-danger" : penaltyCount > 0 ? " stat-card-warning" : ""}`}
|
||
>
|
||
<div
|
||
className={`stat-icon ${penaltyCount >= 3 ? "icon-penalty-critical" : penaltyCount > 0 ? "icon-penalty-warning" : "icon-penalty-ok"}`}
|
||
>
|
||
<FontAwesomeIcon
|
||
icon={
|
||
penaltyCount > 0
|
||
? faExclamationTriangle
|
||
: faShieldAlt
|
||
}
|
||
/>
|
||
</div>
|
||
<p
|
||
className={`stat-value ${penaltyCount >= 3 ? "value-danger" : penaltyCount > 0 ? "value-warning" : ""}`}
|
||
>
|
||
{penaltyCount} €
|
||
</p>
|
||
<p className="stat-label">Score amendes</p>
|
||
</div>
|
||
)}
|
||
</div>
|
||
|
||
{/* Section récompenses par palier */}
|
||
{pointsRewards?.enabled &&
|
||
pointsRewards.reward &&
|
||
pointsRewards.pools.length > 0 && (
|
||
<div className="rewards-section">
|
||
<p className="rewards-section-title">
|
||
<FontAwesomeIcon
|
||
icon={faTrophy}
|
||
className="rewards-section-icon"
|
||
/>
|
||
Récompenses
|
||
</p>
|
||
{pointsRewards.reward.description && (
|
||
<p className="rewards-section-desc">
|
||
{pointsRewards.reward.description}
|
||
</p>
|
||
)}
|
||
{(pointsRewards.reward.reward_items ?? []).filter(
|
||
(it: RewardItemConfig) => it.product_name,
|
||
).length > 0 && (
|
||
<div className="reward-eligible-cats">
|
||
{(
|
||
pointsRewards.reward.reward_items ?? []
|
||
).map(
|
||
(it: RewardItemConfig, idx: number) => (
|
||
<span
|
||
key={idx}
|
||
className="reward-eligible-cat"
|
||
>
|
||
<FontAwesomeIcon
|
||
icon={faGift}
|
||
style={{ marginRight: 4 }}
|
||
/>
|
||
{it.product_name}
|
||
{it.quantity > 0 &&
|
||
it.quantity !== 1
|
||
? ` ×${it.quantity}`
|
||
: ""}
|
||
{it.price > 0
|
||
? ` — ${it.price}€`
|
||
: ""}
|
||
</span>
|
||
),
|
||
)}
|
||
</div>
|
||
)}
|
||
<div className="rewards-pools">
|
||
{pointsRewards.pools.map((pool) => {
|
||
const threshold =
|
||
pointsRewards.reward!.threshold;
|
||
const progress = Math.min(
|
||
100,
|
||
Math.round(
|
||
((pool.points % threshold) /
|
||
threshold) *
|
||
100,
|
||
),
|
||
);
|
||
const remaining =
|
||
threshold - (pool.points % threshold);
|
||
const isClaiming =
|
||
claimingPool === pool.key;
|
||
const feedback =
|
||
claimFeedback?.pool === pool.key
|
||
? claimFeedback
|
||
: null;
|
||
return (
|
||
<div
|
||
key={pool.key}
|
||
className="reward-pool-card"
|
||
>
|
||
<div className="reward-pool-header">
|
||
<span className="reward-pool-name">
|
||
{pool.name}
|
||
</span>
|
||
<span className="reward-pool-pts">
|
||
{pool.points} pts
|
||
</span>
|
||
</div>
|
||
{pool.eligible_configs.length >
|
||
0 && (
|
||
<div className="reward-eligible-cats">
|
||
{pool.eligible_configs.flatMap(
|
||
(cfg) =>
|
||
cfg.all_products
|
||
? [
|
||
<span
|
||
key={
|
||
cfg.category
|
||
}
|
||
className="reward-eligible-cat"
|
||
>
|
||
{
|
||
cfg.category
|
||
}
|
||
</span>,
|
||
]
|
||
: (
|
||
cfg.products ??
|
||
[]
|
||
).map(
|
||
(
|
||
p,
|
||
) => (
|
||
<span
|
||
key={`${cfg.category}-${p.product_id}`}
|
||
className="reward-eligible-cat"
|
||
>
|
||
{
|
||
p.product_name
|
||
}
|
||
</span>
|
||
),
|
||
),
|
||
)}
|
||
</div>
|
||
)}
|
||
<div className="reward-progress-bar">
|
||
<div
|
||
className="reward-progress-fill"
|
||
style={{
|
||
width: `${progress}%`,
|
||
}}
|
||
/>
|
||
</div>
|
||
<div className="reward-pool-info">
|
||
{pool.rewards_available > 0 ? (
|
||
<span className="reward-available">
|
||
{pool.rewards_available}{" "}
|
||
récompense
|
||
{pool.rewards_available >
|
||
1
|
||
? "s"
|
||
: ""}{" "}
|
||
disponible
|
||
{pool.rewards_available >
|
||
1
|
||
? "s"
|
||
: ""}
|
||
</span>
|
||
) : (
|
||
<span className="reward-remaining">
|
||
Encore {remaining} pts
|
||
pour une récompense
|
||
</span>
|
||
)}
|
||
</div>
|
||
{feedback && (
|
||
<p
|
||
className={`reward-feedback reward-feedback-${feedback.type}`}
|
||
>
|
||
{feedback.type ===
|
||
"success" && (
|
||
<FontAwesomeIcon
|
||
icon={faGift}
|
||
style={{
|
||
marginRight: 6,
|
||
}}
|
||
/>
|
||
)}
|
||
{feedback.text}
|
||
</p>
|
||
)}
|
||
{pool.rewards_available > 0 && (
|
||
<button
|
||
className="reward-claim-btn"
|
||
onClick={() =>
|
||
openRewardModal(pool)
|
||
}
|
||
disabled={isClaiming}
|
||
>
|
||
{isClaiming
|
||
? "..."
|
||
: "Réclamer ma récompense"}
|
||
</button>
|
||
)}
|
||
</div>
|
||
);
|
||
})}
|
||
</div>
|
||
</div>
|
||
)}
|
||
|
||
{/* Bouton parrainage */}
|
||
{appSettings.referral_enabled && (
|
||
<div
|
||
className="referral-btn-row"
|
||
onClick={() => navigate("/user/parrainage")}
|
||
>
|
||
<FontAwesomeIcon
|
||
icon={faGift}
|
||
className="referral-btn-icon"
|
||
/>
|
||
<span className="referral-btn-text">
|
||
Parrainage
|
||
{referralBalance > 0
|
||
? ` — ${referralBalance.toFixed(2)} €`
|
||
: ""}
|
||
</span>
|
||
<FontAwesomeIcon
|
||
icon={faChevronRight}
|
||
className="referral-btn-chevron"
|
||
/>
|
||
</div>
|
||
)}
|
||
|
||
{error && (
|
||
<div className="error-banner">
|
||
<FontAwesomeIcon
|
||
icon={faExclamationTriangle}
|
||
size="2x"
|
||
/>
|
||
<p>{error}</p>
|
||
</div>
|
||
)}
|
||
|
||
{orders.length === 0 ? (
|
||
<div className="empty-history">
|
||
<FontAwesomeIcon
|
||
icon={faHistory}
|
||
className="empty-icon"
|
||
/>
|
||
<h2>Aucun historique</h2>
|
||
<p>Vos commandes terminées apparaîtront ici</p>
|
||
<button
|
||
className="browse-button"
|
||
onClick={() => navigate("/user/accueil")}
|
||
>
|
||
Découvrir nos produits
|
||
</button>
|
||
</div>
|
||
) : (
|
||
<>
|
||
<p className="section-title">
|
||
Historique des commandes
|
||
</p>
|
||
<div className="orders-list">
|
||
{orders.map((order) => (
|
||
<div
|
||
key={order.id}
|
||
className="order-card"
|
||
onClick={() => viewOrderDetails(order)}
|
||
>
|
||
<div className="order-card-header">
|
||
<span className="order-card-number">
|
||
Commande #
|
||
{(order.client_order_number ?? 0)
|
||
.toString()
|
||
.padStart(4, "0")}
|
||
</span>
|
||
<span className="order-card-badge">
|
||
<FontAwesomeIcon
|
||
icon={faCheckCircle}
|
||
style={{
|
||
marginRight: "0.35rem",
|
||
}}
|
||
/>
|
||
Livrée
|
||
</span>
|
||
</div>
|
||
|
||
<div className="order-card-row">
|
||
<FontAwesomeIcon
|
||
icon={faMapMarkerAlt}
|
||
className="order-card-row-icon"
|
||
/>
|
||
<span className="order-card-row-text">
|
||
{order.adresse
|
||
? order.adresse.length > 50
|
||
? order.adresse.substring(
|
||
0,
|
||
50,
|
||
) + "…"
|
||
: order.adresse
|
||
: "N/A"}
|
||
</span>
|
||
</div>
|
||
|
||
<div className="order-card-row">
|
||
<FontAwesomeIcon
|
||
icon={faClock}
|
||
className="order-card-row-icon"
|
||
/>
|
||
<span className="order-card-row-text">
|
||
{formatDate(order.created_at)} ·{" "}
|
||
{getOrderAge(order.created_at)}
|
||
</span>
|
||
</div>
|
||
|
||
{order.livreur_assign && (
|
||
<div className="order-card-row">
|
||
<FontAwesomeIcon
|
||
icon={faBicycle}
|
||
className="order-card-row-icon"
|
||
/>
|
||
<span className="order-card-row-text">
|
||
{order.livreur_assign}
|
||
</span>
|
||
</div>
|
||
)}
|
||
|
||
<div className="order-card-footer">
|
||
<span className="order-card-total">
|
||
{formatPrice(
|
||
(order.total_prix || 0) -
|
||
(order.referral_used || 0),
|
||
)}
|
||
{(order.referral_used || 0) > 0 && (
|
||
<span className="order-card-referral">
|
||
{" "}
|
||
— dont{" "}
|
||
{formatPrice(
|
||
order.referral_used ??
|
||
0,
|
||
)}{" "}
|
||
parrainage
|
||
</span>
|
||
)}
|
||
</span>
|
||
<FontAwesomeIcon
|
||
icon={faChevronRight}
|
||
className="order-card-chevron"
|
||
/>
|
||
</div>
|
||
</div>
|
||
))}
|
||
</div>
|
||
</>
|
||
)}
|
||
</div>
|
||
|
||
{/* ============================================ */}
|
||
{/* MODAL — CHOIX DU PRODUIT RÉCOMPENSE */}
|
||
{/* ============================================ */}
|
||
{rewardModalPool && pointsRewards?.reward && (
|
||
<div
|
||
className="reward-modal-overlay"
|
||
onClick={closeRewardModal}
|
||
>
|
||
<div
|
||
className="reward-modal"
|
||
onClick={(e) => e.stopPropagation()}
|
||
>
|
||
<div className="reward-modal-header">
|
||
<div className="reward-modal-header-title">
|
||
<FontAwesomeIcon
|
||
icon={faGift}
|
||
className="reward-modal-icon"
|
||
/>
|
||
<h2>Choisissez votre récompense</h2>
|
||
</div>
|
||
<button
|
||
className="reward-modal-close"
|
||
onClick={closeRewardModal}
|
||
aria-label="Fermer"
|
||
>
|
||
<FontAwesomeIcon icon={faTimes} />
|
||
</button>
|
||
</div>
|
||
|
||
<p className="reward-modal-subtitle">
|
||
{rewardModalPool.name} —{" "}
|
||
{rewardModalPool.rewards_available} récompense
|
||
{rewardModalPool.rewards_available > 1
|
||
? "s"
|
||
: ""}{" "}
|
||
disponible
|
||
{rewardModalPool.rewards_available > 1 ? "s" : ""}
|
||
</p>
|
||
|
||
<div className="reward-modal-products">
|
||
{(rewardModalPool.eligible_reward_items ?? []).map(
|
||
(item) => {
|
||
const isSelected =
|
||
selectedProductId === item.product_id;
|
||
return (
|
||
<button
|
||
key={item.product_id}
|
||
type="button"
|
||
className={`reward-product-card ${isSelected ? "selected" : ""}`}
|
||
onClick={() =>
|
||
setSelectedProductId(
|
||
item.product_id,
|
||
)
|
||
}
|
||
>
|
||
<div className="reward-product-radio">
|
||
{isSelected && (
|
||
<FontAwesomeIcon
|
||
icon={faCheck}
|
||
/>
|
||
)}
|
||
</div>
|
||
<div className="reward-product-info">
|
||
<span className="reward-product-name">
|
||
{item.product_name}
|
||
</span>
|
||
<span className="reward-product-meta">
|
||
{item.quantity > 0 &&
|
||
item.quantity !== 1
|
||
? `×${item.quantity}`
|
||
: ""}
|
||
{item.type ===
|
||
"half_price_product" &&
|
||
item.price > 0
|
||
? ` · ${item.price.toFixed(2)} €`
|
||
: ""}
|
||
</span>
|
||
</div>
|
||
<span className="reward-product-free">
|
||
{item.type ===
|
||
"half_price_product"
|
||
? "-50%"
|
||
: "Offert"}
|
||
</span>
|
||
</button>
|
||
);
|
||
},
|
||
)}
|
||
</div>
|
||
|
||
<div className="reward-modal-actions">
|
||
<button
|
||
className="reward-modal-btn reward-modal-btn-cancel"
|
||
onClick={closeRewardModal}
|
||
>
|
||
Annuler
|
||
</button>
|
||
<button
|
||
className="reward-modal-btn reward-modal-btn-confirm"
|
||
onClick={confirmRewardChoice}
|
||
disabled={selectedProductId === null}
|
||
>
|
||
<FontAwesomeIcon icon={faGift} />
|
||
Confirmer
|
||
</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
)}
|
||
</>
|
||
);
|
||
}
|
||
|
||
export default ConsultationHistorique;
|