Add permanent list delete; move stores out of main nav

- Add DELETE /api/lists/{id}/permanent endpoint for hard-delete alongside existing soft-delete (archive)
- Add Delete button with confirmation on list detail page next to Archive
- Handle ListDeleted SignalR event on lists overview for real-time removal
- Remove Stores from bottom nav; add Manage stores link at bottom of Lists page
- Add back-to-lists navigation on the Stores page

Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Josh Rogers
2026-05-14 21:38:53 -05:00
parent bf01063c3a
commit b7e4ebc15a
5 changed files with 49 additions and 8 deletions
@@ -185,6 +185,20 @@ public static class ShoppingListEndpoints
return Results.NoContent();
});
group.MapDelete("/{id:int}/permanent", async (int id, YesChefDb db, HttpContext http, IHubContext<ShoppingListHub> hub) =>
{
var familyId = http.User.GetFamilyId();
var list = await db.ShoppingLists.FirstOrDefaultAsync(l => l.Id == id && l.FamilyId == familyId);
if (list is null) return Results.NotFound();
db.ShoppingLists.Remove(list);
await db.SaveChangesAsync();
await hub.Clients.Group(OverviewGroup(familyId)).SendAsync("ListDeleted", new { list.Id });
return Results.NoContent();
});
group.MapPost("/{listId:int}/items", async (int listId, AddItemRequest request, YesChefDb db, HttpContext http, IHubContext<ShoppingListHub> hub) =>
{
var familyId = http.User.GetFamilyId();
-1
View File
@@ -17,7 +17,6 @@
const navItems = [
{ href: '/lists', label: 'Lists', icon: '📋' },
{ href: '/recipes', label: 'Recipes', icon: '📖' },
{ href: '/stores', label: 'Stores', icon: '🏪' },
{ href: '/family', label: 'Family', icon: '👪' }
];
@@ -46,6 +46,10 @@
lists = lists.filter((l) => l.id !== data.id);
});
connection.on('ListDeleted', (data: { id: number }) => {
lists = lists.filter((l) => l.id !== data.id);
});
connection.on('ListSummaryUpdated', (data: ListSummary) => {
lists = lists.map((l) =>
l.id === data.id ? { ...l, itemCount: data.itemCount, checkedCount: data.checkedCount, updatedAt: data.updatedAt } : l
@@ -148,4 +152,11 @@
{/each}
</div>
{/if}
<div class="mt-6 border-t border-gray-100 pt-4">
<a href="/stores" class="flex items-center justify-between text-sm text-gray-500">
<span>Manage stores</span>
<span>&rsaquo;</span>
</a>
</div>
</div>
@@ -253,6 +253,12 @@
await api(`/api/lists/${listId}`, { method: 'DELETE' });
goto('/lists');
}
async function deleteList() {
if (!confirm(`Permanently delete "${list!.name}"? This cannot be undone.`)) return;
await api(`/api/lists/${listId}/permanent`, { method: 'DELETE' });
goto('/lists');
}
</script>
{#if loading}
@@ -265,12 +271,20 @@
<h2 class="text-2xl font-bold">{list.name}</h2>
<p class="text-sm text-gray-500">{list.store.name}</p>
</div>
<div class="flex gap-2">
<button
onclick={archiveList}
class="rounded-lg border border-gray-300 px-3 py-1.5 text-sm text-gray-500"
>
Archive
</button>
<button
onclick={deleteList}
class="btn-danger-link text-sm"
>
Delete
</button>
</div>
</div>
<form onsubmit={e => { e.preventDefault(); addItem(); }} class="mb-4 space-y-2">
+4 -1
View File
@@ -152,7 +152,10 @@
</script>
<div>
<h2 class="mb-4 text-2xl font-bold">Stores</h2>
<div class="mb-4">
<a href="/lists" class="text-sm text-gray-500">&larr; Back to lists</a>
<h2 class="mt-1 text-2xl font-bold">Stores</h2>
</div>
<form onsubmit={e => { e.preventDefault(); addStore(); }} class="mb-6 flex gap-2">
<input