2 changed files with 52 additions and 1 deletions
|
|
@ -601,11 +601,22 @@
|
|||
@close="browserSelectedRecipe = null"
|
||||
@cooked="browserSelectedRecipe = null"
|
||||
/>
|
||||
|
||||
<!-- Undo toast for "I cooked this" dismiss -->
|
||||
<div
|
||||
v-if="lastCookedRecipe"
|
||||
class="undo-toast"
|
||||
role="status"
|
||||
aria-live="polite"
|
||||
>
|
||||
Dismissed from suggestions.
|
||||
<button class="btn-link" @click="undoCooked">Undo</button>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, computed, onMounted, nextTick, watch } from 'vue'
|
||||
import { ref, computed, onMounted, onUnmounted, nextTick, watch } from 'vue'
|
||||
import { useRecipesStore } from '../stores/recipes'
|
||||
import { useInventoryStore } from '../stores/inventory'
|
||||
import { useSavedRecipesStore } from '../stores/savedRecipes'
|
||||
|
|
@ -763,11 +774,28 @@ function openRecipe(recipe: RecipeSuggestion) {
|
|||
selectedRecipe.value = recipe
|
||||
}
|
||||
|
||||
const lastCookedRecipe = ref<{ id: number; title: string } | null>(null)
|
||||
let undoTimer: ReturnType<typeof setTimeout> | null = null
|
||||
|
||||
function onCooked(recipe: RecipeSuggestion) {
|
||||
recipesStore.logCook(recipe.id, recipe.title)
|
||||
recipesStore.dismiss(recipe.id)
|
||||
lastCookedRecipe.value = { id: recipe.id, title: recipe.title }
|
||||
if (undoTimer) clearTimeout(undoTimer)
|
||||
undoTimer = setTimeout(() => {
|
||||
lastCookedRecipe.value = null
|
||||
}, 12000)
|
||||
}
|
||||
|
||||
function undoCooked() {
|
||||
if (!lastCookedRecipe.value) return
|
||||
recipesStore.undismiss(lastCookedRecipe.value.id)
|
||||
if (undoTimer) clearTimeout(undoTimer)
|
||||
lastCookedRecipe.value = null
|
||||
}
|
||||
|
||||
onUnmounted(() => { if (undoTimer) clearTimeout(undoTimer) })
|
||||
|
||||
const levels = [
|
||||
{ value: 1, label: 'Use What I Have', description: 'Finds recipes you can make right now using exactly what\'s in your pantry.' },
|
||||
{ value: 2, label: 'Allow Swaps', description: 'Same as above, plus recipes where one or two ingredients can be substituted.' },
|
||||
|
|
@ -1490,4 +1518,21 @@ details[open] .collapsible-summary::before {
|
|||
grid-template-columns: 1fr;
|
||||
}
|
||||
}
|
||||
|
||||
.undo-toast {
|
||||
position: fixed;
|
||||
bottom: var(--spacing-lg);
|
||||
left: 50%;
|
||||
transform: translateX(-50%);
|
||||
background: var(--color-bg-elevated);
|
||||
border: 1px solid var(--color-border);
|
||||
border-radius: var(--radius-md);
|
||||
padding: var(--spacing-sm) var(--spacing-md);
|
||||
box-shadow: var(--shadow-md);
|
||||
font-size: var(--font-size-sm);
|
||||
z-index: 100;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: var(--spacing-sm);
|
||||
}
|
||||
</style>
|
||||
|
|
|
|||
|
|
@ -238,6 +238,11 @@ export const useRecipesStore = defineStore('recipes', () => {
|
|||
}
|
||||
}
|
||||
|
||||
function undismiss(id: number) {
|
||||
dismissedIds.value = new Set([...dismissedIds.value].filter((d) => d !== id))
|
||||
saveDismissed(dismissedIds.value)
|
||||
}
|
||||
|
||||
function clearDismissed() {
|
||||
dismissedIds.value = new Set()
|
||||
localStorage.removeItem(DISMISSED_KEY)
|
||||
|
|
@ -318,6 +323,7 @@ export const useRecipesStore = defineStore('recipes', () => {
|
|||
suggest,
|
||||
loadMore,
|
||||
dismiss,
|
||||
undismiss,
|
||||
clearDismissed,
|
||||
clearResult,
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue