From 916d2a30ada5a03434cc76c65206edb5fff595c7 Mon Sep 17 00:00:00 2001 From: Xor290 Date: Sat, 7 Mar 2026 20:51:48 +0100 Subject: [PATCH] chore: fix --- frontend-admin/src/api/api_admin.ts | 4 +- frontend-admin/src/api/api_cabine.ts | 21 ++ .../src/navigation/AdminNavigator.tsx | 32 ++- frontend-admin/src/navigation/types.ts | 2 +- .../src/screens/admin/SettingsScreen.tsx | 239 ++++++++++-------- .../src/screens/cabine/UsersScreen.tsx | 100 +++++--- mobile/src/api/api.ts | 21 ++ .../src/screens/client/OrderHistoryScreen.tsx | 91 ++++--- 8 files changed, 316 insertions(+), 194 deletions(-) diff --git a/frontend-admin/src/api/api_admin.ts b/frontend-admin/src/api/api_admin.ts index 700335b8..52d3424a 100644 --- a/frontend-admin/src/api/api_admin.ts +++ b/frontend-admin/src/api/api_admin.ts @@ -663,7 +663,9 @@ export const deleteCategoryAdmin = async ( export interface AppSettings { penalties_enabled: boolean; - points_categories: string[]; + points_enabled: boolean; + points_categories_weed: string[]; + points_categories_zipette: string[]; points_separated: boolean; } diff --git a/frontend-admin/src/api/api_cabine.ts b/frontend-admin/src/api/api_cabine.ts index 6d776f50..a89eb15c 100644 --- a/frontend-admin/src/api/api_cabine.ts +++ b/frontend-admin/src/api/api_cabine.ts @@ -339,3 +339,24 @@ export const getAllAlerts = async (): Promise<{ return { success: false, alerts: [], count: 0 }; } }; + +// ============================================ +// PARAMÈTRES PUBLICS +// ============================================ + +export interface PublicSettings { + penalties_enabled: boolean; + points_enabled: boolean; +} + +export const getPublicSettings = async (): Promise => { + try { + const { data } = await apiClient.get(`http://5.181.0.112/api/v1/app-settings`); + return { + penalties_enabled: data.penalties_enabled ?? true, + points_enabled: data.points_enabled ?? true, + }; + } catch { + return { penalties_enabled: true, points_enabled: true }; + } +}; diff --git a/frontend-admin/src/navigation/AdminNavigator.tsx b/frontend-admin/src/navigation/AdminNavigator.tsx index 19488f70..dd70f2cc 100644 --- a/frontend-admin/src/navigation/AdminNavigator.tsx +++ b/frontend-admin/src/navigation/AdminNavigator.tsx @@ -2,6 +2,8 @@ import React from "react"; import { TouchableOpacity, View } from "react-native"; import { createNativeStackNavigator } from "@react-navigation/native-stack"; import { createBottomTabNavigator } from "@react-navigation/bottom-tabs"; +import { useNavigation } from "@react-navigation/native"; +import type { NativeStackNavigationProp } from "@react-navigation/native-stack"; import { Ionicons } from "@expo/vector-icons"; import { useAuth } from "../auth/AuthContext"; import { useTheme } from "../context/ThemeContext"; @@ -26,6 +28,7 @@ const Stack = createNativeStackNavigator(); function AdminTabs() { const { logout } = useAuth(); const { colors, isDark, toggleTheme } = useTheme(); + const navigation = useNavigation>(); const handleLogout = async () => { await logoutAdmin(); @@ -55,6 +58,16 @@ function AdminTabs() { color={colors.textSecondary} /> + navigation.navigate("Settings")} + style={{ marginRight: spacing.m }} + > + + - ( - - ), - }} - /> ); } @@ -224,6 +223,11 @@ export default function AdminNavigator() { component={OrderDetailScreen} options={{ title: "Détail commande" }} /> + ); } diff --git a/frontend-admin/src/navigation/types.ts b/frontend-admin/src/navigation/types.ts index 9aa8b954..e1bffc92 100644 --- a/frontend-admin/src/navigation/types.ts +++ b/frontend-admin/src/navigation/types.ts @@ -14,12 +14,12 @@ export type AdminTabParamList = { Delivery: undefined; Alerts: undefined; Addresses: undefined; - Settings: undefined; }; export type AdminStackParamList = { AdminTabs: undefined; OrderDetail: { orderId: number }; + Settings: undefined; }; export type CabineTabParamList = { diff --git a/frontend-admin/src/screens/admin/SettingsScreen.tsx b/frontend-admin/src/screens/admin/SettingsScreen.tsx index 8c036f7f..485329e8 100644 --- a/frontend-admin/src/screens/admin/SettingsScreen.tsx +++ b/frontend-admin/src/screens/admin/SettingsScreen.tsx @@ -16,6 +16,8 @@ import type { AppSettings, Category } from "../../api/api_admin"; import AlertModal from "../../components/ui/AlertModal"; import { useAlert } from "../../hooks/useAlert"; +type PoolAssignment = "weed" | "zipette" | "none"; + export default function SettingsScreen() { const { colors } = useTheme(); const { alert, showError, showSuccess, hideAlert } = useAlert(); @@ -24,7 +26,9 @@ export default function SettingsScreen() { const [saving, setSaving] = useState(false); const [settings, setSettings] = useState({ penalties_enabled: true, - points_categories: [], + points_enabled: true, + points_categories_weed: [], + points_categories_zipette: [], points_separated: true, }); const [categories, setCategories] = useState([]); @@ -38,7 +42,8 @@ export default function SettingsScreen() { if (settingsRes.success && settingsRes.settings) { setSettings({ ...settingsRes.settings, - points_categories: settingsRes.settings.points_categories ?? [], + points_categories_weed: settingsRes.settings.points_categories_weed ?? [], + points_categories_zipette: settingsRes.settings.points_categories_zipette ?? [], }); } if (categoriesRes) { @@ -51,13 +56,19 @@ export default function SettingsScreen() { loadData(); }, [loadData]); - const toggleCategory = (name: string) => { + const getPoolFor = (name: string): PoolAssignment => { + if ((settings.points_categories_weed ?? []).includes(name)) return "weed"; + if ((settings.points_categories_zipette ?? []).includes(name)) return "zipette"; + return "none"; + }; + + const setPool = (name: string, pool: PoolAssignment) => { setSettings((prev) => { - const cats = prev.points_categories ?? []; - if (cats.includes(name)) { - return { ...prev, points_categories: cats.filter((c) => c !== name) }; - } - return { ...prev, points_categories: [...cats, name] }; + const weed = (prev.points_categories_weed ?? []).filter((c) => c !== name); + const zipette = (prev.points_categories_zipette ?? []).filter((c) => c !== name); + if (pool === "weed") weed.push(name); + else if (pool === "zipette") zipette.push(name); + return { ...prev, points_categories_weed: weed, points_categories_zipette: zipette }; }); }; @@ -73,14 +84,8 @@ export default function SettingsScreen() { }; const s = StyleSheet.create({ - container: { - flex: 1, - backgroundColor: colors.bgPrimary, - }, - content: { - padding: spacing.l, - paddingBottom: spacing.xl * 2, - }, + container: { flex: 1, backgroundColor: colors.bgPrimary }, + content: { padding: spacing.l, paddingBottom: spacing.xl * 2 }, section: { backgroundColor: colors.bgSecondary, borderRadius: borderRadius.lg, @@ -106,53 +111,31 @@ export default function SettingsScreen() { borderTopWidth: 1, borderTopColor: colors.border, }, - rowFirst: { - borderTopWidth: 0, - }, - rowLeft: { - flex: 1, - marginRight: spacing.m, - }, - rowLabel: { - fontSize: fontSize.md, - color: colors.textPrimary, - fontWeight: "600", - }, - rowDesc: { - fontSize: fontSize.sm, - color: colors.textMuted, - marginTop: 2, - }, - categoryRow: { + rowFirst: { borderTopWidth: 0 }, + rowLeft: { flex: 1, marginRight: spacing.m }, + rowLabel: { fontSize: fontSize.md, color: colors.textPrimary, fontWeight: "600" }, + rowDesc: { fontSize: fontSize.sm, color: colors.textMuted, marginTop: 2 }, + catRow: { flexDirection: "row", alignItems: "center", paddingHorizontal: spacing.l, paddingVertical: spacing.m, borderTopWidth: 1, borderTopColor: colors.border, - gap: spacing.m, + gap: spacing.s, }, - categoryRowFirst: { - borderTopWidth: 0, + catRowFirst: { borderTopWidth: 0 }, + colorDot: { width: 10, height: 10, borderRadius: 5 }, + catName: { flex: 1, fontSize: fontSize.md, color: colors.textPrimary }, + chips: { flexDirection: "row", gap: spacing.xs }, + chip: { + paddingHorizontal: spacing.s, + paddingVertical: 4, + borderRadius: borderRadius.sm, + borderWidth: 1, }, - checkBox: { - width: 22, - height: 22, - borderRadius: 6, - borderWidth: 2, - borderColor: colors.accent, - alignItems: "center", - justifyContent: "center", - }, - checkBoxChecked: { - backgroundColor: colors.accent, - }, - categoryName: { - fontSize: fontSize.md, - color: colors.textPrimary, - flex: 1, - }, - allCatsHint: { + chipText: { fontSize: fontSize.sm, fontWeight: "600" }, + hint: { fontSize: fontSize.sm, color: colors.textMuted, fontStyle: "italic", @@ -169,11 +152,7 @@ export default function SettingsScreen() { gap: spacing.s, marginTop: spacing.s, }, - saveButtonText: { - color: "#fff", - fontSize: fontSize.md, - fontWeight: "700", - }, + saveButtonText: { color: "#fff", fontSize: fontSize.md, fontWeight: "700" }, }); if (loading) { @@ -184,13 +163,13 @@ export default function SettingsScreen() { ); } - const selectedCats = settings.points_categories ?? []; - const allSelected = selectedCats.length === 0; + const WEED_COLOR = "#10b981"; + const ZIP_COLOR = "#9333ea"; return ( - {/* Section amendes */} + {/* Amendes */} Amendes @@ -211,16 +190,32 @@ export default function SettingsScreen() { - {/* Section points */} + {/* Système de points */} Système de points - - Points séparés par catégorie + Points activés - Activé : weed → point, zipette → point_zipette{"\n"} - Désactivé : tous les points dans un seul compteur + Les clients voient leurs scores de points et point_zipette.{"\n"} + La cabine peut réinitialiser les points. + + + + setSettings((prev) => ({ ...prev, points_enabled: v })) + } + trackColor={{ false: colors.border, true: colors.accent }} + thumbColor="#fff" + /> + + + + Points séparés par pool + + Activé : Weed → point / Zipette → point_zipette{"\n"} + Désactivé : tout dans un seul compteur (point) - {/* Section catégories éligibles aux points */} + {/* Attribution des catégories */} - Catégories éligibles aux points - {allSelected && ( - - Aucune sélection = comportement par défaut (toutes les catégories sauf gros&semi) - - )} + Attribution des catégories aux points + + Pour chaque catégorie, choisis si elle génère des points Weed, Zipette, ou aucun. + + + {/* Légende */} + + + + Weed + + + + Zipette + + + + Aucun + + + {categories.map((cat, index) => { - const checked = selectedCats.includes(cat.name); + const pool = getPoolFor(cat.name); return ( - toggleCategory(cat.name)} + style={[s.catRow, index === 0 && s.catRowFirst]} > - {checked && ( - + /> + {cat.name} + + {(["weed", "zipette", "none"] as PoolAssignment[]).map( + (p) => { + const active = pool === p; + const chipColor = + p === "weed" + ? WEED_COLOR + : p === "zipette" + ? ZIP_COLOR + : colors.textMuted; + const label = + p === "weed" + ? "W" + : p === "zipette" + ? "Z" + : "—"; + return ( + setPool(cat.name, p)} + > + + {label} + + + ); + }, )} - - {cat.name} - + ); })} + {categories.length === 0 && ( - + Aucune catégorie disponible )} diff --git a/frontend-admin/src/screens/cabine/UsersScreen.tsx b/frontend-admin/src/screens/cabine/UsersScreen.tsx index 4092076b..791c93b7 100644 --- a/frontend-admin/src/screens/cabine/UsersScreen.tsx +++ b/frontend-admin/src/screens/cabine/UsersScreen.tsx @@ -3,7 +3,8 @@ import { View, Text, StyleSheet, FlatList, RefreshControl } from "react-native"; import { spacing, fontSize } from "../../theme"; import { useTheme } from "../../context/ThemeContext"; import { getAllClients } from "../../api/api_admin"; -import { applyClientPenalty, resetClientPenalties, resetClientPoints } from "../../api/api_cabine"; +import { applyClientPenalty, resetClientPenalties, resetClientPoints, getPublicSettings } from "../../api/api_cabine"; +import type { PublicSettings } from "../../api/api_cabine"; import type { ClientResponse } from "../../api/types"; import LoadingSpinner from "../../components/ui/LoadingSpinner"; import Card from "../../components/ui/Card"; @@ -16,6 +17,10 @@ export default function UsersScreen() { const [clients, setClients] = useState([]); const [loading, setLoading] = useState(true); const [refreshing, setRefreshing] = useState(false); + const [appSettings, setAppSettings] = useState({ + penalties_enabled: true, + points_enabled: true, + }); const [penaltyModal, setPenaltyModal] = useState<{ visible: boolean; username: string; @@ -25,7 +30,12 @@ export default function UsersScreen() { const loadData = useCallback(async () => { try { - setClients(await getAllClients()); + const [clientsData, settings] = await Promise.all([ + getAllClients(), + getPublicSettings(), + ]); + setClients(clientsData); + setAppSettings(settings); } catch { /* ignore */ } @@ -35,6 +45,7 @@ export default function UsersScreen() { useEffect(() => { loadData(); }, [loadData]); + const onRefresh = async () => { setRefreshing(true); await loadData(); @@ -115,6 +126,7 @@ export default function UsersScreen() { flexDirection: "row", gap: spacing.s, marginTop: spacing.m, + flexWrap: "wrap", }, empty: { color: colors.textMuted, @@ -132,24 +144,30 @@ export default function UsersScreen() { {item.prenom} {item.nom} - {item.telephone} - - - {item.point} - - Points - - - - {item.points_zipette} - - Zipette - - - - {item.amende} - - Amendes - + {appSettings.points_enabled && ( + <> + + + {item.point} + + Points + + + + {item.points_zipette} + + Zipette + + + )} + {appSettings.penalties_enabled && ( + + + {item.amende} + + Amendes + + )} {item.cancellations_count} @@ -158,24 +176,30 @@ export default function UsersScreen() { -