fix(kiwi-a11y): undo toast for 'I cooked this' dismiss action (#45)

This commit is contained in:
pyr0ball 2026-04-15 10:06:31 -07:00
parent bc04739447
commit 5c135d0860
2 changed files with 52 additions and 1 deletions

View file

@ -601,11 +601,22 @@
@close="browserSelectedRecipe = null" @close="browserSelectedRecipe = null"
@cooked="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> </div>
</template> </template>
<script setup lang="ts"> <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 { useRecipesStore } from '../stores/recipes'
import { useInventoryStore } from '../stores/inventory' import { useInventoryStore } from '../stores/inventory'
import { useSavedRecipesStore } from '../stores/savedRecipes' import { useSavedRecipesStore } from '../stores/savedRecipes'
@ -763,11 +774,28 @@ function openRecipe(recipe: RecipeSuggestion) {
selectedRecipe.value = recipe selectedRecipe.value = recipe
} }
const lastCookedRecipe = ref<{ id: number; title: string } | null>(null)
let undoTimer: ReturnType<typeof setTimeout> | null = null
function onCooked(recipe: RecipeSuggestion) { function onCooked(recipe: RecipeSuggestion) {
recipesStore.logCook(recipe.id, recipe.title) recipesStore.logCook(recipe.id, recipe.title)
recipesStore.dismiss(recipe.id) 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 = [ 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: 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.' }, { 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; 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> </style>

View file

@ -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() { function clearDismissed() {
dismissedIds.value = new Set() dismissedIds.value = new Set()
localStorage.removeItem(DISMISSED_KEY) localStorage.removeItem(DISMISSED_KEY)
@ -318,6 +323,7 @@ export const useRecipesStore = defineStore('recipes', () => {
suggest, suggest,
loadMore, loadMore,
dismiss, dismiss,
undismiss,
clearDismissed, clearDismissed,
clearResult, clearResult,
} }