diff --git a/frontend-admin/src/api/api_admin.ts b/frontend-admin/src/api/api_admin.ts
index c442501c..89e8e2da 100644
--- a/frontend-admin/src/api/api_admin.ts
+++ b/frontend-admin/src/api/api_admin.ts
@@ -1112,19 +1112,13 @@ export interface RewardCategoryConfig {
type: "free_product" | "half_price_product";
all_products: boolean;
product_ids: number[];
-}
-
-export interface RewardItem {
- product_id: number;
- quantity: number;
- price: number;
+ quantity: number; // quantité offerte / à -50% (palier de prix catalogue, ex: 1g)
}
export interface PointsReward {
threshold: number;
description: string;
category_configs: RewardCategoryConfig[];
- reward_items: RewardItem[];
}
export interface PointsPool {
diff --git a/frontend-admin/src/screens/admin/SettingsScreen.tsx b/frontend-admin/src/screens/admin/SettingsScreen.tsx
index 4a35eb79..431d144b 100644
--- a/frontend-admin/src/screens/admin/SettingsScreen.tsx
+++ b/frontend-admin/src/screens/admin/SettingsScreen.tsx
@@ -17,7 +17,7 @@ import { Ionicons } from "@expo/vector-icons";
import { spacing, fontSize, borderRadius } from "../../theme";
import { useTheme } from "../../context/ThemeContext";
import { getSettings, updateSettings, getCategories, getAvailableDeliveryPersons, getAllProductsAdmin, DEFAULT_DELIVERY_SCHEDULE, DEFAULT_POSTAL_ZONES } from "../../api/api_admin";
-import type { AppSettings, Category, CategoryRoute, DeliveryModeConfig, PointsTier, PointsPool, PointsReward, RewardCategoryConfig, RewardItem, DaySchedule, DeliverySchedule, PostalZone } from "../../api/api_admin";
+import type { AppSettings, Category, CategoryRoute, DeliveryModeConfig, PointsTier, PointsPool, PointsReward, RewardCategoryConfig, DaySchedule, DeliverySchedule, PostalZone } from "../../api/api_admin";
import type { Product } from "../../api/types";
import AlertModal from "../../components/ui/AlertModal";
import { useAlert } from "../../hooks/useAlert";
@@ -695,7 +695,6 @@ const EMPTY_REWARD: PointsReward = {
threshold: 20,
description: "",
category_configs: [],
- reward_items: [],
};
// ──────────────────────────────────────────────────────────────
@@ -725,6 +724,27 @@ function CategoryProductPicker({
return (
+ {/* Quantité offerte / à -50% — correspond à un palier de prix du
+ catalogue (ex: si le produit a un palier quantity=1 à 30€,
+ indiquer 1 ici donne "30€ offert" ou "15€ à -50%"). */}
+
+ Quantité :
+ 0 ? String(catConfig.quantity) : ""}
+ onChangeText={(v) => {
+ const n = parseFloat(v);
+ onChange({ ...catConfig, quantity: isNaN(n) ? 0 : n });
+ }}
+ placeholder="1"
+ placeholderTextColor={colors.textMuted}
+ />
+
+ doit correspondre à un palier de prix existant
+
+
+
{/* Toggle tous / sélection */}
r.category_configs.find((c) => c.category === catName && c.type === type) ??
- { category: catName, type, all_products: true, product_ids: [] };
+ { category: catName, type, all_products: true, product_ids: [], quantity: 1 };
const isTypeEnabled = (catName: string, type: RewardCategoryConfig["type"]) =>
r.category_configs.some((c) => c.category === catName && c.type === type);
@@ -836,7 +856,7 @@ function CentralRewardSection({
if (isTypeEnabled(catName, type)) {
update({ category_configs: r.category_configs.filter((c) => !(c.category === catName && c.type === type)) });
} else {
- update({ category_configs: [...r.category_configs, { category: catName, type, all_products: true, product_ids: [] }] });
+ update({ category_configs: [...r.category_configs, { category: catName, type, all_products: true, product_ids: [], quantity: 1 }] });
}
};
@@ -921,134 +941,6 @@ function CentralRewardSection({
/>
- {/* Produits récompense — proposés au client selon le type choisi
- pour la catégorie de chaque produit (voir "Catégories éligibles" ci-dessous) */}
-
- Produits ajoutés au panier
-
- Quand le client réclame sa récompense, ces produits sont automatiquement ajoutés à son panier — gratuits ou à -50% selon le type configuré pour la catégorie du produit. Il doit commander au moins un produit normal.
-
-
- {r.reward_items.map((item, idx) => {
- const allProds = Object.values(productsByCategory).flat();
- const prod = allProds.find((p) => p.id === item.product_id);
- return (
-
- {/* Sélecteur produit */}
-
- {allProds.map((p) => {
- const sel = item.product_id === p.id;
- return (
- {
- const updated = r.reward_items.map((it, i) =>
- i === idx ? { ...it, product_id: p.id } : it
- );
- update({ reward_items: updated });
- }}
- style={{
- flexDirection: "row", alignItems: "center", gap: 4,
- paddingHorizontal: spacing.s, paddingVertical: 4,
- borderRadius: borderRadius.sm, borderWidth: 1.5,
- borderColor: sel ? REWARD_ACCENT : colors.border,
- backgroundColor: sel ? REWARD_ACCENT + "22" : "transparent",
- }}
- >
- {sel && }
-
- {p.name} ({p.category})
-
-
- );
- })}
-
-
- {/* Quantité + Prix + Supprimer */}
-
-
- Qté :
- 0 ? String(item.quantity) : ""}
- onChangeText={(v) => {
- const n = parseFloat(v);
- const updated = r.reward_items.map((it, i) =>
- i === idx ? { ...it, quantity: isNaN(n) ? 0 : n } : it
- );
- update({ reward_items: updated });
- }}
- placeholder="1"
- placeholderTextColor={colors.textMuted}
- />
-
-
- Prix :
- 0 ? String(item.price) : ""}
- onChangeText={(v) => {
- const n = parseFloat(v);
- const updated = r.reward_items.map((it, i) =>
- i === idx ? { ...it, price: isNaN(n) ? 0 : n } : it
- );
- update({ reward_items: updated });
- }}
- placeholder="0"
- placeholderTextColor={colors.textMuted}
- />
- €
-
- update({ reward_items: r.reward_items.filter((_, i) => i !== idx) })}
- style={{ padding: spacing.xs }}
- >
-
-
-
-
- {prod && (
-
- {prod.name}{item.quantity > 0 ? ` · x${item.quantity}` : ""}{item.price > 0 ? ` · ${item.price}€` : ""}
-
- )}
- {!prod && (
-
- Sélectionnez un produit ci-dessus
-
- )}
-
- );
- })}
-
- {Object.values(productsByCategory).flat().length === 0 ? (
- Aucun produit disponible
- ) : (
- update({ reward_items: [...r.reward_items, { product_id: 0, quantity: 1, price: 0 }] })}
- style={{
- flexDirection: "row", alignItems: "center", justifyContent: "center", gap: spacing.xs,
- paddingHorizontal: spacing.m, paddingVertical: spacing.s,
- borderRadius: borderRadius.sm, borderWidth: 1.5,
- borderColor: REWARD_ACCENT + "66",
- }}
- >
-
- Ajouter un produit récompense
-
- )}
-
-
-
{/* Catégories éligibles — chaque catégorie choisit son propre type
(produit offert ou -50%), qui s'applique aux produits récompense
de cette catégorie configurés ci-dessus */}
@@ -1140,7 +1032,7 @@ function CentralRewardSection({
{/* Récapitulatif */}
- {(r.category_configs.length > 0 || r.reward_items.filter((it) => it.product_id > 0).length > 0) && (
+ {r.category_configs.length > 0 && (
Récapitulatif
@@ -1151,20 +1043,9 @@ function CentralRewardSection({
)}
{r.category_configs.map((cfg, idx) => (
- • {REWARD_TYPES.find((x) => x.value === (cfg.type || "free_product"))?.label} : {cfg.category} — {cfg.all_products ? "tous les produits" : `${cfg.product_ids.length} produit(s)`}
+ • {REWARD_TYPES.find((x) => x.value === (cfg.type || "free_product"))?.label} : {cfg.category} — {cfg.all_products ? "tous les produits" : `${cfg.product_ids.length} produit(s)`} · qté {cfg.quantity > 0 ? cfg.quantity : 1}
))}
- {r.reward_items.filter((it) => it.product_id > 0).map((it, idx) => {
- const prod = Object.values(productsByCategory).flat().find((p) => p.id === it.product_id);
- return (
-
-
-
- {prod?.name ?? `Produit #${it.product_id}`}{it.quantity > 0 ? ` · x${it.quantity}` : ""}{it.price > 0 ? ` · ${it.price}€` : ""}
-
-
- );
- })}
)}
@@ -1299,7 +1180,7 @@ export default function SettingsScreen() {
category_routes: s.delivery_mode?.category_routes ?? [],
},
points_reward: s.points_reward
- ? { ...s.points_reward, category_configs: s.points_reward.category_configs ?? [], reward_items: s.points_reward.reward_items ?? [] }
+ ? { ...s.points_reward, category_configs: s.points_reward.category_configs ?? [] }
: null,
});
}
diff --git a/mobile/src/api/api.ts b/mobile/src/api/api.ts
index 58afaa19..6380eac1 100644
--- a/mobile/src/api/api.ts
+++ b/mobile/src/api/api.ts
@@ -967,7 +967,7 @@ export type RewardCategoryConfig = {
all_products: boolean;
product_ids: number[];
product_names: string[];
- amount: number;
+ quantity: number;
};
export type RewardItemConfig = {