-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";
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":
}
}
+/**
+ * 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<Alert[]>([]);
+ const [filter, setFilter] = useState<AlertFilter>("ALL");
+ // abonnement au store (temps réel)
useEffect(() => {
const unsub = alertStore.subscribe(setAlerts);
return () => {
};
}, []);
- 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 (
+ <TouchableOpacity
+ style={[
+ styles.filterBtn,
+ active ? styles.filterBtnActive : styles.filterBtnInactive,
+ ]}
+ onPress={() => setFilter(value)}
+ >
+ <Text style={[styles.filterText, active && styles.filterTextActive]}>
+ {label}
+ </Text>
+ </TouchableOpacity>
+ );
+ };
return (
<SafeAreaView style={styles.safeArea}>
<FlatList
contentContainerStyle={ui.container}
- data={sorted}
+ data={filteredAndSorted}
keyExtractor={(_, idx) => `alert-${idx}`}
+ ListHeaderComponent={
+ <View style={ui.card}>
+ <View style={styles.headerRow}>
+ <Text style={ui.title}>Alertes</Text>
+
+ <TouchableOpacity
+ style={[styles.clearBtn, alerts.length === 0 && styles.clearBtnDisabled]}
+ onPress={confirmClear}
+ disabled={alerts.length === 0}
+ >
+ <Text style={styles.clearBtnText}>Clear</Text>
+ </TouchableOpacity>
+ </View>
+
+ <Text style={ui.muted}>
+ Filtre : {filter === "ALL" ? "Toutes" : filter} — Total : {alerts.length}
+ </Text>
+
+ <View style={styles.filtersRow}>
+ <FilterButton value="ALL" label="Toutes" />
+ <FilterButton value="CRITICAL" label="Critical" />
+ <FilterButton value="WARNING" label="Warning" />
+ <FilterButton value="INFO" label="Info" />
+ </View>
+
+ <Text style={[ui.muted, { marginTop: 6 }]}>
+ Tri par défaut : CRITICAL > WARNING > INFO, puis plus récent.
+ </Text>
+ </View>
+ }
ListEmptyComponent={
<View style={ui.card}>
<Text style={ui.title}>Aucune alerte</Text>
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