From 880f7f21257152dcb5be749ecfabf6fcf6f7b47e Mon Sep 17 00:00:00 2001 From: Gilles Soulier Date: Mon, 25 May 2026 09:39:37 +0200 Subject: [PATCH] =?UTF-8?q?fix+ux(shopping):=20suppression=20via=20bottom?= =?UTF-8?q?=20sheet,=20majuscule=20auto,=20ic=C3=B4ne=20FAB=20v0.4.6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 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 --- frontend/package.json | 2 +- frontend/src/pages/ShoppingPage.tsx | 41 ++++++++++++++++++++--------- 2 files changed, 30 insertions(+), 13 deletions(-) diff --git a/frontend/package.json b/frontend/package.json index 24efddf..168fa30 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -1,7 +1,7 @@ { "name": "homehub-frontend", "private": true, - "version": "0.4.5", + "version": "0.4.6", "type": "module", "scripts": { "dev": "vite", diff --git a/frontend/src/pages/ShoppingPage.tsx b/frontend/src/pages/ShoppingPage.tsx index 7f4b778..847a1e5 100644 --- a/frontend/src/pages/ShoppingPage.tsx +++ b/frontend/src/pages/ShoppingPage.tsx @@ -173,14 +173,20 @@ export default function ShoppingPage() { setSelections(prev => { const idx = prev.findIndex(s => s.type === 'product' && s.product.id === p.id) if (idx === -1) return prev - if (prev[idx].qty <= 1) return prev.filter((_, i) => i !== idx) + const sel = prev[idx] + if (sel.qty <= 1) { + // article pré-chargé : qty=0 = marqué pour suppression + if (sel.existingItemId) return prev.map((s, i) => i === idx ? { ...s, qty: 0 } : s) + return prev.filter((_, i) => i !== idx) + } return prev.map((s, i) => i === idx ? { ...s, qty: s.qty - 1 } : s) }) } function addCustomItem() { - const name = itemSearch.trim() - if (!name) return + const raw = itemSearch.trim() + if (!raw) return + const name = raw.charAt(0).toUpperCase() + raw.slice(1) setSelections(prev => { const exists = prev.some(s => s.type === 'custom' && s.name === name) if (exists) return prev @@ -203,7 +209,11 @@ export default function ShoppingPage() { setSelections(prev => { const idx = prev.findIndex(s => s.type === 'custom' && s.name === name) if (idx === -1) return prev - if (prev[idx].qty <= 1) return prev.filter((_, i) => i !== idx) + const sel = prev[idx] + if (sel.qty <= 1) { + if (sel.existingItemId) return prev.map((s, i) => i === idx ? { ...s, qty: 0 } : s) + return prev.filter((_, i) => i !== idx) + } return prev.map((s, i) => i === idx ? { ...s, qty: s.qty - 1 } : s) }) } @@ -217,7 +227,8 @@ export default function ShoppingPage() { for (const sel of toProcess) { if (sel.type === 'product') { if (sel.existingItemId) { - await updateItem(currentList.id, sel.existingItemId, { quantity: String(sel.qty) }) + if (sel.qty === 0) await deleteItem(currentList.id, sel.existingItemId) + else await updateItem(currentList.id, sel.existingItemId, { quantity: String(sel.qty) }) } else { await addItem(currentList.id, { product_id: sel.product.id, @@ -227,7 +238,8 @@ export default function ShoppingPage() { } } else { if (sel.existingItemId) { - await updateItem(currentList.id, sel.existingItemId, { quantity: String(sel.qty) }) + if (sel.qty === 0) await deleteItem(currentList.id, sel.existingItemId) + else await updateItem(currentList.id, sel.existingItemId, { quantity: String(sel.qty) }) } else if (sel.addToCatalogue) { const newProduct = await createProduct({ name: sel.name }) await addItem(currentList.id, { product_id: newProduct.id, quantity: String(sel.qty) }) @@ -514,7 +526,7 @@ export default function ShoppingPage() { )} - {/* FAB + — masqué quand le sheet est ouvert */} + {/* FAB — masqué quand le sheet est ouvert */} {!showAddSheet && ( + > + + )} )} @@ -620,18 +634,21 @@ export default function ShoppingPage() {
{/* Articles libres sélectionnés */} - {(selections.filter(s => s.type === 'custom') as Extract[]).map(s => ( + {(selections.filter(s => s.type === 'custom') as Extract[]).map(s => { + const willDelete = s.qty === 0 && !!s.existingItemId + return (
-
{s.name}
+
{s.name}
{!s.existingItemId ? (