Files
lifetrack/docs/design/ux-pages.md
T
MeeJayandClaude Opus 5 93f0689c1e Initial import: LifeTrack v1 (santé, vape, finances)
Tracker de vie auto-hébergé : suivi poids/calories/sport avec planning de
pesées, sevrage tabac (vape) avec modèle de coût DIY et économies, et
finances personnelles avec import de relevés bancaires.

Architecture : FastAPI + SQLAlchemy 2.0 + PostgreSQL 16, React 18 + TS +
Vite + Tailwind + ECharts, déploiement Docker Compose. Modules
auto-découverts des deux côtés (pkgutil / import.meta.glob) et framework
de connecteurs à deux voies (importeurs de fichiers + ingestion JSON)
pour brancher de nouvelles sources sans toucher au noyau.

Validé : 292 tests pytest, tsc + vite build, contrat API/web vérifié
contre le schéma OpenAPI, et déploiement Docker réel sur PostgreSQL 16
(28 tables, SPA servie par nginx, wizard de premier démarrage).

Documentation : README.md, docs/GUIDE.md, CONVENTIONS.md, et les
documents de conception et de recherche dans docs/.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-14 10:48:57 +02:00

66 KiB
Raw Blame History

LifeTrack — Spécification UX complète (page par page)

Document de référence pour l'implémentation. Prose en français, identifiants de code en anglais. Stack imposée : React 18 + TypeScript + Vite + TailwindCSS + Apache ECharts. UI 100 % en français, thème sombre par défaut. Ce document est exhaustif : les agents d'implémentation ne feront aucune recherche complémentaire.


Table des matières

  1. Principes globaux et layout
  2. Formatage français (nombres, dates, unités)
  3. Sémantique des couleurs
  4. Design system (tokens Tailwind, composants, palette charts)
  5. Composants transverses
  6. Configuration ECharts commune
  7. Page — Tableau de bord
  8. Page — Poids & Objectif
  9. Page — Nutrition
  10. Page — Activité & Sport
  11. Page — Balance énergétique
  12. Page — Vape
  13. Page — Finances
  14. Page — Imports
  15. Page — Réglages
  16. États vides (empty states) — récapitulatif
  17. Accessibilité

1. Principes globaux et layout

1.1 Structure générale

