85 Commits

Author SHA1 Message Date
gilles 091eead5bb fix(pwa): manifest cassé + icônes PWA non appliquées
Build & Push OCI / build (./backend, backend) (push) Failing after 2m59s
Build & Push OCI / build (./frontend, frontend) (push) Failing after 16s
index.html référençait /manifest.json (inexistant) → nginx renvoyait index.html
en fallback SPA → "Manifest: Line 1, column 1, Syntax error" dans Chromium, et la
PWA n'avait aucune icône valide (d'où les favicons icon-192/512 non utilisées
pour le raccourci).

- Retire le <link rel="manifest" href="/manifest.json"> codé en dur :
  VitePWA injecte déjà /manifest.webmanifest au build
- Ajoute <link rel="apple-touch-icon" href="/icons/icon-192.png"> pour le
  raccourci écran d'accueil iOS (qui n'utilise pas le manifest pour l'icône)

v0.5.17

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-30 10:53:51 +02:00
gilles 36b5760566 chore(deploy): images OCI Gitea + compose production + CI
Recrée les fichiers de déploiement perdus lors de la restauration du repo.

- docker-compose.deploy.yml : production basée sur les images publiées
  (git.maison43gil.com/gilles/home_hub:{backend,frontend}-latest), sans build:,
  avec service backend-migrate (alembic upgrade head) avant le démarrage
- .gitea/workflows/build.yml : CI Gitea Actions, build+push des 2 images
- backend/.dockerignore + frontend/.dockerignore : images propres, sans secrets
- .env.example : template complet sans secret réel (placeholder change-me)
- README : section déploiement OCI (build manuel, CI, serveur, note 413 proxy)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-30 10:45:30 +02:00
gilles 53018c16dd fix(notes): erreur 500 sur les notes antérieures (urls=NULL)
Les notes créées avant la migration 0061 ont urls=NULL en base. Le défaut
NoteResponse.urls=[] ne s'applique qu'à un attribut absent, pas à None, d'où
ResponseValidationError "Input should be a valid list" → GET /api/notes 500
→ "Erreur de chargement" côté UI.

Ajoute un field_validator(mode='before') qui coerce None → [].
Nettoie aussi l'import HttpUrl inutilisé.

