feat(planner): implement C1 weekly planner home screen (#26)
Three-breakpoint layout (mobile/tablet/desktop) with VarietyScoreCard, WeekStrip, DayMealCard components. Server loads week plan and variety score via API; read-only role behavior derived from benutzer.rolle. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
76
frontend/src/lib/planner/DayMealCard.svelte
Normal file
76
frontend/src/lib/planner/DayMealCard.svelte
Normal file
@@ -0,0 +1,76 @@
|
||||
<script lang="ts">
|
||||
interface SlotRecipe {
|
||||
id?: string;
|
||||
name?: string;
|
||||
effort?: string;
|
||||
cookTimeMin?: number;
|
||||
}
|
||||
|
||||
interface Slot {
|
||||
id?: string;
|
||||
slotDate?: string;
|
||||
recipe?: SlotRecipe | null;
|
||||
}
|
||||
|
||||
let {
|
||||
slot,
|
||||
isToday = false,
|
||||
readonly = false
|
||||
}: {
|
||||
slot: Slot;
|
||||
isToday?: boolean;
|
||||
readonly?: boolean;
|
||||
} = $props();
|
||||
|
||||
let metadata = $derived(
|
||||
[
|
||||
slot.recipe?.cookTimeMin != null ? `${slot.recipe.cookTimeMin} Min` : null,
|
||||
slot.recipe?.effort ?? null
|
||||
]
|
||||
.filter(Boolean)
|
||||
.join(' · ')
|
||||
);
|
||||
</script>
|
||||
|
||||
<div
|
||||
data-testid="day-meal-card"
|
||||
data-today={isToday}
|
||||
class="rounded-[var(--radius-lg)] border-2 p-4 transition-colors
|
||||
{isToday ? 'border-[var(--yellow)] bg-[var(--yellow-tint)]' : 'border-[var(--color-border)] bg-[var(--color-surface)]'}"
|
||||
>
|
||||
{#if slot.recipe}
|
||||
<h3 class="font-[var(--font-display)] text-[20px] font-[300] leading-tight text-[var(--color-text)]">
|
||||
{slot.recipe.name}
|
||||
</h3>
|
||||
{#if metadata}
|
||||
<p class="mt-1 font-[var(--font-sans)] text-[13px] text-[var(--color-text-muted)]">{metadata}</p>
|
||||
{/if}
|
||||
|
||||
{#if !readonly}
|
||||
<div class="mt-3 flex gap-2">
|
||||
<a
|
||||
href="/recipes/{slot.recipe.id}/cook"
|
||||
class="rounded-[var(--radius-md)] bg-[var(--green-dark)] px-3 py-2 text-[13px] font-medium tracking-[0.04em] font-[var(--font-sans)] text-white"
|
||||
>
|
||||
Jetzt kochen
|
||||
</a>
|
||||
<button
|
||||
type="button"
|
||||
class="rounded-[var(--radius-md)] border border-[var(--color-border)] px-3 py-2 text-[13px] font-medium tracking-[0.04em] font-[var(--font-sans)] text-[var(--color-text)]"
|
||||
>
|
||||
Tauschen
|
||||
</button>
|
||||
</div>
|
||||
{/if}
|
||||
{:else}
|
||||
<p class="font-[var(--font-sans)] text-[14px] text-[var(--color-text-muted)]">Kein Gericht geplant</p>
|
||||
{#if !readonly}
|
||||
<a
|
||||
href="/planner/suggestions"
|
||||
class="mt-2 inline-block rounded-[var(--radius-md)] border border-dashed border-[var(--color-border)] px-3 py-2 text-[13px] font-medium tracking-[0.04em] font-[var(--font-sans)] text-[var(--color-text-muted)]"
|
||||
>
|
||||
+ Gericht hinzufügen
|
||||
</a>
|
||||
{/if}
|
||||
{/if}
|
||||
</div>
|
||||
Reference in New Issue
Block a user