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:
@@ -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 && (
|
||||
|
||||
Reference in New Issue
Block a user