chore: build
Frontend Admin - EAS Build / build (push) Canceled after 0s
Frontend Client - EAS Build / build (push) Canceled after 0s
Backend - Build & Lint / build (push) Failing after 25m18s
Frontend Web - Build & Lint / build (push) Failing after 9m58s

This commit is contained in:
Xor290
2026-08-06 12:06:05 +02:00
parent c034088bee
commit 22a8d5026c
174 changed files with 30315 additions and 16120 deletions
@@ -1,4 +1,5 @@
import React, { useState, useEffect, useCallback, useRef } from "react";
import { useFocusEffect } from "@react-navigation/native";
import {
View,
Text,
@@ -1155,9 +1156,25 @@ function CentralRewardSection({
);
}
// Palette violette d'origine de l'application (thème par défaut historique)
const ORIGINAL_THEME_COLORS = {
admin_color_primary: "#7c3aed",
admin_color_secondary: "#000000",
admin_color_success: "#4ade80",
admin_color_danger: "#ef4444",
admin_color_warning: "#f59e0b",
client_color_primary: "#7c3aed",
client_color_secondary: "#000000",
client_color_success: "#4ade80",
client_color_danger: "#ef4444",
client_color_warning: "#f59e0b",
client_title_gradient_from: "#a78bfa",
client_title_gradient_to: "#22d3ee",
} as const;
export default function SettingsScreen() {
const { colors } = useTheme();
const { alert, showError, showSuccess, hideAlert } = useAlert();
const { colors, refreshColors } = useTheme();
const { alert, showError, showSuccess, showConfirm, hideAlert } = useAlert();
const navigation = useNavigation();
const { width: screenWidth } = useWindowDimensions();
const inputMd = screenWidth < 380 ? 64 : 80;
@@ -1194,6 +1211,18 @@ export default function SettingsScreen() {
shop_name: "Milieu-Nantais",
contact_telegram: "",
points_reward: null,
admin_color_primary: "#7c3aed",
admin_color_secondary: "#22d3ee",
admin_color_success: "#4ade80",
admin_color_danger: "#ef4444",
admin_color_warning: "#f59e0b",
client_color_primary: "#7c3aed",
client_color_secondary: "#22d3ee",
client_color_success: "#4ade80",
client_color_danger: "#ef4444",
client_color_warning: "#f59e0b",
client_title_gradient_from: "#a78bfa",
client_title_gradient_to: "#22d3ee",
});
const [showApiKey, setShowApiKey] = useState(false);
const [showIpnSecret, setShowIpnSecret] = useState(false);
@@ -1280,9 +1309,13 @@ export default function SettingsScreen() {
}
}, []);
useEffect(() => {
loadData();
}, [loadData]);
useFocusEffect(
useCallback(() => {
if (!isDirty.current) {
loadData();
}
}, [loadData])
);
// Retourne l'index du pool auquel la catégorie est assignée, ou -1 si aucun
const getPoolIndexFor = (catName: string): number => {
@@ -1356,6 +1389,8 @@ export default function SettingsScreen() {
setSaving(false);
if (res.success) {
isDirty.current = false;
await loadData();
await refreshColors();
showSuccess("Succès", "Paramètres sauvegardés");
} else {
showError("Erreur", res.error || "Erreur lors de la sauvegarde");
@@ -1484,6 +1519,7 @@ export default function SettingsScreen() {
<ScrollView contentContainerStyle={s.content}>
{/* Personnalisation */}
<AccordionSection title="Personnalisation" colors={colors} s={s}>
{/* Nom du shop */}
<View style={[s.row, s.rowFirst]}>
<View style={s.rowLeft}>
<Text style={s.rowLabel}>Nom du shop</Text>
@@ -1500,6 +1536,67 @@ export default function SettingsScreen() {
autoCorrect={false}
/>
</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>
{/* Amendes */}
@@ -2182,6 +2279,97 @@ export default function SettingsScreen() {
)}
</AccordionSection>
{/* ============================================ */}
{/* 🎨 COULEURS DE L'INTERFACE */}
{/* ============================================ */}
<AccordionSection title="Couleurs de l'interface" colors={colors} s={s}>
<Text style={[s.hint, { paddingTop: spacing.s, paddingHorizontal: spacing.l }]}>
Personnalisez les couleurs de l'espace admin et de l'app client / site web.
</Text>
<TouchableOpacity
onPress={() => showConfirm(
"Restaurer le thème d'origine",
"Toutes les couleurs personnalisées seront remplacées par le violet de base des premières versions de l'application. Continuer ?",
() => {
Keyboard.dismiss();
setSettings((p) => ({ ...p, ...ORIGINAL_THEME_COLORS }));
},
"Restaurer",
"Annuler",
)}
style={{
flexDirection: "row",
alignItems: "center",
gap: spacing.xs,
marginHorizontal: spacing.l,
marginTop: spacing.m,
paddingVertical: spacing.s,
paddingHorizontal: spacing.m,
borderRadius: borderRadius.sm,
borderWidth: 1,
borderColor: "#7c3aed",
alignSelf: "flex-start",
}}
>
<Ionicons name="color-palette-outline" size={16} color="#7c3aed" />
<Text style={{ fontSize: fontSize.sm, color: "#7c3aed", fontWeight: "600" }}>
Restaurer les couleurs de base
</Text>
</TouchableOpacity>
{(["admin", "client"] as const).map((scope) => (
<View key={scope}>
<Text style={[s.rowLabel, { paddingHorizontal: spacing.l, paddingTop: spacing.l, paddingBottom: spacing.xs }]}>
{scope === "admin" ? "Espace admin" : "App client & site web"}
</Text>
{([
{ label: "Principale", key: `${scope}_color_primary` as const },
{ label: "Secondaire", key: `${scope}_color_secondary` as const },
{ label: "Succès", key: `${scope}_color_success` as const },
{ label: "Danger", key: `${scope}_color_danger` as const },
{ label: "Avertissement", key: `${scope}_color_warning` as const },
] as { label: string; key: keyof typeof settings }[]).map(({ label, key }) => (
<View key={key as string} style={{ paddingHorizontal: spacing.l, paddingBottom: spacing.m }}>
<Text style={[s.rowDesc, { marginBottom: spacing.xs }]}>{label}</Text>
<View style={{ flexDirection: "row", alignItems: "center", gap: spacing.m }}>
<View style={{
width: 36, height: 36,
borderRadius: borderRadius.sm,
backgroundColor: (settings[key] as string) || "#7c3aed",
borderWidth: 1,
borderColor: colors.border,
}} />
<TextInput
style={[s.input, { flex: 1 }]}
value={settings[key] as string}
onChangeText={(v) => setSettings((p) => ({ ...p, [key]: v }))}
placeholder="#7c3aed"
placeholderTextColor={colors.textMuted}
autoCapitalize="none"
autoCorrect={false}
/>
</View>
<View style={{ flexDirection: "row", gap: spacing.xs, marginTop: spacing.xs, flexWrap: "wrap" }}>
{["#7c3aed", "#2563eb", "#0891b2", "#059669", "#d97706", "#dc2626", "#db2777", "#f59e0b", "#4ade80", "#ef4444", "#22d3ee", "#0f172a"].map((color) => (
<TouchableOpacity
key={color}
onPress={() => setSettings((p) => ({ ...p, [key]: color }))}
style={{
width: 26, height: 26, borderRadius: 13,
backgroundColor: color,
borderWidth: (settings[key] as string) === color ? 2.5 : 1,
borderColor: (settings[key] as string) === color ? colors.textPrimary : colors.border,
}}
/>
))}
</View>
</View>
))}
</View>
))}
</AccordionSection>
<TouchableOpacity
style={s.saveButton}
onPress={handleSave}