This commit is contained in:
@@ -1101,6 +1101,8 @@ export interface AppSettings {
|
|||||||
client_color_success: string;
|
client_color_success: string;
|
||||||
client_color_danger: string;
|
client_color_danger: string;
|
||||||
client_color_warning: string;
|
client_color_warning: string;
|
||||||
|
client_title_gradient_from: string;
|
||||||
|
client_title_gradient_to: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const getSettings = async (): Promise<{
|
export const getSettings = async (): Promise<{
|
||||||
|
|||||||
@@ -782,7 +782,7 @@ export default function OrdersScreen() {
|
|||||||
setOpenMenuId(null);
|
setOpenMenuId(null);
|
||||||
handleForceValidate(item.id);
|
handleForceValidate(item.id);
|
||||||
},
|
},
|
||||||
condition: !isDone && item.status !== "livre",
|
condition: !isDone,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: "Supprimer",
|
label: "Supprimer",
|
||||||
|
|||||||
@@ -1205,6 +1205,8 @@ export default function SettingsScreen() {
|
|||||||
client_color_success: "#4ade80",
|
client_color_success: "#4ade80",
|
||||||
client_color_danger: "#ef4444",
|
client_color_danger: "#ef4444",
|
||||||
client_color_warning: "#f59e0b",
|
client_color_warning: "#f59e0b",
|
||||||
|
client_title_gradient_from: "#a78bfa",
|
||||||
|
client_title_gradient_to: "#22d3ee",
|
||||||
});
|
});
|
||||||
const [showApiKey, setShowApiKey] = useState(false);
|
const [showApiKey, setShowApiKey] = useState(false);
|
||||||
const [showIpnSecret, setShowIpnSecret] = useState(false);
|
const [showIpnSecret, setShowIpnSecret] = useState(false);
|
||||||
@@ -1501,6 +1503,7 @@ export default function SettingsScreen() {
|
|||||||
<ScrollView contentContainerStyle={s.content}>
|
<ScrollView contentContainerStyle={s.content}>
|
||||||
{/* Personnalisation */}
|
{/* Personnalisation */}
|
||||||
<AccordionSection title="Personnalisation" colors={colors} s={s}>
|
<AccordionSection title="Personnalisation" colors={colors} s={s}>
|
||||||
|
{/* Nom du shop */}
|
||||||
<View style={[s.row, s.rowFirst]}>
|
<View style={[s.row, s.rowFirst]}>
|
||||||
<View style={s.rowLeft}>
|
<View style={s.rowLeft}>
|
||||||
<Text style={s.rowLabel}>Nom du shop</Text>
|
<Text style={s.rowLabel}>Nom du shop</Text>
|
||||||
@@ -1517,6 +1520,67 @@ export default function SettingsScreen() {
|
|||||||
autoCorrect={false}
|
autoCorrect={false}
|
||||||
/>
|
/>
|
||||||
</View>
|
</View>
|
||||||
|
|
||||||
|
{/* Dégradé titre boutique */}
|
||||||
|
<View style={{ borderTopWidth: 1, borderTopColor: colors.border, paddingHorizontal: spacing.l, paddingVertical: spacing.m, gap: spacing.m }}>
|
||||||
|
<Text style={s.rowLabel}>Dégradé du titre boutique</Text>
|
||||||
|
<Text style={s.rowDesc}>Couleurs du nom de la boutique dans le header du site client.</Text>
|
||||||
|
|
||||||
|
{/* Aperçu du dégradé */}
|
||||||
|
<View style={{ height: 36, borderRadius: borderRadius.sm, overflow: "hidden" }}>
|
||||||
|
<View style={{
|
||||||
|
flex: 1,
|
||||||
|
backgroundColor: settings.client_title_gradient_from,
|
||||||
|
// gradient simulé : deux moitiés de couleur
|
||||||
|
}}>
|
||||||
|
<View style={{
|
||||||
|
position: "absolute", right: 0, top: 0, bottom: 0,
|
||||||
|
width: "50%",
|
||||||
|
backgroundColor: settings.client_title_gradient_to,
|
||||||
|
}} />
|
||||||
|
<View style={{
|
||||||
|
position: "absolute", left: "25%", right: "25%", top: 0, bottom: 0,
|
||||||
|
backgroundColor: `${settings.client_title_gradient_from}00`,
|
||||||
|
}} />
|
||||||
|
</View>
|
||||||
|
</View>
|
||||||
|
|
||||||
|
{/* Couleur de départ */}
|
||||||
|
<View style={{ flexDirection: "row", alignItems: "center", gap: spacing.m }}>
|
||||||
|
<View style={{ width: 32, height: 32, borderRadius: borderRadius.sm, backgroundColor: settings.client_title_gradient_from, borderWidth: 1, borderColor: colors.border }} />
|
||||||
|
<View style={{ flex: 1 }}>
|
||||||
|
<Text style={[s.rowDesc, { marginBottom: 4 }]}>Couleur de départ</Text>
|
||||||
|
<TextInput
|
||||||
|
style={[s.input, { fontFamily: "monospace" }]}
|
||||||
|
value={settings.client_title_gradient_from}
|
||||||
|
onChangeText={(v) => setSettings((p) => ({ ...p, client_title_gradient_from: v }))}
|
||||||
|
placeholder="#a78bfa"
|
||||||
|
placeholderTextColor={colors.textMuted}
|
||||||
|
autoCorrect={false}
|
||||||
|
autoCapitalize="none"
|
||||||
|
maxLength={7}
|
||||||
|
/>
|
||||||
|
</View>
|
||||||
|
</View>
|
||||||
|
|
||||||
|
{/* Couleur de fin */}
|
||||||
|
<View style={{ flexDirection: "row", alignItems: "center", gap: spacing.m }}>
|
||||||
|
<View style={{ width: 32, height: 32, borderRadius: borderRadius.sm, backgroundColor: settings.client_title_gradient_to, borderWidth: 1, borderColor: colors.border }} />
|
||||||
|
<View style={{ flex: 1 }}>
|
||||||
|
<Text style={[s.rowDesc, { marginBottom: 4 }]}>Couleur de fin</Text>
|
||||||
|
<TextInput
|
||||||
|
style={[s.input, { fontFamily: "monospace" }]}
|
||||||
|
value={settings.client_title_gradient_to}
|
||||||
|
onChangeText={(v) => setSettings((p) => ({ ...p, client_title_gradient_to: v }))}
|
||||||
|
placeholder="#22d3ee"
|
||||||
|
placeholderTextColor={colors.textMuted}
|
||||||
|
autoCorrect={false}
|
||||||
|
autoCapitalize="none"
|
||||||
|
maxLength={7}
|
||||||
|
/>
|
||||||
|
</View>
|
||||||
|
</View>
|
||||||
|
</View>
|
||||||
</AccordionSection>
|
</AccordionSection>
|
||||||
|
|
||||||
{/* Amendes */}
|
{/* Amendes */}
|
||||||
|
|||||||
@@ -6,8 +6,8 @@ import {
|
|||||||
StyleSheet,
|
StyleSheet,
|
||||||
RefreshControl,
|
RefreshControl,
|
||||||
TouchableOpacity,
|
TouchableOpacity,
|
||||||
Alert,
|
|
||||||
} from "react-native";
|
} from "react-native";
|
||||||
|
import AlertModal from "../../components/ui/AlertModal";
|
||||||
import { Ionicons } from "@expo/vector-icons";
|
import { Ionicons } from "@expo/vector-icons";
|
||||||
import { useFocusEffect } from "@react-navigation/native";
|
import { useFocusEffect } from "@react-navigation/native";
|
||||||
import { useTheme } from "../../context/ThemeContext";
|
import { useTheme } from "../../context/ThemeContext";
|
||||||
@@ -197,6 +197,8 @@ export default function StatsScreen() {
|
|||||||
const [refreshing, setRefreshing] = useState(false);
|
const [refreshing, setRefreshing] = useState(false);
|
||||||
const [resetting, setResetting] = useState<StatSection | null>(null);
|
const [resetting, setResetting] = useState<StatSection | null>(null);
|
||||||
const [prodSort, setProdSort] = useState<ProdSort>("quantity");
|
const [prodSort, setProdSort] = useState<ProdSort>("quantity");
|
||||||
|
const [confirmModal, setConfirmModal] = useState<{ section: StatSection; label: string } | null>(null);
|
||||||
|
const [errorModal, setErrorModal] = useState<string | null>(null);
|
||||||
|
|
||||||
const loadStats = useCallback(async () => {
|
const loadStats = useCallback(async () => {
|
||||||
try {
|
try {
|
||||||
@@ -215,25 +217,21 @@ export default function StatsScreen() {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const handleReset = (section: StatSection, label: string) => {
|
const handleReset = (section: StatSection, label: string) => {
|
||||||
Alert.alert(
|
setConfirmModal({ section, label });
|
||||||
`Réinitialiser — ${label}`,
|
};
|
||||||
"Les données de cette section repartiront de zéro. Les commandes restent en base.\n\nConfirmer ?",
|
|
||||||
[
|
const doReset = async () => {
|
||||||
{ text: "Annuler", style: "cancel" },
|
if (!confirmModal) return;
|
||||||
{
|
const { section } = confirmModal;
|
||||||
text: "Réinitialiser",
|
setConfirmModal(null);
|
||||||
style: "destructive",
|
|
||||||
onPress: async () => {
|
|
||||||
setResetting(section);
|
setResetting(section);
|
||||||
try {
|
try {
|
||||||
await resetAdminStats(section);
|
await resetAdminStats(section);
|
||||||
await loadStats();
|
await loadStats();
|
||||||
} catch { /* ignore */ }
|
} catch (e: any) {
|
||||||
|
setErrorModal(e?.response?.data?.error || e?.message || "Impossible de réinitialiser");
|
||||||
|
}
|
||||||
setResetting(null);
|
setResetting(null);
|
||||||
},
|
|
||||||
},
|
|
||||||
],
|
|
||||||
);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// Produits triés selon le sélecteur
|
// Produits triés selon le sélecteur
|
||||||
@@ -271,6 +269,24 @@ export default function StatsScreen() {
|
|||||||
));
|
));
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
<>
|
||||||
|
<AlertModal
|
||||||
|
visible={confirmModal !== null}
|
||||||
|
type="confirm"
|
||||||
|
title={`Réinitialiser — ${confirmModal?.label ?? ""}`}
|
||||||
|
message={`Les données "${confirmModal?.label ?? ""}" repartiront de zéro.\nLes commandes restent en base.`}
|
||||||
|
confirmText="Réinitialiser"
|
||||||
|
cancelText="Annuler"
|
||||||
|
onClose={() => setConfirmModal(null)}
|
||||||
|
onConfirm={doReset}
|
||||||
|
/>
|
||||||
|
<AlertModal
|
||||||
|
visible={errorModal !== null}
|
||||||
|
type="error"
|
||||||
|
title="Erreur"
|
||||||
|
message={errorModal ?? ""}
|
||||||
|
onClose={() => setErrorModal(null)}
|
||||||
|
/>
|
||||||
<ScrollView
|
<ScrollView
|
||||||
style={styles.container}
|
style={styles.container}
|
||||||
contentContainerStyle={styles.content}
|
contentContainerStyle={styles.content}
|
||||||
@@ -561,5 +577,6 @@ export default function StatsScreen() {
|
|||||||
)}
|
)}
|
||||||
</Section>
|
</Section>
|
||||||
</ScrollView>
|
</ScrollView>
|
||||||
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user