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>
873 lines
66 KiB
Markdown
873 lines
66 KiB
Markdown
# 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](#1-principes-globaux-et-layout)
|
||
2. [Formatage français (nombres, dates, unités)](#2-formatage-français)
|
||
3. [Sémantique des couleurs](#3-sémantique-des-couleurs)
|
||
4. [Design system (tokens Tailwind, composants, palette charts)](#4-design-system)
|
||
5. [Composants transverses](#5-composants-transverses)
|
||
6. [Configuration ECharts commune](#6-configuration-echarts-commune)
|
||
7. [Page — Tableau de bord](#7-page--tableau-de-bord)
|
||
8. [Page — Poids & Objectif](#8-page--poids--objectif)
|
||
9. [Page — Nutrition](#9-page--nutrition)
|
||
10. [Page — Activité & Sport](#10-page--activité--sport)
|
||
11. [Page — Balance énergétique](#11-page--balance-énergétique)
|
||
12. [Page — Vape](#12-page--vape)
|
||
13. [Page — Finances](#13-page--finances)
|
||
14. [Page — Imports](#14-page--imports)
|
||
15. [Page — Réglages](#15-page--réglages)
|
||
16. [États vides (empty states) — récapitulatif](#16-états-vides)
|
||
17. [Accessibilité](#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 (768–1023) | 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` | 1–2 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`)
|
||
|
||
```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 « 1–20 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` :
|
||
|
||
```ts
|
||
// 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 80–100 %, 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 |rythme| ≥ objectif dans le bon sens) |
|
||
| **IMC** | `26,3` | `Surpoids` (catégories : `< 18,5 Maigreur` · `18,5–25 Corpulence normale` · `25–30 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. `Objectif` — `markLine` 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 : 20–300 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., 11–15 h déjeuner, 15–18 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` : `< 0` → `semantic-positive` (déficit), `> 0` → `semantic-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 80–100 %, 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.*
|