chore: update
This commit is contained in:
@@ -585,6 +585,11 @@ export default function OrderDetailScreen() {
|
|||||||
<Text style={styles.info}>
|
<Text style={styles.info}>
|
||||||
Total: {command.total_prix?.toFixed(2)} €
|
Total: {command.total_prix?.toFixed(2)} €
|
||||||
</Text>
|
</Text>
|
||||||
|
{command.referral_used > 0 && (
|
||||||
|
<Text style={[styles.info, { color: colors.success }]}>
|
||||||
|
Parrainage utilisé: -{command.referral_used?.toFixed(2)} €
|
||||||
|
</Text>
|
||||||
|
)}
|
||||||
{command.livreur_assign && (
|
{command.livreur_assign && (
|
||||||
<Text style={styles.info}>
|
<Text style={styles.info}>
|
||||||
Livreur: {command.livreur_assign}
|
Livreur: {command.livreur_assign}
|
||||||
|
|||||||
@@ -29,6 +29,7 @@ function TiersSection({
|
|||||||
colors,
|
colors,
|
||||||
s,
|
s,
|
||||||
accentColor,
|
accentColor,
|
||||||
|
active,
|
||||||
}: {
|
}: {
|
||||||
title: string;
|
title: string;
|
||||||
tiers: PointsTier[];
|
tiers: PointsTier[];
|
||||||
@@ -36,6 +37,7 @@ function TiersSection({
|
|||||||
colors: any;
|
colors: any;
|
||||||
s: any;
|
s: any;
|
||||||
accentColor: string;
|
accentColor: string;
|
||||||
|
active: boolean;
|
||||||
}) {
|
}) {
|
||||||
const updateTier = (i: number, field: keyof PointsTier, raw: string) => {
|
const updateTier = (i: number, field: keyof PointsTier, raw: string) => {
|
||||||
const val = field === "points" ? parseInt(raw, 10) : parseFloat(raw);
|
const val = field === "points" ? parseInt(raw, 10) : parseFloat(raw);
|
||||||
@@ -54,7 +56,22 @@ function TiersSection({
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<View style={s.section}>
|
<View style={s.section}>
|
||||||
<Text style={s.sectionTitle}>{title}</Text>
|
<View style={{ flexDirection: "row", alignItems: "center", justifyContent: "space-between", paddingRight: spacing.l }}>
|
||||||
|
<Text style={s.sectionTitle}>{title}</Text>
|
||||||
|
<View style={{
|
||||||
|
paddingHorizontal: spacing.s,
|
||||||
|
paddingVertical: 2,
|
||||||
|
borderRadius: 10,
|
||||||
|
backgroundColor: active ? accentColor + "25" : colors.border + "40",
|
||||||
|
borderWidth: 1,
|
||||||
|
borderColor: active ? accentColor : colors.border,
|
||||||
|
marginBottom: spacing.s,
|
||||||
|
}}>
|
||||||
|
<Text style={{ fontSize: fontSize.xs, fontWeight: "700", color: active ? accentColor : colors.textMuted }}>
|
||||||
|
{active ? "Actif" : "Ignoré"}
|
||||||
|
</Text>
|
||||||
|
</View>
|
||||||
|
</View>
|
||||||
<Text style={s.hint}>
|
<Text style={s.hint}>
|
||||||
Définissez chaque palier : de X€ à Y€ = N points.{"\n"}
|
Définissez chaque palier : de X€ à Y€ = N points.{"\n"}
|
||||||
Max = 0 signifie illimité (pas de borne supérieure).
|
Max = 0 signifie illimité (pas de borne supérieure).
|
||||||
@@ -319,10 +336,8 @@ export default function SettingsScreen() {
|
|||||||
const ZIP_COLOR = "#9333ea";
|
const ZIP_COLOR = "#9333ea";
|
||||||
const TOTAL_COLOR = "#f97316";
|
const TOTAL_COLOR = "#f97316";
|
||||||
|
|
||||||
// Chips disponibles selon le mode
|
// Tous les chips toujours disponibles — la config reste modifiable à tout moment
|
||||||
const availableChips: PoolAssignment[] = settings.points_separated
|
const allChips: PoolAssignment[] = ["weed", "zipette", "total", "none"];
|
||||||
? ["weed", "zipette", "none"] // mode séparé : T désactivé
|
|
||||||
: ["total", "none"]; // mode non-séparé : W/Z désactivés
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<View style={s.container}>
|
<View style={s.container}>
|
||||||
@@ -430,29 +445,25 @@ export default function SettingsScreen() {
|
|||||||
<Text style={s.sectionTitle}>Attribution des catégories aux points</Text>
|
<Text style={s.sectionTitle}>Attribution des catégories aux points</Text>
|
||||||
<Text style={s.hint}>
|
<Text style={s.hint}>
|
||||||
{settings.points_separated
|
{settings.points_separated
|
||||||
? "Mode séparé : W = pool Weed, Z = pool Zipette."
|
? "Mode séparé actif : W et Z utilisés pour le calcul. T ignoré."
|
||||||
: "Mode non-séparé : T = pool Total (barème sur W+Z). W et Z désactivés."}
|
: "Mode non-séparé actif : T utilisé pour le calcul. W et Z ignorés."}
|
||||||
|
{"\n"}Vous pouvez modifier les attributions à tout moment.
|
||||||
</Text>
|
</Text>
|
||||||
|
|
||||||
{/* Légende */}
|
{/* Légende */}
|
||||||
<View style={{ flexDirection: "row", gap: spacing.m, paddingHorizontal: spacing.l, paddingBottom: spacing.m, flexWrap: "wrap" }}>
|
<View style={{ flexDirection: "row", gap: spacing.m, paddingHorizontal: spacing.l, paddingBottom: spacing.m, flexWrap: "wrap" }}>
|
||||||
{settings.points_separated ? (
|
<View style={{ flexDirection: "row", alignItems: "center", gap: spacing.xs }}>
|
||||||
<>
|
<View style={[s.colorDot, { backgroundColor: WEED_COLOR }]} />
|
||||||
<View style={{ flexDirection: "row", alignItems: "center", gap: spacing.xs }}>
|
<Text style={{ fontSize: fontSize.sm, color: colors.textMuted }}>Weed</Text>
|
||||||
<View style={[s.colorDot, { backgroundColor: WEED_COLOR }]} />
|
</View>
|
||||||
<Text style={{ fontSize: fontSize.sm, color: colors.textMuted }}>Weed</Text>
|
<View style={{ flexDirection: "row", alignItems: "center", gap: spacing.xs }}>
|
||||||
</View>
|
<View style={[s.colorDot, { backgroundColor: ZIP_COLOR }]} />
|
||||||
<View style={{ flexDirection: "row", alignItems: "center", gap: spacing.xs }}>
|
<Text style={{ fontSize: fontSize.sm, color: colors.textMuted }}>Zipette</Text>
|
||||||
<View style={[s.colorDot, { backgroundColor: ZIP_COLOR }]} />
|
</View>
|
||||||
<Text style={{ fontSize: fontSize.sm, color: colors.textMuted }}>Zipette</Text>
|
<View style={{ flexDirection: "row", alignItems: "center", gap: spacing.xs }}>
|
||||||
</View>
|
<View style={[s.colorDot, { backgroundColor: TOTAL_COLOR }]} />
|
||||||
</>
|
<Text style={{ fontSize: fontSize.sm, color: colors.textMuted }}>Total</Text>
|
||||||
) : (
|
</View>
|
||||||
<View style={{ flexDirection: "row", alignItems: "center", gap: spacing.xs }}>
|
|
||||||
<View style={[s.colorDot, { backgroundColor: TOTAL_COLOR }]} />
|
|
||||||
<Text style={{ fontSize: fontSize.sm, color: colors.textMuted }}>Total</Text>
|
|
||||||
</View>
|
|
||||||
)}
|
|
||||||
<View style={{ flexDirection: "row", alignItems: "center", gap: spacing.xs }}>
|
<View style={{ flexDirection: "row", alignItems: "center", gap: spacing.xs }}>
|
||||||
<View style={[s.colorDot, { backgroundColor: colors.border }]} />
|
<View style={[s.colorDot, { backgroundColor: colors.border }]} />
|
||||||
<Text style={{ fontSize: fontSize.sm, color: colors.textMuted }}>Aucun</Text>
|
<Text style={{ fontSize: fontSize.sm, color: colors.textMuted }}>Aucun</Text>
|
||||||
@@ -474,7 +485,7 @@ export default function SettingsScreen() {
|
|||||||
/>
|
/>
|
||||||
<Text style={s.catName}>{cat.name}</Text>
|
<Text style={s.catName}>{cat.name}</Text>
|
||||||
<View style={s.chips}>
|
<View style={s.chips}>
|
||||||
{availableChips.map((p) => {
|
{allChips.map((p) => {
|
||||||
const active = pool === p;
|
const active = pool === p;
|
||||||
const chipColor =
|
const chipColor =
|
||||||
p === "weed"
|
p === "weed"
|
||||||
@@ -541,6 +552,7 @@ export default function SettingsScreen() {
|
|||||||
colors={colors}
|
colors={colors}
|
||||||
s={s}
|
s={s}
|
||||||
accentColor={WEED_COLOR}
|
accentColor={WEED_COLOR}
|
||||||
|
active={settings.points_separated}
|
||||||
/>
|
/>
|
||||||
<TiersSection
|
<TiersSection
|
||||||
title="Barème Zipette"
|
title="Barème Zipette"
|
||||||
@@ -549,6 +561,7 @@ export default function SettingsScreen() {
|
|||||||
colors={colors}
|
colors={colors}
|
||||||
s={s}
|
s={s}
|
||||||
accentColor={ZIP_COLOR}
|
accentColor={ZIP_COLOR}
|
||||||
|
active={settings.points_separated}
|
||||||
/>
|
/>
|
||||||
<TiersSection
|
<TiersSection
|
||||||
title="Barème Total"
|
title="Barème Total"
|
||||||
@@ -557,6 +570,7 @@ export default function SettingsScreen() {
|
|||||||
colors={colors}
|
colors={colors}
|
||||||
s={s}
|
s={s}
|
||||||
accentColor={TOTAL_COLOR}
|
accentColor={TOTAL_COLOR}
|
||||||
|
active={!settings.points_separated}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<TouchableOpacity
|
<TouchableOpacity
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import {
|
|||||||
FlatList,
|
FlatList,
|
||||||
RefreshControl,
|
RefreshControl,
|
||||||
TouchableOpacity,
|
TouchableOpacity,
|
||||||
|
TextInput as RNTextInput,
|
||||||
} from "react-native";
|
} from "react-native";
|
||||||
import { Ionicons } from "@expo/vector-icons";
|
import { Ionicons } from "@expo/vector-icons";
|
||||||
import { spacing, fontSize, borderRadius } from "../../theme";
|
import { spacing, fontSize, borderRadius } from "../../theme";
|
||||||
@@ -126,6 +127,7 @@ export default function UsersScreen() {
|
|||||||
const [loading, setLoading] = useState(true);
|
const [loading, setLoading] = useState(true);
|
||||||
const [refreshing, setRefreshing] = useState(false);
|
const [refreshing, setRefreshing] = useState(false);
|
||||||
const [filter, setFilter] = useState<RoleFilter>("all");
|
const [filter, setFilter] = useState<RoleFilter>("all");
|
||||||
|
const [search, setSearch] = useState("");
|
||||||
|
|
||||||
// Edit client modal
|
// Edit client modal
|
||||||
const [editClientModal, setEditClientModal] = useState<{
|
const [editClientModal, setEditClientModal] = useState<{
|
||||||
@@ -224,6 +226,22 @@ export default function UsersScreen() {
|
|||||||
merged = merged.filter((u) => u.role === filter);
|
merged = merged.filter((u) => u.role === filter);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (search.trim()) {
|
||||||
|
const q = search.trim().toLowerCase();
|
||||||
|
merged = merged.filter((u) => {
|
||||||
|
if (u.username.toLowerCase().includes(q)) return true;
|
||||||
|
if (u.clientData) {
|
||||||
|
const c = u.clientData;
|
||||||
|
return (
|
||||||
|
c.nom.toLowerCase().includes(q) ||
|
||||||
|
c.prenom.toLowerCase().includes(q) ||
|
||||||
|
c.telephone.toLowerCase().includes(q)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
return merged;
|
return merged;
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -542,6 +560,26 @@ export default function UsersScreen() {
|
|||||||
marginTop: spacing.xxl,
|
marginTop: spacing.xxl,
|
||||||
},
|
},
|
||||||
|
|
||||||
|
// Search bar
|
||||||
|
searchContainer: {
|
||||||
|
flexDirection: "row",
|
||||||
|
alignItems: "center",
|
||||||
|
marginHorizontal: spacing.m,
|
||||||
|
marginBottom: spacing.s,
|
||||||
|
paddingHorizontal: spacing.m,
|
||||||
|
borderRadius: borderRadius.md,
|
||||||
|
backgroundColor: colors.bgSecondary,
|
||||||
|
borderWidth: 1,
|
||||||
|
borderColor: colors.border,
|
||||||
|
height: 44,
|
||||||
|
gap: spacing.s,
|
||||||
|
},
|
||||||
|
searchInput: {
|
||||||
|
flex: 1,
|
||||||
|
color: colors.textWhite,
|
||||||
|
fontSize: fontSize.sm,
|
||||||
|
},
|
||||||
|
|
||||||
// Add button
|
// Add button
|
||||||
addButton: {
|
addButton: {
|
||||||
flexDirection: "row",
|
flexDirection: "row",
|
||||||
@@ -847,6 +885,26 @@ export default function UsersScreen() {
|
|||||||
})}
|
})}
|
||||||
</View>
|
</View>
|
||||||
|
|
||||||
|
{/* Search bar */}
|
||||||
|
<View style={styles.searchContainer}>
|
||||||
|
<Ionicons name="search-outline" size={18} color={colors.textMuted} />
|
||||||
|
<RNTextInput
|
||||||
|
style={styles.searchInput}
|
||||||
|
placeholder="Rechercher par nom, username, téléphone..."
|
||||||
|
placeholderTextColor={colors.textMuted}
|
||||||
|
value={search}
|
||||||
|
onChangeText={setSearch}
|
||||||
|
autoCapitalize="none"
|
||||||
|
autoCorrect={false}
|
||||||
|
clearButtonMode="while-editing"
|
||||||
|
/>
|
||||||
|
{search.length > 0 && (
|
||||||
|
<TouchableOpacity onPress={() => setSearch("")}>
|
||||||
|
<Ionicons name="close-circle" size={18} color={colors.textMuted} />
|
||||||
|
</TouchableOpacity>
|
||||||
|
)}
|
||||||
|
</View>
|
||||||
|
|
||||||
{/* Add user button */}
|
{/* Add user button */}
|
||||||
<TouchableOpacity
|
<TouchableOpacity
|
||||||
style={styles.addButton}
|
style={styles.addButton}
|
||||||
|
|||||||
Reference in New Issue
Block a user