feat: D1 — Shopping list (Issue #30) #43

Merged
marcel merged 24 commits from feat/issue-30-shopping-list into master 2026-04-08 22:22:02 +02:00
2 changed files with 107 additions and 106 deletions
Showing only changes of commit 3cd9154550 - Show all commits

View File

@@ -0,0 +1,92 @@
<script lang="ts">
import ChecklistItem from '$lib/shopping/ChecklistItem.svelte';
import AddCustomItem from '$lib/shopping/AddCustomItem.svelte';
interface RecipeRef {
id?: string;
name?: string;
}
interface Item {
id?: string;
name?: string;
quantity?: number;
unit?: string;
isChecked?: boolean;
sourceRecipes?: RecipeRef[];
}
interface Props {
listId: string;
uncheckedItems: Item[];
checkedItems: Item[];
totalItems: number;
filteredStaplesCount?: number;
showFilteredStaples?: boolean;
}
let {
listId,
uncheckedItems,
checkedItems,
totalItems,
filteredStaplesCount = 0,
showFilteredStaples = false
}: Props = $props();
let checkedCount = $derived(checkedItems.length);
</script>
{#if uncheckedItems.length > 0}
<div class="divide-y divide-[var(--color-border)]">
{#each uncheckedItems as item (item.id)}
<ChecklistItem
{listId}
itemId={item.id ?? ''}
name={item.name ?? ''}
quantity={item.quantity ?? null}
unit={item.unit ?? null}
isChecked={false}
sourceRecipes={item.sourceRecipes ?? []}
/>
{/each}
</div>
{:else if totalItems > 0}
<p class="py-4 text-center font-[var(--font-sans)] text-[14px] text-[var(--color-text-muted)]">
Alles erledigt!
</p>
{/if}
<div class="mt-3">
<AddCustomItem {listId} />
</div>
{#if showFilteredStaples && filteredStaplesCount > 0}
<div class="mt-3 rounded-[var(--radius-md)] border border-[var(--color-border)] bg-[var(--color-surface)] px-3 py-2">
<p class="font-[var(--font-sans)] text-[12px] text-[var(--color-text-muted)]">
{filteredStaplesCount} Grundzutaten ausgeblendet ·
<a href="/pantry" class="font-medium text-[var(--green-dark)] hover:underline">Vorrat bearbeiten</a>
</p>
</div>
{/if}
{#if checkedItems.length > 0}
<div class="mt-4">
<p class="mb-1 font-[var(--font-sans)] text-[12px] font-medium uppercase tracking-wide text-[var(--color-text-muted)]">
Abgehakt ({checkedCount})
</p>
<div class="divide-y divide-[var(--color-border)]">
{#each checkedItems as item (item.id)}
<ChecklistItem
{listId}
itemId={item.id ?? ''}
name={item.name ?? ''}
quantity={item.quantity ?? null}
unit={item.unit ?? null}
isChecked={true}
sourceRecipes={item.sourceRecipes ?? []}
/>
{/each}
</div>
</div>
{/if}

View File

@@ -1,7 +1,6 @@
<script lang="ts">
import ShoppingHeader from '$lib/shopping/ShoppingHeader.svelte';
import ChecklistItem from '$lib/shopping/ChecklistItem.svelte';
import AddCustomItem from '$lib/shopping/AddCustomItem.svelte';
import ShoppingChecklist from '$lib/shopping/ShoppingChecklist.svelte';
import RecipeReferencePanel from '$lib/shopping/RecipeReferencePanel.svelte';
let { data } = $props();
@@ -65,63 +64,14 @@
{/if}
</div>
{:else}
<!-- Unchecked items -->
{#if uncheckedItems.length > 0}
<div class="divide-y divide-[var(--color-border)]">
{#each uncheckedItems as item (item.id)}
<ChecklistItem
{listId}
itemId={item.id ?? ''}
name={item.name ?? ''}
quantity={item.quantity ?? null}
unit={item.unit ?? null}
isChecked={false}
sourceRecipes={item.sourceRecipes ?? []}
/>
{/each}
</div>
{:else if totalItems > 0}
<p class="py-4 text-center font-[var(--font-sans)] text-[14px] text-[var(--color-text-muted)]">
Alles erledigt!
</p>
{/if}
<!-- Add custom item -->
<div class="mt-3">
<AddCustomItem {listId} />
</div>
<!-- Filtered staples info (mobile) -->
{#if filteredStaplesCount > 0}
<div class="mt-3 rounded-[var(--radius-md)] border border-[var(--color-border)] bg-[var(--color-surface)] px-3 py-2">
<p class="font-[var(--font-sans)] text-[12px] text-[var(--color-text-muted)]">
{filteredStaplesCount} Grundzutaten ausgeblendet ·
<a href="/pantry" class="font-medium text-[var(--green-dark)] hover:underline">Vorrat bearbeiten</a>
</p>
</div>
{/if}
<!-- Checked items -->
{#if checkedItems.length > 0}
<div class="mt-4">
<p class="mb-1 font-[var(--font-sans)] text-[12px] font-medium uppercase tracking-wide text-[var(--color-text-muted)]">
Abgehakt ({checkedCount})
</p>
<div class="divide-y divide-[var(--color-border)]">
{#each checkedItems as item (item.id)}
<ChecklistItem
{listId}
itemId={item.id ?? ''}
name={item.name ?? ''}
quantity={item.quantity ?? null}
unit={item.unit ?? null}
isChecked={true}
sourceRecipes={item.sourceRecipes ?? []}
/>
{/each}
</div>
</div>
{/if}
<ShoppingChecklist
{listId}
{uncheckedItems}
{checkedItems}
{totalItems}
{filteredStaplesCount}
showFilteredStaples={true}
/>
{/if}
</main>
</div>
@@ -170,53 +120,12 @@
{/if}
</div>
{:else}
<!-- Unchecked items -->
{#if uncheckedItems.length > 0}
<div class="divide-y divide-[var(--color-border)]">
{#each uncheckedItems as item (item.id)}
<ChecklistItem
{listId}
itemId={item.id ?? ''}
name={item.name ?? ''}
quantity={item.quantity ?? null}
unit={item.unit ?? null}
isChecked={false}
sourceRecipes={item.sourceRecipes ?? []}
/>
{/each}
</div>
{:else if totalItems > 0}
<p class="py-4 text-center font-[var(--font-sans)] text-[14px] text-[var(--color-text-muted)]">
Alles erledigt!
</p>
{/if}
<!-- Add custom item -->
<div class="mt-4">
<AddCustomItem {listId} />
</div>
<!-- Checked items -->
{#if checkedItems.length > 0}
<div class="mt-6 border-t border-[var(--color-border)] pt-4">
<p class="mb-1 font-[var(--font-sans)] text-[12px] font-medium uppercase tracking-wide text-[var(--color-text-muted)]">
Abgehakt ({checkedCount})
</p>
<div class="divide-y divide-[var(--color-border)]">
{#each checkedItems as item (item.id)}
<ChecklistItem
{listId}
itemId={item.id ?? ''}
name={item.name ?? ''}
quantity={item.quantity ?? null}
unit={item.unit ?? null}
isChecked={true}
sourceRecipes={item.sourceRecipes ?? []}
/>
{/each}
</div>
</div>
{/if}
<ShoppingChecklist
{listId}
{uncheckedItems}
{checkedItems}
{totalItems}
/>
{/if}
</main>