chore: ajout .gitignore, CLAUDE.md, design system et docs Phase 2
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
+258
@@ -0,0 +1,258 @@
|
||||
# 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
|
||||
- [ ] Structure projet FastAPI (`app/api/`, `app/core/`, `app/models/`, `app/schemas/`, `app/services/`)
|
||||
- [ ] Configuration SQLAlchemy 2.0 async + pool de connexions
|
||||
- [ ] Création des schémas PostgreSQL (`todos`, `shopping`, `notes`) via Alembic
|
||||
- [ ] Migration initiale : toutes les tables (voir spec section 3)
|
||||
- [ ] Endpoint santé : `GET /api/health`
|
||||
- [ ] Module media (`app/services/media.py`) :
|
||||
- [ ] Validation des formats acceptés (JPG, PNG, SVG, WebP, WebM, M4A)
|
||||
- [ ] Génération miniature Pillow : 150×150 (shopping), 300×300 (notes), 400×300 (inline)
|
||||
- [ ] Sauvegarde `originals/` + `thumbnails/` sur le volume
|
||||
- [ ] Endpoint `POST /api/media/upload` → retourne `{ file_path, thumbnail_path }`
|
||||
- [ ] Endpoint `DELETE /api/media/{uuid}` → supprime original + miniature
|
||||
- [ ] Middleware CORS pour réseau local `10.0.0.0/22`
|
||||
- [ ] Configuration via variables d'environnement (`.env`)
|
||||
- [ ] Dockerfile backend (Python 3.12 slim)
|
||||
|
||||
### Frontend
|
||||
- [ ] Scaffold Vite + React 18 + TypeScript
|
||||
- [ ] Tailwind CSS configuré avec les tokens Gruvbox (via `tokens.json`)
|
||||
- [ ] Intégration `design_system/components/ui-kit.jsx`
|
||||
- [ ] `@vite-pwa/plugin` configuré (Service Worker + manifest)
|
||||
- [ ] `manifest.json` avec icônes iOS + Android
|
||||
- [ ] Routage React Router v6
|
||||
- [ ] Layout de base : navigation mobile (bottom bar) + navigation laptop (sidebar)
|
||||
- [ ] Dockerfile frontend (Nginx)
|
||||
|
||||
### Infra
|
||||
- [ ] `docker-compose.yml` avec 3 services (frontend, backend, db)
|
||||
- [ ] Volume PostgreSQL persistant
|
||||
- [ ] Volume uploads persistant (`/uploads/`)
|
||||
- [ ] `docker-compose.dev.yml` avec hot reload (volumes montés)
|
||||
- [ ] Fichier `.env.example`
|
||||
- [ ] Script `dev.sh` pour démarrage local rapide
|
||||
- [ ] Script de seed (`backend/app/data/seed.py`) : charge `seed_products.json` (113 produits) + `seed_stores.json` (9 magasins) en base au premier démarrage
|
||||
|
||||
---
|
||||
|
||||
## Phase 2 — Module Todos
|
||||
|
||||
**Objectif** : créer, lister, modifier, terminer et reporter des tâches depuis mobile et laptop.
|
||||
|
||||
### Backend
|
||||
- [ ] `GET /api/todos` — liste avec filtres (domaine, statut, priorité, tags)
|
||||
- [ ] `POST /api/todos` — création
|
||||
- [ ] `PATCH /api/todos/{id}` — mise à jour partielle
|
||||
- [ ] `DELETE /api/todos/{id}` — suppression
|
||||
- [ ] `POST /api/todos/{id}/postpone` — incrémente postponed_count + décale due_date
|
||||
- [ ] Schémas Pydantic : TodoCreate, TodoUpdate, TodoResponse
|
||||
|
||||
### Frontend Mobile
|
||||
- [ ] Page Todos : liste des tâches en cours, groupées par domaine
|
||||
- [ ] Bouton "+ Tâche" flottant → formulaire rapide (titre + domaine + date optionnelle)
|
||||
- [ ] Swipe droite → marquer done
|
||||
- [ ] Swipe gauche → actions (reporter 1j / reporter 1 semaine / supprimer)
|
||||
- [ ] Badge compteur par domaine
|
||||
|
||||
### Frontend Laptop
|
||||
- [ ] Vue tableau avec filtres : domaine, statut, priorité, tags, période
|
||||
- [ ] Formulaire complet (tous les champs)
|
||||
- [ ] Tri multi-colonnes
|
||||
- [ ] Sélection multiple + actions groupées
|
||||
|
||||
---
|
||||
|
||||
## Phase 3 — Module Notes / Listes diverses
|
||||
|
||||
**Objectif** : saisie rapide de notes avec photo, audio et GPS depuis smartphone.
|
||||
|
||||
### Backend
|
||||
- [ ] `GET /api/notes` — liste avec search full-text + filtres tags/catégorie
|
||||
- [ ] `POST /api/notes` — création
|
||||
- [ ] `PATCH /api/notes/{id}` — mise à jour
|
||||
- [ ] `DELETE /api/notes/{id}` — suppression
|
||||
- [ ] `POST /api/notes/{id}/attachments` — upload fichier (image/audio)
|
||||
- [ ] `DELETE /api/notes/{id}/attachments/{att_id}` — suppression pièce jointe
|
||||
- [ ] Compression image Pillow → WebP (fallback serveur)
|
||||
- [ ] Recherche FTS PostgreSQL français
|
||||
|
||||
### Frontend Mobile
|
||||
- [ ] Page Notes : liste chronologique avec aperçu
|
||||
- [ ] Bouton "+ Note" → formulaire rapide (contenu + tags)
|
||||
- [ ] Bouton 📷 → Camera API (capture directe ou import galerie)
|
||||
- [ ] Bouton 🎤 → MediaRecorder (enregistrement audio avec waveform visuel)
|
||||
- [ ] Bouton 📍 → Geolocation API → affichage coordonnées
|
||||
- [ ] Compression WebP côté client (Canvas API) avant upload
|
||||
- [ ] Visionneuse photo inline
|
||||
- [ ] Lecteur audio inline
|
||||
- [ ] Bouton 📄 → OCR sur photo → texte extrait inséré dans le contenu de la note
|
||||
|
||||
### Frontend Laptop
|
||||
- [ ] Grille notes avec vignettes photo
|
||||
- [ ] Recherche full-text avec surlignage
|
||||
- [ ] Filtres : catégorie, tags, avec photo, avec audio, avec GPS
|
||||
- [ ] Formulaire étendu avec champs métadonnées libres (clé/valeur)
|
||||
- [ ] Vue carte pour les notes avec GPS (Leaflet.js)
|
||||
|
||||
---
|
||||
|
||||
## Phase 4 — Module Shopping (base)
|
||||
|
||||
**Objectif** : liste de courses fonctionnelle en magasin depuis smartphone.
|
||||
|
||||
### Backend
|
||||
- [ ] `GET /api/shopping/products` — catalogue avec recherche
|
||||
- [ ] `POST /api/shopping/products` — ajout produit catalogue
|
||||
- [ ] `PATCH /api/shopping/products/{id}` — modification produit
|
||||
- [ ] `GET /api/shopping/stores` — liste magasins
|
||||
- [ ] `POST /api/shopping/stores` — ajout magasin
|
||||
- [ ] `GET /api/shopping/lists` — listes de courses
|
||||
- [ ] `POST /api/shopping/lists` — création liste (avec génération auto depuis frequency_score)
|
||||
- [ ] `GET /api/shopping/lists/{id}/items` — articles de la liste
|
||||
- [ ] `POST /api/shopping/lists/{id}/items` — ajout article
|
||||
- [ ] `PATCH /api/shopping/list_items/{id}` — cocher / modifier prix relevé
|
||||
- [ ] `POST /api/shopping/lists/{id}/finish` — terminer les courses (report automatique articles non cochés → nouvelle liste)
|
||||
- [ ] Logique de report : créer liste semaine+1 avec `carried_over = true` sur articles non cochés
|
||||
|
||||
### Frontend Mobile — Création liste
|
||||
- [ ] Page accueil shopping : liste des semaines
|
||||
- [ ] "Nouvelle liste" → picker semaine + suggestions top produits (frequency_score)
|
||||
- [ ] Recherche produit dans catalogue + ajout 1 tap
|
||||
- [ ] Ajout rapide produit hors catalogue → création à la volée dans catalogue
|
||||
|
||||
### Frontend Mobile — Mode courses
|
||||
- [ ] Vue liste triée par catégorie/rayon
|
||||
- [ ] Wake Lock API activée automatiquement à l'ouverture
|
||||
- [ ] Grand bouton checkbox par article (48px+)
|
||||
- [ ] Tap sur prix → champ numérique rapide
|
||||
- [ ] Swipe gauche → retirer de la liste
|
||||
- [ ] Bouton "Terminer les courses" avec confirmation
|
||||
|
||||
### Frontend Laptop — Gestion catalogue
|
||||
- [ ] Tableau produits avec photo, marque, catégorie, frequency_score
|
||||
- [ ] CRUD complet produits
|
||||
- [ ] Gestion magasins
|
||||
- [ ] Import/export catalogue CSV
|
||||
|
||||
---
|
||||
|
||||
## Phase 4b — Module scan + enrichissement catalogue produits
|
||||
|
||||
**Objectif** : scan code-barres depuis mobile, auto-remplissage depuis OpenFoodFacts, recherche image via SearXNG sur laptop.
|
||||
|
||||
### Services Docker
|
||||
- [ ] Dockerfile `product-search/` : Python slim + `requests` + client OpenFoodFacts
|
||||
- [ ] `GET /lookup?barcode={code}` → OpenFoodFacts barcode API
|
||||
- [ ] `GET /search?q={nom}` → OpenFoodFacts text search
|
||||
- [ ] `GET /image-search?q={nom}` → délègue à SearXNG (images uniquement)
|
||||
- [ ] Ajout service `product-search` dans `docker-compose.yml` (port 8002)
|
||||
- [ ] Ajout service `searxng` dans `docker-compose.yml` (image officielle, port 8080 interne)
|
||||
- [ ] Configuration SearXNG : désactiver toutes les catégories sauf `images`
|
||||
|
||||
### Backend (endpoints proxy)
|
||||
- [ ] `GET /api/products/lookup?barcode=` → proxifie vers product-search
|
||||
- [ ] `GET /api/products/search?q=` → proxifie vers product-search
|
||||
- [ ] `GET /api/products/image-search?q=` → proxifie vers product-search (laptop uniquement)
|
||||
- [ ] `POST /api/products/import` → crée un produit dans le catalogue depuis les données OpenFoodFacts retournées
|
||||
|
||||
### 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 → appel `/api/products/lookup` → auto-remplissage formulaire
|
||||
- [ ] Si barcode inconnu → formulaire vide (saisie manuelle)
|
||||
|
||||
### Frontend Laptop — enrichissement catalogue
|
||||
- [ ] Champ de recherche texte → `/api/products/search` → liste de résultats OpenFoodFacts
|
||||
- [ ] Bouton "Chercher une image" → `/api/products/image-search` → grille de miniatures SearXNG
|
||||
- [ ] Clic sur une image → téléchargement + génération miniature via module media
|
||||
|
||||
---
|
||||
|
||||
## Phase 5 — Service OCR (conteneur dédié, partagé)
|
||||
|
||||
**Objectif** : service OCR partagé, prérequis pour le shopping avancé et les notes avancées.
|
||||
|
||||
- [ ] Dockerfile `ocr/` : image Python slim + Tesseract 5 + langues (fra, eng) + Pillow
|
||||
- [ ] `POST /extract` : reçoit une image (multipart), retourne `{ text, confidence }`
|
||||
- [ ] Pré-processing Pillow : redimensionnement, niveaux de gris, amélioration contraste
|
||||
- [ ] Endpoint backend unifié `POST /api/ocr/extract` : proxifie vers `ocr:8001`
|
||||
- [ ] Gestion du service inactif : retour d'erreur propre sans bloquer le module appelant
|
||||
- [ ] Ajout du service `ocr` dans `docker-compose.yml`
|
||||
|
||||
---
|
||||
|
||||
## Phase 6 — Module Shopping avancé (OCR + suivi prix)
|
||||
|
||||
**Objectif** : OCR étiquettes et tickets, graphiques d'évolution des prix.
|
||||
|
||||
### Backend
|
||||
- [ ] `POST /api/shopping/ocr/price-tag` — photo étiquette → extraction prix via service OCR
|
||||
- [ ] `POST /api/shopping/ocr/receipt` — photo ticket → reconciliation avec liste
|
||||
- [ ] `GET /api/shopping/products/{id}/price-history` — historique prix par produit + magasin
|
||||
|
||||
### Frontend Mobile
|
||||
- [ ] Bouton 📷 sur chaque article → OCR étiquette → pré-remplissage prix
|
||||
- [ ] Bouton "Scanner ticket" en fin de courses → OCR receipt → confirmation reconciliation
|
||||
|
||||
### Frontend Laptop
|
||||
- [ ] Graphique courbe d'évolution du prix par produit (Recharts ou Chart.js)
|
||||
- [ ] Comparaison prix par magasin
|
||||
- [ ] Export CSV historique des prix
|
||||
|
||||
---
|
||||
|
||||
## Phase 7 — 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 6 outils (voir spec section 2.4)
|
||||
- [ ] Tests avec Claude Code (MCP client)
|
||||
- [ ] Documentation OpenAPI des outils MCP
|
||||
|
||||
---
|
||||
|
||||
## Phases futures (hors scope initial)
|
||||
|
||||
---
|
||||
|
||||
## Phases futures (hors scope initial)
|
||||
|
||||
### Phase 8 — Authentification multi-utilisateurs
|
||||
- Auth JWT (login / refresh token)
|
||||
- Activation de `owner_id` dans toutes les tables
|
||||
- Middleware d'authentification FastAPI
|
||||
- Page connexion React
|
||||
|
||||
### Phase 9 — 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 10 — Vision IA
|
||||
- Endpoint analyse frigo (photo → suggestions liste de courses)
|
||||
- Amélioration OCR via modèle Vision local (Ollama)
|
||||
|
||||
---
|
||||
|
||||
## Ordre de développement recommandé
|
||||
|
||||
```
|
||||
Phase 1 → Phase 2 → Phase 4 (shopping base) → Phase 4b (scan + OpenFoodFacts) → Phase 3 (notes) → Phase 5 (OCR) → Phase 6 (shopping avancé) → Phase 7 (MCP)
|
||||
```
|
||||
|
||||
Rationale : les todos sont les plus simples (validation du socle), le shopping mobile de base est prioritaire pour usage réel, les notes viennent ensuite, le service OCR est posé avant d'en avoir besoin, puis les features avancées.
|
||||
+388
@@ -0,0 +1,388 @@
|
||||
# HomeHub — Spécification Fonctionnelle
|
||||
|
||||
> Version 0.1 — mai 2026
|
||||
> Application d'organisation personnelle auto-hébergée (PWA)
|
||||
> Stack : FastAPI · React/Vite/TypeScript · PostgreSQL 16 · Docker Compose
|
||||
|
||||
---
|
||||
|
||||
## 1. Contexte et objectifs
|
||||
|
||||
HomeHub est une PWA auto-hébergée sur Proxmox 9, conçue pour un usage intensif mobile (smartphone en magasin, sur le terrain) et une consultation plus complète sur laptop. L'application est **mono-utilisateur** au démarrage, avec une architecture prévue pour évoluer vers le multi-utilisateur sans refactoring majeur (colonne `owner_id` nullable dans toutes les tables).
|
||||
|
||||
---
|
||||
|
||||
## 2. Architecture technique
|
||||
|
||||
### 2.1 Services Docker Compose (6 services)
|
||||
|
||||
| Service | Image | Port interne | Rôle |
|
||||
|---------|-------|-------------|------|
|
||||
| `frontend` | Nginx léger | 3000 | Sert le build React/Vite (PWA statique) |
|
||||
| `backend` | Python 3.12 / FastAPI | 8000 | API REST + MCP server |
|
||||
| `db` | PostgreSQL 16 | 5432 | Base de données multi-schémas |
|
||||
| `ocr` | Tesseract + Pillow | 8001 | Service OCR partagé (toutes les photos de l'app) |
|
||||
| `product-search` | Python slim | 8002 | Lookup OpenFoodFacts + fallback SearXNG |
|
||||
| `searxng` | SearXNG officiel | 8080 | Métamoteur de recherche — fallback image produits |
|
||||
|
||||
**Services isolés et arrêtables indépendamment :**
|
||||
- `ocr` (4) — arrêtable sans impact sur la liste de courses. Seule la saisie OCR de prix devient indisponible
|
||||
- `product-search` (5) — arrêtable sans impact. Seule la recherche OpenFoodFacts devient indisponible
|
||||
- `searxng` (6) — arrêtable sans impact. Seul le fallback image produit devient indisponible
|
||||
|
||||
Les services 1/2/3 (`frontend`, `backend`, `db`) sont les seuls **obligatoires** pour le fonctionnement de base. Un `docker compose stop ocr product-search searxng` laisse l'app 100% fonctionnelle pour la liste de courses.
|
||||
|
||||
**Nginx Proxy Manager** (déjà en place sur le homelab) gère l'entrée HTTPS et le routage vers ces services.
|
||||
|
||||
### 2.2 Évolutions prévues (non bloquantes)
|
||||
|
||||
- Redis → ajouté avec la sync Google Calendar (Phase 6)
|
||||
- Auth JWT → activation de `owner_id` + écran de connexion (Phase 7)
|
||||
- Agent Hermes (Vision LLM) → analyse photo frigo
|
||||
- Webhooks Gitea → intégration Kanban
|
||||
- Home Assistant → capteurs tâches en retard
|
||||
|
||||
### 2.3 Stockage fichiers et module media
|
||||
|
||||
Volume Docker `/uploads/` servi par le backend. Toutes les images passent par le module `media` du backend à la réception.
|
||||
|
||||
**Pipeline de traitement à l'upload :**
|
||||
1. Compression côté client (Canvas API → WebP) avant envoi — réduit la bande passante
|
||||
2. Validation format serveur (JPG, PNG, SVG, WebP acceptés)
|
||||
3. Génération automatique d'une miniature (Pillow) — stockée séparément
|
||||
4. Les deux fichiers sont enregistrés sur le volume
|
||||
|
||||
**Structure du volume :**
|
||||
```
|
||||
/uploads/
|
||||
images/
|
||||
originals/{uuid}.webp ← image compressée (pleine résolution utile)
|
||||
thumbnails/{uuid}_thumb.webp ← miniature générée par Pillow
|
||||
audio/
|
||||
{uuid}.webm ← enregistrements audio
|
||||
```
|
||||
|
||||
**Tailles de miniatures par contexte :**
|
||||
|
||||
| Contexte | Taille thumbnail |
|
||||
|----------|-----------------|
|
||||
| Produit catalogue shopping | 150 × 150 px (carré, centré) |
|
||||
| Note (vignette liste) | 300 × 300 px (carré, centré) |
|
||||
| Pièce jointe (aperçu inline) | 400 × 300 px (paysage, centré) |
|
||||
|
||||
Les miniatures sont toujours servies pour les vues liste/grille. L'original n'est chargé qu'à la demande (plein écran, zoom). Formats audio acceptés : WebM, M4A.
|
||||
|
||||
### 2.4 MCP Server
|
||||
|
||||
Intégré dans FastAPI sur `/mcp` (protocole SSE). Expose les outils suivants aux agents IA (Hermes, Claude, etc.) :
|
||||
|
||||
| Outil | Description |
|
||||
|-------|-------------|
|
||||
| `get_todos()` | Retourne les tâches urgentes / en cours |
|
||||
| `add_todo(title, due_date)` | Crée une tâche |
|
||||
| `get_shopping_list()` | Retourne la liste de courses active |
|
||||
| `add_shopping_item(name, category)` | Ajoute un article à la liste active |
|
||||
| `search_notes(query)` | Recherche full-text dans les notes |
|
||||
| `add_note(title, content, tags)` | Crée une note |
|
||||
|
||||
---
|
||||
|
||||
## 3. Schéma de base de données
|
||||
|
||||
### 3.1 Schema `shopping`
|
||||
|
||||
#### `shopping.products` — Catalogue global
|
||||
| Colonne | Type | Description |
|
||||
|---------|------|-------------|
|
||||
| id | UUID PK | |
|
||||
| name | VARCHAR(150) | Nom produit |
|
||||
| brand | VARCHAR(100) | Marque |
|
||||
| category | VARCHAR(50) | Rayon magasin (tri in-store) |
|
||||
| image_path | VARCHAR(255) | Chemin original `/uploads/images/originals/` |
|
||||
| thumbnail_path | VARCHAR(255) | Chemin miniature `/uploads/images/thumbnails/` |
|
||||
| default_unit | VARCHAR(20) | kg / L / unité |
|
||||
| barcode | VARCHAR(50) | Code-barres (scan futur) |
|
||||
| frequency_score | INT DEFAULT 0 | Score d'habitude (suggestions auto) |
|
||||
| owner_id | UUID NULL | Prévu multi-user |
|
||||
| created_at | TIMESTAMPTZ | |
|
||||
|
||||
**Catégories produits (tri rayon en magasin) :**
|
||||
`Fruits` · `Légumes` · `Viandes` · `Charcuterie` · `Poissons` · `Produits laitiers` · `Boulangerie` · `Épicerie salée` · `Épicerie sucrée` · `Condiments` · `Boissons` · `Entretien` · `Pharmacie` · `Animaux` · `Carburant` · `Électronique` · `Divers`
|
||||
|
||||
**Magasins pré-configurés (seed) :** Lidl · Intermarché · Super U · Gamm Vert · Weldom · Cosi · Bricocash · Tinel · Marie Blachère
|
||||
|
||||
**Données de démarrage :** 113 produits dans `backend/app/data/seed_products.json`, 9 magasins dans `seed_stores.json` — chargés automatiquement au premier démarrage.
|
||||
|
||||
#### `shopping.stores` — Magasins
|
||||
| Colonne | Type | Description |
|
||||
|---------|------|-------------|
|
||||
| id | UUID PK | |
|
||||
| name | VARCHAR(100) | Ex: Lidl, Carrefour |
|
||||
| location | TEXT | Adresse ou coordonnées GPS |
|
||||
| owner_id | UUID NULL | |
|
||||
|
||||
#### `shopping.price_history` — Historique des prix
|
||||
| Colonne | Type | Description |
|
||||
|---------|------|-------------|
|
||||
| id | UUID PK | |
|
||||
| product_id | UUID → products | |
|
||||
| store_id | UUID → stores NULL | |
|
||||
| price | NUMERIC(8,2) | Prix relevé |
|
||||
| unit | VARCHAR(20) | kg / L / unité |
|
||||
| quantity | NUMERIC(8,3) | Quantité associée |
|
||||
| source | VARCHAR(20) | `manual` / `ocr_tag` / `ocr_receipt` |
|
||||
| recorded_at | TIMESTAMPTZ | |
|
||||
|
||||
#### `shopping.lists` — Listes de courses
|
||||
| Colonne | Type | Description |
|
||||
|---------|------|-------------|
|
||||
| id | UUID PK | |
|
||||
| name | VARCHAR(100) | Ex: "Semaine du 26 mai" |
|
||||
| store_id | UUID NULL | Magasin cible |
|
||||
| week_date | DATE | Date de la semaine cible |
|
||||
| status | VARCHAR(20) | `draft` / `active` / `done` |
|
||||
| owner_id | UUID NULL | |
|
||||
| created_at | TIMESTAMPTZ | |
|
||||
|
||||
#### `shopping.list_items` — Articles dans une liste
|
||||
| Colonne | Type | Description |
|
||||
|---------|------|-------------|
|
||||
| id | UUID PK | |
|
||||
| list_id | UUID → lists | |
|
||||
| product_id | UUID → products NULL | Lié au catalogue (optionnel) |
|
||||
| custom_name | VARCHAR(150) | Si article hors catalogue |
|
||||
| quantity | NUMERIC(8,3) | |
|
||||
| unit | VARCHAR(20) | |
|
||||
| is_checked | BOOLEAN DEFAULT FALSE | Coché en magasin |
|
||||
| price_recorded | NUMERIC(8,2) | Prix relevé pendant les courses |
|
||||
| carried_over | BOOLEAN DEFAULT FALSE | Reporté depuis la semaine précédente |
|
||||
| sort_order | INT | Tri par rayon/catégorie |
|
||||
|
||||
### 3.2 Schema `todos`
|
||||
|
||||
#### `todos.items`
|
||||
| Colonne | Type | Description |
|
||||
|---------|------|-------------|
|
||||
| id | UUID PK | |
|
||||
| title | VARCHAR(255) | |
|
||||
| body | TEXT | Texte libre |
|
||||
| url | TEXT | Lien optionnel |
|
||||
| domain | VARCHAR(50) | Voir liste des domaines |
|
||||
| category | VARCHAR(50) | Sous-catégorie libre |
|
||||
| tags | VARCHAR(50)[] | Array · index GIN |
|
||||
| status | VARCHAR(20) | `pending` / `done` / `cancelled` |
|
||||
| priority | VARCHAR(10) | `low` / `medium` / `high` |
|
||||
| due_date | TIMESTAMPTZ | Date objectif |
|
||||
| postponed_count | INT DEFAULT 0 | Nombre de reports |
|
||||
| created_at | TIMESTAMPTZ DEFAULT NOW() | Auto |
|
||||
| updated_at | TIMESTAMPTZ | |
|
||||
| owner_id | UUID NULL | |
|
||||
|
||||
**Domaines disponibles** : `informatique` · `diy` · `electronique` · `domotique` · `bricolage` · `jardin` · `cuisine` · `voyage` · `animaux`
|
||||
|
||||
### 3.3 Schema `notes`
|
||||
|
||||
#### `notes.items`
|
||||
| Colonne | Type | Description |
|
||||
|---------|------|-------------|
|
||||
| id | UUID PK | |
|
||||
| title | VARCHAR(255) | Optionnel |
|
||||
| content | TEXT NOT NULL | Texte de la note |
|
||||
| category | VARCHAR(50) | |
|
||||
| tags | VARCHAR(50)[] | Index GIN |
|
||||
| gps_lat | NUMERIC(10,7) | Latitude GPS |
|
||||
| gps_lon | NUMERIC(10,7) | Longitude GPS |
|
||||
| metadata | JSONB | Paires clé/valeur libres (référence, magasin…) |
|
||||
| created_at | TIMESTAMPTZ DEFAULT NOW() | |
|
||||
| owner_id | UUID NULL | |
|
||||
|
||||
Index : `GIN(tags)` + `GIN(to_tsvector('french', title || ' ' || content))` (recherche full-text)
|
||||
|
||||
#### `notes.attachments`
|
||||
| Colonne | Type | Description |
|
||||
|---------|------|-------------|
|
||||
| id | UUID PK | |
|
||||
| note_id | UUID → items | |
|
||||
| file_path | VARCHAR(255) | Chemin original `/uploads/images/originals/` |
|
||||
| thumbnail_path | VARCHAR(255) | Chemin miniature (NULL si audio) |
|
||||
| file_type | VARCHAR(20) | `image` / `audio` |
|
||||
| original_name | VARCHAR(255) | |
|
||||
| created_at | TIMESTAMPTZ | |
|
||||
|
||||
---
|
||||
|
||||
## 4. Modules fonctionnels
|
||||
|
||||
### 4.1 Module Shopping — Liste de courses
|
||||
|
||||
#### Principe fondamental — ne jamais bloquer
|
||||
|
||||
> **L'objectif principal est simple : créer une liste en début de semaine, cocher les articles pendant les courses.**
|
||||
> Toutes les autres fonctionnalités (prix, OCR, stats, suggestions automatiques) sont **optionnelles** et n'apparaissent jamais dans le chemin principal. Un utilisateur qui ignore tout ça doit pouvoir utiliser l'app sans friction.
|
||||
|
||||
#### Concept général
|
||||
La liste de courses est générée **à partir du catalogue global** de produits. Le `frequency_score` de chaque produit augmente à chaque achat — ce score alimente les suggestions automatiques de la semaine. L'utilisateur valide/modifie/complète la liste avant de partir en courses.
|
||||
|
||||
Les articles **non cochés** à la fin des courses sont automatiquement reportés (`carried_over = true`) dans la liste de la semaine suivante.
|
||||
|
||||
La saisie de prix est **toujours optionnelle** — le champ prix est discret, accessible d'un tap long ou d'un bouton secondaire, jamais dans le chemin de cocher un article.
|
||||
|
||||
#### Interface mobile (priorité)
|
||||
- **Création de liste** : interface ultra-simple — picker de la semaine, suggestion automatique des produits habituels (top frequency_score), ajout rapide d'un produit (recherche dans le catalogue ou création à la volée)
|
||||
- **Mode courses** (liste active) :
|
||||
- Grands boutons tactiles (min 48px)
|
||||
- Articles triés par rayon/catégorie
|
||||
- Tap pour cocher un article
|
||||
- Swipe gauche → supprimer de la liste
|
||||
- Champ prix rapide au tap (clavier numérique)
|
||||
- **Wake Lock API** : l'écran ne se verrouille pas pendant les courses
|
||||
- Bouton "Terminer les courses" → génère la liste suivante avec les articles non cochés
|
||||
|
||||
#### Interface laptop (complète)
|
||||
- Vue tableau avec filtre par magasin, catégorie, statut
|
||||
- Gestion du catalogue global de produits (CRUD complet)
|
||||
- Gestion des magasins
|
||||
- Graphiques d'évolution des prix par produit
|
||||
- Export CSV de l'historique des prix
|
||||
|
||||
#### Module OCR (service Docker isolé, partagé entre tous les modules)
|
||||
|
||||
Le service OCR tourne dans son propre conteneur Docker (`ocr:8001`). Il est utilisable par tous les modules de l'application via l'endpoint unifié `POST /api/ocr/extract` du backend.
|
||||
|
||||
| Module | Usage OCR |
|
||||
|--------|-----------|
|
||||
| Shopping | Lecture étiquette prix en rayon → pré-remplissage prix |
|
||||
| Shopping | Lecture ticket de caisse → réconciliation finale liste |
|
||||
| Notes | Extraction texte depuis une photo (photo de document, panneau, référence) |
|
||||
| Futur | Tout import photo dans l'application |
|
||||
|
||||
Le module OCR est **toujours optionnel** : si le service est arrêté, les autres fonctionnalités de l'app ne sont pas impactées. Backend : Tesseract (local). Fallback possible : Ollama Vision (Hermes) en remplaçant simplement le service `ocr` dans le Docker Compose.
|
||||
|
||||
#### Suivi des prix
|
||||
Chaque prix saisi (manuel, OCR étiquette ou OCR ticket) alimente `price_history`. Sur laptop, un graphique en courbe par produit montre l'évolution du prix dans le temps, par magasin.
|
||||
|
||||
### 4.2 Module Notes / Listes diverses
|
||||
|
||||
Saisie rapide de notes avec support multimédia et géolocalisation.
|
||||
|
||||
**Champs** :
|
||||
- Titre (optionnel)
|
||||
- Contenu texte libre (saisie rapide)
|
||||
- Date (auto à la création, modifiable)
|
||||
- Catégorie
|
||||
- Tags (multi-valeurs, autocomplétion)
|
||||
- Photo(s) : capture directe via Camera API ou import depuis galerie. Compression WebP avant upload
|
||||
- Audio : enregistrement via MediaRecorder API. Format WebM/M4A
|
||||
- GPS : bouton "Localiser" → Geolocation API → stocke lat/lon, affiche adresse via reverse geocoding (optionnel)
|
||||
- Métadonnées libres : paires clé/valeur (ex: "Référence: X12-34", "Magasin: Brico Dépôt")
|
||||
|
||||
**Recherche** : full-text PostgreSQL en français sur titre + contenu + métadonnées. Filtres par catégorie, tags, présence de photo/audio/GPS.
|
||||
|
||||
### 4.3 Module Todos
|
||||
|
||||
Gestion de tâches classées par domaine.
|
||||
|
||||
**Champs** :
|
||||
- Titre
|
||||
- Texte libre (description)
|
||||
- URL (lien externe optionnel)
|
||||
- Domaine (liste fermée : voir section 3.2)
|
||||
- Catégorie (libre)
|
||||
- Tags (multi-valeurs)
|
||||
- Statut : `pending` / `done` / `cancelled`
|
||||
- Priorité : `low` / `medium` / `high`
|
||||
- Date de création (auto)
|
||||
- Date objectif (due_date)
|
||||
- Compteur de reports (`postponed_count`)
|
||||
|
||||
**Interface mobile** : ajout rapide en 1 clic, vue liste épurée, actions swipe (reporter / terminer). Boutons "Reporter d'1 jour", "Reporter à la semaine prochaine".
|
||||
|
||||
**Interface laptop** : vue complète avec filtres multi-critères (domaine, statut, priorité, tags), tri, recherche textuelle.
|
||||
|
||||
---
|
||||
|
||||
## 5. Module scan code-barres / QR code
|
||||
|
||||
### Fonctionnement
|
||||
- Librairie JS `zxing-js` (open-source, cross-platform) intégrée dans le frontend
|
||||
- Accès via le flux Camera API (pas de capture photo — lecture en temps réel)
|
||||
- Fonctionne sur iOS Safari et Android Chrome
|
||||
- Déclenché par un bouton 📷 "Scanner" dans le formulaire d'ajout de produit
|
||||
|
||||
### Flux de résolution produit
|
||||
```
|
||||
Scan code-barres (EAN-13 / QR)
|
||||
→ service product-search : GET /lookup?barcode={code}
|
||||
→ 1. OpenFoodFacts API (base ~3M produits alimentaires)
|
||||
→ trouvé : nom, marque, catégorie, image → auto-remplit le formulaire
|
||||
→ 2. Si pas trouvé : retourne vide → saisie manuelle
|
||||
→ enrichissement optionnel via recherche texte + SearXNG (laptop uniquement)
|
||||
```
|
||||
|
||||
### Service `product-search` (Docker)
|
||||
API Python légère qui centralise :
|
||||
- `GET /lookup?barcode={code}` → OpenFoodFacts par code-barres
|
||||
- `GET /search?q={nom}` → OpenFoodFacts par nom de produit
|
||||
- `GET /image-search?q={nom}` → SearXNG image search (fallback, laptop uniquement)
|
||||
|
||||
Le backend FastAPI expose ces fonctionnalités via `/api/products/lookup`, `/api/products/search`, `/api/products/image-search`.
|
||||
|
||||
### Service `searxng` (Docker)
|
||||
Instance SearXNG auto-hébergée, utilisée exclusivement pour la recherche d'images de produits non trouvés dans OpenFoodFacts. Accessible uniquement depuis l'interface laptop lors de l'enrichissement du catalogue. Non exposé publiquement via NPM.
|
||||
|
||||
---
|
||||
|
||||
## 6. Capacités natives smartphone (PWA)
|
||||
|
||||
| API navigateur | Usage |
|
||||
|---------------|-------|
|
||||
| Camera API | Capture photo directe (notes, OCR, produits) |
|
||||
| MediaRecorder API | Enregistrement audio (notes vocales) |
|
||||
| Geolocation API | Localisation GPS sur les notes |
|
||||
| Wake Lock API | Écran actif pendant les courses |
|
||||
| Web Share API | Partage de notes/listes vers autres apps |
|
||||
| Liens `webcal://` | Abonnement calendrier natif iOS/Android (futur) |
|
||||
|
||||
---
|
||||
|
||||
## 7. Design system
|
||||
|
||||
Le design system **Gruvbox seventies** est intégré dès le départ.
|
||||
|
||||
- Fichiers : `design_system/tokens/tokens.css` · `design_system/components/ui-kit.jsx`
|
||||
- Thème : dark par défaut, light disponible via `data-theme`
|
||||
- Palette : orange brûlé `#fe8019` (accent), fond brun `#2a231d`, texte `#f2e5c7`
|
||||
- 14 composants React disponibles (Button, IconButton, Toggle, StatusLed, BatteryGauge, RadialGauge, Popup, TreeNav, Sparkline, LineChart, Icon…)
|
||||
- Fonts : Inter (UI) · JetBrains Mono (données) · Share Tech Mono (terminal/logs)
|
||||
- Règles absolues : jamais de hex en dur, toujours `var(--token)`, `data-theme` obligatoire sur un parent
|
||||
|
||||
---
|
||||
|
||||
## 8. Interface utilisateur — principes
|
||||
|
||||
### Mobile-first
|
||||
- Touch target minimum : 48px
|
||||
- Composants d'action accessibles en 1 main
|
||||
- Swipe sur les listes (reporter, terminer, supprimer)
|
||||
- Chargement hors-ligne via Service Worker (cache assets + données critiques)
|
||||
- Feedback haptique si disponible
|
||||
|
||||
### Distinction mobile / laptop
|
||||
Certains modules ont deux pages dédiées selon la taille d'écran :
|
||||
|
||||
| Module | Mobile | Laptop |
|
||||
|--------|--------|--------|
|
||||
| Shopping | Création liste simple + mode courses | Catalogue produits + gestion magasins + graphiques prix |
|
||||
| Todos | Ajout rapide + liste swipeable | Tableau filtrable multi-critères |
|
||||
| Notes | Saisie rapide + media capture | Liste complète + recherche avancée |
|
||||
|
||||
---
|
||||
|
||||
## 9. Hors scope initial (Phase 1-5)
|
||||
|
||||
- Authentification multi-utilisateurs (Phase 7)
|
||||
- Google Calendar / CalDAV (Phase 6)
|
||||
- Kanban / Gitea webhooks (Phase 8)
|
||||
- Home Assistant (Phase 8)
|
||||
- Analyse frigo par Vision LLM / Hermes (Phase 9)
|
||||
- Scan code-barres produits
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user