diff --git a/frontend-admin/src/api/api_admin.ts b/frontend-admin/src/api/api_admin.ts
index 458af2b5..cefcae2f 100644
--- a/frontend-admin/src/api/api_admin.ts
+++ b/frontend-admin/src/api/api_admin.ts
@@ -661,6 +661,12 @@ export const deleteCategoryAdmin = async (
// PARAMÈTRES GLOBAUX
// ============================================
+export interface PointsTier {
+ min: number;
+ max: number; // 0 = illimité
+ points: number;
+}
+
export interface AppSettings {
penalties_enabled: boolean;
show_amende_score: boolean;
@@ -668,10 +674,8 @@ export interface AppSettings {
points_categories_weed: string[];
points_categories_zipette: string[];
points_separated: boolean;
- points_weed_threshold_price: number;
- points_weed_per_threshold: number;
- points_zipette_threshold_price: number;
- points_zipette_per_threshold: number;
+ points_weed_tiers: PointsTier[];
+ points_zipette_tiers: PointsTier[];
}
export const getSettings = async (): Promise<{ success: boolean; settings?: AppSettings; error?: string }> => {
diff --git a/frontend-admin/src/screens/admin/SettingsScreen.tsx b/frontend-admin/src/screens/admin/SettingsScreen.tsx
index 570789eb..b9563532 100644
--- a/frontend-admin/src/screens/admin/SettingsScreen.tsx
+++ b/frontend-admin/src/screens/admin/SettingsScreen.tsx
@@ -13,12 +13,127 @@ import { Ionicons } from "@expo/vector-icons";
import { spacing, fontSize, borderRadius } from "../../theme";
import { useTheme } from "../../context/ThemeContext";
import { getSettings, updateSettings, getCategories } from "../../api/api_admin";
-import type { AppSettings, Category } from "../../api/api_admin";
+import type { AppSettings, Category, PointsTier } from "../../api/api_admin";
import AlertModal from "../../components/ui/AlertModal";
import { useAlert } from "../../hooks/useAlert";
type PoolAssignment = "weed" | "zipette" | "none";
+// ──────────────────────────────────────────────────────────────
+// Composant éditeur de paliers
+// ──────────────────────────────────────────────────────────────
+function TiersSection({
+ title,
+ tiers,
+ onChange,
+ colors,
+ s,
+ accentColor,
+}: {
+ title: string;
+ tiers: PointsTier[];
+ onChange: (tiers: PointsTier[]) => void;
+ colors: any;
+ s: any;
+ accentColor: string;
+}) {
+ const updateTier = (i: number, field: keyof PointsTier, raw: string) => {
+ const val = field === "points" ? parseInt(raw, 10) : parseFloat(raw);
+ if (isNaN(val)) return;
+ const next = tiers.map((t, idx) => (idx === i ? { ...t, [field]: val } : t));
+ onChange(next);
+ };
+
+ const addTier = () => {
+ onChange([...tiers, { min: 0, max: 0, points: 1 }]);
+ };
+
+ const removeTier = (i: number) => {
+ onChange(tiers.filter((_, idx) => idx !== i));
+ };
+
+ return (
+
+ {title}
+
+ Définissez chaque palier : de X€ à Y€ = N points.{"\n"}
+ Max = 0 signifie illimité (pas de borne supérieure).
+
+
+ {/* En-tête colonnes */}
+
+ Min €
+ Max €
+ Pts
+
+
+ {tiers.map((tier, i) => (
+
+ updateTier(i, "min", v)}
+ />
+ →
+ updateTier(i, "max", v)}
+ placeholder="0=∞"
+ placeholderTextColor={colors.textMuted}
+ />
+ =
+ updateTier(i, "points", v)}
+ />
+ pts
+ removeTier(i)}>
+
+
+
+ ))}
+
+ {tiers.length === 0 && (
+ Aucun palier défini
+ )}
+
+
+
+
+ Ajouter un palier
+
+
+
+ );
+}
+
export default function SettingsScreen() {
const { colors } = useTheme();
const { alert, showError, showSuccess, hideAlert } = useAlert();
@@ -32,10 +147,8 @@ export default function SettingsScreen() {
points_categories_weed: [],
points_categories_zipette: [],
points_separated: true,
- points_weed_threshold_price: 30,
- points_weed_per_threshold: 1,
- points_zipette_threshold_price: 30,
- points_zipette_per_threshold: 1,
+ points_weed_tiers: [],
+ points_zipette_tiers: [],
});
const [categories, setCategories] = useState([]);
@@ -372,77 +485,22 @@ export default function SettingsScreen() {
{/* Barème de points */}
-
- Barème de points
-
- Pour chaque tranche de prix dépensée, le client gagne un nombre de points.{"\n"}
- Exemple : 50€ = 2 pts → 100€ = 4 pts, 150€ = 6 pts...
-
-
- {/* Weed */}
-
-
- Pool Weed / Hash
- Prix seuil → points gagnés
-
-
- {
- const n = parseFloat(v);
- if (!isNaN(n) && n > 0)
- setSettings((p) => ({ ...p, points_weed_threshold_price: n }));
- }}
- />
- € =
- {
- const n = parseInt(v, 10);
- if (!isNaN(n) && n >= 0)
- setSettings((p) => ({ ...p, points_weed_per_threshold: n }));
- }}
- />
- pts
-
-
-
- {/* Zipette */}
-
-
- Pool Zipette
- Prix seuil → points gagnés
-
-
- {
- const n = parseFloat(v);
- if (!isNaN(n) && n > 0)
- setSettings((p) => ({ ...p, points_zipette_threshold_price: n }));
- }}
- />
- € =
- {
- const n = parseInt(v, 10);
- if (!isNaN(n) && n >= 0)
- setSettings((p) => ({ ...p, points_zipette_per_threshold: n }));
- }}
- />
- pts
-
-
-
+ setSettings((p) => ({ ...p, points_weed_tiers: tiers }))}
+ colors={colors}
+ s={s}
+ accentColor={WEED_COLOR}
+ />
+ setSettings((p) => ({ ...p, points_zipette_tiers: tiers }))}
+ colors={colors}
+ s={s}
+ accentColor={ZIP_COLOR}
+ />