refactor(shopping): extract ShoppingChecklist.svelte to eliminate mobile/desktop duplication
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
92
frontend/src/lib/shopping/ShoppingChecklist.svelte
Normal file
92
frontend/src/lib/shopping/ShoppingChecklist.svelte
Normal 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}
|
||||||
@@ -1,7 +1,6 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import ShoppingHeader from '$lib/shopping/ShoppingHeader.svelte';
|
import ShoppingHeader from '$lib/shopping/ShoppingHeader.svelte';
|
||||||
import ChecklistItem from '$lib/shopping/ChecklistItem.svelte';
|
import ShoppingChecklist from '$lib/shopping/ShoppingChecklist.svelte';
|
||||||
import AddCustomItem from '$lib/shopping/AddCustomItem.svelte';
|
|
||||||
import RecipeReferencePanel from '$lib/shopping/RecipeReferencePanel.svelte';
|
import RecipeReferencePanel from '$lib/shopping/RecipeReferencePanel.svelte';
|
||||||
|
|
||||||
let { data } = $props();
|
let { data } = $props();
|
||||||
@@ -65,63 +64,14 @@
|
|||||||
{/if}
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
{:else}
|
{:else}
|
||||||
<!-- Unchecked items -->
|
<ShoppingChecklist
|
||||||
{#if uncheckedItems.length > 0}
|
{listId}
|
||||||
<div class="divide-y divide-[var(--color-border)]">
|
{uncheckedItems}
|
||||||
{#each uncheckedItems as item (item.id)}
|
{checkedItems}
|
||||||
<ChecklistItem
|
{totalItems}
|
||||||
{listId}
|
{filteredStaplesCount}
|
||||||
itemId={item.id ?? ''}
|
showFilteredStaples={true}
|
||||||
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}
|
|
||||||
{/if}
|
{/if}
|
||||||
</main>
|
</main>
|
||||||
</div>
|
</div>
|
||||||
@@ -170,53 +120,12 @@
|
|||||||
{/if}
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
{:else}
|
{:else}
|
||||||
<!-- Unchecked items -->
|
<ShoppingChecklist
|
||||||
{#if uncheckedItems.length > 0}
|
{listId}
|
||||||
<div class="divide-y divide-[var(--color-border)]">
|
{uncheckedItems}
|
||||||
{#each uncheckedItems as item (item.id)}
|
{checkedItems}
|
||||||
<ChecklistItem
|
{totalItems}
|
||||||
{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}
|
|
||||||
{/if}
|
{/if}
|
||||||
</main>
|
</main>
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user