From dd81507bf6b6ecb8dc9634e605ae1f04a927bbc4 Mon Sep 17 00:00:00 2001 From: Thibaud Moustier Date: Wed, 25 Feb 2026 14:11:05 +0100 Subject: [PATCH] Mobile : assets placeholder move to 'Wallette\mbole' + modification AlertsScreen --- .../mobile/assets}/adaptive-icon.png | Bin .../mobile/assets}/favicon.png | Bin {assets => Wallette/mobile/assets}/icon.png | Bin .../mobile/assets}/splash-icon.png | Bin Wallette/mobile/src/screens/AlertsScreen.tsx | 149 +++++++++++++++++- 5 files changed, 142 insertions(+), 7 deletions(-) rename {assets => Wallette/mobile/assets}/adaptive-icon.png (100%) rename {assets => Wallette/mobile/assets}/favicon.png (100%) rename {assets => Wallette/mobile/assets}/icon.png (100%) rename {assets => Wallette/mobile/assets}/splash-icon.png (100%) diff --git a/assets/adaptive-icon.png b/Wallette/mobile/assets/adaptive-icon.png similarity index 100% rename from assets/adaptive-icon.png rename to Wallette/mobile/assets/adaptive-icon.png diff --git a/assets/favicon.png b/Wallette/mobile/assets/favicon.png similarity index 100% rename from assets/favicon.png rename to Wallette/mobile/assets/favicon.png diff --git a/assets/icon.png b/Wallette/mobile/assets/icon.png similarity index 100% rename from assets/icon.png rename to Wallette/mobile/assets/icon.png diff --git a/assets/splash-icon.png b/Wallette/mobile/assets/splash-icon.png similarity index 100% rename from assets/splash-icon.png rename to Wallette/mobile/assets/splash-icon.png diff --git a/Wallette/mobile/src/screens/AlertsScreen.tsx b/Wallette/mobile/src/screens/AlertsScreen.tsx index 4333d5d..dff674a 100644 --- a/Wallette/mobile/src/screens/AlertsScreen.tsx +++ b/Wallette/mobile/src/screens/AlertsScreen.tsx @@ -1,4 +1,4 @@ -import { View, Text, StyleSheet, FlatList } from "react-native"; +import { View, Text, StyleSheet, FlatList, TouchableOpacity, Alert as RNAlert } from "react-native"; import { useEffect, useMemo, useState } from "react"; import { SafeAreaView } from "react-native-safe-area-context"; @@ -6,10 +6,14 @@ import type { Alert } from "../types/Alert"; import { alertStore } from "../services/alertStore"; import { ui } from "../components/ui/uiStyles"; -// Types locaux pour le tri/couleurs (évite dépendre d'exports manquants) +/** + * Types locaux (évite dépendre d'exports qui ne sont pas toujours présents) + */ type AlertLevel = "CRITICAL" | "WARNING" | "INFO"; type TradeDecision = "BUY" | "SELL" | "HOLD" | "STOP_LOSS"; +type AlertFilter = "ALL" | AlertLevel; + function levelRank(level: AlertLevel) { switch (level) { case "CRITICAL": @@ -48,9 +52,20 @@ function getActionColor(action: TradeDecision): string { } } +/** + * AlertsScreen + * ------------ + * Affiche les alertes reçues via Socket.IO (stockées dans alertStore). + * + * - Filtre optionnel (ALL / CRITICAL / WARNING / INFO) + * - Par défaut : ALL avec tri CRITICAL > WARNING > INFO puis plus récent + * - Bouton Clear avec confirmation + */ export default function AlertsScreen() { const [alerts, setAlerts] = useState([]); + const [filter, setFilter] = useState("ALL"); + // abonnement au store (temps réel) useEffect(() => { const unsub = alertStore.subscribe(setAlerts); return () => { @@ -58,27 +73,97 @@ export default function AlertsScreen() { }; }, []); - const sorted = useMemo(() => { - return [...alerts].sort((a, b) => { - // cast car ton type Alert peut ne pas déclarer explicitement les unions + const filteredAndSorted = useMemo(() => { + const filtered = + filter === "ALL" + ? alerts + : alerts.filter((a) => (a.alertLevel as AlertLevel) === filter); + + return [...filtered].sort((a, b) => { const la = a.alertLevel as AlertLevel; const lb = b.alertLevel as AlertLevel; + // tri par niveau d'alerte (desc) const byLevel = levelRank(lb) - levelRank(la); if (byLevel !== 0) return byLevel; + // tri par date (desc) si dispo const ta = a.timestamp ?? 0; const tb = b.timestamp ?? 0; return tb - ta; }); - }, [alerts]); + }, [alerts, filter]); + + const confirmClear = () => { + if (alerts.length === 0) return; + + RNAlert.alert( + "Supprimer les alertes ?", + "Cette action efface la liste locale des alertes reçues (cela ne touche pas la base de données).", + [ + { text: "Annuler", style: "cancel" }, + { + text: "Supprimer", + style: "destructive", + onPress: () => alertStore.clear(), + }, + ] + ); + }; + + const FilterButton = ({ value, label }: { value: AlertFilter; label: string }) => { + const active = filter === value; + return ( + setFilter(value)} + > + + {label} + + + ); + }; return ( `alert-${idx}`} + ListHeaderComponent={ + + + Alertes + + + Clear + + + + + Filtre : {filter === "ALL" ? "Toutes" : filter} — Total : {alerts.length} + + + + + + + + + + + Tri par défaut : CRITICAL > WARNING > INFO, puis plus récent. + + + } ListEmptyComponent={ Aucune alerte @@ -137,4 +222,54 @@ const styles = StyleSheet.create({ flex: 1, backgroundColor: ui.screen.backgroundColor, }, + + headerRow: { + flexDirection: "row", + justifyContent: "space-between", + alignItems: "center", + }, + + clearBtn: { + paddingHorizontal: 12, + paddingVertical: 8, + borderRadius: 10, + borderWidth: 1, + borderColor: "#e5e7eb", + backgroundColor: "#fff", + }, + clearBtnDisabled: { + opacity: 0.5, + }, + clearBtnText: { + fontWeight: "900", + color: "#dc2626", + }, + + filtersRow: { + flexDirection: "row", + flexWrap: "wrap", + gap: 8, + marginTop: 10, + }, + filterBtn: { + paddingHorizontal: 12, + paddingVertical: 8, + borderRadius: 999, + borderWidth: 1, + }, + filterBtnInactive: { + borderColor: "#e5e7eb", + backgroundColor: "#fff", + }, + filterBtnActive: { + borderColor: "#14532D", + backgroundColor: "#14532D22", + }, + filterText: { + fontWeight: "900", + color: "#0f172a", + }, + filterTextActive: { + color: "#14532D", + }, }); \ No newline at end of file -- 2.50.1