v0.5.16

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-30 10:15:15 +02:00
gilles 6c889f1561 fix(mcp+alembic): désactive DNS rebinding (421) + rechaîne migrations 006
MCP :
- FastMCP recevait Host=localhost (sans port) mais le pattern par défaut
  allowed_hosts=["localhost:*", ...] EXIGE un port → 421 Invalid Host header
  pour tout accès non-localhost (ex: Hermes via http://10.0.0.50:3001/mcp)
- Désactive enable_dns_rebinding_protection : le Bearer MCP_API_KEY est la
  vraie barrière (protection rebinding = anti-attaque navigateur, inutile ici)
- nginx /mcp : retour à Host $host (le rewrite localhost était cassé)

Alembic :
- Collision : 006_notes_urls et 006_product_tags partageaient revision='006'
  → "Multiple head revisions" au démarrage
- Renumérote notes_urls en 0061, chaîné après product_tags
  Chaîne finale : 005 -> 006 (product_tags) -> 0061 (notes_urls) -> 007

v0.5.15

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-30 10:07:00 +02:00
gilles 7bf6caa3dd feat(shopping): listes projet + déduplication nommage hebdo
Backend :
- Migration 007 : list_type VARCHAR(20) sur shopping.lists (weekly/project),
  url/description/image_url sur shopping.list_items
- Modèle ShoppingList : champ list_type
- Modèle ListItem : champs url, description, image_url
- Schémas : list_type sur Create/Response, nouveaux champs sur ItemCreate/Update/Response
- _unique_week_label() : évite les doublons S22 2026 → S22 2026 (2)
- finish_shopping : carry-over uniquement pour list_type='weekly'

Frontend :
- api/shopping.ts : list_type, champs enrichis item, createProjectList()
- ProjectItemCard.tsx : carte avec image, description, URL, boutique, cochage
- ShoppingPage :
  · Séparation weekly / project dans la sélection de liste active
  · Section "Listes projet" sur l'écran vide avec navigation
  · Badge PROJET dans l'en-tête
  · Bouton "Clôturer la semaine" et badge "semaine dépassée" masqués sur projet
  · Bouton "+ Ajouter" (mobile + laptop) sur les listes projet
  · Vue grille ProjectItemCard pour les listes projet
  · Modale création liste projet (nom + boutique)
  · Modale ajout/édition item projet (nom, description, URL, image URL)

v0.5.14

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-30 09:59:53 +02:00
gilles 031708ad8f feat(notes): ajout de liens nommés (label + url) sur les notes
Backend :
- Migration 006 : colonne urls JSONB nullable sur notes.items
- Modèle NoteItem : champ urls list[dict]
- Schémas : NoteUrl (label + url avec validation http/https),
  NoteCreate/NoteUpdate/NoteResponse exposent urls

Frontend :
- api/notes.ts : interface NoteUrl + champ urls sur Note/NoteCreate
- NoteForm : section "Liens" avec ajout (libellé + URL), suppression,
  validation http/https, confirmation par Enter
- NotesPage : badge compteur liens dans metaLine (semi/collapsed),
  section liens cliquables dans le mode expanded

v0.5.13

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-30 09:47:49 +02:00
gilles b084905226 fix(ui): icône logo TopBar → fa-circle-nodes (cohérence avec icon hub)
Remplace fa-house par fa-circle-nodes dans la TopBar pour aligner
le logo affiché dans l'app avec la nouvelle icône PWA/favicon hub.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-30 09:42:49 +02:00
gilles 7a500e2435 chore(design): nouvelle icône hub (réseau de nœuds) favicon + PWA
Remplace la maison par un graphe central orange (accent) avec 3 nœuds
satellites crème (d5c4a1) sur fond sombre Gruvbox. Décliné en
favicon.svg, icon-192.png et icon-512.png (PWA shortcut smartphone).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-30 09:29:28 +02:00
gilles 4c616fa5d3 fix(ui): bouton + laptop, overflow mobile, clôture semaine, backup complet
- TodosPage/ShoppingPage : bouton « + » visible en laptop (hidden lg:flex)
- ShoppingPage : renomme « Terminer » en « Clôturer la semaine », badge ⚠
  si semaine ISO dépassée, confirmation modale avec décompte non-cochés
- NotesPage : overflowWrap:anywhere sur titre/contenu/markdown (URLs longues
  qui débordaient hors de la tuile sur smartphone)
- index.css : overflow-x:hidden + max-width:100vw sur html/body (garde-fou global)
- admin.py : backup remplacé par archive .tar.gz (pg_dump + uploads/) streamée
  au navigateur ; restore via multipart upload avec extraction sécurisée
- admin.ts : downloadBackup() (blob trigger) + uploadAndRestore() avec progression XHR
- ConfigPage : refonte section backup avec boutons Télécharger/Restaurer
  et barre de progression upload

v0.5.11

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-30 08:52:37 +02:00
gilles 69c2042995 fix(mcp): nginx Host=localhost pour passer la protection DNS rebinding FastMCP 2026-05-28 10:42:57 +02:00
gilles 0a798d2791 chore: bump version — MCP server v0.5.10 2026-05-28 06:54:46 +02:00
gilles 8ebdccb543 feat(mcp): câblage FastAPI + nginx proxy + docker-compose MCP_API_KEY
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-28 06:49:50 +02:00
gilles 273e032245 3 2026-05-25 21:27:13 +02:00
gilles f81be12a38 feat(notes): renderer markdown étendu v0.5.9
- Tableaux (pipe syntax) avec header distinct
- Task lists - [ ] / - [x] avec checkbox colorée
- Listes imbriquées avec indentation (• / ◦)
- Texte barré ~~strikethrough~~
- Liens [texte](url) cliquables (target _blank)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-25 20:13:14 +02:00
gilles ec87bc091d feat(sse): sync temps réel multi-appareils via Server-Sent Events v0.5.8
- Broadcaster asyncio.Queue avec keepalive 25s (prévient timeout proxy)
- Endpoint GET /api/events/stream (StreamingResponse text/event-stream)
- Broadcast notes_changed / todos_changed / shopping_changed sur toutes mutations
- Hook useServerEvents: EventSource avec reconnexion automatique (3s)
- Pages Notes, Todos, Shopping abonnées aux événements SSE
- nginx: location SSE dédiée (proxy_buffering off, timeout 24h)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-25 20:12:02 +02:00
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
gilles c72ca04fd2 feat(config): stats vidéo + user 1000:1000 dans docker-compose
Admin stats : ajout video (count + size_bytes) dans /api/admin/stats.
ConfigPage : grille médias 3 colonnes (Photos / Audio / Vidéos).

docker-compose : backend et backend-worker tournent en user 1000:1000
pour que les fichiers écrits dans ./data/ appartiennent à l'utilisateur
hôte et non à root.

v0.5.6

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-25 16:44:31 +02:00
gilles dd4ce6f52b feat(notes): 3 états de tuile + renderer pseudo-markdown
Tuile note : semi (défaut, 3 lignes tronquées) / expanded (markdown complet
+ médias) / collapsed (titre + date uniquement). Bouton toggle fa-chevron
en haut à droite qui cycle entre les états.

Renderer pseudo-markdown inline : # ## ###, - * listes, 1. numérotées,
> citations, --- séparateur, **gras** *italique* `code`, ``` blocs.

Méta de tuile : icônes fa-image / fa-microphone / fa-video / fa-location-dot
visibles en état semi et expanded.

v0.5.5

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-25 16:43:44 +02:00
gilles 6c9ebcaab7 feat(notes): support vidéo + transcodage audio AAC universel
Audio : ffmpeg transcode toute entrée (webm/ogg/m4a) vers AAC/m4a
au moment de l'upload → lecture Safari iOS garantie.

Vidéo : nouveau save_video(), webm transcodé en H.264/mp4, mp4/quicktime
stocké directement. Lecteur <video> inline dans NoteCard.

Frontend :
- Bouton vidéo (fa-video) dans les actions de chaque note
- Icônes fa-image / fa-microphone / fa-video / fa-location-dot dans la méta
- Filtres rapides : Photo / Audio / Vidéo / GPS (avec icônes fa)
- Boutons actions migrés vers icônes Font Awesome
- client_max_body_size nginx : 15m → 200m pour les vidéos

v0.5.4

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-25 16:31:05 +02:00
gilles b3c365d773 fix(notes): GPS lat/lon sérialisé en float au lieu de Decimal
Decimal Python → string JSON causait TypeError: z.toFixed is not a function
dans NoteCard (title attribute de l'icône GPS). Tous les champs gps_lat/gps_lon
passent maintenant en float | None dans les schémas Pydantic.

v0.5.3

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-25 16:13:01 +02:00
gilles 454dbadb2f feat(config): section statistiques — BDD, médias, entités
- GET /api/admin/stats : taille BDD (pg_database_size), nb+poids photos/audio
  (scan filesystem), nb notes/todos/listes (requêtes SQL directes)
- ConfigPage : grille 3 colonnes todos/notes/listes + 2 tuiles médias + ligne BDD

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-25 16:04:08 +02:00
gilles de9a1e3c73 fix(photo): supprimer capture=environment — accès bibliothèque + appareil photo
Sans cet attribut, iOS affiche le menu natif (Prendre une photo / Bibliothèque /
Fichiers) et Android propose un sélecteur de source.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-25 15:57:33 +02:00
gilles 9aaa5fb562 fix(audio+gps): lecture audio multi-navigateur + icône GPS dans tuile note
Audio :
- MediaRecorder détecte le format supporté : webm (Chrome/Firefox) ou mp4 (Safari/iOS)
- Extension sauvegardée correctement (.webm ou .m4a) selon le navigateur
- Backend : ALLOWED_AUDIO_PREFIXES remplace le set strict, strip des codec suffixes

GPS (note card) :
- Icône fa-location-dot (accent vert) avec tooltip lat/lon remplace l'emoji 📍

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-25 15:55:35 +02:00
gilles 9de8ad5f3e fix(gps): erreur explicite + saisie manuelle si GPS indisponible (HTTP/laptop)
navigator.geolocation est undefined sur HTTP hors localhost (contexte non sécurisé).
- Message d'erreur visible selon le cas (permission, HTTPS, timeout)
- Fallback : deux champs lat/lon s'affichent automatiquement
- TodoForm : bouton GPS toujours actif (plus disabled sur navigator.geolocation)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-25 15:52:41 +02:00
gilles be0c8bceb6 feat: export Markdown notes (ARQ/Redis) + backup/restore BDD — v0.5.2
- Volume data/ (bind mount ./data) remplace le volume uploads nommé
  data/notes/ → .md auto-générés, data/uploads/ → médias, data/backup/ → dumps
- Service Redis (redis:7-alpine) + worker ARQ (backend-worker)
- notes_markdown.py : frontmatter YAML + contenu + pièces jointes (liens relatifs)
  Nom : YYYY-MM-DD_slug-titre_shortid.md, rotation si titre modifié
- api/notes.py : publie export_note_markdown / remove_note_markdown sur Redis
  après chaque create / update / delete / add_attachment / delete_attachment
- api/admin.py : POST /backup, GET /backups, POST /restore/{filename} (pg_dump/pg_restore)
- Backend Dockerfile : postgresql-client ; requirements : arq==0.26.1
- ConfigPage : section "Base de données" avec sauvegarde + liste + restauration

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-25 15:33:29 +02:00
gilles 3d77ed6cc7 fix(ui): TopBar visible uniquement sur la page d'accueil
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-25 14:00:15 +02:00
gilles 31d60983b2 feat(ui): header global visible sur toutes les pages — identité app + compte
- TopBar enrichi : icône maison + nom + version (gauche), thème + compte (droite)
- Bouton compte désactivé (placeholder pour future auth multi-utilisateurs)
- Suppression de la carte HomeHub redondante sur la page d'accueil

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-25 13:53:27 +02:00
gilles 3285ce7622 fix(ux): bottom sheet au-dessus du clavier iOS + suppression URL suggestion
BottomSheet.tsx
- visualViewport API : calcule la hauteur du clavier en temps réel
- bottom: keyboardOffset → le sheet monte exactement au-dessus du clavier
- maxHeight s'adapte à l'espace visible restant
- border-radius passe à 16px partout quand le clavier est ouvert
- Transition douce 0.15s sur bottom/maxHeight/border-radius

ShoppingPage.tsx
- type="search" sur le champ de recherche → supprime la suggestion URL iOS
  dans la barre QuickType du clavier
- autoFocus → clavier s'ouvre automatiquement à l'ouverture du sheet

v0.5.1

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-25 13:44:46 +02:00
gilles bd0e06c5dd feat: thème dark/light/system, taille police, page config, paste photo todo
ThemeContext
- ThemeMode dark/light/system persisté en localStorage
- fontScale 0.8–1.4 appliqué via CSS zoom sur <html>
- Écoute prefers-color-scheme pour le mode système

index.html
- Script anti-flash : applique thème et zoom synchrone avant le premier rendu

Layout
- TopBar fixe 44px : bouton icône qui cycle dark→light→system→dark
- Contenu décalé de 44px vers le bas

ConfigPage (/config)
- Sélecteur de thème (3 boutons avec icônes)
- Slider taille de texte avec aperçu temps réel
- Bouton Réinitialiser

HomePage
- Tuile Paramètres (fa-sliders) → /config

TodoForm
- Paste Ctrl+V pour ajouter une photo (même mécanique que CatalogueModal)
- Indice visuel "ou Ctrl+V"

v0.5.0

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-25 13:41:42 +02:00
gilles 3dbd554eeb fix(media): upload photo — taille, formats et nginx
- nginx : client_max_body_size 15m (photos smartphone > 1 Mo rejetées silencieusement)
- backend : redimensionnement original à 500×500 max (aspect ratio conservé) avant sauvegarde WEBP
- backend : thumbnail généré depuis l'image déjà redimensionnée (économie mémoire)
- backend : formats acceptés étendus — image/heic, image/heif, image/jpg
- backend : normalisation content-type en lowercase (robustesse navigateurs)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-25 13:29:45 +02:00
gilles 1210d4f9f3 feat(todos): case à cocher «Pas de date» dans le formulaire todo
Cochée par défaut à la création (aucune date soumise).
Décochée si la tâche éditée a déjà une date (champ date pré-rempli).
Décocher affiche le sélecteur de date ; cocher le masque et efface la date.

v0.4.14

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-25 13:22:07 +02:00
gilles fa1a642240 feat(catalogue): coller une image depuis le presse-papier (Ctrl+V)
Écoute l'événement paste sur window quand le formulaire est ouvert.
Si le presse-papier contient une image, déclenche le même upload
que le bouton fichier. Indice visuel "ou Ctrl+V" affiché sous le bouton.
Les deux méthodes (fichier + coller) coexistent.

v0.4.13

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-25 13:18:09 +02:00
gilles 4e5e6f7c36 fix(nginx): ^~ sur /media/ pour éviter le matching regex des .webp
La règle ~* \.(webp|...) prenait priorité sur /media/ et servait
les images uploadées depuis le HTML statique au lieu de les proxifier
vers le backend — causant un 404 sur toutes les photos d'articles.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-25 13:12:24 +02:00
gilles aa9ac2a6ea feat(shopping): tags sur les articles du catalogue
- Migration 006 : colonne tags TEXT[] sur shopping.products
- Modèle SQLAlchemy + schémas Pydantic mis à jour (ProductCreate/Update/Response)
- Interface TypeScript Product/ProductCreate/ProductUpdate avec tags?: string[]
- CatalogueModal : chip input (Entrée/virgule pour ajouter, clic pour supprimer, Backspace pour retirer le dernier)
- Recherche dans le catalogue et le bottom sheet étendue aux tags (insensible aux accents)
- Tags affichés en pills dans la liste du catalogue

v0.4.12

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-25 12:57:25 +02:00
gilles 8e878e2e5a fix(catalogue): recherche insensible aux accents — filtrage côté client v0.4.11
Le backend utilisait ILIKE (insensible à la casse uniquement) — "iles" ne
trouvait pas "Îles flottantes". Passage au filtrage client avec matchesSearch
(normalize NFD) identique au bottom sheet. Charge tous les produits une fois
puis filtre sur name, brand et category sans aller-retour serveur.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-25 12:16:10 +02:00
gilles 606145f9ed ux(nav): supprimer le cercle dock du slot bouton action v0.4.10
Le cercle de fond était visible derrière le FAB — deux cercles superposés.
Slot conservé (flex, positionnement) mais sans visuel propre.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-25 10:31:41 +02:00
gilles 58b5de15bb fix(shopping): augmenter limite catalogue 50→500 — tous les articles visibles
Avec 119 articles en base et limit=50, les articles en fin de liste
alphabétique (Îles flottantes, Éponge…) n'apparaissaient pas dans
le bottom sheet. Limite portée à 500 côté backend ET frontend.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-25 10:28:09 +02:00
gilles 6677604ad1 chore: bump version v0.4.9
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-25 10:21:14 +02:00
gilles 264dd180ee fix(shopping): capitalisation 1re lettre — catalogue + migration BDD v0.4.9
- CatalogueModal : cleanForm() capitalise désormais le nom avant envoi API
  (création et modification d'article)
- Migration 005 : met à jour shopping.products.name et
  shopping.list_items.custom_name pour capitaliser les données existantes

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-25 10:21:03 +02:00
gilles 52771644de fix(shopping): rafraîchir le catalogue après fermeture du modal Articles
Les produits créés/modifiés via le modal Catalogue n'apparaissaient pas
dans le bottom sheet car products n'était chargé qu'au montage de la page.
refreshProducts() est désormais appelé à chaque fermeture du CatalogueModal.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-25 10:18:15 +02:00
gilles 4ce5d92bba fix(shopping): accolade manquante dans le map des articles libres
Fermeture incorrecte du .map(s => { return (...) }) — le } de la
fonction arrow était absent, causant une erreur TS1005 au build.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-25 10:09:26 +02:00
gilles cbb2d81279 feat(nav): bouton action intégré dans la navbar v0.4.8
- ActionButtonContext : contexte React permettant aux pages d'injecter
  leur bouton action dans la navbar (Shopping=fa-cart-plus, Todos/Notes=+)
- BottomNav : 5e slot dédié avec dock circulaire visuel permanent ;
  bouton rendu 10px au-dessus du centre du slot (effet soulevé)
- Layout : ActionButtonProvider + overflow visible sur le conteneur nav
- Pages : useEffect enregistre/vide le bouton action — plus de FAB flottant
  sur le contenu, liste entièrement visible

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-25 10:02:03 +02:00
gilles d6d3acd1fe ux(fab): repositionner le bouton action sur la nav bar v0.4.7
FAB déplacé sur la barre de nav (bottom: 10px) au lieu de flotter
au-dessus de la liste — liste entièrement visible sur Shopping,
Todos et Notes. paddingBottom liste réduit à 64px (hauteur nav seule).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-25 09:51:33 +02:00
gilles 880f7f2125 fix+ux(shopping): suppression via bottom sheet, majuscule auto, icône FAB v0.4.6
- Bug: décrémentation à 0 d'un article pré-chargé (existingItemId) conserve
  la sélection à qty=0 (marqué pour DELETE) → bouton ✓ devient accessible
  Visuel: fond rouge + barré + opacité 0.6 pour les articles à supprimer
- UX: première lettre en majuscule auto lors de l'ajout d'un article libre
- UX: FAB remplace '+' par fa-cart-plus pour mieux signifier l'ajout à la liste

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-25 09:39:37 +02:00
gilles a1ecd0945e feat(shopping): optimisation bottom sheet — icônes haut, tab bar couverte v0.4.5
- Boutons Annuler (fa-xmark, outline err) et Valider (fa-check, ok)
  déplacés en haut du sheet, pill shape, touch target 48px
- Suppression du bouton sticky en bas
- zIndex sheet 70 > tab bar 50 : bottom sheet couvre la nav
- maxHeight 85dvh → 92dvh pour exploiter l'espace libéré
- Poignée de drag supprimée

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-25 09:22:59 +02:00
gilles a949a22cca feat(shopping): pré-remplissage bottom sheet + toggle catalogue v0.4.4
- openAddSheet() pré-charge les articles non cochés de la liste courante
  (qty existante, PATCH si modifiée / POST si nouvel article)
- Toggle "Ajouter au catalogue" sur les articles libres nouvellement saisis
  (coché par défaut, créé via createProduct puis lié en product_id)
- Bouton "Confirmer (N)" comptant uniquement les actions réelles
  (new items + existing avec qty modifiée)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-25 09:10:46 +02:00
gilles 56f0815667 feat(shopping): bottom sheet multi-select — quantités inline +/- par article v0.4.3
- QtyControls: boutons +/- design system (var(--ok)/var(--err)/var(--bg-5))
- qty=0 → article non sélectionné, seul "+" visible (var(--ok) plein)
- qty>0 → fond teinté vert, "−" + valeur mono + "+" ; "−" retire si qty atteint 1
- Quantité transmise à addItem lors de la confirmation
- Articles libres : même comportement +/- en tête de liste

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-25 08:50:22 +02:00
gilles 8b2081764e chore: bump version v0.4.2 (UX: swipe-edit, bottom-sheet multi-select, user-select)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-25 08:08:35 +02:00
gilles c9cf452585 feat(shopping): bottom sheet multi-select pour l'ajout d'articles
- BottomSheet.tsx: panneau ancré en bas, max-height 85dvh (clavier-aware),
  centré sur laptop (max-width 600px), backdrop, drag handle visuel
- ShoppingPage: remplace le modal centré par le BottomSheet multi-select
  · sélection multiple avec toggle (cercle vert + fond teinté)
  · articles libres affichés en tête avec tag "article libre"
  · bouton "Ajouter (N)" sticky, grisé à 0 sélection
  · pas d'autoFocus → liste visible d'emblée, clavier fermé
  · FAB + masqué quand le sheet est ouvert

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-25 08:06:48 +02:00
gilles a821b27fc6 fix(shopping): champ recherche article — autoComplete off, liste adaptative dvh
- autocomplete/autocorrect/autocapitalize off + spellCheck false sur le
  champ de recherche → supprime la barre de suggestions iOS et le popup autofill
- maxHeight liste: min(240px, 35dvh) → reste visible quand le clavier est ouvert

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-25 07:42:20 +02:00