Files
gilles 2129da4f55 fix(notes): audio overflow + volume 50%, grille 3col, bouton laptop, sidebar BDD
- Audio : minWidth:0 + onLoadedMetadata volume=0.5 (plus de débordement)
- Grille : repeat(3,1fr) sur laptop, 1fr sur mobile (était auto-fill)
- Header laptop : bouton "Nouvelle note" (fa-plus + accent) visible lg:flex
- SideNav : DbStatusBar en bas — LED verte/rouge + taille BDD, polling 30s
- docs/plan.md : Phase 4c documentée

v0.5.7

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-25 16:51:44 +02:00

351 lines
17 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# HomeHub — Plan de développement
> Approche itérative : chaque phase livre quelque chose d'utilisable.
> Stack : FastAPI · React/Vite/TS · PostgreSQL 16 · Docker Compose
---
## Phase 1 — Socle technique ✅
**Objectif** : environnement opérationnel, rien de métier, mais tout tourne.
### Backend
- [x] Structure projet FastAPI (`app/api/`, `app/core/`, `app/models/`, `app/schemas/`, `app/services/`)
- [x] Configuration SQLAlchemy 2.0 async + pool de connexions
- [x] Création des schémas PostgreSQL (`todos`, `shopping`, `notes`) via Alembic
- [x] Migration initiale : toutes les tables (voir spec section 3)
- [x] Endpoint santé : `GET /api/health`
- [x] Module media (`app/services/media.py`) :
- [x] Validation des formats acceptés (JPG, PNG, SVG, WebP, WebM, M4A)
- [x] Génération miniature Pillow : 150×150 (shopping), 300×300 (notes), 400×300 (inline)
- [x] Sauvegarde `originals/` + `thumbnails/` sur le volume
- [x] Endpoint `POST /api/media/upload` → retourne `{ file_path, thumbnail_path }`
- [x] Endpoint `DELETE /api/media/{uuid}` → supprime original + miniature
- [x] Middleware CORS pour réseau local `10.0.0.0/22`
- [x] Configuration via variables d'environnement (`.env`)
- [x] Dockerfile backend (Python 3.12 slim)
### Frontend
- [x] Scaffold Vite + React 18 + TypeScript
- [x] Tailwind CSS configuré avec les tokens Gruvbox (via `tokens.json`)
- [x] Intégration `design_system/components/ui-kit.jsx`
- [x] `@vite-pwa/plugin` configuré (Service Worker + manifest)
- [x] `manifest.json` avec icônes iOS + Android
- [x] Routage React Router v6
- [x] Layout de base : navigation mobile (bottom bar) + navigation laptop (sidebar)
- [x] Dockerfile frontend (Nginx + proxy `/api/` → backend)
- [x] Favicon maison (SVG Gruvbox orange)
### Infra
- [x] `docker-compose.yml` avec 3 services (frontend, backend, db)
- [x] Volume PostgreSQL persistant
- [x] Volume uploads persistant (`/uploads/`)
- [x] Fichier `.env.example`
- [x] Script de seed (`backend/app/data/seed.py`) : 113 produits + 9 magasins
---
## Phase 2 — Module Todos ✅
**Objectif** : créer, lister, modifier, terminer et reporter des tâches depuis mobile et laptop.
### Backend
- [x] `GET /api/todos` — liste avec filtres (domaine, statut, priorité, tags, période)
- [x] `POST /api/todos` — création
- [x] `PATCH /api/todos/{id}` — mise à jour partielle
- [x] `DELETE /api/todos/{id}` — suppression
- [x] `POST /api/todos/{id}/postpone` — incrémente postponed_count + décale due_date
- [x] Schémas Pydantic : TodoCreate, TodoUpdate, TodoResponse
- [x] Tests d'intégration (9 tests)
### Frontend Mobile
- [x] Page Todos : liste des tâches en cours, groupées par domaine
- [x] Bouton "+ Tâche" flottant → Modal création (titre + domaine + date + priorité + tags)
- [x] Double-tap → Modal édition pré-rempli
- [x] Swipe droite → marquer done
- [x] Swipe gauche → actions (reporter 1j / reporter 1 semaine / supprimer)
- [x] Badge compteur par domaine
### Frontend Laptop
- [x] Vue tableau avec filtres : domaine, statut, priorité, période
- [x] Formulaire complet (tous les champs) via Modal
- [x] Double-clic sur titre → Modal édition
- [x] Actions inline : ✓ done / +1j / +1S / ✕ supprimer
---
## Phase 3 — Module Shopping (liste de courses) ✅
**Objectif** : liste de courses fonctionnelle en magasin depuis smartphone, avec génération automatique.
### Backend
- [x] `GET /api/shopping/stores` — liste magasins
- [x] `GET /api/shopping/products` — catalogue avec recherche (param `q`)
- [x] `GET /api/shopping/lists` — listes de courses avec compteurs
- [x] `POST /api/shopping/lists` — création liste
- [x] `GET /api/shopping/lists/{id}` — détail liste avec articles
- [x] `PATCH /api/shopping/lists/{id}` — mise à jour liste
- [x] `DELETE /api/shopping/lists/{id}` — suppression
- [x] `POST /api/shopping/lists/{id}/items` — ajout article (produit catalogue ou custom)
- [x] `PATCH /api/shopping/lists/{id}/items/{item_id}` — cocher / modifier
- [x] `DELETE /api/shopping/lists/{id}/items/{item_id}` — suppression article
- [x] `POST /api/shopping/lists/{id}/finish` — terminer les courses (report articles non cochés → nouvelle liste draft avec `carried_over=True`)
- [x] `POST /api/shopping/lists/generate` — liste magique V1 (score = retard / intervalle moyen, articles cochés comme historique)
- [x] Schémas Pydantic complets (listes, articles, produits, magasins)
- [x] Tests d'intégration (10 tests)
- [x] Champ `tags TEXT[]` sur les produits du catalogue (migration 006)
### Frontend
- [x] Page Shopping avec 3 vues : liste des listes / détail / mode magasin
- [x] Vue "listes" : cartes par liste (statut, compteur articles), FAB + bouton "Liste magique"
- [x] Bouton "Liste magique" : désactivé si une liste `draft`/`active` existe
- [x] Vue "détail" : articles avec swipe-to-delete, FAB ajout article, bouton ✏️ modal gestion
- [x] Modal ✏️ : ajout rapide d'article + bouton rouge "Supprimer la liste en cours"
- [x] Vue "Mode magasin" : plein écran, grands boutons (48px+), section cochés/non cochés
- [x] Wake Lock API activée automatiquement en mode magasin
- [x] Composant `Modal` générique réutilisable (overlay + Escape + stopPropagation)
- [x] Composant `ItemRow` : checkbox circulaire + swipe-to-delete + mode magasin
- [x] Hook `useWakeLock` avec fallback gracieux (mode économie d'énergie)
- [x] Client API TypeScript typé (`frontend/src/api/shopping.ts`)
- [x] Migration TodoForm vers Modal (plus de panneau inline)
- [x] Bottom sheet multi-select pour l'ajout d'articles depuis le catalogue
- [x] Stats d'achat dans le catalogue : dernier achat + intervalle moyen par produit
- [x] Swipe gauche → modal édition (TodoPage + ShoppingPage)
- [x] Tags sur les produits : chip-input dans CatalogueModal, recherche étendue aux tags
- [x] Recherche article : `type="search"` supprime la suggestion URL iOS, `autoFocus` automatique
- [x] Upload photo produit : support HEIC/HEIF, redimensionnement 500×500 max (ratio conservé), miniature auto
- [x] Collage Ctrl+V photo dans CatalogueModal et TodoForm
---
## Phase 4 — Module Notes ✅
**Objectif** : saisie rapide de notes avec photo, audio et GPS depuis smartphone.
### Backend
- [x] `GET /api/notes` — liste avec search full-text + filtres tags/catégorie
- [x] `POST /api/notes` — création
- [x] `PATCH /api/notes/{id}` — mise à jour
- [x] `DELETE /api/notes/{id}` — suppression
- [x] `POST /api/notes/{id}/attachments` — upload fichier (image/audio)
- [x] `DELETE /api/notes/{id}/attachments/{att_id}` — suppression pièce jointe
- [x] Compression image Pillow → WebP (service media partagé)
- [x] Recherche FTS PostgreSQL français
### Frontend Mobile
- [x] Page Notes : liste chronologique avec aperçu
- [x] Bouton "+ Note" → formulaire rapide (contenu + tags) via Modal
- [x] Bouton 📷 → Camera API (capture directe ou import galerie)
- [x] Bouton 🎤 → MediaRecorder (enregistrement audio inline)
- [x] Bouton 📍 → Geolocation API → affichage coordonnées
- [x] Visionneuse photo inline + lecteur audio inline
- [ ] Compression WebP côté client (Canvas API) — différé Phase 5+
- [ ] Waveform visuel enregistrement — différé Phase 5+
### Frontend Laptop
- [x] Grille notes avec vignettes photo
- [x] Recherche full-text
- [x] Filtres rapides : avec photo, avec audio, avec GPS
- [ ] Vue carte pour les notes avec GPS (Leaflet.js) — différé Phase 5+
---
## Phase 4c — Notes v2 : états de tuile + médias ✅
**Objectif** : refonte de l'interface Notes avec tuiles à 3 états, support vidéo, transcodage audio universel.
### Backend
- [x] `ffmpeg` dans le Dockerfile backend — transcodage audio et vidéo
- [x] `save_audio()` : transcode toute entrée (webm/ogg/m4a) → AAC `.m4a` — lecture Safari iOS garantie
- [x] `save_video()` : stockage mp4/quicktime direct, webm → H.264/mp4 via ffmpeg
- [x] `ALLOWED_VIDEO_TYPES` : mp4, webm, quicktime, m4v, 3gpp
- [x] `GET /api/notes` : filtre `has_video` ajouté
- [x] `POST /api/notes/{id}/attachments` : gère `file_type = "video"`
- [x] `GET /api/admin/stats` : section `media.video` (count + size_bytes)
- [x] Schémas Pydantic notes : `gps_lat/gps_lon` passés en `float | None` (fix `Decimal` sérialisé en string → TypeError JS)
### Frontend
- [x] NoteCard à 3 états : **semi** (défaut, 3 lignes + actions) / **expanded** (markdown complet + médias) / **collapsed** (titre + date)
- [x] Bouton toggle `fa-chevron-down / fa-minus / fa-chevron-right` dans le coin haut-droit de chaque tuile
- [x] Renderer pseudo-markdown : `# ## ###`, `- * 1.` listes, `> citations`, `---`, `` **gras** *italique* `code` ``, ` ``` ` blocs
- [x] Icônes méta sur la tuile : `fa-image` / `fa-microphone` / `fa-video` / `fa-location-dot`
- [x] Bouton vidéo `fa-video` dans les actions de chaque note
- [x] Lecteur `<video playsInline>` inline dans l'état expanded
- [x] Audio : `onLoadedMetadata` règle le volume à 50% + fix overflow (`minWidth: 0`)
- [x] Filtres rapides : Photo / Audio / Vidéo / GPS (avec icônes Font Awesome)
- [x] Grille : 3 colonnes max sur laptop (`repeat(3, 1fr)`), 1 colonne sur mobile
- [x] Bouton "Nouvelle note" dans le header, visible sur laptop uniquement
- [x] Sidebar laptop : indicateur de statut BDD (LED verte/rouge + taille) avec polling 30s sur `/api/health`
- [x] ConfigPage : grille médias 3 colonnes (Photos / Audio / Vidéos)
- [x] `client_max_body_size nginx` : 15m → 200m pour les vidéos
- [x] `docker-compose.yml` : `user: "1000:1000"` sur backend et backend-worker
---
## Phase 4b — UX transversale ✅
**Objectif** : améliorations d'expérience applicables à tous les modules.
### Thème et apparence
- [x] `ThemeContext` : gestion dark / light / system avec persistance `localStorage`
- [x] Script anti-flash dans `index.html` : thème et zoom appliqués avant le premier rendu
- [x] Page `/config` : boutons thème + slider police (0.81.4, pas de 0.05)
- [x] Aperçu temps réel du texte sur le slider
- [x] Zoom CSS sur `<html>` : scale global compatible avec les tailles en pixels fixes
- [x] `TopBar` : en-tête fixe 44px avec bouton de thème cyclique (lune / soleil / demi-cercle)
- [x] Tuile "Paramètres" sur la page d'accueil (`/config`, icône `fa-sliders`)
### Mobile — clavier iOS
- [x] `BottomSheet` : `visualViewport` API pour remonter le panneau au-dessus du clavier virtuel
- [x] Transition fluide (0.15s) sur `bottom`, `max-height`, `border-radius` à l'ouverture du clavier
### Todos
- [x] Case "Pas de date" pré-cochée par défaut dans `TodoForm` (champ date masqué)
- [x] Collage Ctrl+V pour coller une image directement dans le formulaire Todo
### Infra / Media
- [x] nginx : `location ^~ /media/` pour éviter que la règle regex WebP prenne la priorité
- [x] nginx : `client_max_body_size 15m` pour les photos de smartphone (38 Mo)
---
## Phase 5 — Export Markdown notes + Backup BDD ✅
**Objectif** : persistance double des notes (BDD source de vérité + fichiers Markdown partagés), infrastructure Redis, backup/restore depuis l'interface.
### Architecture
- Volume Docker `./data/` (bind mount) remplace le volume nommé `uploads`
- `data/notes/` — fichiers `.md` auto-générés
- `data/uploads/` — médias (photos, audio)
- `data/backup/` — dumps PostgreSQL
### Backend
- [x] Service Redis : `redis:7-alpine` dans docker-compose
- [x] Worker ARQ (`backend-worker`) : consomme la queue `notes:markdown`, même image que backend
- [x] `app/core/redis.py` — pool ARQ, `enqueue()` best-effort (silence si Redis down)
- [x] `app/workers/notes_worker.py` — tâches `export_note_markdown` + `remove_note_markdown`
- [x] `app/services/notes_markdown.py` — génère le frontmatter YAML + contenu + pièces jointes (liens relatifs `../uploads/…`)
- Nom de fichier : `YYYY-MM-DD_slug-du-titre_shortid.md`
- Rotation automatique si titre modifié (recherche par `*_{shortid}.md`)
- [x] `app/api/notes.py` — publie sur Redis après create / update / delete / add_attachment / delete_attachment
- [x] `app/api/admin.py``POST /api/admin/backup`, `GET /api/admin/backups`, `POST /api/admin/restore/{filename}`
- [x] `backend/Dockerfile` — ajout `postgresql-client` pour pg_dump / pg_restore
- [x] `requirements.txt` — ajout `arq==0.26.1`
### Frontend
- [x] `frontend/src/api/admin.ts` — client TypeScript backup/restore
- [x] Page Config — section "Base de données" : bouton sauvegarde + liste des dumps + bouton Restaurer par fichier
---
## Phase 6 — Scan produits + enrichissement catalogue
**Objectif** : scan code-barres depuis mobile, auto-remplissage depuis OpenFoodFacts.
### Services Docker
- [ ] Dockerfile `product-search/` : Python slim + `requests` + client OpenFoodFacts
- [ ] Ajout service `product-search` dans `docker-compose.yml` (port 8002)
- [ ] Ajout service `searxng` dans `docker-compose.yml` (images uniquement)
### Backend (endpoints proxy)
- [ ] `GET /api/products/lookup?barcode=` → proxifie vers product-search
- [ ] `GET /api/products/search?q=` → proxifie vers product-search
- [ ] `POST /api/products/import` → crée produit depuis données OpenFoodFacts
### Frontend Mobile — scan
- [ ] Intégration `zxing-js` (BarcodeReader via flux caméra)
- [ ] Bouton "Scanner" dans le formulaire d'ajout de produit
- [ ] Sur scan réussi → auto-remplissage formulaire
### Frontend Laptop — enrichissement catalogue
- [ ] CRUD complet produits dans catalogue
- [ ] Recherche texte → OpenFoodFacts → import 1 clic
- [ ] Gestion magasins
---
## Phase 7 — Service OCR (conteneur dédié)
**Objectif** : OCR partagé, prérequis pour shopping avancé et notes avancées.
- [ ] Dockerfile `ocr/` : Python slim + Tesseract 5 + langues (fra, eng) + Pillow
- [ ] `POST /extract` : reçoit image (multipart), retourne `{ text, confidence }`
- [ ] Endpoint backend `POST /api/ocr/extract` : proxifie vers `ocr:8001`
- [ ] Ajout du service `ocr` dans `docker-compose.yml`
---
## Phase 8 — Shopping avancé (OCR + suivi prix)
### Backend
- [ ] `POST /api/shopping/ocr/price-tag` — photo étiquette → extraction prix
- [ ] `POST /api/shopping/ocr/receipt` — photo ticket → reconciliation liste
- [ ] `GET /api/shopping/products/{id}/price-history` — historique prix par produit + magasin
### Frontend
- [ ] Bouton 📷 sur chaque article → OCR étiquette → pré-remplissage prix
- [ ] Graphique d'évolution du prix par produit (Recharts)
- [ ] Comparaison prix par magasin
---
## Phase 9 — MCP Server
**Objectif** : exposer les outils HomeHub aux agents IA (Hermes, Claude, etc.).
- [ ] Intégration SDK MCP Python (`mcp` package)
- [ ] Endpoint SSE `/mcp`
- [ ] Implémentation des outils : `get_todos()`, `add_todo()`, `add_shopping_item()`, `search_notes()`
- [ ] Documentation OpenAPI des outils MCP
---
## Phases futures (hors scope initial)
### Phase 9 — Authentification multi-utilisateurs
- Auth JWT (login / refresh token)
- Activation de `owner_id` dans toutes les tables
- Page connexion React
### Phase 10 — Calendrier + intégrations
- Sync Google Calendar (OAuth2 + worker)
- Endpoint CalDAV pour iOS
- Redis pour la queue de synchronisation
- Webhooks Gitea → Kanban
- Home Assistant : capteur "tâches en retard"
### Phase 11 — Vision IA
- Endpoint analyse frigo (photo → suggestions liste de courses)
- Amélioration OCR via modèle Vision local (Ollama)
### Phase 12 — Éditeur Markdown pour les Notes
Refonte du module Notes autour d'un vrai éditeur Markdown orienté mobile.
**Concept** : une seule interface unifiée — champ titre + corps éditeur — avec une barre d'outils flottante au-dessus du clavier.
**Barre d'outils (ordre d'affichage)** :
- `H` — Titre (`# `)
- `•` — Liste à puces (`- `)
- `1.` — Liste numérotée (`1. `)
- `</>` — Bloc code (`` ` `` inline ou ` ``` ` bloc)
- 📷 — Insérer image (upload direct, syntaxe `![](path)`)
- 🎤 — Enregistrement audio (MediaRecorder, lien `[enregistrement.m4a](path)`)
- 📍 — Position GPS (insère `[GPS](geo:lat,lon)` ou coordonnées brutes)
**Rendu** : aperçu Markdown en lecture (pas d'édition WYSIWYG pure — mode split ou toggle édit/aperçu).
**Contraintes techniques** :
- Bibliothèque candidate : `@uiw/react-md-editor` (légère, compatible mobile) ou éditeur custom avec `textarea` + parsing `marked`/`micromark`
- La barre d'outils doit rester au-dessus du clavier virtuel iOS/Android (même logique que `BottomSheet` avec `visualViewport`)
- Compatibilité avec le format des fichiers `.md` déjà exportés en Phase 5 (frontmatter YAML conservé)
- Le backend ne change pas : `content` reste un champ texte libre (Markdown brut stocké tel quel)
---
## Ordre de développement
```
Phase 1 ✅ → Phase 2 ✅ → Phase 3 ✅ → Phase 4 ✅ → Phase 4b ✅ → Phase 5 ✅ → Phase 6 (Scan) → Phase 7 (OCR) → Phase 8 (Shopping avancé) → Phase 9 (MCP)
```