From ea8b5be05856bde973b79fb4923f22cf6ef5ce1d Mon Sep 17 00:00:00 2001 From: AI Company Date: Tue, 21 Jul 2026 19:24:59 +0000 Subject: [PATCH] feat: evolve MetaBloom into full product foundation --- .gitea/workflows/ci.yml | 28 +++++ App.tsx | 46 +++++--- README.md | 110 +++++++++++------- app.json | 5 +- screenshots/01-aujourdhui.png | Bin 144735 -> 0 bytes screenshots/01-onboarding.png | Bin 0 -> 111780 bytes screenshots/02-journal.png | Bin 101399 -> 0 bytes screenshots/02-parcours.png | Bin 0 -> 147735 bytes screenshots/03-aujourdhui.png | Bin 0 -> 159031 bytes screenshots/03-progres.png | Bin 111299 -> 0 bytes screenshots/04-journal.png | Bin 0 -> 113824 bytes screenshots/04-profil.png | Bin 130575 -> 0 bytes screenshots/05-progres.png | Bin 0 -> 116240 bytes screenshots/06-coach.png | Bin 0 -> 167878 bytes screenshots/07-profil.png | Bin 0 -> 151048 bytes server/.dockerignore | 7 ++ server/.env.example | 6 + server/Dockerfile | 10 ++ server/README.md | 51 ++++++++ server/app/__init__.py | 1 + .../app/__pycache__/__init__.cpython-311.pyc | Bin 0 -> 176 bytes server/app/__pycache__/config.cpython-311.pyc | Bin 0 -> 1963 bytes server/app/__pycache__/db.cpython-311.pyc | Bin 0 -> 1354 bytes server/app/__pycache__/main.cpython-311.pyc | Bin 0 -> 11473 bytes server/app/__pycache__/models.cpython-311.pyc | Bin 0 -> 5763 bytes .../app/__pycache__/schemas.cpython-311.pyc | Bin 0 -> 4391 bytes .../app/__pycache__/security.cpython-311.pyc | Bin 0 -> 3068 bytes server/app/config.py | 22 ++++ server/app/db.py | 22 ++++ server/app/main.py | 109 +++++++++++++++++ server/app/models.py | 57 +++++++++ server/app/schemas.py | 64 ++++++++++ server/app/security.py | 43 +++++++ server/docker-compose.example.yml | 27 +++++ server/requirements-dev.txt | 4 + server/requirements.txt | 10 ++ .../test_api.cpython-311-pytest-8.3.4.pyc | Bin 0 -> 20396 bytes server/tests/test_api.py | 46 ++++++++ 38 files changed, 610 insertions(+), 58 deletions(-) create mode 100644 .gitea/workflows/ci.yml delete mode 100644 screenshots/01-aujourdhui.png create mode 100644 screenshots/01-onboarding.png delete mode 100644 screenshots/02-journal.png create mode 100644 screenshots/02-parcours.png create mode 100644 screenshots/03-aujourdhui.png delete mode 100644 screenshots/03-progres.png create mode 100644 screenshots/04-journal.png delete mode 100644 screenshots/04-profil.png create mode 100644 screenshots/05-progres.png create mode 100644 screenshots/06-coach.png create mode 100644 screenshots/07-profil.png create mode 100644 server/.dockerignore create mode 100644 server/.env.example create mode 100644 server/Dockerfile create mode 100644 server/README.md create mode 100644 server/app/__init__.py create mode 100644 server/app/__pycache__/__init__.cpython-311.pyc create mode 100644 server/app/__pycache__/config.cpython-311.pyc create mode 100644 server/app/__pycache__/db.cpython-311.pyc create mode 100644 server/app/__pycache__/main.cpython-311.pyc create mode 100644 server/app/__pycache__/models.cpython-311.pyc create mode 100644 server/app/__pycache__/schemas.cpython-311.pyc create mode 100644 server/app/__pycache__/security.cpython-311.pyc create mode 100644 server/app/config.py create mode 100644 server/app/db.py create mode 100644 server/app/main.py create mode 100644 server/app/models.py create mode 100644 server/app/schemas.py create mode 100644 server/app/security.py create mode 100644 server/docker-compose.example.yml create mode 100644 server/requirements-dev.txt create mode 100644 server/requirements.txt create mode 100644 server/tests/__pycache__/test_api.cpython-311-pytest-8.3.4.pyc create mode 100644 server/tests/test_api.py diff --git a/.gitea/workflows/ci.yml b/.gitea/workflows/ci.yml new file mode 100644 index 0000000..f4f0a8d --- /dev/null +++ b/.gitea/workflows/ci.yml @@ -0,0 +1,28 @@ +name: MetaBloom CI + +on: + push: + branches: [main] + pull_request: + +jobs: + app: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-node@v4 + with: + node-version: 22 + cache: npm + - run: npm ci + - run: npm run typecheck + - run: npm run build:web + api: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-python@v5 + with: + python-version: '3.12' + - run: pip install -r server/requirements-dev.txt + - run: pytest -q server/tests diff --git a/App.tsx b/App.tsx index 9270c97..ae06b50 100644 --- a/App.tsx +++ b/App.tsx @@ -2,10 +2,11 @@ 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 { Alert, Pressable, SafeAreaView, ScrollView, StyleSheet, Text, TextInput, View, useWindowDimensions } from 'react-native'; +import { Alert, Pressable, SafeAreaView, ScrollView, Share, StyleSheet, Switch, Text, TextInput, View, useWindowDimensions } from 'react-native'; -type Tab = 'today' | 'journal' | 'progress' | 'profile'; -type Entry = { date: string; weight: number; water: number; steps: number; glucose?: number }; +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 }; 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}, @@ -17,22 +18,27 @@ function Card({children, style}: any){return {child function Pill({children, tone='green'}:any){return {children}} function Metric({emoji,label,value,sub,onPress}:any){return [s.metric,pressed&&{opacity:.7}]}>{emoji}{label}{value}{sub}} function ProgressBar({value,color=C.green}:any){return } -function Nav({tab,setTab}:any){const items:[Tab,string,string][]=[['today','⌂','Aujourd’hui'],['journal','+','Journal'],['progress','↗','Progrès'],['profile','○','Profil']];return {items.map(([id,icon,label])=>setTab(id)} style={s.navItem}>{icon}{label})}} +function Nav({tab,setTab}:any){const items:[Tab,string,string][]=[['today','⌂','Aujourd’hui'],['journal','+','Journal'],['progress','↗','Progrès'],['coach','✦','Coach'],['profile','○','Profil']];return {items.map(([id,icon,label])=>setTab(id)} style={s.navItem}>{icon}{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 [saved,setSaved]=useState(false); const {width}=useWindowDimensions(); - useEffect(()=>{AsyncStorage.getItem('metabloom').then(v=>{if(v){const d=JSON.parse(v);setEntries(d.entries||seed);setWater(d.water||1250);setSteps(d.steps||6240)}}).catch(()=>{})},[]); - useEffect(()=>{AsyncStorage.setItem('metabloom',JSON.stringify({entries,water,steps})).catch(()=>{})},[entries,water,steps]); + 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(d.entries||seed);setWater(d.water||1250);setSteps(d.steps||6240);setProfile(d.profile||profile)}}).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)+.82+.75)*25); - const save=()=>{const w=parseFloat(weight.replace(',','.')); const g=parseFloat(glucose); if(!w||w<30||w>300){Alert.alert('Valeur à vérifier','Saisis un poids compris entre 30 et 300 kg.');return} setEntries(x=>[...x.slice(0,6),{date:'Auj.',weight:w,water,steps,glucose:g||undefined}]);setSaved(true);setTimeout(()=>setSaved(false),2200)}; - const content=useMemo(()=>tab==='today'?:tab==='journal'?:tab==='progress'?:{setEntries(seed);setWater(1250);setSteps(6240);AsyncStorage.removeItem('metabloom')}}/>,[tab,score,water,steps,weight,glucose,saved,entries]); - return 900?1120:640}]}>{content}