--- /dev/null
+# Default ignored files
+/shelf/
+/workspace.xml
+# Ignored default folder with query files
+/queries/
+# Datasource local storage ignored files
+/dataSources/
+/dataSources.local.xml
+# Editor-based HTTP Client requests
+/httpRequests/
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+<project version="4">
+ <component name="GradleSettings">
+ <option name="linkedExternalProjectsSettings">
+ <GradleProjectSettings>
+ <option name="externalProjectPath" value="$PROJECT_DIR$/node_modules/@react-native/gradle-plugin" />
+ <option name="gradleJvm" value="21" />
+ <option name="modules">
+ <set>
+ <option value="$PROJECT_DIR$/node_modules/@react-native/gradle-plugin" />
+ <option value="$PROJECT_DIR$/node_modules/@react-native/gradle-plugin/react-native-gradle-plugin" />
+ <option value="$PROJECT_DIR$/node_modules/@react-native/gradle-plugin/settings-plugin" />
+ <option value="$PROJECT_DIR$/node_modules/@react-native/gradle-plugin/shared" />
+ <option value="$PROJECT_DIR$/node_modules/@react-native/gradle-plugin/shared-testutil" />
+ </set>
+ </option>
+ </GradleProjectSettings>
+ <GradleProjectSettings>
+ <option name="externalProjectPath" value="$PROJECT_DIR$/node_modules/expo-modules-autolinking/android/expo-gradle-plugin" />
+ <option name="gradleJvm" value="21" />
+ <option name="modules">
+ <set>
+ <option value="$PROJECT_DIR$/node_modules/expo-modules-autolinking/android/expo-gradle-plugin" />
+ <option value="$PROJECT_DIR$/node_modules/expo-modules-autolinking/android/expo-gradle-plugin/expo-autolinking-plugin" />
+ <option value="$PROJECT_DIR$/node_modules/expo-modules-autolinking/android/expo-gradle-plugin/expo-autolinking-plugin-shared" />
+ <option value="$PROJECT_DIR$/node_modules/expo-modules-autolinking/android/expo-gradle-plugin/expo-autolinking-settings-plugin" />
+ </set>
+ </option>
+ </GradleProjectSettings>
+ <GradleProjectSettings>
+ <option name="externalProjectPath" value="$PROJECT_DIR$/node_modules/expo-modules-autolinking/scripts/android" />
+ <option name="gradleJvm" value="21" />
+ </GradleProjectSettings>
+ <GradleProjectSettings>
+ <option name="externalProjectPath" value="$PROJECT_DIR$/node_modules/expo-modules-core/android" />
+ <option name="gradleJvm" value="21" />
+ </GradleProjectSettings>
+ <GradleProjectSettings>
+ <option name="externalProjectPath" value="$PROJECT_DIR$/node_modules/expo-modules-core/expo-module-gradle-plugin" />
+ <option name="gradleJvm" value="21" />
+ <option name="modules">
+ <set>
+ <option value="$PROJECT_DIR$/node_modules/expo-modules-core/expo-module-gradle-plugin" />
+ </set>
+ </option>
+ </GradleProjectSettings>
+ <GradleProjectSettings>
+ <option name="externalProjectPath" value="$PROJECT_DIR$/node_modules/expo/android" />
+ <option name="gradleJvm" value="21" />
+ </GradleProjectSettings>
+ <GradleProjectSettings>
+ <option name="externalProjectPath" value="$PROJECT_DIR$/node_modules/expo/node_modules/expo-asset/android" />
+ <option name="gradleJvm" value="21" />
+ </GradleProjectSettings>
+ <GradleProjectSettings>
+ <option name="externalProjectPath" value="$PROJECT_DIR$/node_modules/expo/node_modules/expo-constants/android" />
+ <option name="gradleJvm" value="21" />
+ </GradleProjectSettings>
+ <GradleProjectSettings>
+ <option name="externalProjectPath" value="$PROJECT_DIR$/node_modules/expo/node_modules/expo-constants/scripts" />
+ <option name="gradleJvm" value="21" />
+ </GradleProjectSettings>
+ <GradleProjectSettings>
+ <option name="externalProjectPath" value="$PROJECT_DIR$/node_modules/expo/node_modules/expo-file-system/android" />
+ <option name="gradleJvm" value="21" />
+ </GradleProjectSettings>
+ <GradleProjectSettings>
+ <option name="externalProjectPath" value="$PROJECT_DIR$/node_modules/expo/node_modules/expo-font/android" />
+ <option name="gradleJvm" value="21" />
+ </GradleProjectSettings>
+ <GradleProjectSettings>
+ <option name="externalProjectPath" value="$PROJECT_DIR$/node_modules/expo/node_modules/expo-keep-awake/android" />
+ <option name="gradleJvm" value="21" />
+ </GradleProjectSettings>
+ <GradleProjectSettings>
+ <option name="externalProjectPath" value="$PROJECT_DIR$/node_modules/expo/scripts" />
+ <option name="gradleJvm" value="21" />
+ </GradleProjectSettings>
+ <GradleProjectSettings>
+ <option name="externalProjectPath" value="$PROJECT_DIR$/node_modules/react-native" />
+ <option name="gradleJvm" value="21" />
+ </GradleProjectSettings>
+ <GradleProjectSettings>
+ <option name="externalProjectPath" value="$PROJECT_DIR$/node_modules/react-native-safe-area-context/android" />
+ <option name="gradleJvm" value="21" />
+ <option name="modules">
+ <set>
+ <option value="$PROJECT_DIR$/node_modules/react-native-safe-area-context/android" />
+ </set>
+ </option>
+ </GradleProjectSettings>
+ </option>
+ </component>
+</project>
\ No newline at end of file
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+<project version="4">
+ <component name="KotlinJpsPluginSettings">
+ <option name="externalSystemId" value="Gradle" />
+ <option name="version" value="2.1.20" />
+ </component>
+</project>
\ No newline at end of file
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+<project version="4">
+ <component name="ExternalStorageConfigurationManager" enabled="true" />
+ <component name="FrameworkDetectionExcludesConfiguration">
+ <file type="web" url="file://$PROJECT_DIR$/node_modules/@react-native/gradle-plugin" />
+ <file type="web" url="file://$PROJECT_DIR$/node_modules/expo-modules-autolinking/android/expo-gradle-plugin" />
+ <file type="web" url="file://$PROJECT_DIR$/node_modules/expo-modules-core/expo-module-gradle-plugin" />
+ <file type="web" url="file://$PROJECT_DIR$/node_modules/react-native-safe-area-context/android" />
+ </component>
+ <component name="ProjectRootManager" version="2">
+ <output url="file://$PROJECT_DIR$/out" />
+ </component>
+</project>
\ No newline at end of file
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+<project version="4">
+ <component name="ProjectModuleManager">
+ <modules>
+ <module fileurl="file://$PROJECT_DIR$/.idea/pdw25-26.iml" filepath="$PROJECT_DIR$/.idea/pdw25-26.iml" />
+ </modules>
+ </component>
+</project>
\ No newline at end of file
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+<module type="JAVA_MODULE" version="4">
+ <component name="NewModuleRootManager" inherit-compiler-output="true">
+ <exclude-output />
+ <content url="file://$MODULE_DIR$" />
+ <orderEntry type="inheritedJdk" />
+ <orderEntry type="sourceFolder" forTests="false" />
+ </component>
+</module>
\ No newline at end of file
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+<project version="4">
+ <component name="VcsDirectoryMappings">
+ <mapping directory="" vcs="Git" />
+ </component>
+</project>
\ No newline at end of file
import DashboardScreen from "./src/screens/DashboardScreen";
+import SettingsScreen from "./src/screens/SettingsScreen";
export default function App() {
return <DashboardScreen />;
{
"expo": {
- "name": "conseiller-crypto-mobile",
- "slug": "conseiller-crypto-mobile",
+ "name": "wall-e-tte",
+ "slug": "wall-e-tte",
"version": "1.0.0",
"orientation": "portrait",
"icon": "./assets/icon.png",
{
- "name": "conseiller-crypto-mobile",
+ "name": "wall-e-tte",
"version": "1.0.0",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
- "name": "conseiller-crypto-mobile",
+ "name": "wall-e-tte",
"version": "1.0.0",
"dependencies": {
+ "@react-native-async-storage/async-storage": "2.2.0",
"expo": "~54.0.33",
"expo-status-bar": "~3.0.9",
"react": "19.1.0",
"@jridgewell/sourcemap-codec": "^1.4.14"
}
},
+ "node_modules/@react-native-async-storage/async-storage": {
+ "version": "2.2.0",
+ "resolved": "https://registry.npmjs.org/@react-native-async-storage/async-storage/-/async-storage-2.2.0.tgz",
+ "integrity": "sha512-gvRvjR5JAaUZF8tv2Kcq/Gbt3JHwbKFYfmb445rhOj6NUMx3qPLixmDx5pZAyb9at1bYvJ4/eTUipU5aki45xw==",
+ "license": "MIT",
+ "dependencies": {
+ "merge-options": "^3.0.4"
+ },
+ "peerDependencies": {
+ "react-native": "^0.0.0-0 || >=0.65 <1.0"
+ }
+ },
"node_modules/@react-native/assets-registry": {
"version": "0.81.5",
"resolved": "https://registry.npmjs.org/@react-native/assets-registry/-/assets-registry-0.81.5.tgz",
"node": ">=0.12.0"
}
},
+ "node_modules/is-plain-obj": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-2.1.0.tgz",
+ "integrity": "sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ }
+ },
"node_modules/is-wsl": {
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-2.2.0.tgz",
"integrity": "sha512-zYiwtZUcYyXKo/np96AGZAckk+FWWsUdJ3cHGGmld7+AhvcWmQyGCYUh1hc4Q/pkOhb65dQR/pqCyK0cOaHz4Q==",
"license": "MIT"
},
+ "node_modules/merge-options": {
+ "version": "3.0.4",
+ "resolved": "https://registry.npmjs.org/merge-options/-/merge-options-3.0.4.tgz",
+ "integrity": "sha512-2Sug1+knBjkaMsMgf1ctR1Ujx+Ayku4EdJN4Z+C2+JzoeF7A3OZ9KM2GY0CpQS51NR61LTurMJrRKPhSs3ZRTQ==",
+ "license": "MIT",
+ "dependencies": {
+ "is-plain-obj": "^2.1.0"
+ },
+ "engines": {
+ "node": ">=10"
+ }
+ },
"node_modules/merge-stream": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz",
"resolved": "https://registry.npmjs.org/react-native/-/react-native-0.81.5.tgz",
"integrity": "sha512-1w+/oSjEXZjMqsIvmkCRsOc8UBYv163bTWKTI8+1mxztvQPhCRYGTvZ/PL1w16xXHneIj/SLGfxWg2GWN2uexw==",
"license": "MIT",
+ "peer": true,
"dependencies": {
"@jest/create-cache-key-function": "^29.7.0",
"@react-native/assets-registry": "0.81.5",
{
- "name": "conseiller-crypto-mobile",
+ "name": "wall-e-tte",
"version": "1.0.0",
"main": "index.ts",
"scripts": {
"web": "expo start --web"
},
"dependencies": {
+ "@react-native-async-storage/async-storage": "2.2.0",
"expo": "~54.0.33",
"expo-status-bar": "~3.0.9",
"react": "19.1.0",
import { DashboardSummary } from "../types/DashboardSummary";
+function delay(ms: number): Promise<void> {
+ return new Promise((resolve) => setTimeout(resolve, ms));
+}
+
export async function getDashboardSummary(): Promise<DashboardSummary> {
+ await delay(500); // simulation réseau
+
return {
pair: "BTC/EUR",
price: 42150.23,
decision: "BUY",
confidence: 0.82,
reason: "RSI < 30, retournement haussier détecté",
+ alertLevel: "WARNING",
timestamp: Date.now(),
};
}
+
--- /dev/null
+export type Currency = "EUR" | "USD";
+
+export type AlertPreference =
+ | "critical"
+ | "warning"
+ | "all";
+
+export type RefreshMode =
+ | "manual"
+ | "auto";
+
+export interface UserSettings {
+ currency: Currency;
+ favoriteSymbol: string;
+ alertPreference: AlertPreference;
+ refreshMode: RefreshMode;
+}
--- /dev/null
+import { View, Text, StyleSheet, ScrollView, TouchableOpacity } from "react-native";
+import { useEffect, useState } from "react";
+import { DashboardSummary, TradeDecision, AlertLevel } from "../types/DashboardSummary";
+import { fetchDashboardSummary } from "../services/dashboardService";
+import { SafeAreaView } from "react-native-safe-area-context";
+import { loadSettings } from "../utils/settingsStorage";
+import { UserSettings } from "../models/UserSettings";
+
+export default function DashboardScreen() {
+ const [summary, setSummary] = useState<DashboardSummary | null>(null);
+ const [loading, setLoading] = useState<boolean>(true);
+ const [error, setError] = useState<string | null>(null);
+ const [settings, setSettings] = useState<UserSettings | null>(null);
+
+useEffect(() => {
+ async function loadData() {
+ try {
+ const [dashboardData, userSettings] = await Promise.all([
+ fetchDashboardSummary(),
+ loadSettings(),
+ ]);
+
+ setSummary(dashboardData);
+ setSettings(userSettings);
+ } catch (err) {
+ setError("Impossible de charger le dashboard.");
+ } finally {
+ setLoading(false);
+ }
+ }
+
+ loadData();
+}, []);
+
+ if (loading) {
+ return (
+ <View style={styles.container}>
+ <Text>Chargement du dashboard…</Text>
+ </View>
+ );
+ }
+
+ if (error) {
+ return (
+ <View style={styles.container}>
+ <Text style={{ color: "red" }}>{error}</Text>
+ </View>
+ );
+ }
+
+if (!summary || !settings) {
+ return (
+ <View style={styles.container}>
+ <Text>Initialisation…</Text>
+ </View>
+ );
+}
+
+ const getDecisionColor = (decision: TradeDecision) => {
+ switch (decision) {
+ case "BUY":
+ return "#16a34a";
+ case "SELL":
+ return "#dc2626";
+ case "STOP_LOSS":
+ return "#991b1b";
+ default:
+ return "#ca8a04";
+ }
+ };
+
+ const getAlertColor = (level: AlertLevel) => {
+ switch (level) {
+ case "CRITICAL":
+ return "#b91c1c";
+ case "WARNING":
+ return "#ca8a04";
+ case "INFO":
+ return "#2563eb";
+ default:
+ return "#6b7280";
+ }
+ };
+
+ 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>
+
+ <Text
+ style={{
+ textAlign: "center",
+ fontWeight: "bold",
+ color: getAlertColor(summary.alertLevel),
+ marginBottom: 6,
+ }}
+ >
+ {summary.alertLevel}
+ </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 */}
+ <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>
+ );
+}
+
+const styles = StyleSheet.create({
+ container: {
+ padding: 16,
+ },
+
+ safeArea: {
+ flex: 1,
+ backgroundColor: "#fff",
+ },
+
+ card: {
+ borderWidth: 1,
+ borderColor: "#aaa",
+ padding: 12,
+ marginBottom: 12,
+ borderRadius: 6,
+ },
+
+ sectionTitle: {
+ fontWeight: "bold",
+ marginBottom: 6,
+ fontSize: 16,
+ },
+
+ 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",
+ },
+});
+
--- /dev/null
+import { View, Text, StyleSheet, TouchableOpacity } from "react-native";
+import { useEffect, useState } from "react";
+import { loadSettings, saveSettings } from "../utils/settingsStorage";
+import { UserSettings } from "../models/UserSettings";
+import { SafeAreaView } from "react-native-safe-area-context";
+
+export default function SettingsScreen() {
+ const [settings, setSettings] = useState<UserSettings | null>(null);
+
+ useEffect(() => {
+ async function init() {
+ const data = await loadSettings();
+ setSettings(data);
+ }
+ init();
+ }, []);
+
+ if (!settings) {
+ return (
+ <View style={styles.container}>
+ <Text>Chargement des paramètres…</Text>
+ </View>
+ );
+ }
+
+ const toggleCurrency = () => {
+ const newCurrency = settings.currency === "EUR" ? "USD" : "EUR";
+ setSettings({ ...settings, currency: newCurrency });
+ };
+
+ const toggleRefreshMode = () => {
+ const newMode = settings.refreshMode === "manual" ? "auto" : "manual";
+ setSettings({ ...settings, refreshMode: newMode });
+ };
+
+ const handleSave = async () => {
+ await saveSettings(settings);
+ };
+
+ return (
+ <SafeAreaView style={styles.safeArea}>
+ <View style={styles.container}>
+ <Text style={styles.title}>Paramètres</Text>
+
+ <Text>Devise : {settings.currency}</Text>
+ <TouchableOpacity style={styles.button} onPress={toggleCurrency}>
+ <Text style={styles.buttonText}>Changer devise</Text>
+ </TouchableOpacity>
+
+ <Text>Mode rafraîchissement : {settings.refreshMode}</Text>
+ <TouchableOpacity style={styles.button} onPress={toggleRefreshMode}>
+ <Text style={styles.buttonText}>Changer mode</Text>
+ </TouchableOpacity>
+
+ <TouchableOpacity style={styles.saveButton} onPress={handleSave}>
+ <Text style={styles.buttonText}>Sauvegarder</Text>
+ </TouchableOpacity>
+ </View>
+ </SafeAreaView>
+ );
+}
+
+const styles = StyleSheet.create({
+ safeArea: {
+ flex: 1,
+ backgroundColor: "#fff",
+ },
+ container: {
+ padding: 16,
+ },
+ title: {
+ fontSize: 22,
+ fontWeight: "bold",
+ marginBottom: 16,
+ },
+ button: {
+ backgroundColor: "#555",
+ padding: 10,
+ marginVertical: 6,
+ borderRadius: 4,
+ },
+ saveButton: {
+ backgroundColor: "#2563eb",
+ padding: 12,
+ marginTop: 20,
+ borderRadius: 6,
+ },
+ buttonText: {
+ color: "#fff",
+ fontWeight: "bold",
+ textAlign: "center",
+ },
+});
--- /dev/null
+import { DashboardSummary } from "../types/DashboardSummary";
+import { getDashboardSummary } from "../mocks/dashboard.mock";
+
+export async function fetchDashboardSummary(): Promise<DashboardSummary> {
+ try {
+ return await getDashboardSummary();
+ } catch (error) {
+ console.error("Erreur lors du chargement du dashboard:", error);
+ throw error;
+ }
+}
confidence: 0.82,
reason: "RSI inférieur à 30 avec retournement haussier",
timestamp: Date.now(),
+ alertLevel: "WARNING",
};
export const getDashboardSummary = async (): Promise<DashboardSummary> => {
--- /dev/null
+export type TradeDecision =
+ | "BUY"
+ | "SELL"
+ | "HOLD"
+ | "STOP_LOSS";
+
+export type AlertLevel =
+ | "CRITICAL"
+ | "WARNING"
+ | "INFO";
+
+export interface DashboardSummary {
+ pair: string;
+ price: number;
+ strategy: string;
+ decision: TradeDecision;
+ confidence: number;
+ reason: string;
+ alertLevel: AlertLevel;
+ timestamp: number;
+}
+
};
export async function loadSettings(): Promise<UserSettings> {
- const raw = await AsyncStorage.getItem(KEY);
- if (!raw) return DEFAULT_SETTINGS;
-
try {
+ const raw = await AsyncStorage.getItem(KEY);
+
+ if (!raw) {
+ return DEFAULT_SETTINGS;
+ }
+
const parsed = JSON.parse(raw) as Partial<UserSettings>;
- return { ...DEFAULT_SETTINGS, ...parsed };
- } catch {
+
+ return {
+ ...DEFAULT_SETTINGS,
+ ...parsed,
+ };
+ } catch (error) {
+ console.error("Erreur lors du chargement des settings :", error);
return DEFAULT_SETTINGS;
}
}
export async function saveSettings(settings: UserSettings): Promise<void> {
- await AsyncStorage.setItem(KEY, JSON.stringify(settings));
+ try {
+ await AsyncStorage.setItem(KEY, JSON.stringify(settings));
+ } catch (error) {
+ console.error("Erreur lors de la sauvegarde des settings :", error);
+ throw error;
+ }
}
--- /dev/null
+{
+ "compilerOptions": {},
+ "extends": "expo/tsconfig.base"
+}
+++ /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
-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
-}