feat: aggiungi pulsante svuota piano settimanale
- Sposta .btn-clear da ShoppingList.vue (scoped) a style.css (globale) - Aggiunge hasMeals, clearAll e btn-clear in MealPlanner.vue - Aggiunge 4 test di integrazione per il nuovo pulsante Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -22,6 +22,8 @@
|
|||||||
Genera lista della spesa
|
Genera lista della spesa
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
|
<button v-if="hasMeals" class="btn-clear" @click="clearAll">Svuota piano</button>
|
||||||
|
|
||||||
<div class="btn-share-row">
|
<div class="btn-share-row">
|
||||||
<button class="btn-share" @click="openShare">
|
<button class="btn-share" @click="openShare">
|
||||||
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||||||
@@ -99,7 +101,7 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
import { reactive, watch, ref, onUnmounted, nextTick } from 'vue'
|
import { reactive, watch, ref, computed, onUnmounted, nextTick } from 'vue'
|
||||||
import MealCard from '../components/MealCard.vue'
|
import MealCard from '../components/MealCard.vue'
|
||||||
import { save, load } from '../utils/storage.js'
|
import { save, load } from '../utils/storage.js'
|
||||||
import QRCode from 'qrcode'
|
import QRCode from 'qrcode'
|
||||||
@@ -137,6 +139,14 @@ function removeItem(day, slot, idx) {
|
|||||||
meals[day][slot].splice(idx, 1)
|
meals[day][slot].splice(idx, 1)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const hasMeals = computed(() =>
|
||||||
|
days.some(d => ['colazione', 'pranzo', 'cena'].some(s => meals[d.id][s].length > 0))
|
||||||
|
)
|
||||||
|
|
||||||
|
function clearAll() {
|
||||||
|
if (confirm('Svuotare tutto il piano settimanale?')) Object.assign(meals, defaultMeals())
|
||||||
|
}
|
||||||
|
|
||||||
function generateShopping() {
|
function generateShopping() {
|
||||||
const allItems = days.flatMap(d =>
|
const allItems = days.flatMap(d =>
|
||||||
['colazione', 'pranzo', 'cena'].flatMap(slot => meals[d.id][slot])
|
['colazione', 'pranzo', 'cena'].flatMap(slot => meals[d.id][slot])
|
||||||
|
|||||||
@@ -144,21 +144,6 @@ function clearAll() {
|
|||||||
background: var(--color-border);
|
background: var(--color-border);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* bottone distruttivo in ghost style — meno aggressivo del solid rosso */
|
|
||||||
.btn-clear {
|
|
||||||
width: 100%;
|
|
||||||
background: transparent;
|
|
||||||
color: var(--color-danger);
|
|
||||||
border: 1.5px solid var(--color-danger);
|
|
||||||
margin-top: 16px;
|
|
||||||
font-weight: 600;
|
|
||||||
}
|
|
||||||
|
|
||||||
.btn-clear:active {
|
|
||||||
background: var(--color-danger-muted);
|
|
||||||
opacity: 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
.empty-state {
|
.empty-state {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
|
|||||||
@@ -78,6 +78,21 @@ button {
|
|||||||
|
|
||||||
button:active { opacity: 0.75; }
|
button:active { opacity: 0.75; }
|
||||||
|
|
||||||
|
/* bottone distruttivo in ghost style — meno aggressivo del solid rosso */
|
||||||
|
.btn-clear {
|
||||||
|
width: 100%;
|
||||||
|
background: transparent;
|
||||||
|
color: var(--color-danger);
|
||||||
|
border: 1.5px solid var(--color-danger);
|
||||||
|
margin-top: 16px;
|
||||||
|
font-weight: 600;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-clear:active {
|
||||||
|
background: var(--color-danger-muted);
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
|
||||||
input[type="text"],
|
input[type="text"],
|
||||||
input[type="number"] {
|
input[type="number"] {
|
||||||
font-size: 1rem;
|
font-size: 1rem;
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { describe, it, expect } from 'vitest'
|
import { describe, it, expect, vi } from 'vitest'
|
||||||
import { mount } from '@vue/test-utils'
|
import { mount } from '@vue/test-utils'
|
||||||
import { nextTick } from 'vue'
|
import { nextTick } from 'vue'
|
||||||
import MealPlanner from '../../src/pages/MealPlanner.vue'
|
import MealPlanner from '../../src/pages/MealPlanner.vue'
|
||||||
@@ -150,3 +150,41 @@ describe('MealPlanner — genera lista della spesa', () => {
|
|||||||
expect(load('shopping', [])).toHaveLength(0)
|
expect(load('shopping', [])).toHaveLength(0)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
describe('MealPlanner — svuota piano', () => {
|
||||||
|
it('non mostra il pulsante se il piano è vuoto', () => {
|
||||||
|
const w = mountPlanner()
|
||||||
|
expect(w.find('.btn-clear').exists()).toBe(false)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('mostra il pulsante se c\'è almeno un pasto', () => {
|
||||||
|
seedMeals({ lunedi: { colazione: ['caffè'], pranzo: [], cena: [] } })
|
||||||
|
const w = mountPlanner()
|
||||||
|
expect(w.find('.btn-clear').exists()).toBe(true)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('svuota il piano dopo conferma', async () => {
|
||||||
|
vi.spyOn(window, 'confirm').mockReturnValue(true)
|
||||||
|
seedMeals({ lunedi: { colazione: ['caffè'], pranzo: ['pasta'], cena: [] } })
|
||||||
|
const w = mountPlanner()
|
||||||
|
|
||||||
|
await w.find('.btn-clear').trigger('click')
|
||||||
|
await nextTick()
|
||||||
|
|
||||||
|
const saved = load('meals', {})
|
||||||
|
expect(saved.lunedi.colazione).toHaveLength(0)
|
||||||
|
expect(saved.lunedi.pranzo).toHaveLength(0)
|
||||||
|
expect(w.find('.btn-clear').exists()).toBe(false)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('non svuota se l\'utente annulla', async () => {
|
||||||
|
vi.spyOn(window, 'confirm').mockReturnValue(false)
|
||||||
|
seedMeals({ lunedi: { colazione: ['caffè'], pranzo: [], cena: [] } })
|
||||||
|
const w = mountPlanner()
|
||||||
|
|
||||||
|
await w.find('.btn-clear').trigger('click')
|
||||||
|
await nextTick()
|
||||||
|
|
||||||
|
expect(load('meals', {}).lunedi.colazione).toHaveLength(1)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|||||||
Reference in New Issue
Block a user