chore(planner): delete orphaned SuggestionCard component and test
Unused since the suggestions route was removed (commit 4333dc0).
RecipePicker.test.ts is the active coverage for suggestion rendering.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -1,83 +0,0 @@
|
|||||||
<script lang="ts">
|
|
||||||
interface SlotRecipe {
|
|
||||||
id?: string;
|
|
||||||
name?: string;
|
|
||||||
effort?: string;
|
|
||||||
cookTimeMin?: number;
|
|
||||||
}
|
|
||||||
|
|
||||||
interface Suggestion {
|
|
||||||
recipe?: SlotRecipe;
|
|
||||||
simulatedScore?: number;
|
|
||||||
reasoningType?: 'good' | 'warning';
|
|
||||||
reasoningLabel?: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
let {
|
|
||||||
suggestion,
|
|
||||||
rank,
|
|
||||||
planId,
|
|
||||||
slotDate,
|
|
||||||
weekStart
|
|
||||||
}: {
|
|
||||||
suggestion: Suggestion;
|
|
||||||
rank: number;
|
|
||||||
planId: string;
|
|
||||||
slotDate: string;
|
|
||||||
weekStart: string;
|
|
||||||
} = $props();
|
|
||||||
|
|
||||||
let metadata = $derived(
|
|
||||||
[
|
|
||||||
suggestion.recipe?.cookTimeMin != null ? `${suggestion.recipe.cookTimeMin} Min` : null,
|
|
||||||
suggestion.recipe?.effort ?? null
|
|
||||||
]
|
|
||||||
.filter(Boolean)
|
|
||||||
.join(' · ')
|
|
||||||
);
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<div class="flex items-start gap-3 rounded-[var(--radius-md)] border border-[var(--color-border)] bg-[var(--color-surface)] px-4 py-3 shadow-[var(--shadow-card)]">
|
|
||||||
<!-- Rank number -->
|
|
||||||
<div class="w-10 flex-shrink-0 self-start text-right">
|
|
||||||
<span class="font-[var(--font-display)] text-[32px] font-[300] leading-none text-[var(--color-text-muted)]">{rank}</span>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Card content -->
|
|
||||||
<div class="flex-1 min-w-0">
|
|
||||||
<p class="font-[var(--font-sans)] text-[15px] font-medium text-[var(--color-text)] line-clamp-2">
|
|
||||||
{suggestion.recipe?.name ?? 'Unbekanntes Rezept'}
|
|
||||||
</p>
|
|
||||||
{#if metadata}
|
|
||||||
<p class="mt-0.5 font-[var(--font-sans)] text-[12px] text-[var(--color-text-muted)]">{metadata}</p>
|
|
||||||
{/if}
|
|
||||||
|
|
||||||
<!-- Reasoning badge -->
|
|
||||||
{#if suggestion.reasoningType && suggestion.reasoningLabel}
|
|
||||||
<div
|
|
||||||
data-testid="reasoning-badge"
|
|
||||||
data-type={suggestion.reasoningType}
|
|
||||||
class="mt-2 inline-flex items-center rounded-full px-2 py-0.5 font-[var(--font-sans)] text-[11px] font-medium
|
|
||||||
{suggestion.reasoningType === 'good'
|
|
||||||
? 'bg-[var(--green-tint)] text-[var(--green-dark)]'
|
|
||||||
: 'bg-[var(--yellow-tint)] text-[var(--yellow-text)]'}"
|
|
||||||
>
|
|
||||||
{suggestion.reasoningType === 'good' ? '✓' : '⚠'} {suggestion.reasoningLabel}
|
|
||||||
</div>
|
|
||||||
{/if}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Pick action -->
|
|
||||||
<form method="POST" action="?/pickSuggestion" class="flex-shrink-0">
|
|
||||||
<input type="hidden" name="planId" value={planId} />
|
|
||||||
<input type="hidden" name="recipeId" value={suggestion.recipe?.id} />
|
|
||||||
<input type="hidden" name="slotDate" value={slotDate} />
|
|
||||||
<input type="hidden" name="weekStart" value={weekStart} />
|
|
||||||
<button
|
|
||||||
type="submit"
|
|
||||||
class="font-[var(--font-sans)] text-[13px] font-medium tracking-[0.04em] text-[var(--green-dark)] hover:underline"
|
|
||||||
>
|
|
||||||
Wählen
|
|
||||||
</button>
|
|
||||||
</form>
|
|
||||||
</div>
|
|
||||||
@@ -1,60 +0,0 @@
|
|||||||
import { describe, it, expect } from 'vitest';
|
|
||||||
import { render, screen } from '@testing-library/svelte';
|
|
||||||
import SuggestionCard from './SuggestionCard.svelte';
|
|
||||||
|
|
||||||
const goodSuggestion = {
|
|
||||||
recipe: { id: 'r1', name: 'Pasta al Limone', effort: 'Easy', cookTimeMin: 25 },
|
|
||||||
simulatedScore: 9.2,
|
|
||||||
reasoningType: 'good' as const,
|
|
||||||
reasoningLabel: 'Frisches Protein · Aufwandsbalance'
|
|
||||||
};
|
|
||||||
|
|
||||||
const warningSuggestion = {
|
|
||||||
recipe: { id: 'r2', name: 'Hühnchen Curry', effort: 'Medium', cookTimeMin: 45 },
|
|
||||||
simulatedScore: 6.1,
|
|
||||||
reasoningType: 'warning' as const,
|
|
||||||
reasoningLabel: 'Hähnchen schon 2 Tage dabei'
|
|
||||||
};
|
|
||||||
|
|
||||||
describe('SuggestionCard', () => {
|
|
||||||
it('renders recipe name', () => {
|
|
||||||
render(SuggestionCard, { props: { suggestion: goodSuggestion, rank: 1, planId: 'p1', slotDate: '2026-04-01', weekStart: '2026-03-30' } });
|
|
||||||
expect(screen.getByText('Pasta al Limone')).toBeTruthy();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('renders rank number', () => {
|
|
||||||
render(SuggestionCard, { props: { suggestion: goodSuggestion, rank: 1, planId: 'p1', slotDate: '2026-04-01', weekStart: '2026-03-30' } });
|
|
||||||
expect(screen.getByText('1')).toBeTruthy();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('renders cook time and effort metadata', () => {
|
|
||||||
render(SuggestionCard, { props: { suggestion: goodSuggestion, rank: 1, planId: 'p1', slotDate: '2026-04-01', weekStart: '2026-03-30' } });
|
|
||||||
expect(screen.getByText(/25 Min/)).toBeTruthy();
|
|
||||||
expect(screen.getByText(/Easy/)).toBeTruthy();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('renders green reasoning badge for good suggestions', () => {
|
|
||||||
render(SuggestionCard, { props: { suggestion: goodSuggestion, rank: 1, planId: 'p1', slotDate: '2026-04-01', weekStart: '2026-03-30' } });
|
|
||||||
const badge = screen.getByTestId('reasoning-badge');
|
|
||||||
expect(badge.getAttribute('data-type')).toBe('good');
|
|
||||||
expect(badge.textContent).toContain('Frisches Protein');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('renders yellow reasoning badge for warnings', () => {
|
|
||||||
render(SuggestionCard, { props: { suggestion: warningSuggestion, rank: 2, planId: 'p1', slotDate: '2026-04-01', weekStart: '2026-03-30' } });
|
|
||||||
const badge = screen.getByTestId('reasoning-badge');
|
|
||||||
expect(badge.getAttribute('data-type')).toBe('warning');
|
|
||||||
expect(badge.textContent).toContain('Hähnchen');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('renders a pick button/form', () => {
|
|
||||||
render(SuggestionCard, { props: { suggestion: goodSuggestion, rank: 1, planId: 'p1', slotDate: '2026-04-01', weekStart: '2026-03-30' } });
|
|
||||||
expect(screen.getByRole('button', { name: /Wählen/i })).toBeTruthy();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('card without reasoning renders without crashing', () => {
|
|
||||||
const noReasoning = { ...goodSuggestion, reasoningType: undefined, reasoningLabel: undefined };
|
|
||||||
render(SuggestionCard, { props: { suggestion: noReasoning, rank: 1, planId: 'p1', slotDate: '2026-04-01', weekStart: '2026-03-30' } });
|
|
||||||
expect(screen.getByText('Pasta al Limone')).toBeTruthy();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
Reference in New Issue
Block a user