]> git.digitality.be Git - pdw25-26/commitdiff
Modification Dashboard et ajout page setting
authorThibaud Moustier <thibaudmoustier0@gmail.com>
Mon, 16 Feb 2026 10:59:50 +0000 (11:59 +0100)
committerThibaud Moustier <thibaudmoustier0@gmail.com>
Mon, 16 Feb 2026 10:59:50 +0000 (11:59 +0100)
25 files changed:
.idea/.gitignore [new file with mode: 0644]
.idea/gradle.xml [new file with mode: 0644]
.idea/kotlinc.xml [new file with mode: 0644]
.idea/misc.xml [new file with mode: 0644]
.idea/modules.xml [new file with mode: 0644]
.idea/pdw25-26.iml [new file with mode: 0644]
.idea/vcs.xml [new file with mode: 0644]
mobile-ui/App.tsx [moved from App.tsx with 67% similarity]
mobile-ui/app.json [moved from app.json with 88% similarity]
mobile-ui/index.ts [moved from index.ts with 100% similarity]
mobile-ui/package-lock.json [moved from package-lock.json with 99% similarity]
mobile-ui/package.json [moved from package.json with 85% similarity]
mobile-ui/src/mocks/dashboard.mock.ts [moved from src/mocks/dashboard.mock.ts with 66% similarity]
mobile-ui/src/models/UserSettings.ts [new file with mode: 0644]
mobile-ui/src/screens/DashboardScreen.tsx [new file with mode: 0644]
mobile-ui/src/screens/SettingsScreen.tsx [new file with mode: 0644]
mobile-ui/src/services/dashboardService.ts [new file with mode: 0644]
mobile-ui/src/services/mockApi.ts [moved from src/services/mockApi.ts with 95% similarity]
mobile-ui/src/tsconfig.json [moved from tsconfig.json with 100% similarity]
mobile-ui/src/types/DashboardSummary.ts [new file with mode: 0644]
mobile-ui/src/utils/settingsStorage.ts [moved from src/utils/settingsStorage.ts with 55% similarity]
mobile-ui/tsconfig.json [new file with mode: 0644]
src/screens/DashboardScreen.tsx [deleted file]
src/services/dashboardService.ts [deleted file]
src/types/DashboardSummary.ts [deleted file]

diff --git a/.idea/.gitignore b/.idea/.gitignore
new file mode 100644 (file)
index 0000000..ab1f416
--- /dev/null
@@ -0,0 +1,10 @@
+# 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/
diff --git a/.idea/gradle.xml b/.idea/gradle.xml
new file mode 100644 (file)
index 0000000..b2f7e44
--- /dev/null
@@ -0,0 +1,94 @@
+<?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
diff --git a/.idea/kotlinc.xml b/.idea/kotlinc.xml
new file mode 100644 (file)
index 0000000..88cf87c
--- /dev/null
@@ -0,0 +1,7 @@
+<?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
diff --git a/.idea/misc.xml b/.idea/misc.xml
new file mode 100644 (file)
index 0000000..d13baaf
--- /dev/null
@@ -0,0 +1,13 @@
+<?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
diff --git a/.idea/modules.xml b/.idea/modules.xml
new file mode 100644 (file)
index 0000000..576b273
--- /dev/null
@@ -0,0 +1,8 @@
+<?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
diff --git a/.idea/pdw25-26.iml b/.idea/pdw25-26.iml
new file mode 100644 (file)
index 0000000..d6ebd48
--- /dev/null
@@ -0,0 +1,9 @@
+<?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
diff --git a/.idea/vcs.xml b/.idea/vcs.xml
new file mode 100644 (file)
index 0000000..35eb1dd
--- /dev/null
@@ -0,0 +1,6 @@
+<?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
similarity index 67%
rename from App.tsx
rename to mobile-ui/App.tsx
index bbfe4f38c7525a1978c12ece24a2abc2ddf51e61..a4eeb87981fcca40e22a6055c3d640403a7c0b0f 100644 (file)
--- a/App.tsx
@@ -1,4 +1,5 @@
 import DashboardScreen from "./src/screens/DashboardScreen";
