From 19005a65f743c95063d0f76e3e257dcf0d463115 Mon Sep 17 00:00:00 2001 From: Thibaud Moustier Date: Mon, 9 Feb 2026 18:11:54 +0100 Subject: [PATCH] feat(dashboard): Dashboard fonctionnel (mock, styles, services) --- App.tsx | 21 +--- package-lock.json | 14 ++- package.json | 3 +- src/mocks/dashboard.mock.ts | 13 ++ src/screens/DashboardScreen.tsx | 198 +++++++++++++++++++++++++++++++ src/services/dashboardService.ts | 6 + src/services/mockApi.ts | 18 +++ src/types/DashboardSummary.ts | 11 ++ src/utils/settingsStorage.ts | 27 +++++ 9 files changed, 291 insertions(+), 20 deletions(-) create mode 100644 src/mocks/dashboard.mock.ts create mode 100644 src/screens/DashboardScreen.tsx create mode 100644 src/services/dashboardService.ts create mode 100644 src/services/mockApi.ts create mode 100644 src/types/DashboardSummary.ts create mode 100644 src/utils/settingsStorage.ts diff --git a/App.tsx b/App.tsx index 0329d0c..bbfe4f3 100644 --- a/App.tsx +++ b/App.tsx @@ -1,20 +1,5 @@ -import { StatusBar } from 'expo-status-bar'; -import { StyleSheet, Text, View } from 'react-native'; +import DashboardScreen from "./src/screens/DashboardScreen"; export default function App() { - return ( - - Open up App.tsx to start working on your app! - - - ); -} - -const styles = StyleSheet.create({ - container: { - flex: 1, - backgroundColor: '#fff', - alignItems: 'center', - justifyContent: 'center', - }, -}); + return ; +} \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index 45dcfaf..d8dffc8 100644 --- a/package-lock.json +++ b/package-lock.json @@ -11,7 +11,8 @@ "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", @@ -4267,6 +4268,7 @@ "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", @@ -7313,6 +7315,16 @@ "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", diff --git a/package.json b/package.json index ddcfc76..0e7b4c4 100644 --- a/package.json +++ b/package.json @@ -12,7 +12,8 @@ "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", diff --git a/src/mocks/dashboard.mock.ts b/src/mocks/dashboard.mock.ts new file mode 100644 index 0000000..fa3d321 --- /dev/null +++ b/src/mocks/dashboard.mock.ts @@ -0,0 +1,13 @@ +import { DashboardSummary } from "../types/DashboardSummary"; + +export async function getDashboardSummary(): Promise { + 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(), + }; +} diff --git a/src/screens/DashboardScreen.tsx b/src/screens/DashboardScreen.tsx new file mode 100644 index 0000000..d632cfb --- /dev/null +++ b/src/screens/DashboardScreen.tsx @@ -0,0 +1,198 @@ +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(null); + + useEffect(() => { + fetchDashboardSummary().then(setSummary); + }, []); + + if (!summary) { + return ( + + Chargement du dashboard… + + ); + } + + const getDecisionColor = (decision: TradeDecision) => { + switch (decision) { + case "BUY": + return "#16a34a"; + case "SELL": + return "#dc2626"; + default: + return "#ca8a04"; + } + }; + + return ( + + + {/* Carte Marché */} + + Marché + + {summary.pair} + + + Prix actuel + + {summary.price.toFixed(2)} € + + + + + Dernière mise à jour :{" "} + {new Date(summary.timestamp).toLocaleString()} + + + + {/* Carte Stratégie */} + + Stratégie + + {summary.strategy} + + + {summary.decision} + + + + Confiance + + {(summary.confidence * 100).toFixed(1)} % + + + + {summary.reason} + + + {/* Carte Portefeuille (Step 1 simplifié) */} + + Portefeuille + + + Valeur totale + 10 000 € + + + + Step 1 : mono-utilisateur / mono-crypto + + + + {/* Carte Actions */} + + Actions + + + + Voir stratégie + + + + Historique + + + + + + ); +} + +/* ===================== 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 diff --git a/src/services/dashboardService.ts b/src/services/dashboardService.ts new file mode 100644 index 0000000..3ee7bb0 --- /dev/null +++ b/src/services/dashboardService.ts @@ -0,0 +1,6 @@ +import { DashboardSummary } from "../types/DashboardSummary"; +import { getDashboardSummary } from "./mockApi"; + +export async function fetchDashboardSummary(): Promise { + return await getDashboardSummary(); +} diff --git a/src/services/mockApi.ts b/src/services/mockApi.ts new file mode 100644 index 0000000..3b3b889 --- /dev/null +++ b/src/services/mockApi.ts @@ -0,0 +1,18 @@ +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 => { + // simulation d'appel API + return new Promise((resolve) => { + setTimeout(() => resolve(dashboardMock), 300); + }); +}; diff --git a/src/types/DashboardSummary.ts b/src/types/DashboardSummary.ts new file mode 100644 index 0000000..461f66b --- /dev/null +++ b/src/types/DashboardSummary.ts @@ -0,0 +1,11 @@ +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 +} diff --git a/src/utils/settingsStorage.ts b/src/utils/settingsStorage.ts new file mode 100644 index 0000000..a623b96 --- /dev/null +++ b/src/utils/settingsStorage.ts @@ -0,0 +1,27 @@ +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 { + const raw = await AsyncStorage.getItem(KEY); + if (!raw) return DEFAULT_SETTINGS; + + try { + const parsed = JSON.parse(raw) as Partial; + return { ...DEFAULT_SETTINGS, ...parsed }; + } catch { + return DEFAULT_SETTINGS; + } +} + +export async function saveSettings(settings: UserSettings): Promise { + await AsyncStorage.setItem(KEY, JSON.stringify(settings)); +} -- 2.50.1