┌────────────┬──────────────────────────────────────────────┐
│            │  Topbar : titre de page + PeriodSelector +   │
│  Sidebar   │  actions rapides + menu utilisateur          │
│  (nav)     ├──────────────────────────────────────────────┤
│            │  Contenu : grille de KpiCard, ChartCard,     │
│            │  tables, sections                            │
└────────────┴──────────────────────────────────────────────┘
  • Sidebar (desktop ≥ 1024 px) : largeur 260px, repliable en mode icônes 72px (bouton chevron en bas de la sidebar, état persisté dans localStorage). Fond bg-base (#0D0D0D), séparée du contenu par une bordure hairline.
  • Entrées de navigation (ordre fixe, icônes Lucide entre parenthèses) :
    1. Tableau de bord (layout-dashboard) — route /
    2. Poids & Objectif (scale) — route /poids
    3. Nutrition (utensils) — route /nutrition
    4. Activité & Sport (footprints) — route /activite
    5. Balance énergétique (flame) — route /balance
    6. Vape (cloud) — route /vape
    7. Finances (wallet) — route /finances
    8. Imports (upload) — route /imports
    9. Réglages (settings) — route /reglages
  • Item actif : fond bg-surface-2, barre verticale 3px couleur accent (#3987E5) à gauche, texte text-primary. Items inactifs : text-secondary, hover bg-surface-2/50.
  • En bas de sidebar : logo + version + bouton repli.
  • Topbar : hauteur 56px, contient (gauche → droite) : titre de la page (h1, 18 px semibold), PeriodSelector (voir §5.1), bouton « + Ajouter » (menu déroulant d'actions rapides, voir §5.6), avatar/menu utilisateur (Profil, Réglages, Déconnexion).

1.2 Responsive

Breakpoints Tailwind standard : sm 640, md 768, lg 1024, xl 1280, 2xl 1536.

Élément Mobile (< 768) Tablette (7681023) Desktop (≥ 1024)
Navigation Barre inférieure fixe 5 items : Tableau de bord, Poids, Nutrition, Vape, « Plus » (bottom-sheet listant les autres pages) Sidebar repliée (icônes) Sidebar complète
PeriodSelector Menu déroulant compact (icône calendrier + libellé court) Segmented control Segmented control
Grille KPI 2 colonnes 3 colonnes 4 à 6 colonnes
ChartCard Pleine largeur, hauteur min 240px 12 colonnes Grille 2 colonnes (graphique principal en pleine largeur)
Tables Défilement horizontal dans la carte (overflow-x-auto), colonnes clés épinglées à gauche (Date, Libellé) ; jamais de scroll horizontal de page idem Table complète
Ajout rapide FAB + en bas à droite (au-dessus de la nav), ouvre le menu d'actions rapides Bouton topbar Bouton topbar
  • Toutes les zones tactiles ≥ 44px. Les graphiques restent lisibles à 360 px de large (labels d'axe X inclinés ou échantillonnés via axisLabel.interval: 'auto').
  • Les modales deviennent des bottom-sheets plein écran sur mobile.

1.3 Thème

  • Sombre par défaut. Un thème clair existe (Réglages → Application) mais le sombre est la référence de conception. Tout hex de ce document est donné pour le fond sombre.
  • color-scheme: dark sur :root[data-theme="dark"] ; bascule via attribut data-theme (dark | light | auto).

2. Formatage français

Toutes les valeurs affichées passent par des helpers centralisés (src/lib/format.ts) basés sur Intl avec locale fr-FR. Le séparateur de milliers est l'espace fine insécable (U+202F, produite nativement par Intl.NumberFormat('fr-FR')), le séparateur décimal est la virgule.

Type Helper Exemple
Monnaie formatCurrency(v)Intl.NumberFormat('fr-FR', {style:'currency', currency:'EUR'}) 1 234,56 € · 45,90 €
Poids formatWeight(v) — 1 décimale 82,4 kg
Calories formatKcal(v) — entier groupé 1 850 kcal
Millilitres formatMl(v) — 1 décimale 4,2 ml
Nicotine formatMg(v) — 1 décimale 12,6 mg
Distance formatKm(v) — 2 décimales 5,25 km
Pas formatSteps(v) — entier groupé 9 542
Pourcentage formatPercent(v) — espace avant % 82 %
Grammes (macros) formatG(v) — entier 132 g
Durée formatDuration(min) 1 h 05 · 45 min
Date formatDate(d)dd/MM/yyyy 13/08/2026
Date + heure formatDateTime(d)dd/MM/yyyy HH:mm 13/08/2026 19:30
Date courte (axes) formatDateShort(d)dd/MM 13/08
Mois (axes) formatMonth(d) août 2026 (axe : août 26)
Semaine (axes) formatWeek(d) Sem. 33 (tooltip : du 10/08 au 16/08)
Nombre signé formatSigned(v) — signe explicite, « » U+2212 +0,3 kg · 450 kcal

Règles :

  • Saisie : les champs numériques acceptent la virgule et le point comme séparateur décimal (normalisation à la volée).
  • Stockage : UTC en base ; affichage converti en Europe/Paris côté client (le backend renvoie de l'ISO 8601 UTC).
  • « Aujourd'hui » et « Hier » remplacent la date dans les listes quand pertinent (journal nutrition, dernières transactions).
  • Alignement : montants et nombres alignés à droite dans les tables, avec font-variant-numeric: tabular-nums.

3. Sémantique des couleurs

Deux familles distinctes : la palette de séries (identité, §4.4) et les couleurs sémantiques (polarité/état). Ne jamais utiliser une couleur sémantique comme couleur de série ordinaire.

Signification Couleur (fond sombre) Usage
Positif / favorable — déficit calorique, économies vape, cashflow positif, budget respecté, perte de poids (si l'objectif est de perdre) #0CA30C (semantic-positive) Barres de balance nette en déficit, aire « Économies cumulées », deltas KPI favorables, cashflow > 0
Négatif / défavorable — surplus calorique, dépassement de budget, cashflow négatif, reprise de poids #D03B3B (semantic-negative) Barres de surplus, budget > 100 %, deltas KPI défavorables
Avertissement — budget entre 80 et 100 %, résistance en fin de vie, import partiel #FAB219 (semantic-warning) Badges, jauges, statuts
Sérieux (entre warning et negative) #EC835A (semantic-serious) Réservé aux statuts d'import et alertes
Neutre / information #3987E5 (slot 1 de la palette) Liens, éléments actifs, info

Règles impératives :

  • Direction consciente de l'objectif : pour le poids, le vert signifie « va dans le sens de l'objectif ». Si goal.direction = lose, une variation négative est verte ; si gain, c'est l'inverse. Centraliser dans un helper deltaTone(value, goalDirection).
  • Une couleur sémantique n'est jamais seule porteuse de sens : elle est toujours accompagnée d'un signe (+/), d'une icône (trending-down, alert-triangle…) ou d'un libellé.
  • Montants dans les tables Finances : dépenses en text-primary avec signe , revenus en semantic-positive avec signe + (le signe porte l'info, la couleur renforce).

4. Design system

4.1 Tokens Tailwind (extrait de tailwind.config.ts)

// tailwind.config.ts — theme.extend
colors: {
  base:      '#0D0D0D',   // fond de page
  surface:   '#1A1A19',   // fond des cartes / graphiques
  'surface-2': '#242423', // hover, éléments imbriqués, inputs
  border:    'rgba(255,255,255,0.10)', // bordure hairline
  ink: {
    DEFAULT:  '#FFFFFF',  // text-primary
    secondary:'#C3C2B7',
    muted:    '#898781',  // labels d'axes, placeholders
  },
  grid:      '#2C2C2A',   // lignes de grille des graphiques
  axis:      '#383835',   // ligne de base / axe
  accent:    '#3987E5',
  semantic: {
    positive: '#0CA30C',
    negative: '#D03B3B',
    warning:  '#FAB219',
    serious:  '#EC835A',
  },
  chart: {
    1: '#3987E5', 2: '#D95926', 3: '#199E70', 4: '#C98500',
    5: '#D55181', 6: '#008300', 7: '#9085E9', 8: '#E66767',
  },
},
borderRadius: { card: '12px' },
fontFamily: { sans: ['system-ui', '-apple-system', '"Segoe UI"', 'sans-serif'] },

Thème clair (non prioritaire, mêmes rôles) : base #F9F9F7, surface #FCFCFB, ink #0B0B0B, ink-secondary #52514E, border rgba(11,11,11,0.10), grid #E1E0D9, palette charts clair : #2A78D6, #EB6834, #1BAF7A, #EDA100, #E87BA4, #008300, #4A3AA7, #E34948, semantic-positive texte : #006300.

4.2 Composant Card

Base de toutes les cartes (KPI, graphiques, tables, sections de réglages) :

  • Fond bg-surface, rounded-card (12 px), bordure 1px solid border, padding 16px (mobile) / 20px (desktop). Pas d'ombre portée (thème sombre) — la séparation vient du contraste fond/carte et de la bordure.
  • En-tête optionnel : titre 14 px semibold text-ink, sous-titre 12 px text-ink-muted, zone d'actions à droite.

4.3 KpiCard

┌──────────────────────────────┐
│ LIBELLÉ (12px, muted, caps)  │
│ 82,4 kg   (28px, semibold)   │
│ ▼ 0,4 kg sur 7 j (12px)     │  ← delta coloré + icône
│ [sparkline optionnelle 40px] │
└──────────────────────────────┘
  • Valeur principale : 28 px desktop / 24 px mobile, text-ink.
  • Delta : icône trending-up/trending-down + valeur signée, colorée via deltaTone.
  • Variante avec jauge de progression (barre 6 px arrondie) pour les KPI « X / budget ».
  • Toute la carte est cliquable quand elle renvoie vers une page de détail (curseur pointer + hover bg-surface-2/40).

4.4 Palette de graphiques (fond sombre — validée CVD, ne pas réordonner)

L'ordre des slots est un mécanisme de sécurité daltonisme (validé par script : pire paire adjacente CVD ΔE 8,4 ; vision normale 19,3 ; tous les slots ≥ 3:1 de contraste sur #1A1A19). Attribution fixe par entité — jamais recyclée, jamais réassignée quand une série est filtrée.

Slot Hex Attribution LifeTrack (identité fixe)
chart-1 bleu #3987E5 Poids (tendance), dépense énergétique (kcal out), distance, solde total
chart-2 orange #D95926 Apports caloriques (kcal in), kcal actives
chart-3 vert d'eau #199E70 Protéines, pas quotidiens
chart-4 jaune #C98500 Glucides, coût vape
chart-5 magenta #D55181 Lipides, nicotine
chart-6 vert #008300 (réservé — éviter près de semantic-positive)
chart-7 violet #9085E9 Vape (ml), poids théorique
chart-8 rouge #E66767 Dernier recours (jamais pour un sens « négatif »)
  • Variantes d'une même entité (ex. projection du poids) : teintes séquentielles du même bleu#86B6EF (clair), #3987E5 (base), #1C5CAB (foncé) — jamais un nouveau slot.
  • Catégories de dépenses (Finances) : slots 1→7 dans l'ordre, au-delà regroupement « Autres » en ink-muted (#898781). Pour les donuts et le sankey (formes où toutes les paires se comparent), limiter à 7 catégories + « Autres ».
  • Chaque catégorie financière reçoit un slot à sa création (stocké en base) → couleur stable dans le temps.
  • ≥ 2 séries ⇒ légende toujours affichée ; 1 série ⇒ pas de légende (le titre nomme la série).
  • Jamais de valeur numérique sur chaque point : labels directs sélectifs (dernier point, max, min) uniquement.
  • Jamais de double axe Y. Deux mesures d'échelles différentes = deux graphiques empilés partageant le même axe X et le même zoom (echarts.connect).

4.5 Boutons et champs

  • Bouton primaire : fond accent, texte blanc, rounded-lg, hauteur 40 px. Secondaire : fond surface-2, bordure hairline. Destructif : fond semantic-negative. Ghost : texte accent.
  • Inputs : fond surface-2, bordure hairline, focus ring accent, hauteur 40 px, labels au-dessus (12 px text-secondary), erreurs en 12 px semantic-negative sous le champ.
  • Selects natifs stylés + combobox avec recherche pour listes longues (catégories, aliments).

5. Composants transverses

5.1 PeriodSelector (sélecteur de période)

  • Segmented control : 7 j · 30 j · 90 j · 1 an · Tout · Personnalisé. Option active : fond surface-2, texte text-ink ; inactives text-secondary.
  • Personnalisé ouvre un popover avec deux champs date (dd/MM/yyyy, datepicker fr, lundi premier jour de semaine) + raccourcis « Ce mois-ci », « Le mois dernier », « Cette année ». Libellé affiché ensuite : 01/06/2026 13/08/2026.
  • Par défaut : 30 j partout, sauf Finances (Ce mois-ci) et Imports (pas de période).
  • La période est globale à la page, persistée par page (localStorage, clé period:<route>) et reflétée dans l'URL (?periode=30j ou ?du=2026-06-01&au=2026-08-13).
  • Chaque ChartCard peut surcharger localement sa période (menu ⋯), la surcharge est signalée par un badge sur la carte.

5.2 ChartCard

Card + en-tête standard : titre du graphique, sous-titre optionnel (période effective), actions à droite : menu avec « Voir les données » (bascule le graphique en table triable des mêmes valeurs — obligatoire, c'est la vue accessible), « Exporter PNG », « Exporter CSV », « Plein écran ». Hauteur par défaut du canvas : 280 px (mini-graphes du tableau de bord : 120 px ; graphiques principaux : 360 px).

5.3 DataTable

  • En-têtes triables (flèche), lignes hauteur 44 px, zébrage désactivé (bordures hairline entre lignes), hover surface-2/40.
  • Pagination 20 lignes (50/100 au choix), pied « 120 sur 254 ».
  • Barre de filtres au-dessus, sur une seule ligne (wrap sur mobile) ; champ recherche à gauche, filtres à droite.
  • Ligne vide → composant EmptyState (voir §16).

5.4 EmptyState

Illustration légère (icône 48 px ink-muted), titre 16 px, texte d'aide 14 px text-secondary, 1 à 2 boutons d'action (primaire = action de saisie ou d'import). Textes exacts par page en §16.

5.5 Modales

Overlay rgba(0,0,0,0.6), carte centrée max-w-md (formulaires rapides) ou max-w-2xl (règles, mapping d'import). Titre + croix de fermeture. Boutons en pied : « Annuler » (ghost) + action primaire. Esc ferme, Entrée valide les formulaires à un champ principal. Sur mobile : bottom-sheet plein écran.

5.6 Menu « + Ajouter » (actions rapides globales)

Disponible partout (topbar / FAB mobile). Items :

  1. « Pesée » → modale Quick-add poids (§8.5)
  2. « Aliment » → modale Quick-add aliment (§9.5)
  3. « Recharge vape » → modale Quick-add recharge (§12.5)
  4. « Résistance changée » → action un clic + toast (§12.6)
  5. « Séance de sport » → modale séance (§10.5)
  6. « Transaction » → modale transaction manuelle (§13.6)
  7. « Importer un fichier » → route /imports

Raccourcis clavier : a ouvre le menu ; p pesée ; n aliment ; v recharge.

5.7 Toasts

En bas à droite (desktop) / haut (mobile). Succès : « Pesée enregistrée ✓ » avec lien « Annuler » (undo 5 s). Erreur : semantic-negative + détail.


6. Configuration ECharts commune

Fichier src/lib/echarts.ts : enregistrement des composants nécessaires (tree-shaking), locale FR d'ECharts, et un thème lifetrack-dark :

// Theme ECharts "lifetrack-dark" (extrait)
{
  backgroundColor: 'transparent',
  color: ['#3987E5','#D95926','#199E70','#C98500','#D55181','#008300','#9085E9','#E66767'],
  textStyle: { color: '#C3C2B7', fontFamily: 'system-ui, "Segoe UI", sans-serif' },
  axisLine:   { lineStyle: { color: '#383835' } },
  splitLine:  { lineStyle: { color: '#2C2C2A' } },
  axisLabel:  { color: '#898781', fontSize: 11 },
  legend:     { textStyle: { color: '#C3C2B7' }, icon: 'circle', itemWidth: 8, itemHeight: 8, top: 0 },
  tooltip: {
    backgroundColor: '#1A1A19', borderColor: 'rgba(255,255,255,0.10)',
    textStyle: { color: '#FFFFFF' }, padding: [8, 12],
  },
}

Règles communes à tous les graphiques :

  • grid: { left: 8, right: 16, top: 36, bottom: 8, containLabel: true } (bottom 44 si dataZoom slider).
  • Tooltip : trigger: 'axis' avec axisPointer: { type: 'line' } pour les séries temporelles ; trigger: 'item' pour donut/sankey/barres horizontales. Formatter systématiquement en français via les helpers de §2 (date complète en tête, puis ● Série valeur par ligne, valeurs alignées à droite).
  • Zoom : tous les graphiques temporels reçoivent dataZoom: [{ type: 'inside' }] (molette + pincement) ; les graphiques « principaux » de page (poids, transactions par mois, ml vape) ajoutent { type: 'slider', height: 20 }.
  • Lignes : width: 2, symbol: 'circle', symbolSize: 6, showSymbol: false (symboles visibles au hover seulement) ; smooth: 0.2 maximum, jamais de lissage exagéré.
  • Barres : barMaxWidth: 28, coins arrondis côté extrémité de donnée uniquement (borderRadius: [4,4,0,0] vers le haut, inversé vers le bas), espace de 2 px entre segments empilés (itemStyle.borderColor: '#1A1A19', borderWidth: 1).
  • Aires : dégradé vertical de la couleur de série à 25 % → 0 % d'opacité.
  • Axe Y : commence à 0 pour les barres ; pour le poids (ligne), min/max auto avec marge (scale: true).
  • markLine (budgets, objectifs) : pointillés [4,4], couleur #C3C2B7, label au bout à droite (ex. « Budget 1 800 »), symbol: 'none'.
  • Pas d'animation à la mise à jour de période > 300 ms (animationDuration: 300).
  • Chaque graphique expose aria-label descriptif et l'alternative « Voir les données » (§5.2).

7. Page — Tableau de bord

Route / · Titre « Tableau de bord » · Période par défaut : 30 j (s'applique aux mini-graphes ; les KPI « aujourd'hui » et « ce mois-ci » l'ignorent).

Objectif UX : une vue croisée de tous les modules en un écran, chaque bloc cliquable vers sa page de détail. Composé d'une rangée de 6 KPI, d'une rangée d'actions rapides, puis d'une grille de 6 cartes-modules avec mini-graphe.

7.1 KPI (rangée 1 — 6 cartes, 2×3 sur mobile)

# Libellé Valeur Sous-texte Couleur delta
1 Poids actuel 82,4 kg (dernière pesée) 0,4 kg sur 7 j (delta de la tendance EMA) deltaTone selon objectif
2 Calories aujourd'hui 1 450 / 1 800 kcal jauge + Reste 350 kcal (ou Dépassement de 120 kcal en rouge) vert ≤ budget, rouge >
3 Déficit cumulé (30 j) 12 450 kcal 1,6 kg théoriques vert si déficit
4 Vape aujourd'hui 3,8 ml ≈ 11,4 mg de nicotine neutre
5 Économies vape 1 245,80 € depuis le 15/03/2025 toujours vert
6 Dépenses du mois 1 234,56 € / 1 500,00 € jauge + 82 % du budget global vert < 80 %, jaune 80100 %, rouge > 100 %

Si un module n'a pas de données, sa KpiCard affiche + lien « Configurer ».

7.2 Actions rapides (rangée 2)

Boutons pleine largeur sur une ligne : « + Pesée » · « + Aliment » · « + Recharge vape » · « Résistance changée » · « + Séance » · « Importer » — mêmes actions que §5.6, en accès direct.

7.3 Cartes-modules (grille 2 colonnes desktop, 1 colonne mobile)

Chaque carte : titre + valeur clé + mini-graphe ECharts (120 px, sans axe Y visible, axe X en dates courtes, tooltip actif, pas de zoom) + lien « Voir le détail → ».

  1. « Poids » — mini-line : tendance EMA (bleu chart-1, 2 px) + markLine horizontale objectif (pointillés muted, label « Objectif 78,0 kg »). Période sélectionnée.
  2. « Nutrition » — mini-bar 7 derniers jours : kcal/jour, chaque barre colorée semantic-positive si ≤ budget du jour, semantic-negative sinon + markLine budget.
  3. « Balance énergétique » — mini-bar : balance nette/jour (in out), barres vertes si < 0 (déficit), rouges si > 0, ligne zéro visible (axisLine sur y=0).
  4. « Vape » — mini-line ml/jour (violet chart-7) + moyenne mobile 7 j en trait plein, valeurs brutes en points 40 % d'opacité.
  5. « Économies » — mini-area cumulée verte (#0CA30C, dégradé) ; label direct sur le dernier point : 1 245,80 €.
  6. « Finances » — mini-bar 6 derniers mois : dépenses/mois (barres chart-1) + markLine budget mensuel global ; le mois courant est hachuré (mois incomplet, decal ECharts).

7.4 États particuliers

  • Première visite (aucune donnée nulle part) : le tableau de bord est remplacé par un écran d'accueil : « Bienvenue sur LifeTrack 👋 » + 3 cartes d'onboarding : « Configurez votre profil » (→ /reglages), « Définissez votre objectif de poids » (→ /reglages?tab=objectif), « Importez vos premières données » (→ /imports).

8. Page — Poids & Objectif

Route /poids · Titre « Poids & Objectif » · Période par défaut : 90 j.

Définitions de calcul (implémentation) :

  • trendWeight : EMA des pesées, alpha = 0.1 (≈ tendance sur ~20 jours), calculée sur jours calendaires (interpolation : l'EMA n'avance que sur les jours avec pesée).
  • weeklyRate : pente de la tendance sur 7 jours glissants, en kg/semaine.
  • projection : régression linéaire de la tendance sur les 21 derniers jours, extrapolée jusqu'à goal.targetWeight (bornée à +365 j).
  • bmi = poids / taille².

8.1 KPI (6 cartes)

Libellé Valeur Sous-texte
Poids actuel 82,4 kg Pesée du 13/08/2026
Tendance (EMA) 82,7 kg 0,4 kg sur 7 j
Rythme hebdo 0,45 kg/sem Objectif : 0,50 kg/sem (vert si
IMC 26,3 Surpoids (catégories : < 18,5 Maigreur · 18,525 Corpulence normale · 2530 Surpoids · ≥ 30 Obésité)
Objectif 78,0 kg Reste 4,4 kg · 61 % + jauge (départ → objectif)
Atteinte estimée 24/10/2026 dans 10 semaines (ou — · rythme insuffisant si la pente ne converge pas)

8.2 Graphiques

G1 — « Évolution du poids » (principal, pleine largeur, 360 px)

  • Type : line + scatter combinés, axe X time (dates), axe Y kg (scale: true, marge ±1 kg).
  • Séries :
    1. Pesées — scatter, symboles 6 px, #3987E5 à 45 % d'opacité ;
    2. Tendance — line 2 px #3987E5, sans symboles ;
    3. Projection — line pointillée [6,4] #86B6EF, ne démarre qu'au dernier point de tendance, s'étend dans le futur ;
    4. ObjectifmarkLine horizontale à targetWeight, pointillés, label « Objectif 78,0 kg » ;
    5. markPoint discret à l'intersection projection/objectif avec label date estimée.
  • Interactions : tooltip axe (Date · Pesée · Tendance), dataZoom inside + slider, légende (3 séries), clic-légende pour masquer les pesées brutes.

G2 — « Rythme hebdomadaire » (demi-largeur)

  • Type : bar, une barre par semaine ISO, axe X semaines (Sem. 31…), axe Y kg/sem.
  • Couleur par pièce (visualMap.pieces) : variation dans le sens de l'objectif → semantic-positive, sens inverse → semantic-negative (direction-aware §3).
  • markLine au rythme cible (ex. 0,50). Tooltip item : Sem. 32 · du 03/08 au 09/08 — 0,45 kg.

G3 — « IMC » (demi-largeur)

  • Type : line (IMC de la tendance), axe Y IMC min 16, max 35.
  • markArea de fond aux 4 bandes (opacité 6 % : bleu maigreur, vert normal, jaune surpoids, rouge obésité) + labels de bande à droite en 10 px muted. Tooltip : 13/08/2026 — IMC 26,3 (Surpoids).

G4 — « Mensurations » (pleine largeur, affiché seulement si ≥ 1 mesure existe)

  • Type : line multi-séries, axe Y cm. Séries (ordre de palette) : Tour de taille (1), Hanches (2), Poitrine (3), Bras (4), Cuisse (5). Légende obligatoire + labels directs en bout de ligne. Tooltip axe. connect du zoom avec G1.

8.3 Table « Historique des pesées »

Colonnes : Date · Poids · Tendance · Variation (vs pesée précédente, signée et colorée) · Note · actions (✎ modifier, 🗑 supprimer avec confirmation). Tri par date desc. Bouton d'en-tête « + Ajouter une pesée ».

8.4 Carte « Objectif » (résumé)

Rappel de l'objectif configuré : Départ 89,2 kg (12/01/2026) → Objectif 78,0 kg + jauge de progression + « Modifier l'objectif » (→ Réglages, onglet Objectif).

8.5 Modale « Ajouter une pesée » (quick-add global)

Champs :

  • Poids (kg) — numérique, pas 0,1, focus auto, pré-rempli avec la dernière valeur. Validation : 20300 kg.
  • Date — datepicker, défaut aujourd'hui. Heure — optionnelle, défaut maintenant.
  • Note — texte libre optionnel (placeholder : « ex. après le sport »).
  • Section repliable « Mensurations (optionnel) » : Tour de taille, Hanches, Poitrine, Bras, Cuisse (cm, 1 décimale).
  • Si une pesée existe déjà ce jour : avertissement « Une pesée existe déjà le 13/08/2026 (82,6 kg). Enregistrer remplacera cette valeur. » avec choix « Remplacer » / « Ajouter quand même ».
  • Boutons : « Annuler » / « Enregistrer » → toast « Pesée enregistrée ✓ ».

9. Page — Nutrition

Route /nutrition · Titre « Nutrition » · Période par défaut : 30 j (graphes) ; le journal est journalier avec son propre navigateur de date.

Le budget kcal du jour vient de la Balance énergétique (§15.3) : dailyBudget = TDEE targetDeficit, ou valeur manuelle.

9.1 KPI (6 cartes)

Libellé Valeur Sous-texte
Aujourd'hui 1 450 / 1 800 kcal jauge + Reste 350 kcal
Moyenne 7 j 1 720 kcal/j Budget moyen : 1 800 kcal/j
Protéines aujourd'hui 96 / 130 g jauge (objectif §15.3)
Répartition du jour P 27 % · G 45 % · L 28 % en % des kcal
Jours dans le budget 18 / 30 j sur la période sélectionnée
Écart moyen au budget 80 kcal/j vert si négatif

9.2 Graphiques

G1 — « Calories par jour » (principal, pleine largeur)

  • Type : bar + line. Axe X jours, axe Y kcal (un seul axe — même unité).
  • Série 1 Apports : barres, couleur par jour via callback itemStyle.color : semantic-positive si kcal ≤ budgetOfDay, semantic-negative sinon (comparaison au budget du jour, le budget peut évoluer).
  • Série 2 Budget : ligne en escalier (step: 'end'), pointillés, #C3C2B7.
  • Tooltip axe : mar. 12/08 — Apports 1 940 kcal · Budget 1 800 kcal · Écart +140 kcal. Zoom inside + slider. Légende (2 séries).

G2 — « Macronutriments par jour » (pleine largeur)

  • Type : bar empilées, unité kcal (P ×4, G ×4, L ×9 — permet la comparaison visuelle avec G1). Séries : Protéines #199E70, Glucides #C98500, Lipides #D55181 (espace 2 px entre segments). Légende. Tooltip axe avec grammes ET kcal : Protéines 96 g (384 kcal). Toggle dans le menu ⋯ : « Afficher en grammes ».

G3 — « Répartition du jour » (tiers de largeur)

  • Type : pie (donut, radius ['55%','80%']). 3 parts P/G/L, mêmes couleurs que G2. Centre : 1 450 kcal (graphic text). Labels directs : Protéines 27 %. Tooltip item : Glucides — 652 kcal (45 %) · 163 g. Suit la date du journal (§9.4).

G4 — « Répartition par repas » (deux tiers de largeur)

  • Type : bar empilées 100 % (stack + normalisation), une barre par jour. Séries : Petit-déjeuner (slot 1), Déjeuner (2), Dîner (3), Collations (4). Tooltip : kcal réels + %. Légende. Objectif : voir si un repas dérape (grignotage).

G5 — « Top aliments » (pleine largeur, 320 px)

  • Type : bar horizontales, top 10 des aliments par kcal totales sur la période. Axe Y : noms d'aliments (tronqués à 24 caractères + tooltip complet), axe X kcal. Une seule série → couleur unique chart-2, pas de légende. Label direct à droite de chaque barre : 12 340 kcal. Tooltip item : Pain complet — 12 340 kcal · 28 fois · 441 kcal/fois en moyenne. Clic sur une barre → filtre le journal sur cet aliment (badge de filtre actif au-dessus du journal).

9.3 Bibliothèque d'aliments (panneau secondaire)

Accessible par un bouton « Mes aliments » dans l'en-tête de page : panneau latéral (drawer) listant les aliments mémorisés — Nom · kcal/100 g (ou /portion) · P/G/L · Utilisé n fois · actions ✎ 🗑. Recherche en tête. C'est cette bibliothèque qu'interroge l'autocomplete du quick-add (§9.5) ; les aliments importés de Foodvisor y sont ajoutés automatiquement (dédupliqués par nom normalisé).

9.4 Journal alimentaire (section « Journal du jour »)

  • Navigateur de date : mer. 13/08/2026 + bouton « Aujourd'hui ». Swipe gauche/droite sur mobile.
  • Bandeau du jour : Total : 1 450 / 1 800 kcal · P 96 g · G 163 g · L 45 g + jauge.
  • 4 groupes repliables : Petit-déjeuner / Déjeuner / Dîner / Collations, chacun avec sous-total (620 kcal) et bouton « + Ajouter un aliment ».
  • Ligne d'entrée : nom · quantité (150 g) · kcal · P/G/L (masqués sur mobile, visibles au tap) · actions ✎ / 🗑 / ⧉ (dupliquer vers un autre jour/repas).
  • Les entrées importées de Foodvisor portent un badge source Foodvisor (tooltip : « Importé le 10/08/2026 »).

9.5 Modale « Ajouter un aliment » (quick-add global)

  • Recherche (focus auto) : autocomplete sur la bibliothèque personnelle (aliments déjà saisis/importés), résultats avec kcal/100 g. Sélection → pré-remplit tout.
  • Nom (texte, requis) · Repas (select : Petit-déjeuner / Déjeuner / Dîner / Collations — défaut selon l'heure : < 11 h petit-déj., 1115 h déjeuner, 1518 h collations, > 18 h dîner) · Date (défaut : jour affiché dans le journal).
  • Quantité + Unité (g / ml / portion) — si l'aliment vient de la bibliothèque, kcal et macros se recalculent proportionnellement.
  • Calories (kcal) (requis) · Protéines (g) · Glucides (g) · Lipides (g) (optionnels).
  • Case « Mémoriser cet aliment dans ma bibliothèque » (cochée par défaut pour une saisie manuelle).
  • Boutons : « Annuler » / « Ajouter » / « Ajouter et continuer » (garde la modale ouverte, vide la recherche — saisie en rafale d'un repas).

10. Page — Activité & Sport

Route /activite · Titre « Activité & Sport » · Période par défaut : 30 j.

Sources : Health Connect (pas, distance, kcal actives via bridge), FitShow (séances tapis), saisie manuelle. stepsGoal configurable (défaut 10 000).

10.1 KPI (6 cartes)

Libellé Valeur Sous-texte
Pas aujourd'hui 7 842 / 10 000 jauge
Moyenne pas (7 j) 9 120 /j ▲ +6 % vs 7 j précédents
Kcal actives aujourd'hui 320 kcal Moyenne 7 j : 410 kcal/j
Distance (période) 86,4 km ≈ 2,9 km/j
Séances cette semaine 3 séances · 2 h 15 Semaine dernière : 4 · 3 h 05
Objectif atteint 21 / 30 j jours ≥ objectif de pas sur la période

10.2 Graphiques

G1 — « Pas par jour » (principal, pleine largeur)

  • Type : bar + line. Série Pas : barres #199E70 (chart-3) ; jours ≥ objectif : opacité 100 %, sinon 55 % (renfort non-couleur : l'axe le montre aussi). Série Moyenne 7 j : ligne 2 px blanche à 60 %. markLine objectif (« Objectif 10 000 »). Tooltip axe, zoom inside + slider. Légende (2 séries).

G2 — « Calories actives par jour » (demi-largeur)

  • Type : bar, série unique #D95926 (chart-2), pas de légende. markLine moyenne de période (label « Moy. 410 »). Tooltip axe.

G3 — « Entraînement par semaine » (demi-largeur)

  • Type : bar empilées par semaine ISO, axe Y heures (1 h 30 au tooltip). Séries = types de séance : Tapis de course (1), Marche (2), Vélo (3), Renforcement (4), Autre (5, muted). Légende. Tooltip axe : détail par type + total semaine.

G4 — « Distance cumulée » (pleine largeur, 240 px)

  • Type : line en aire, cumul de la distance (pas + séances) depuis le début de la période, #3987E5 avec dégradé. Label direct sur le dernier point (86,4 km). Tooltip axe : 12/08 — cumul 84,1 km (+2,3 km ce jour).

10.3 Carte « Records » (stat tiles, pas un graphique)

4 tuiles : Max pas en un jour 18 452 · 21/06/2026 · Plus longue séance 1 h 32 · tapis · 05/07/2026 · Meilleure distance en séance 12,4 km · Meilleure semaine 78 500 pas · Sem. 25. Chaque record est cliquable → surligne le jour dans G1.

10.4 Table « Séances »

Colonnes : Date · Type (badge icône) · Durée · Distance · Kcal · FC moy. (si dispo) · Source (badge : FitShow / Health Connect / Manuel) · actions ✎ 🗑. Filtres : type, source. Tri par date desc.

10.5 Modale « Ajouter une séance »

Champs : Type (select : Tapis de course / Marche / Course à pied / Vélo / Renforcement / Natation / Autre) · Date (défaut aujourd'hui) · Heure de début · Durée (champ hh:mm, requis) · Distance (km) (optionnel) · Calories (kcal) (optionnel — placeholder « estimées automatiquement si vide », estimation MET simple par type) · FC moyenne (bpm) (optionnel) · Note. Boutons « Annuler » / « Enregistrer ».


11. Page — Balance énergétique

Route /balance · Titre « Balance énergétique » · Période par défaut : 30 j.

Définitions (constantes en src/lib/energy.ts) :

  • BMR : Mifflin-St Jeor (10×kg + 6.25×cm 5×âge + s, s = +5 homme / 161 femme), recalculé chaque jour avec le poids de tendance.
  • TDEE = BMR × activityFactor (§15.1) ou BMR + kcal actives mesurées si la source d'activité est complète (choix dans Réglages : tdeeMode: 'factor' | 'measured').
  • netBalance(day) = kcalIn TDEE(day). Négatif = déficit = vert.
  • KCAL_PER_KG = 7700 pour toutes les conversions kcal ↔ kg.
  • Un jour sans journal alimentaire est exclu des cumuls (et hachuré dans les graphes) plutôt que compté à 0 — règle anti-fausses-données.

11.1 KPI (6 cartes)

Libellé Valeur Sous-texte
Balance aujourd'hui 520 kcal Apports 1 450 · Dépense 1 970
Déficit moyen (7 j) 430 kcal/j Cible : 550 kcal/j
Cumul (période) 12 450 kcal 1,6 kg théoriques
TDEE estimé 2 350 kcal/j BMR 1 780 × 1,32 (ou BMR + actives mesurées)
Budget quotidien 1 800 kcal TDEE 550
Réel vs théorique +0,4 kg le réel décroche au-dessus du modèle (voir G4)

11.2 Graphiques

G1 — « Entrées vs sorties » (principal, pleine largeur)

  • Type : bar en miroir sur un seul axe kcal : série Apports en valeurs positives (#D95926, chart-2), série Dépense énergétique en valeurs négatives (#3987E5, chart-1, arrondis vers le bas). Ligne zéro marquée (axisLine y=0 en #383835).
  • Tooltip axe : mar. 12/08 — Apports 1 940 · Dépense 2 310 · Balance 370 kcal (balance colorée). Légende (2 séries). Zoom inside + slider. Jours sans journal : barres hachurées (decal) + mention tooltip « journal incomplet ».

G2 — « Balance nette quotidienne » (demi-largeur)

  • Type : bar, une série net = in out. visualMap.pieces : < 0semantic-positive (déficit), > 0semantic-negative (surplus). markLine à la cible de déficit (550, pointillés). Tooltip : 12/08 — Balance 370 kcal (déficit).

G3 — « Déficit cumulé » (demi-largeur)

  • Type : line en aire, cumul de net depuis le début de période. Couleur semantic-positive si le cumul est négatif (cas normal), l'aire se remplit sous zéro. Axe Y kcal ; le tooltip donne la double lecture : Cumul 12 450 kcal ≈ 1,6 kg (pas de second axe — l'équivalence kg vit dans le tooltip et le KPI).

G4 — « Poids théorique vs poids réel » (pleine largeur)

  • Type : line, 2 séries, axe Y kg (même unité, un seul axe) :
    1. Poids réel (tendance)#3987E5, 2 px ;
    2. Poids théorique#9085E9 (chart-7), pointillés [6,4] : startWeight + cumul(net)/7700, ancré sur la tendance au 1er jour de la période.
  • Légende + labels directs en bout de lignes. Tooltip axe : les deux valeurs + Écart +0,4 kg. C'est le graphique de vérité du module : si le réel et le théorique divergent durablement, le TDEE est recalibré (voir carte méthode).

11.3 Carte « Méthode & calibration »

Carte informative repliable : explication en 3 phrases du modèle (BMR Mifflin-St Jeor, TDEE, 7 700 kcal/kg), et ligne de calibration : « Sur les 30 derniers jours, votre dépense réelle estimée d'après la pesée est de 2 410 kcal/j (modèle : 2 350). » + bouton « Utiliser cette valeur comme TDEE » (écrit un override dans Réglages).

11.4 État dégradé

Cette page exige pesées + journal alimentaire. S'il manque l'un des deux sur la période : bandeau jaune « Données incomplètes : X jours sans journal alimentaire sur la période. Les cumuls excluent ces jours. »


12. Page — Vape

Route /vape · Titre « Vape » · Période par défaut : 30 j.

Modèle de coût (constantes dans Réglages §15.4) :

  • costPerMl = (prix base/ml + prix nicotine/ml au taux cible + prix arôme/ml au dosage) — détail affiché en §12.4.
  • coilCostPerDay = prix résistance ÷ durée de vie moyenne (jours).
  • vapeCostPerDay(d) = ml(d) × costPerMl + coilCostPerDay.
  • tobaccoCostPerDay = (cigarettes/jour avant arrêt ÷ cigarettes/paquet) × prix du paquet — figé à la config baseline, avec historique de prix optionnel.
  • savings(d) = Σ depuis quitDate (tobaccoCostPerDay vapeCostPerDay).
  • avoidedCigarettes = jours depuis quitDate × cigarettes/jour avant (compteur temps réel au prorata de la journée).
  • Consommation ml/jour : dérivée des recharges (une recharge de 10 ml le 12/08 puis une le 15/08 ⇒ ~3,3 ml/j lissés sur l'intervalle) ; une saisie quotidienne directe est aussi possible — les deux modes coexistent, la donnée quotidienne prime.

12.1 KPI (8 cartes, 2 rangées)

Libellé Valeur Sous-texte
Aujourd'hui 3,8 ml Moyenne 7 j : 4,1 ml/j
Nicotine aujourd'hui 11,4 mg 3,8 ml × 3,0 mg/ml
Coût moyen / jour 0,62 €/j dont résistances 0,17 €/j
Économies cumulées 1 245,80 € vs 8,50 €/j de tabac — toujours vert
Cigarettes évitées 7 654 compteur animé (odometer), ≈ 15/j
Sans tabac depuis 511 jours depuis le 15/03/2025
Résistance actuelle J+9 Moyenne : 12 j — à surveiller (badge jaune si ≥ moyenne 2 j, rouge si > moyenne)
Stock estimé 34 ml ≈ 8 jours restants (si suivi de stock activé, sinon carte masquée)

12.2 Graphiques

G1 — « Consommation d'e-liquide » (principal, pleine largeur)

  • Type : line + scatter. Série ml/jour : points #9085E9 (chart-7) à 45 % ; série Moyenne mobile 7 j : ligne 2 px #9085E9. markLine optionnelle « Objectif » si un objectif de réduction est défini (§15.4). Tooltip axe : 12/08 — 4,2 ml · moyenne 7 j : 4,0 ml. Zoom inside + slider. Légende (2 séries).
  • Les jours de changement de résistance apparaissent en markPoint discrets (petit pictogramme ⚙ sous l'axe, tooltip « Résistance changée »).

G2 — « Nicotine absorbée » (demi-largeur)

  • Type : bar, série unique mg/jour #D55181 (chart-5). markLine moyenne de période. Tooltip : 12/08 — 12,6 mg (4,2 ml × 3,0 mg/ml). Si le taux a changé dans la période, chaque jour utilise le taux effectif de la recharge courante.

G3 — « Coût quotidien » (demi-largeur)

  • Type : line, série €/jour #C98500 (chart-4), moyenne mobile 7 j (le brut en points 45 %). markLine de référence : Tabac : 8,50 €/j (pointillés #C3C2B7) — l'écart visuel entre la ligne et la markLine est l'économie quotidienne. Tooltip : 12/08 — vape 0,66 € · tabac évité 8,50 € · gain 7,84 €.

G4 — « Économies cumulées » (pleine largeur, 320 px — le graphique plaisir)

  • Type : line en aire, cumul savings depuis quitDate, couleur #0CA30C avec dégradé d'aire. Période forcée « Tout » par défaut (surcharge locale possible). Label direct sur le dernier point : 1 245,80 €. markPoint aux paliers franchis : 100 €, 250 €, 500 €, 1 000 €, 2 000 €, 5 000 € (pin avec étiquette). Tooltip axe : 12/08 — 1 238,40 € économisés. Zoom inside + slider.

G5 — « Durée de vie des résistances » (pleine largeur, 260 px)

  • Type : bar, une barre par résistance terminée, axe X = date de changement (catégorie), axe Y = durée en jours. Couleur unique chart-7 ; la résistance courante (en cours) apparaît en dernière barre hachurée (decal) avec sa durée provisoire. markLine moyenne (label « Moy. 12 j »). Tooltip item : Changée le 04/08/2026 — a duré 13 j · ~52 ml vapotés · GT Mesh 0,15 Ω.

12.3 Timeline « Jalons santé » (composant liste, pas ECharts)

Timeline verticale (rail à gauche, points verts = atteint, gris = à venir, le prochain jalon porte une jauge de progression). Jalons depuis quitDate (libellés exacts) :

Échéance Libellé
20 minutes « Pouls et tension redescendent »
8 heures « Le monoxyde de carbone diminue de moitié »
24 heures « Le CO est éliminé, les poumons évacuent les résidus »
48 heures « Goût et odorat s'améliorent »
72 heures « Respirer devient plus facile »
2 semaines « La circulation sanguine s'améliore »
1 mois « La peau retrouve son éclat »
3 mois « Toux et fatigue diminuent, souffle nettement meilleur »
6 mois « Capacité pulmonaire en nette hausse »
1 an « Risque d'infarctus réduit de moitié »
5 ans « Risque d'AVC redevenu comparable à un non-fumeur »
10 ans « Risque de cancer du poumon réduit de moitié »

12.4 Carte « Modèle de coût » (repliable)

Décomposition affichée : Base PG/VG 0,08 €/ml + Nicotine 0,11 €/ml + Arôme 0,09 €/ml = 0,28 €/ml · Résistance : 3,90 € ÷ 12 j = 0,33 €/j · Référence tabac : 15 cig./j × (12,50 € / 20) = 9,38 €/j (valeurs = exemples). Bouton « Modifier le modèle » → Réglages §15.4.

12.5 Modale « Recharge » (quick-add global)

Champs : Quantité (ml) — numérique, défaut = contenance de flacon par défaut (§15.4), boutons rapides 10 30 50 · Taux de nicotine (mg/ml) — défaut = taux courant des Réglages · Date et heure — défaut maintenant · Arôme / recette (texte optionnel, autocomplete sur les précédents) · Note. Boutons « Annuler » / « Enregistrer » → toast « Recharge de 10,0 ml enregistrée ✓ ». Variante dans la même modale (onglets) : « Conso du jour » — saisie directe ml pour une date (corrige/remplace l'estimation par recharges).

12.6 Bouton « Résistance changée » (action un clic)

Depuis le menu « + Ajouter », la page Vape (bouton dédié dans l'en-tête de G5) ou le tableau de bord : enregistre coilChange(now) immédiatement, toast « Résistance changée ✓ — la précédente a duré 13 jours » + lien « Ajouter un détail » (ouvre une mini-modale : Modèle (texte, autocomplete), Valeur (Ω), Prix unitaire (défaut Réglages), Note).

12.7 Tables

  • « Historique des recharges » : Date · Quantité · Nicotine · Arôme · Coût estimé · actions ✎ 🗑.
  • « Historique des résistances » : Posée le · Retirée le · Durée · Conso (ml) · Modèle · Prix · actions ✎ 🗑.

13. Page — Finances

Route /finances · Titre « Finances » · Période par défaut : Ce mois-ci (le PeriodSelector affiche ici en tête : Ce mois-ci · 3 mois · 6 mois · 1 an · Tout · Personnalisé).

La page est organisée en 4 onglets internes (tabs sous la topbar) : Vue d'ensemble · Transactions · Budgets · Récurrents. L'URL les reflète (/finances, /finances/transactions, /finances/budgets, /finances/recurrents).

13.1 KPI (communs, affichés sur « Vue d'ensemble »)

Libellé Valeur Sous-texte
Solde total 4 812,34 € 3 comptes (au dernier import)
Dépenses du mois 1 234,56 € Moyenne 6 mois : 1 480,00 €
Revenus du mois 2 300,00 €
Cashflow du mois +1 065,44 € vert si > 0, rouge sinon
Budget global 82 % jauge 1 234,56 € / 1 500,00 €
À catégoriser 14 transactions carte cliquable → onglet Transactions filtré catégorie = aucune (badge jaune si > 0)

13.2 Onglet « Vue d'ensemble » — graphiques

G1 — « Évolution du solde total » (pleine largeur)

  • Type : line en aire, solde total reconstitué jour par jour (somme des comptes), #3987E5. Tooltip axe : 12/08 — 4 812,34 €. Zoom inside + slider. Sous le graphique, rangée de tuiles par compte : nom du compte, solde 1 852,10 €, date de dernière donnée (badge jaune « Dernier import il y a 32 j » si > 30 j).

G2 — « Dépenses par catégorie » (demi-largeur, période courante)

  • Type : pie donut (radius ['50%','78%']), top 7 catégories + part Autres (#898781). Couleur = slot fixe de chaque catégorie (§4.4). Centre : total 1 234,56 €. Labels directs : Alimentation 24 %. Tooltip item : Alimentation — 296,40 € (24 %) · 18 transactions. Clic sur une part → onglet Transactions filtré sur la catégorie + la période.

G3 — « Dépenses mensuelles par catégorie » (demi-largeur)

  • Type : bar empilées, 12 derniers mois, mêmes couleurs de catégories (top 7 + Autres, espaces 2 px). Légende. Tooltip axe : détail par catégorie + total du mois. Mois courant hachuré (decal, incomplet). Clic sur un segment → Transactions filtrées (mois + catégorie).

G4 — « Cashflow mensuel » (pleine largeur)

  • Type : bar groupées + line, un seul axe € : Revenus (barres semantic-positive), Dépenses (barres semantic-negative, affichées en positif, côte à côte), Net (ligne blanche 2 px, peut passer sous zéro). Légende (3). Tooltip axe : juin 2026 — Revenus 2 300,00 € · Dépenses 1 890,50 € · Net +409,50 €.

G5 — « Flux du mois » (pleine largeur, 380 px)

  • Type : sankey, orientation gauche → droite : nœuds Revenus (par catégorie de revenu : Salaire, Autres revenus) → nœud central Budget du mois → catégories de dépenses (top 7 + Autres) → l'écart restant sort vers un nœud Épargne (vert) si positif. Liens colorés par catégorie cible à 40 % d'opacité. Tooltip lien : Budget → Alimentation : 296,40 €. Nœuds : label + montant. Pas de zoom ; drag des nœuds désactivé (draggable: false).

13.3 Onglet « Transactions »

Barre de filtres (une ligne, wrap mobile) : Recherche texte (libellé) · Compte (multi-select) · Catégorie (multi-select avec « Sans catégorie ») · Type (Tout / Dépenses / Revenus) · Montant min/max · période (héritée de la page). Badges de filtres actifs effaçables ; bouton « Réinitialiser ».

Table (le cœur du module) — colonnes :

Colonne Contenu
Date dd/MM/yyyy (groupes visuels par mois au scroll : sous-en-tête « août 2026 — 1 234,56 € de dépenses »)
Libellé libellé bancaire nettoyé, sous-ligne : libellé brut original en 11 px muted
Compte badge court (« Courant », « PayPal »)
Catégorie éditable en ligne : badge coloré (pastille couleur du slot) cliquable → combobox avec recherche ; Sans catégorie = badge jaune pointillé
Montant aligné droite, tabular ; dépenses 45,90 € en text-ink, revenus +2 300,00 € en semantic-positive
Actions (hover / menu ⋯) ✎ Modifier · « Créer une règle » · 🗑 Supprimer (les transactions importées se suppriment avec confirmation renforcée)
  • Sélection multiple (checkbox) → barre d'actions groupées : « Catégoriser (n) », « Supprimer (n) ».
  • Une transaction re-catégorisée à la main affiche une pastille « manuel » (les règles ne l'écrasent plus).

Modale « Créer une règle » (ouverte depuis une transaction, pré-remplie) :

  • Si le libellé contient (texte, pré-rempli avec le mot significatif du libellé, ex. CARREFOUR) — select d'opérateur : contient / commence par / expression régulière.
  • Conditions optionnelles (ajout par bouton « + Condition ») : Compte est (select) · Montant entre (min/max) · Type (dépense/revenu).
  • Alors catégoriser en : combobox catégorie (+ « Créer une catégorie… » inline).
  • Aperçu en direct : « 12 transactions existantes correspondent à cette règle » + mini-liste des 5 premières.
  • Case « Appliquer aux transactions existantes non catégorisées manuellement » (cochée par défaut).
  • Boutons « Annuler » / « Créer la règle ». Toast : « Règle créée ✓ — 12 transactions catégorisées ».

13.4 Onglet « Budgets »

  • Sélecteur de mois ( août 2026 ).
  • Liste de barres de progression par catégorie (composant, pas ECharts) : pastille + nom · 296,40 € / 350,00 € · barre 8 px (vert < 80 %, jaune 80100 %, rouge > 100 % — le dépassement déborde en rouge au-delà de 100 % avec largeur plafonnée et libellé 118 %) · reste 53,60 € ou Dépassé de 22,40 €.
  • En tête : budget global (somme) avec la même barre + « Rythme » : Au 13/08, vous avez dépensé 82 % du budget pour 42 % du mois écoulé (jauge à double repère : position du jour vs consommation).
  • Graphique G6 — « Budget vs réalisé (6 mois) » : bar groupées par mois : Budget (barres #898781 40 %) vs Dépensé (barres chart-1), un axe €. Légende. Tooltip axe.
  • Bouton « Modifier les budgets » → Réglages §15.5 (ou édition inline du montant au clic).

13.5 Onglet « Récurrents »

  • Détection automatique : transactions au libellé similaire, périodicité ~mensuelle/hebdo/annuelle (tolérance ±4 j), ≥ 3 occurrences.
  • Table « Dépenses récurrentes détectées » : Libellé · Catégorie · Fréquence (Mensuel, Annuel…) · Montant moyen · Dernière occurrence · Prochaine échéance estimée (badge jaune si dépassée = possible résiliation ou changement) · Coût annuel (tri par défaut, desc) · actions : « Confirmer » / « Ignorer » (les ignorés vont dans un panneau repliable).
  • KPI d'onglet : « Total récurrent mensuel » 184,90 €/mois · « Part des dépenses » 15 % · « Coût annuel » 2 218,80 €.
  • Graphique G7 — « Échéancier du mois » : frise horizontale du mois (composant custom léger) avec les échéances positionnées au jour, passées en plein / à venir en contour.

13.6 Modale « Ajouter une transaction » (manuelle)

Champs : Type (toggle Dépense / Revenu) · Montant (€) · Date · Libellé · Compte (select) · Catégorie (combobox) · Note. Utilisée pour les espèces ; badge source « Manuel ».


14. Page — Imports

Route /imports · Titre « Imports » · pas de PeriodSelector.

14.1 Zone d'upload (carte principale)

  • Drag & drop pleine largeur : pointillés border, icône upload-cloud, texte : « Glissez un fichier ici ou cliquez pour parcourir » · sous-texte : « CSV, OFX, XLSX ou ZIP — 20 Mo max ». Multi-fichiers accepté (file d'attente).
  • Choix du profil de source (select, avec auto-détection : le parseur tente de reconnaître en-têtes/format et pré-sélectionne, badge « détecté automatiquement ») :
    • Health Connect — export (zip/csv) — pas, poids, distance, kcal actives, séances
    • Foodvisor — export — journal alimentaire
    • FitShow — export — séances tapis
    • Relevé bancaire — CSV générique (avec étape de mappage)
    • Relevé bancaire — OFX
    • PayPal — activité (CSV)
    • Pesées — CSV générique (date; poids[; note])
    • Vape — CSV générique (date; ml[; mg/ml])
  • Flux en 3 étapes (stepper « 1. Fichier → 2. Vérification → 3. Import ») :
    1. Fichier : upload + profil (+ pour les profils bancaires : select Compte de destination, avec « Créer un compte… » inline).
    2. Vérification : pour le CSV générique, écran de mappage de colonnes (aperçu des 5 premières lignes ; pour chaque colonne cible — Date, Libellé, Montant (ou Débit/Crédit séparés), etc. — un select de colonne source ; format de date détecté ; option « ignorer la première ligne ») ; le mappage est mémorisé par profil+banque. Puis aperçu : table des 20 premières lignes normalisées + bandeau : « 254 lignes lues · 12 doublons ignorés (déjà importés) · 2 lignes en erreur ».
    3. Import : barre de progression, puis récapitulatif.
  • Déduplication : hash par ligne normalisée (source + date + montant + libellé, ou date+valeur pour les mesures) — les ré-imports du même fichier sont sans effet (idempotent).

14.2 Table « Historique des imports »

Colonnes : Date (13/08/2026 19:32) · Fichier (nom + taille) · Profil (badge) · Lignes (254) · Importées (240 en vert) · Doublons (12 en muted) · Erreurs (2 — badge rouge cliquable) · Statut (Terminé vert / Partiel jaune / Échec rouge / Annulé muted) · actions : « Détails », « Annuler cet import » (rollback : supprime les lignes créées par cet import, avec confirmation « Cette action supprimera les 240 entrées créées par cet import. »).

14.3 Panneau « Erreurs » (détail d'un import)

Ouvert depuis la table : liste des lignes en erreur — N° de ligne · Contenu brut (mono, tronqué) · Motif en français (« Date invalide : "31/02/2026" », « Montant illisible : "12,3O" », « Colonne "Poids" absente »). Bouton « Télécharger le rapport d'erreurs (CSV) ».

14.4 Carte « Connecteurs & API »

Rappel : « Votre téléphone peut envoyer les données Health Connect automatiquement via l'application compagnon. » + statut de la dernière synchronisation (Dernière réception : 13/08/2026 07:12 · 4 types de données) + bouton « Gérer les clés d'appareil » → Réglages §15.6.


15. Page — Réglages

Route /reglages · Titre « Réglages » · navigation par onglets verticaux (desktop) / accordéon (mobile) : Profil · Objectif · Nutrition · Vape · Finances · Appareils & API · Application. Chaque onglet = cartes de formulaires avec bouton « Enregistrer » par carte (toast de confirmation).

15.1 Onglet « Profil » (profil corporel)

  • Prénom / pseudonyme (affichage) · Sexe (radio : Homme / Femme — utilisé pour le BMR) · Date de naissance (datepicker) · Taille (cm).
  • Niveau d'activité (select avec descriptions) : Sédentaire (×1,2) · Légèrement actif (×1,375) · Modérément actif (×1,55) · Très actif (×1,725) · Extrêmement actif (×1,9).
  • Mode de calcul du TDEE (radio) : « Facteur d'activité (recommandé au départ) » / « BMR + calories actives mesurées » / « Valeur manuelle : ___ kcal/j » (renseignée aussi par la calibration §11.3).
  • Encart calculé en direct : BMR : 1 780 kcal · TDEE estimé : 2 350 kcal/j.

15.2 Onglet « Objectif »

  • Poids de départ (pré-rempli : première pesée) et date de départ.
  • Poids cible (kg) · Rythme visé (slider 0,25 — 1,0 kg/semaine, graduations 0,25 · repère « recommandé : 0,5 ») ;
  • Lignes calculées en direct : Déficit quotidien nécessaire : ≈ 550 kcal/j · Date d'atteinte estimée : 24/10/2026 ;
  • Alternative : saisir une date cible → le rythme et le déficit se recalculent (les trois champs sont liés, le dernier modifié gagne).
  • Garde-fou : si le budget résultant < 1 200 kcal/j (femme) / 1 500 (homme) : avertissement jaune « Ce rythme impose un budget très bas. Envisagez un objectif plus progressif. »

15.3 Onglet « Nutrition »

  • Budget calorique (radio) : « Automatique : TDEE déficit (1 800 kcal/j actuellement) » / « Manuel : ___ kcal/j ».
  • Objectifs de macros : Protéines (g/kg de poids — défaut 1,6 — ou g fixes), Glucides / Lipides en % des kcal restantes (deux sliders liés).
  • Repas : liste réordonnable des repas du journal (défaut : Petit-déjeuner, Déjeuner, Dîner, Collations) — renommage possible.
  • Objectif de pas (utilisé page Activité) : défaut 10 000.

15.4 Onglet « Vape »

Carte « Référence tabac (avant l'arrêt) » :

  • Date d'arrêt du tabac (datepicker — ancre des économies et jalons) · Cigarettes par jour (défaut 15) · Cigarettes par paquet (défaut 20) · Prix du paquet (€) (défaut 12,50) — ligne calculée : Coût tabac de référence : 9,38 €/j.

Carte « Modèle de coût DIY » :

  • Base PG/VG : prix (€) + contenance (ml) → €/ml calculé.
  • Booster de nicotine : prix du flacon (€), contenance (ml, défaut 10), concentration (mg/ml, défaut 20).
  • Taux de nicotine cible (mg/ml) de la préparation (défaut 3,0) → part booster calculée.
  • Arôme : prix (€), contenance (ml), dosage ( %) (défaut 10 %).
  • Résistances : prix unitaire (€) (+ prix du pack et nombre, au choix).
  • Contenance de flacon par défaut (ml) pour le quick-add recharge (défaut 10).
  • Encart calculé : Coût de revient : 0,28 €/ml · avec résistances : ≈ 0,62 €/j au rythme actuel.
  • Objectif de réduction (optionnel) : ml/jour visés et/ou taux nicotine visé → matérialisés en markLine sur G1/G2 de la page Vape.

15.5 Onglet « Finances »

Quatre cartes :

  1. Comptes — table CRUD : Nom (« Compte courant ») · Type (Courant / Épargne / PayPal / Espèces / Autre) · Banque · Solde initial + Date du solde initial · Devise (EUR fixe v1) · actions ✎ 🗑 (suppression bloquée si transactions liées — proposer l'archivage).
  2. Catégories — liste hiérarchique (2 niveaux max) : pastille couleur (slot proposé automatiquement, modifiable parmi les 8), icône (picker), nom ; catégories par défaut livrées : Alimentation, Logement, Transports, Santé, Loisirs, Abonnements, Restaurants, Shopping, Vape/Tabac, Épargne, Revenus (type revenu), Autres. Drag pour réordonner.
  3. Règles de catégorisation — table : Priorité (drag & drop, la première règle qui matche gagne) · Condition (résumé lisible : « Libellé contient "CARREFOUR" ») · Catégorie · Nb de transactions touchées · actif (switch) · actions ✎ 🗑. Bouton « Tester les règles » : ré-exécute sur les non-catégorisées (aperçu avant application).
  4. Budgets mensuels — table : catégorie · montant €/mois (input inline) · switch « reporter le non-dépensé » (v2, désactivé) ; ligne de total.

15.6 Onglet « Appareils & API »

  • Texte d'intro : « Créez une clé pour permettre à l'application compagnon Android (Health Connect) d'envoyer ses données à LifeTrack. »
  • Table des clés : Nom (« Pixel de Julien ») · Préfixe (ltk_a3f4…) · Portées (badges : health:write, weight:write…) · Créée le · Dernière utilisation (« il y a 2 h » — vert si < 24 h) · actions : 🗑 « Révoquer » (confirmation).
  • Bouton « Créer une clé » → modale : Nom, portées (checkboxes), puis écran d'affichage unique de la clé complète avec bouton copier + avertissement « Cette clé ne sera plus jamais affichée. »
  • Encart développeur repliable : méthode + URL du endpoint d'ingestion (POST /api/v1/ingest), en-tête Authorization: Bearer <clé>, exemple de payload JSON minimal.
  • Journal de synchronisation : 20 dernières réceptions (date, appareil, types de données, nb d'enregistrements, statut).

15.7 Onglet « Application »

  • Thème : Sombre (défaut) / Clair / Automatique. · Langue : Français (v1). · Fuseau horaire : Europe/Paris (info, non modifiable v1). · Premier jour de la semaine : Lundi.
  • Accessibilité : switch « Motifs et textures sur les graphiques » (active les textures ECharts decal pour daltonisme/impression).
  • Sauvegarde : « Exporter toutes mes données (JSON) » · « Exporter (CSV par module, ZIP) ».
  • Zone dangereuse (bordure rouge) : « Vider un module… » (select module + confirmation par saisie du mot SUPPRIMER) · « Supprimer le compte ».
  • Utilisateurs (préparation multi-utilisateur) : table des utilisateurs (v1 : le seul admin) + bouton « Inviter » désactivé avec badge « bientôt ».

15.8 Assistant de premier lancement (first-run wizard)

Plein écran, 4 étapes, jamais bloquant (« Passer » partout sauf étape 1) :

  1. « Créez votre compte administrateur » — e-mail, mot de passe (×2, jauge de robustesse).
  2. « Votre profil » — sexe, naissance, taille, poids actuel, niveau d'activité (§15.1).
  3. « Votre objectif » — poids cible + rythme (§15.2), affichage immédiat du budget kcal.
  4. « Vos modules » — 3 cartes activables : Vape (si activée → mini-formulaire baseline tabac §15.4), Finances (créer le premier compte), Import (lien vers /imports). Bouton final : « C'est parti → » (vers le tableau de bord).

16. États vides

Chaque page/section a un EmptyState (§5.4) avec ce texte exact :

Contexte Titre Texte Actions
Tableau de bord (tout vide) « Bienvenue sur LifeTrack 👋 » « Configurez votre profil et importez vos premières données pour voir vos tableaux de bord prendre vie. » « Configurer mon profil » · « Importer des données »
Poids « Aucune pesée pour l'instant » « Ajoutez votre première pesée ou importez un historique — la tendance et les projections apparaîtront dès 3 pesées. » « + Ajouter une pesée » · « Importer un CSV »
Nutrition (journal du jour) « Rien dans le journal aujourd'hui » « Ajoutez votre premier aliment ou importez votre historique Foodvisor depuis la page Imports. » « + Ajouter un aliment » · « Importer Foodvisor »
Activité « Aucune activité enregistrée » « Connectez l'application compagnon Health Connect, importez un export FitShow, ou saisissez une séance manuellement. » « + Ajouter une séance » · « Voir les imports »
Balance énergétique « Il manque des données » « La balance énergétique a besoin de vos pesées et de votre journal alimentaire. Complétez ces deux modules pour débloquer cette page. » « Aller à Poids » · « Aller à Nutrition »
Vape « Module vape non configuré » « Renseignez votre consommation de cigarettes avant l'arrêt et votre modèle de coût : LifeTrack calculera vos économies au centime près. » « Configurer la vape »
Vape (configurée, sans données) « Aucune recharge enregistrée » « Enregistrez votre première recharge d'e-liquide — deux clics suffisent. » « + Recharge »
Finances « Aucune transaction » « Importez un relevé bancaire (CSV ou OFX) ou un export PayPal pour démarrer. La catégorisation automatique fera le tri. » « Importer un relevé »
Finances → Récurrents « Pas encore de récurrents détectés » « La détection a besoin d'au moins 3 mois de transactions pour repérer vos abonnements et charges fixes. »
Imports (historique) « Aucun import pour l'instant » « Déposez un fichier ci-dessus : LifeTrack détecte le format et vous montre un aperçu avant d'importer quoi que ce soit. »
Table filtrée sans résultat « Aucun résultat » « Aucune ligne ne correspond à ces filtres. » « Réinitialiser les filtres »
Graphique sans données sur la période (dans le canvas) « Pas de données sur cette période » « Élargir la période » (passe à Tout)

17. Accessibilité

  • Palette validée (script de validation CVD — voir §4.4) ; l'ordre des slots ne doit pas être modifié sans re-validation.
  • La couleur n'est jamais seule : légendes systématiques dès 2 séries, signes +/, icônes de tendance, labels directs sélectifs, badges texte sur les statuts.
  • « Voir les données » sur chaque ChartCard = équivalent tabulaire complet (triable, exportable CSV).
  • Option « Motifs et textures » (§15.7) : applique des hachures decal à 45°/135° sur les séries des graphiques empilés et miroirs.
  • Navigation clavier complète : sidebar et tabs focusables, modales avec focus trap, Esc ferme, tables navigables aux flèches.
  • Contrastes : textes ≥ 4,5:1 sur surface ; les 8 slots de série ≥ 3:1 sur #1A1A19.
  • prefers-reduced-motion : désactive l'animation des graphiques et le compteur odometer (affichage direct).
  • Tooltips ECharts doublés d'un aria-label descriptif par graphique (« Graphique en ligne : évolution du poids du 15/05 au 13/08, de 85,1 à 82,4 kg »).

Fin de la spécification UX. Toute divergence d'implémentation doit être signalée et arbitrée contre ce document.