+import SettingsScreen from "./src/screens/SettingsScreen";
 
 export default function App() {
   return <DashboardScreen />;
similarity index 88%
rename from app.json
rename to mobile-ui/app.json
index 2fe1a756868e9bf3d483addafaf6967cef75d7c5..c5fe7c6af128a1e0b6c5cbd7507dc8b150ad088b 100644 (file)
--- a/app.json
@@ -1,7 +1,7 @@
 {
   "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",
similarity index 100%
rename from index.ts
rename to mobile-ui/index.ts
similarity index 99%
rename from package-lock.json
rename to mobile-ui/package-lock.json
index d8dffc82479c827485337f82e7460103e061f378..f065b536136f5e99310a1e429d1690e6cadb9c4b 100644 (file)
@@ -1,13 +1,14 @@
 {
-  "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",
similarity index 85%
rename from package.json
rename to mobile-ui/package.json
index 0e7b4c476baf80043d24addd96c96e682ad8c1e3..095c042d9f86a30134cdb04f06a46fc422e98e2c 100644 (file)
@@ -1,5 +1,5 @@
 {
-  "name": "conseiller-crypto-mobile",
+  "name": "wall-e-tte",
   "version": "1.0.0",
   "main": "index.ts",
   "scripts": {
@@ -9,6 +9,7 @@
     "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",
similarity index 66%
rename from src/mocks/dashboard.mock.ts
rename to mobile-ui/src/mocks/dashboard.mock.ts
index fa3d321dd32269582997d7481db010a0d45ded2d..ae3cdb1f3c6f73f921e68d19cc04045b41b1cd24 100644 (file)
@@ -1,6 +1,12 @@
 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,
@@ -8,6 +14,8 @@ export async function getDashboardSummary(): Promise<DashboardSummary> {
     decision: "BUY",
     confidence: 0.82,
     reason: "RSI < 30, retournement haussier détecté",
+    alertLevel: "WARNING",
     timestamp: Date.now(),
   };
 }
+
diff --git a/mobile-ui/src/models/UserSettings.ts b/mobile-ui/src/models/UserSettings.ts
new file mode 100644 (file)
index 0000000..c8e552e
--- /dev/null
@@ -0,0 +1,17 @@
+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;
+}
diff --git a/mobile-ui/src/screens/DashboardScreen.tsx b/mobile-ui/src/screens/DashboardScreen.tsx
new file mode 100644 (file)
index 0000000..30d380b
--- /dev/null
@@ -0,0 +1,261 @@
+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",
+  },
+});
+
diff --git a/mobile-ui/src/screens/SettingsScreen.tsx b/mobile-ui/src/screens/SettingsScreen.tsx
new file mode 100644 (file)
index 0000000..66dd3b2
--- /dev/null
@@ -0,0 +1,93 @@
+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",
+  },
+});
diff --git a/mobile-ui/src/services/dashboardService.ts b/mobile-ui/src/services/dashboardService.ts
new file mode 100644 (file)
index 0000000..ef2b300
--- /dev/null
@@ -0,0 +1,11 @@
+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;
+  }
+}
similarity index 95%
rename from src/services/mockApi.ts
rename to mobile-ui/src/services/mockApi.ts
index 3b3b889192b98804b4e24fd364315ccbbfe07375..546eb84f9394ca09855e6c25178fad083941401b 100644 (file)
@@ -8,6 +8,7 @@ export const dashboardMock: DashboardSummary = {
   confidence: 0.82,
   reason: "RSI inférieur à 30 avec retournement haussier",
   timestamp: Date.now(),
+  alertLevel: "WARNING",
 };
 
 export const getDashboardSummary = async (): Promise<DashboardSummary> => {
similarity index 100%
rename from tsconfig.json
rename to mobile-ui/src/tsconfig.json
diff --git a/mobile-ui/src/types/DashboardSummary.ts b/mobile-ui/src/types/DashboardSummary.ts
new file mode 100644 (file)
index 0000000..78af51b
--- /dev/null
@@ -0,0 +1,22 @@
+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;
+}
+
similarity index 55%
rename from src/utils/settingsStorage.ts
rename to mobile-ui/src/utils/settingsStorage.ts
index a623b960abaae5c93040b1e29e40d582ed37b7f8..4936eec8b0804e08004e12c75a6cfe925b8e50f8 100644 (file)
@@ -11,17 +11,30 @@ const DEFAULT_SETTINGS: UserSettings = {
 };
 
 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;
+  }
 }
diff --git a/mobile-ui/tsconfig.json b/mobile-ui/tsconfig.json
new file mode 100644 (file)
index 0000000..0e6371f
--- /dev/null
@@ -0,0 +1,4 @@
+{
+  "compilerOptions": {},
+  "extends": "expo/tsconfig.base"
+}
diff --git a/src/screens/DashboardScreen.tsx b/src/screens/DashboardScreen.tsx
deleted file mode 100644 (file)
index d632cfb..0000000
+++ /dev/null
@@ -1,198 +0,0 @@
-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
diff --git a/src/services/dashboardService.ts b/src/services/dashboardService.ts
deleted file mode 100644 (file)
index 3ee7bb0..0000000
+++ /dev/null
@@ -1,6 +0,0 @@
-import { DashboardSummary } from "../types/DashboardSummary";
-import { getDashboardSummary } from "./mockApi";
-
-export async function fetchDashboardSummary(): Promise<DashboardSummary> {
-  return await getDashboardSummary();
-}
diff --git a/src/types/DashboardSummary.ts b/src/types/DashboardSummary.ts
deleted file mode 100644 (file)
index 461f66b..0000000
+++ /dev/null
@@ -1,11 +0,0 @@
-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
-}