feat(planner): add remove meal with undo; fix RecipePicker badge for neutral delta
- MealActionSheet: new onremove prop + Entfernen button (guarded by #if) - +page.svelte: handleRemoveMeal submits delete form, shows undo bar; undo re-adds via addSlot form; refactored handleUndo to undoCallback pattern; desktop day-detail panel also gets Entfernen button - RecipePicker: only show green +delta badge when scoreDelta > 0; neutral (scoreDelta = 0) shows no badge instead of ⚠ Variationskonflikt - Tests: page.test.ts remove-meal describe, RecipePicker neutral badge test Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -100,6 +100,7 @@
|
||||
// UndoBar
|
||||
let undoVisible = $state(false);
|
||||
let undoMessage = $state('');
|
||||
let undoCallback = $state<(() => void) | null>(null);
|
||||
|
||||
$effect(() => {
|
||||
if (!activePickerDate || !weekPlan?.id) {
|
||||
@@ -162,7 +163,33 @@
|
||||
|
||||
function handleUndo() {
|
||||
undoVisible = false;
|
||||
undoCallback?.();
|
||||
}
|
||||
|
||||
async function handleRemoveMeal(slot: { id: string; slotDate: string; recipe: { id: string; name: string } | null }) {
|
||||
// Capture primitive values immediately — slot may be a reactive proxy that
|
||||
// becomes stale after the first await (tick flushes state + re-render).
|
||||
const slotId = slot.id;
|
||||
const slotDate = slot.slotDate;
|
||||
const recipeName = slot.recipe?.name ?? '';
|
||||
const recipeId = slot.recipe?.id ?? '';
|
||||
if (!slotId || !recipeId) return;
|
||||
|
||||
actionSheetOpen = false;
|
||||
undoCallback = async () => {
|
||||
addPlanId = weekPlan!.id;
|
||||
addSlotDate = slotDate;
|
||||
addRecipeId = recipeId;
|
||||
addRecipeName = recipeName;
|
||||
await tick();
|
||||
addSlotFormEl.requestSubmit();
|
||||
};
|
||||
delPlanId = weekPlan!.id;
|
||||
delSlotId = slotId;
|
||||
await tick();
|
||||
deleteSlotFormEl.requestSubmit();
|
||||
undoMessage = `${recipeName} entfernt`;
|
||||
undoVisible = true;
|
||||
}
|
||||
|
||||
async function handleSwapPick(recipeId: string, recipeName: string) {
|
||||
@@ -315,12 +342,13 @@
|
||||
/>
|
||||
</BottomSheet>
|
||||
|
||||
<!-- Mobile: meal exists → action sheet (Swap / Cook / View / Cancel) -->
|
||||
<!-- Mobile: meal exists → action sheet (Swap / Cook / View / Remove / Cancel) -->
|
||||
<MealActionSheet
|
||||
open={actionSheetOpen}
|
||||
slot={selectedSlot}
|
||||
onswap={() => { actionSheetOpen = false; swapSheetOpen = true; }}
|
||||
oncancel={() => (actionSheetOpen = false)}
|
||||
onremove={isPlanner && selectedSlot.id ? () => handleRemoveMeal(selectedSlot as any) : undefined}
|
||||
/>
|
||||
|
||||
<!-- Mobile: swap suggestions sheet -->
|
||||
@@ -530,6 +558,15 @@
|
||||
>
|
||||
Gericht tauschen
|
||||
</button>
|
||||
{#if detailSlot.id}
|
||||
<button
|
||||
type="button"
|
||||
onclick={() => { handleRemoveMeal(detailSlot as any); panelState = { kind: 'idle' }; }}
|
||||
class="block w-full rounded-[var(--radius-md)] border border-[var(--color-error,#d9534f)] px-3 py-2 text-center text-[13px] font-medium tracking-[0.04em] font-[var(--font-sans)] text-[var(--color-error,#d9534f)] hover:bg-[var(--color-surface)]"
|
||||
>
|
||||
Entfernen
|
||||
</button>
|
||||
{/if}
|
||||
{/if}
|
||||
</div>
|
||||
{:else}
|
||||
@@ -611,8 +648,10 @@
|
||||
formData.set('recipeId', addRecipeId);
|
||||
return async ({ result, update }) => {
|
||||
if (result.type === 'success' && result.data?.success) {
|
||||
const slotId = (result.data as any)?.slot?.id ?? '';
|
||||
delPlanId = addPlanId;
|
||||
delSlotId = (result.data as any)?.slot?.id ?? '';
|
||||
delSlotId = slotId;
|
||||
undoCallback = () => deleteSlotFormEl.requestSubmit();
|
||||
undoMessage = `${addRecipeName} hinzugefügt`;
|
||||
undoVisible = true;
|
||||
}
|
||||
@@ -639,6 +678,7 @@
|
||||
if (result.type === 'success' && result.data?.success) {
|
||||
delPlanId = updPlanId;
|
||||
delSlotId = (result.data as any)?.slot?.id ?? '';
|
||||
undoCallback = () => deleteSlotFormEl.requestSubmit();
|
||||
undoMessage = `${updRecipeName} eingetragen`;
|
||||
undoVisible = true;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user