Assiettly
Suivi nutritionnel par IA pour le marché français — SaaS web (pas une app
mobile), pensé pour la langue, les aliments et la culture culinaire
françaises, avec un système d'engagement à base de "flammes" (streaks)
inspiré de Duolingo/Strava.
Architecture
Monorepo pnpm :
apps/
web/ SaaS — Next.js 14 (App Router), déployé sur Vercel
packages/
shared/ Types, schémas de validation (zod) et logique métier partagée
(calcul des objectifs caloriques, projection, calcul de streak)
- Auth & base de données : Supabase (Auth gérée +
Postgres managé, région UE pour la conformité RGPD).
- App web : Next.js App Router — Server Components + Server Actions pour
les mutations (pas d'API REST séparée), Prisma pour l'accès à la base.
- Paiement : Stripe Checkout (abonnement avec essai gratuit de 7 jours,
carte requise mais non débitée avant la fin de l'essai) + webhook pour
synchroniser le statut d'abonnement.
- Aliments emballés : Open Food Facts
comme base de données de départ, avec cache local en base au premier scan.
- IA vision (scan photo de repas) : API Claude (Anthropic,
claude-sonnet-4-5) avec structured outputs — identification des
aliments, estimation nutritionnelle et niveau de confiance par ingrédient
(voir PROGRESS.md).
- E-mails transactionnels : Resend + templates
React Email aux couleurs de la marque — rappel
quotidien envoyé (via un cron Vercel) à qui risque de perdre sa flamme.
Voir PROGRESS.md pour le détail des décisions et l'avancement.
Identité de marque
- Ton : chaleureux, simple, motivant — jamais culpabilisant sur
l'alimentation.
- Palette : corail (
#F2603C) comme couleur d'action, sarcelle
(#1F7A6C) comme accent santé/fraîcheur, ambre pour la mécanique de
flamme, sur fond ivoire chaud — volontairement distincte des codes
visuels rouge/orange des apps concurrentes.
- Typographie : Fredoka (titres, arrondie et chaleureuse) + Inter (corps
de texte, lisible).
- Marque flamme : icône SVG propre à Assiettly (dégradé ambre → corail),
pas un émoji ni une icône tierce.
Palette et typo définies dans apps/web/tailwind.config.ts et
apps/web/src/app/layout.tsx.
Prérequis
- Node.js ≥ 20, pnpm ≥ 9
- Un projet Supabase (région UE recommandée)
- Un compte Stripe en mode test pour la facturation
Mise en route
1. Installer les dépendances
pnpm install
2. Configurer Supabase
- Crée un projet sur supabase.com (région UE, ex. Paris/Frankfurt).
- Récupère dans Project Settings > API :
Project URL, anon public key.
- Récupère la chaîne de connexion Postgres dans Project Settings > Database > Connection string.
3. Configurer Stripe
- Crée deux prix récurrents (mensuel + annuel) dans le Dashboard Stripe.
- Crée un endpoint de webhook pointant vers
<NEXT_PUBLIC_APP_URL>/api/webhooks/stripe,
écoutant customer.subscription.created|updated|deleted.
4. Configurer les notifications push
Génère une paire de clés VAPID (une seule fois — ne jamais les régénérer
ensuite, ça invaliderait tous les abonnements existants) :
npx web-push generate-vapid-keys
Renseigne NEXT_PUBLIC_VAPID_PUBLIC_KEY, VAPID_PRIVATE_KEY et
VAPID_SUBJECT (mailto:[email protected]).
5. Configurer Resend (rappels de flamme par e-mail)
- Crée un compte sur resend.com, récupère une clé API.
- Ajoute et vérifie ton domaine d'envoi (DNS DKIM/SPF fournis par Resend).
- Choisis un
CRON_SECRET (chaîne aléatoire) — Vercel Cron l'enverra
automatiquement dans l'en-tête Authorization pour protéger la route.
6. Configurer et lancer l'app
cp apps/web/.env.example apps/web/.env
# renseigner DATABASE_URL, DIRECT_URL, NEXT_PUBLIC_SUPABASE_URL,
# NEXT_PUBLIC_SUPABASE_ANON_KEY, STRIPE_SECRET_KEY, STRIPE_WEBHOOK_SECRET,
# STRIPE_PRICE_ID_*, NEXT_PUBLIC_APP_URL, RESEND_API_KEY, RESEND_FROM_EMAIL,
# CRON_SECRET, NEXT_PUBLIC_VAPID_PUBLIC_KEY, VAPID_PRIVATE_KEY, VAPID_SUBJECT
pnpm --filter @assiettly/web prisma:migrate # crée les tables
pnpm --filter @assiettly/web dev # démarre sur http://localhost:3000
Le cron des rappels de flamme (apps/web/vercel.json) ne tourne
automatiquement qu'une fois déployé sur Vercel. En local, teste-le
directement :
curl -H "Authorization: Bearer $CRON_SECRET" http://localhost:3000/api/cron/streak-reminders
Scripts utiles
| Commande | Description |
|---|
pnpm --filter @assiettly/web dev | Démarre l'app en mode développement |
pnpm --filter @assiettly/web build | Build de production (Vercel l'utilise automatiquement) |
pnpm --filter @assiettly/web prisma:studio | Explorateur visuel de la base |
pnpm typecheck | Vérifie les types sur tous les packages |
Fonctionnalités du MVP actuel
- Landing page publique (
/) : hero différenciant (cuisine française +
mécanique de flamme), fonctionnalités clés, réassurance factuelle
(composant SocialProof togglable, sans faux chiffres/témoignages),
tarifs, footer — pensée pour un premier rendu rapide (SSR, mockup du
dashboard en SVG/CSS sans image à charger)
- Authentification (Supabase Auth) : email/mot de passe, connexion
rapide Google/Apple, mot de passe oublié — redirection automatique vers
l'onboarding ou le dashboard selon l'état du compte
- Onboarding en 13 écrans (une question par écran, barre de progression,
pickers à molette natifs pour les valeurs numériques et la date de
naissance) : sexe, date de naissance, niveau d'activité, taille, poids
actuel, app de suivi déjà utilisée, coach/diététicien, objectif principal,
freins habituels, poids cible (avec avertissement doux si hors fourchette
santé), type d'alimentation, motivation principale, puis notifications
- Écran final animé qui calcule réellement les objectifs (Mifflin-St Jeor)
pendant l'animation de progression, avant redirection vers le paywall
- Paywall après l'onboarding : comparatif gratuit/premium, essai Stripe
de 7 jours (carte requise, non débitée avant la fin), ou accès gratuit
- Dashboard avec navigation par barre basse (Accueil/Progrès/Groupes/
Profil + bouton "+" d'ajout rapide) : sélecteur de jours de la semaine,
anneau calorique du jour, sous-cartes macro paginées (protéines/glucides/
lipides puis fibres/score santé), repas récents
- Détail nutritionnel d'un repas : réglage de portion en temps réel,
calories/macros, liste d'ingrédients
- Ajout de repas : recherche d'aliments, scan de code-barres (Open Food
Facts), quantité ajustable
- Écran Progrès unifié : poids + objectif, série avec mini-semaine,
graphique d'évolution du poids (période, % de l'objectif, point
interactif), message d'encouragement contextuel, moyenne calorique et
tendance, calendrier mensuel des flammes
- Système de flamme : calcul quotidien, freeze (2/mois), paliers de badges
(7/30/100/365 jours)
- Rappel quotidien par e-mail (Resend) et notification push
(opt-in à l'onboarding, activable/désactivable depuis le profil) si la
flamme est en danger
- Scan de repas par photo : capture caméra réelle, analyse par l'API
Claude (identification des aliments, estimation nutritionnelle,
confiance par ingrédient et globale), résultat éditable avant validation
- Aperçu non connecté (architecture posée, données de démonstration) : flux
social par groupes
- PWA installable : manifest, service worker (shell mis en cache),
écran dédié en fin d'onboarding qui adapte le tutoriel d'installation
selon la plateforme (bouton natif sur Android, tutoriel manuel en 3
étapes sur iOS, QR code sur desktop), et un moyen de la re-proposer
depuis le profil
Non implémenté dans ce MVP (prévu ensuite)
- Connexion Google / Apple : boutons et flux OAuth déjà câblés côté app,
mais les fournisseurs ne sont pas encore activés dans Supabase Auth
- Scanner de code-barres via la caméra (saisie manuelle du code pour l'instant)
- Animation de célébration des paliers de badges à l'ouverture de l'app
- Fonctionnalités sociales (classement, défis — V2)
Conformité RGPD
Le MVP pose les bases (hébergement UE via Supabase, données de santé isolées
par utilisateur) mais les mentions légales, CGU, politique de confidentialité,
et les endpoints d'export/suppression de données restent à écrire avant tout
lancement public.