feat(shopping): bouton liste magique + modal édition liste avec suppression

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-05-24 15:52:44 +02:00
parent da5eb4916e
commit e21349511d
+142 -2
View File
@@ -3,7 +3,7 @@ import { useState, useEffect, useCallback } from 'react'
import type { ShoppingList, ShoppingListDetail, ShoppingItem, Store } from '../api/shopping'
import {
fetchLists, createList, fetchListDetail, updateList, deleteList,
addItem, updateItem, deleteItem, finishShopping, fetchStores,
addItem, updateItem, deleteItem, finishShopping, fetchStores, generateMagicList,
} from '../api/shopping'
import Modal from '../components/Modal'
import ItemRow from '../components/shopping/ItemRow'
@@ -44,6 +44,8 @@ export default function ShoppingPage() {
const [error, setError] = useState<string | null>(null)
const [showCreateModal, setShowCreateModal] = useState(false)
const [showAddItemModal, setShowAddItemModal] = useState(false)
const [showEditListModal, setShowEditListModal] = useState(false)
const [generating, setGenerating] = useState(false)
const [newListName, setNewListName] = useState('')
const [newListStore, setNewListStore] = useState('')
@@ -164,6 +166,33 @@ export default function ShoppingPage() {
}
}
async function handleGenerateMagicList() {
setGenerating(true)
setError(null)
try {
const newList = await generateMagicList()
void loadLists()
setActiveList(newList)
setView('detail')
} catch {
setError('Erreur lors de la génération')
} finally {
setGenerating(false)
}
}
async function handleDeleteActiveList() {
if (!activeList) return
try {
await deleteList(activeList.id)
setView('lists')
setActiveList(null)
void loadLists()
} catch {
setError('Erreur lors de la suppression')
}
}
// ── Vue mode magasin ──────────────────────────────────────────────────────
if (view === 'store' && activeList) {
const unchecked = activeList.items.filter(i => !i.is_checked)
@@ -250,6 +279,22 @@ export default function ShoppingPage() {
{activeList.checked_count}/{activeList.item_count} cochés
</div>
</div>
<button
onClick={() => setShowEditListModal(true)}
title="Modifier / gérer la liste"
style={{
background: 'var(--bg-3)',
border: '1px solid var(--bg-5)',
borderRadius: 8,
color: 'var(--ink-2)',
cursor: 'pointer',
padding: '8px 12px',
fontSize: 16,
minHeight: 44,
}}
>
<i className="fa-solid fa-pen" />
</button>
<button
onClick={() => setView('store')}
style={{
@@ -331,6 +376,75 @@ export default function ShoppingPage() {
</div>
</Modal>
)}
{showEditListModal && (
<Modal title="Gérer la liste" onClose={() => setShowEditListModal(false)}>
<p style={{ color: 'var(--ink-3)', fontSize: 12, fontFamily: 'var(--font-ui)', margin: 0, textTransform: 'uppercase', letterSpacing: 1 }}>
Ajouter un article
</p>
<input
style={inputStyle}
placeholder="Nom de l'article *"
value={newItemName}
onChange={e => setNewItemName(e.target.value)}
autoFocus
onKeyDown={async e => {
if (e.key === 'Enter') {
await handleAddItem()
setShowEditListModal(false)
}
}}
/>
<div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 8 }}>
<input
style={inputStyle}
placeholder="Quantité"
value={newItemQty}
onChange={e => setNewItemQty(e.target.value)}
/>
<input
style={inputStyle}
placeholder="Unité (kg, L…)"
value={newItemUnit}
onChange={e => setNewItemUnit(e.target.value)}
/>
</div>
<button
onClick={async () => {
await handleAddItem()
if (!error) setShowEditListModal(false)
}}
style={{
padding: '10px 20px', borderRadius: 8, border: 'none',
background: 'var(--accent)', color: '#1d2021', cursor: 'pointer',
fontFamily: 'var(--font-ui)', fontWeight: 600, minHeight: 48, width: '100%',
}}
>
<i className="fa-solid fa-plus" style={{ marginRight: 8 }} />
Ajouter
</button>
<div style={{ borderTop: '1px solid var(--bg-4)', margin: '4px 0' }} />
<button
onClick={() => {
setShowEditListModal(false)
void handleDeleteActiveList()
}}
style={{
padding: '10px 20px', borderRadius: 8,
border: '1px solid var(--err)',
background: 'transparent', color: 'var(--err)',
cursor: 'pointer', fontFamily: 'var(--font-ui)',
fontWeight: 600, minHeight: 48, width: '100%',
display: 'flex', alignItems: 'center', justifyContent: 'center', gap: 8,
}}
>
<i className="fa-solid fa-trash" />
Supprimer la liste en cours
</button>
</Modal>
)}
</div>
)
}
@@ -338,8 +452,34 @@ export default function ShoppingPage() {
// ── Vue liste des listes ───────────────────────────────────────────────────
return (
<div className="p-4">
<div style={{ display: 'flex', alignItems: 'center', marginBottom: 16 }}>
<div style={{ display: 'flex', alignItems: 'center', gap: 10, marginBottom: 16 }}>
<h1 style={{ color: 'var(--accent)', fontFamily: 'var(--font-mono)', margin: 0, flex: 1 }}>Courses</h1>
<button
onClick={() => void handleGenerateMagicList()}
disabled={generating || lists.some(l => l.status === 'draft' || l.status === 'active')}
title="Générer une liste automatiquement"
style={{
background: 'var(--bg-3)',
border: '1px solid var(--bg-5)',
borderRadius: 8,
color: lists.some(l => l.status === 'draft' || l.status === 'active')
? 'var(--ink-4)'
: 'var(--accent)',
cursor: lists.some(l => l.status === 'draft' || l.status === 'active')
? 'not-allowed'
: 'pointer',
padding: '8px 12px',
fontFamily: 'var(--font-ui)',
fontSize: 13,
display: 'flex',
alignItems: 'center',
gap: 6,
minHeight: 44,
}}
>
<i className="fa-solid fa-wand-magic-sparkles" />
{generating ? 'Génération…' : 'Liste magique'}
</button>
</div>
{error && (