diff --git a/frontend-admin/app.json b/frontend-admin/app.json index 969ca7d5..5b66f180 100644 --- a/frontend-admin/app.json +++ b/frontend-admin/app.json @@ -2,7 +2,7 @@ "expo": { "name": "Admin Panel", "slug": "frontend-admin", - "version": "1.0.0", + "version": "1.0.1", "orientation": "portrait", "icon": "./assets/icon.png", "userInterfaceStyle": "dark", diff --git a/frontend-admin/src/screens/admin/SettingsScreen.tsx b/frontend-admin/src/screens/admin/SettingsScreen.tsx index e58d03a3..4a35eb79 100644 --- a/frontend-admin/src/screens/admin/SettingsScreen.tsx +++ b/frontend-admin/src/screens/admin/SettingsScreen.tsx @@ -819,29 +819,44 @@ function CentralRewardSection({ const update = (patch: Partial) => onChange({ ...r, ...patch }); - const getCatConfig = (catName: string): RewardCategoryConfig => - r.category_configs.find((c) => c.category === catName) ?? - { category: catName, type: "free_product", all_products: true, product_ids: [] }; + // Une catégorie peut avoir jusqu'à deux configs actives en parallèle + // (une "free_product" et une "half_price_product"), chacune avec sa + // propre sélection de produits — d'où la recherche par (catégorie, type). + const getCatConfig = (catName: string, type: RewardCategoryConfig["type"]): RewardCategoryConfig => + r.category_configs.find((c) => c.category === catName && c.type === type) ?? + { category: catName, type, all_products: true, product_ids: [] }; + + const isTypeEnabled = (catName: string, type: RewardCategoryConfig["type"]) => + r.category_configs.some((c) => c.category === catName && c.type === type); const isCatSelected = (catName: string) => r.category_configs.some((c) => c.category === catName); - const toggleCategory = (catName: string) => { - if (isCatSelected(catName)) { - update({ category_configs: r.category_configs.filter((c) => c.category !== catName) }); + const toggleCategoryType = (catName: string, type: RewardCategoryConfig["type"]) => { + 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: "free_product", all_products: true, product_ids: [] }] }); + update({ category_configs: [...r.category_configs, { category: catName, type, all_products: true, product_ids: [] }] }); } }; const updateCatConfig = (cfg: RewardCategoryConfig) => { update({ category_configs: r.category_configs.map((c) => - c.category === cfg.category ? cfg : c + c.category === cfg.category && c.type === cfg.type ? cfg : c ), }); }; + const [expandedCats, setExpandedCats] = useState>(new Set()); + const toggleExpanded = (catName: string) => { + setExpandedCats((prev) => { + const next = new Set(prev); + if (next.has(catName)) next.delete(catName); else next.add(catName); + return next; + }); + }; + const rewardBadge = ( Catégories éligibles - Sélectionnez les catégories, choisissez le type de récompense pour chacune, puis tous les produits ou une sélection. + Sélectionnez une catégorie pour activer, indépendamment, un lot de produits offerts et/ou un lot de produits à -50%, chacun avec sa propre sélection de produits. {allCategories.length === 0 ? ( Aucune catégorie disponible @@ -1048,12 +1063,13 @@ function CentralRewardSection({ {allCategories.map((cat) => { const selected = isCatSelected(cat.name); + const expanded = expandedCats.has(cat.name); const catColor = cat.color || REWARD_ACCENT; return ( - {/* Chip catégorie */} + {/* Chip catégorie (couleur = au moins un type actif, clic = déplier/replier) */} toggleCategory(cat.name)} + onPress={() => toggleExpanded(cat.name)} style={{ flexDirection: "row", alignItems: "center", gap: spacing.xs, alignSelf: "flex-start", @@ -1068,46 +1084,52 @@ function CentralRewardSection({ {cat.name} - {/* Type + sélecteur produits (visible si catégorie sélectionnée) */} - {selected && ( - - - {REWARD_TYPES.map((rt) => { - const cfg = getCatConfig(cat.name); - const sel = (cfg.type || "free_product") === rt.value; - return ( + {/* Les deux types de récompense, activables indépendamment */} + {expanded && ( + + {REWARD_TYPES.map((rt) => { + const typeEnabled = isTypeEnabled(cat.name, rt.value); + return ( + updateCatConfig({ ...cfg, type: rt.value })} + onPress={() => toggleCategoryType(cat.name, rt.value)} style={{ flexDirection: "row", alignItems: "center", gap: 4, + alignSelf: "flex-start", paddingHorizontal: spacing.s, paddingVertical: 4, borderRadius: borderRadius.sm, borderWidth: 1.5, - borderColor: sel ? REWARD_ACCENT : colors.border, - backgroundColor: sel ? REWARD_ACCENT + "22" : "transparent", + borderColor: typeEnabled ? REWARD_ACCENT : colors.border, + backgroundColor: typeEnabled ? REWARD_ACCENT + "22" : "transparent", }} > - - + + + {rt.label} - ); - })} - - + {typeEnabled && ( + + )} + + ); + })} )} @@ -1127,8 +1149,8 @@ function CentralRewardSection({ {r.description !== "" && ( "{r.description}" )} - {r.category_configs.map((cfg) => ( - + {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)`} ))}