-import { StatusBar } from 'expo-status-bar';
-import { StyleSheet, Text, View } from 'react-native';
+import DashboardScreen from "./src/screens/DashboardScreen";
export default function App() {
- return (
- <View style={styles.container}>
- <Text>Open up App.tsx to start working on your app!</Text>
- <StatusBar style="auto" />
- </View>
- );
-}
-
-const styles = StyleSheet.create({
- container: {
- flex: 1,
- backgroundColor: '#fff',
- alignItems: 'center',
- justifyContent: 'center',
- },
-});
+ return <DashboardScreen />;
+}
\ No newline at end of file
"expo": "~54.0.33",
"expo-status-bar": "~3.0.9",
"react": "19.1.0",
- "react-native": "0.81.5"
+ "react-native": "0.81.5",
+ "react-native-safe-area-context": "~5.6.0"
},
"devDependencies": {
"@types/react": "~19.1.0",
"resolved": "https://registry.npmjs.org/expo/-/expo-54.0.33.tgz",
"integrity": "sha512-3yOEfAKqo+gqHcV8vKcnq0uA5zxlohnhA3fu4G43likN8ct5ZZ3LjAh9wDdKteEkoad3tFPvwxmXW711S5OHUw==",
"license": "MIT",
+ "peer": true,
"dependencies": {
"@babel/runtime": "^7.20.0",
"@expo/cli": "54.0.23",
"react-native": "*"
}
},
+ "node_modules/react-native-safe-area-context": {
+ "version": "5.6.2",
+ "resolved": "https://registry.npmjs.org/react-native-safe-area-context/-/react-native-safe-area-context-5.6.2.tgz",
+ "integrity": "sha512-4XGqMNj5qjUTYywJqpdWZ9IG8jgkS3h06sfVjfw5yZQZfWnRFXczi0GnYyFyCc2EBps/qFmoCH8fez//WumdVg==",
+ "license": "MIT",
+ "peerDependencies": {
+ "react": "*",
+ "react-native": "*"
+ }
+ },
"node_modules/react-native/node_modules/@react-native/virtualized-lists": {
"version": "0.81.5",
"resolved": "https://registry.npmjs.org/@react-native/virtualized-lists/-/virtualized-lists-0.81.5.tgz",
"expo": "~54.0.33",
"expo-status-bar": "~3.0.9",
"react": "19.1.0",
- "react-native": "0.81.5"
+ "react-native": "0.81.5",
+ "react-native-safe-area-context": "~5.6.0"
},
"devDependencies": {
"@types/react": "~19.1.0",
--- /dev/null
+import { DashboardSummary } from "../types/DashboardSummary";
+
+export async function getDashboardSummary(): Promise<DashboardSummary> {
+ return {
+ pair: "BTC/EUR",
+ price: 42150.23,
+ strategy: "RSI_SIMPLE",
+ decision: "BUY",
+ confidence: 0.82,
+ reason: "RSI < 30, retournement haussier détecté",
+ timestamp: Date.now(),
+ };
+}
--- /dev/null
+import { View, Text, StyleSheet, ScrollView, TouchableOpacity } from "react-native";
+import { useEffect, useState } from "react";
+import { DashboardSummary, TradeDecision } from "../types/DashboardSummary";
+import { fetchDashboardSummary } from "../services/dashboardService";
+import { SafeAreaView } from "react-native-safe-area-context";
+
+export default function DashboardScreen() {
+ const [summary, setSummary] = useState<DashboardSummary | null>(null);
+
+ useEffect(() => {
+ fetchDashboardSummary().then(setSummary);
+ }, []);
+
+ if (!summary) {
+ return (
+ <View style={styles.container}>
+ <Text>Chargement du dashboard…</Text>
+ </View>
+ );
+ }
+
+ const getDecisionColor = (decision: TradeDecision) => {
+ switch (decision) {
+ case "BUY":
+ return "#16a34a";
+ case "SELL":
+ return "#dc2626";
+ default:
+ return "#ca8a04";
+ }
+ };
+
+ return (
+ <SafeAreaView style={styles.safeArea}>
+ <ScrollView contentContainerStyle={styles.container}>
+ {/* Carte Marché */}
+ <View style={styles.card}>
+ <Text style={styles.sectionTitle}>Marché</Text>
+
+ <Text style={styles.price}>{summary.pair}</Text>
+
+ <View style={styles.priceRow}>
+ <Text style={styles.value}>Prix actuel</Text>
+ <Text style={styles.valueBold}>
+ {summary.price.toFixed(2)} €
+ </Text>
+ </View>
+
+ <Text style={styles.updated}>
+ Dernière mise à jour :{" "}
+ {new Date(summary.timestamp).toLocaleString()}
+ </Text>
+ </View>
+
+ {/* Carte Stratégie */}
+ <View style={styles.card}>
+ <Text style={styles.sectionTitle}>Stratégie</Text>
+
+ <Text style={styles.valueBold}>{summary.strategy}</Text>
+
+ <Text
+ style={[
+ styles.decision,
+ { color: getDecisionColor(summary.decision) },
+ ]}
+ >
+ {summary.decision}
+ </Text>
+
+ <View style={styles.priceRow}>
+ <Text style={styles.value}>Confiance</Text>
+ <Text style={styles.valueBold}>
+ {(summary.confidence * 100).toFixed(1)} %
+ </Text>
+ </View>
+
+ <Text style={styles.reason}>{summary.reason}</Text>
+ </View>
+
+ {/* Carte Portefeuille (Step 1 simplifié) */}
+ <View style={styles.card}>
+ <Text style={styles.sectionTitle}>Portefeuille</Text>
+
+ <View style={styles.priceRow}>
+ <Text style={styles.value}>Valeur totale</Text>
+ <Text style={styles.valueBold}>10 000 €</Text>
+ </View>
+
+ <Text style={styles.updated}>
+ Step 1 : mono-utilisateur / mono-crypto
+ </Text>
+ </View>
+
+ {/* Carte Actions */}
+ <View style={styles.card}>
+ <Text style={styles.sectionTitle}>Actions</Text>
+
+ <View style={styles.row}>
+ <TouchableOpacity style={styles.button}>
+ <Text style={styles.buttonText}>Voir stratégie</Text>
+ </TouchableOpacity>
+
+ <TouchableOpacity style={styles.button}>
+ <Text style={styles.buttonText}>Historique</Text>
+ </TouchableOpacity>
+ </View>
+ </View>
+ </ScrollView>
+ </SafeAreaView>
+ );
+}
+
+/* ===================== STYLES ===================== */
+
+const styles = StyleSheet.create({
+ container: {
+ padding: 16,
+ },
+
+ card: {
+ borderWidth: 1,
+ borderColor: "#aaa",
+ padding: 12,
+ marginBottom: 12,
+ borderRadius: 6,
+ },
+
+ sectionTitle: {
+ fontWeight: "bold",
+ marginBottom: 6,
+ fontSize: 16,
+ },
+
+ safeArea: {
+ flex: 1,
+ backgroundColor: "#fff",
+},
+
+ price: {
+ fontSize: 26,
+ fontWeight: "bold",
+ textAlign: "center",
+ marginVertical: 6,
+ },
+
+ decision: {
+ fontSize: 32,
+ fontWeight: "bold",
+ textAlign: "center",
+ marginVertical: 10,
+ },
+
+ row: {
+ flexDirection: "row",
+ justifyContent: "space-between",
+ alignItems: "center",
+ },
+
+ priceRow: {
+ flexDirection: "row",
+ justifyContent: "space-between",
+ alignItems: "center",
+ marginVertical: 4,
+ },
+
+ value: {
+ fontSize: 16,
+ },
+
+ valueBold: {
+ fontSize: 16,
+ fontWeight: "bold",
+ },
+
+ reason: {
+ marginTop: 6,
+ fontStyle: "italic",
+ },
+
+ button: {
+ backgroundColor: "#555",
+ paddingHorizontal: 10,
+ paddingVertical: 6,
+ borderRadius: 4,
+ },
+
+ buttonText: {
+ color: "#fff",
+ fontWeight: "bold",
+ },
+
+ updated: {
+ fontSize: 12,
+ opacity: 0.6,
+ marginTop: 6,
+ textAlign: "center",
+ },
+});
\ No newline at end of file
--- /dev/null
+import { DashboardSummary } from "../types/DashboardSummary";
+import { getDashboardSummary } from "./mockApi";
+
+export async function fetchDashboardSummary(): Promise<DashboardSummary> {
+ return await getDashboardSummary();
+}
--- /dev/null
+import { DashboardSummary } from "../types/DashboardSummary";
+
+export const dashboardMock: DashboardSummary = {
+ pair: "BTC/EUR",
+ price: 42150.23,
+ strategy: "RSI_SIMPLE",
+ decision: "BUY",
+ confidence: 0.82,
+ reason: "RSI inférieur à 30 avec retournement haussier",
+ timestamp: Date.now(),
+};
+
+export const getDashboardSummary = async (): Promise<DashboardSummary> => {
+ // simulation d'appel API
+ return new Promise((resolve) => {
+ setTimeout(() => resolve(dashboardMock), 300);
+ });
+};
--- /dev/null
+export type TradeDecision = "BUY" | "SELL" | "HOLD";
+
+export interface DashboardSummary {
+ pair: string; // ex: "BTC/EUR"
+ price: number; // dernier close_price
+ strategy: string; // user_strategies.strategy_key
+ decision: TradeDecision;
+ confidence: number; // signals.confidence (0..1)
+ reason: string; // signals.reason
+ timestamp: number; // signals.timestamp_ms
+}
--- /dev/null
+import AsyncStorage from "@react-native-async-storage/async-storage";
+import type { UserSettings } from "../models/UserSettings";
+
+const KEY = "userSettings";
+
+const DEFAULT_SETTINGS: UserSettings = {
+ currency: "EUR",
+ favoriteSymbol: "BTC",
+ alertPreference: "critical",
+ refreshMode: "manual",
+};
+
+export async function loadSettings(): Promise<UserSettings> {
+ const raw = await AsyncStorage.getItem(KEY);
+ if (!raw) return DEFAULT_SETTINGS;
+
+ try {
+ const parsed = JSON.parse(raw) as Partial<UserSettings>;
+ return { ...DEFAULT_SETTINGS, ...parsed };
+ } catch {
+ return DEFAULT_SETTINGS;
+ }
+}
+
+export async function saveSettings(settings: UserSettings): Promise<void> {
+ await AsyncStorage.setItem(KEY, JSON.stringify(settings));
+}