import AsyncStorage from "@react-native-async-storage/async-storage"; import { LinearGradient } from "expo-linear-gradient"; import { StatusBar } from "expo-status-bar"; import React, { useEffect, useMemo, useState } from "react"; import { ActivityIndicator, Alert, Pressable, SafeAreaView, ScrollView, Share, StyleSheet, Switch, Text, TextInput, View, useWindowDimensions, } from "react-native"; import { Activity, Apple, Bell, Bot, Camera, Check, ChevronRight, CircleUserRound, Download, Droplets, Footprints, HeartPulse, Home, Lightbulb, Minus, Moon, Plus, Scale, Send, ShieldCheck, Sparkles, Sprout, TrendingUp, UserRound, Utensils, } from "lucide-react-native"; type LucideIcon = any; type Tab = "today" | "journal" | "progress" | "coach" | "profile"; type Entry = { date: string; iso?: string; weight: number; water: number; steps: number; glucose?: number; sleep?: number; meals?: number; mood?: string; }; type ProfileData = { name: string; goal: string; journey: string; glucoseUnit: "mg/dL" | "mmol/L"; reminders: boolean; complete: boolean; }; type ChatMessage = { id: string; role: "user" | "assistant"; content: string }; const C = { ink: "#15211c", muted: "#637169", cream: "#f6f7f2", white: "#fff", green: "#1d6b50", mint: "#dcefe5", coral: "#ef835f", yellow: "#f4d58d", line: "#e5e9e3", }; const seed: Entry[] = [ { date: "Lun", weight: 91.4, water: 1500, steps: 6100, glucose: 112 }, { date: "Mar", weight: 91.2, water: 1800, steps: 7400, glucose: 108 }, { date: "Mer", weight: 91.1, water: 1600, steps: 6900, glucose: 110 }, { date: "Jeu", weight: 90.9, water: 2000, steps: 8200, glucose: 105 }, { date: "Ven", weight: 90.8, water: 2100, steps: 8600, glucose: 104 }, { date: "Sam", weight: 90.7, water: 1750, steps: 7200, glucose: 107 }, { date: "Auj.", weight: 90.6, water: 1250, steps: 6240, glucose: 103 }, ]; function Card({ children, style }: any) { return {children}; } function Pill({ children, tone = "green" }: any) { return ( {children} ); } function IconTile({ icon: Icon, color = C.green, bg = C.mint, size = 22, }: any) { return ( ); } function Metric({ icon, label, value, sub, onPress }: any) { const content = ( <> {label} {value} {sub} ); return onPress ? ( [s.metric, pressed && s.pressed]} > {content} ) : ( {content} ); } function ProgressBar({ value, color = C.green }: any) { return ( ); } function Nav({ tab, setTab }: any) { const items: any[] = [ ["today", Home, "Aujourd’hui"], ["journal", Plus, "Journal"], ["progress", TrendingUp, "Progrès"], ["coach", Sparkles, "Coach"], ["profile", CircleUserRound, "Profil"], ]; return ( {items.map(([id, Icon, label]) => { const active = tab === id; return ( setTab(id)} style={({ pressed }) => [ s.navItem, active && s.navItemActive, pressed && s.pressed, ]} > {label} ); })} ); } export default function App() { const [tab, setTab] = useState("today"); const [entries, setEntries] = useState(seed); const [water, setWater] = useState(1250); const [steps, setSteps] = useState(6240); const [weight, setWeight] = useState("90.6"); const [glucose, setGlucose] = useState("103"); const [sleep, setSleep] = useState("7.2"); const [meals, setMeals] = useState(2); const [mood, setMood] = useState("🙂"); const [saved, setSaved] = useState(false); const [loaded, setLoaded] = useState(false); const [profile, setProfile] = useState({ name: "", goal: "Retrouver mon équilibre", journey: "Prévention", glucoseUnit: "mg/dL", reminders: false, complete: false, }); const { width } = useWindowDimensions(); useEffect(() => { AsyncStorage.getItem("metabloom-v2") .then((v) => { if (v) { const d = JSON.parse(v); setEntries( Array.isArray(d.entries) && d.entries.length ? d.entries : seed, ); setWater(typeof d.water === "number" ? d.water : 1250); setSteps(typeof d.steps === "number" ? d.steps : 6240); setProfile(d.profile || profile); } }) .catch(() => {}) .finally(() => setLoaded(true)); }, []); useEffect(() => { if (loaded) AsyncStorage.setItem( "metabloom-v2", JSON.stringify({ entries, water, steps, profile }), ).catch(() => {}); }, [entries, water, steps, profile, loaded]); const score = Math.round( (Math.min(water / 2000, 1) + Math.min(steps / 8000, 1) + 0.82 + 0.75) * 25, ); const save = () => { const w = parseFloat(weight.replace(",", ".")); const g = parseFloat(glucose.replace(",", ".")); const sl = parseFloat(sleep.replace(",", ".")); if (!Number.isFinite(w) || w < 30 || w > 300) { Alert.alert( "Valeur à vérifier", "Saisis un poids compris entre 30 et 300 kg.", ); return; } const glucoseBounds = profile.glucoseUnit === "mmol/L" ? [1.1, 33.3] : [20, 600]; if ( glucose.trim() && (!Number.isFinite(g) || g < glucoseBounds[0] || g > glucoseBounds[1]) ) { Alert.alert( "Valeur à vérifier", "La glycémie saisie semble hors de la plage de saisie autorisée. Vérifie l’unité et la valeur.", ); return; } if (!Number.isFinite(sl) || sl < 0 || sl > 24) { Alert.alert( "Valeur à vérifier", "Le sommeil doit être compris entre 0 et 24 heures.", ); return; } const now = new Date(); setEntries((x) => [ ...x.slice(-29), { date: "Auj.", iso: now.toISOString(), weight: w, water, steps, glucose: Number.isFinite(g) ? g : undefined, sleep: sl, meals, mood, }, ]); setSaved(true); setTimeout(() => setSaved(false), 2200); }; const reset = () => { setEntries(seed); setWater(1250); setSteps(6240); setProfile({ ...profile, complete: false }); AsyncStorage.removeItem("metabloom-v2"); }; const content = useMemo( () => tab === "today" ? ( ) : tab === "journal" ? ( ) : tab === "progress" ? ( ) : tab === "coach" ? ( ) : ( ), [ tab, score, water, steps, weight, glucose, sleep, meals, mood, saved, entries, profile, ], ); if (!loaded) return ; if (!profile.complete) return ; return ( 900 ? 1120 : 680 }]}> {content}