feat(settings): implement settings hub page with three cards (Vorräte, Haushalt, Profil)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -1 +1,70 @@
|
||||
<h1 class="text-2xl font-medium p-6">Einstellungen</h1>
|
||||
<script lang="ts">
|
||||
import SettingsCard from '$lib/components/SettingsCard.svelte';
|
||||
|
||||
interface Props {
|
||||
data: {
|
||||
stapleCount: number;
|
||||
memberCount: number;
|
||||
userName: string;
|
||||
};
|
||||
}
|
||||
|
||||
let { data }: Props = $props();
|
||||
</script>
|
||||
|
||||
<h1>Einstellungen</h1>
|
||||
|
||||
<div class="grid grid-cols-1 sm:grid-cols-2 gap-4">
|
||||
<!-- Card 1: Vorräte (inline, conditional content) -->
|
||||
<a
|
||||
href="/household/staples?ctx=settings"
|
||||
class="flex flex-col gap-3 rounded-[var(--radius-md)] border border-[var(--color-border)] bg-[var(--color-surface)] p-4 no-underline text-[var(--color-text)] hover:shadow-[var(--shadow-raised)] hover:border-[#C0BFB8] transition-[box-shadow,border-color] duration-150 ease focus-visible:outline focus-visible:outline-2 focus-visible:outline-[var(--green-dark)] focus-visible:outline-offset-2 border-l-[3px] border-l-[var(--green-dark)]"
|
||||
>
|
||||
<span class="font-[var(--font-sans)] text-[14px] font-medium">Vorräte</span>
|
||||
|
||||
{#if data.stapleCount > 0}
|
||||
<p class="font-[var(--font-sans)] text-[13px] text-[var(--color-text-muted)] m-0">
|
||||
<span
|
||||
data-testid="staple-count"
|
||||
class="font-[var(--font-display)] text-[36px] leading-[1] text-[var(--green-dark)]"
|
||||
>{data.stapleCount}</span>
|
||||
</p>
|
||||
{:else}
|
||||
<p class="font-[var(--font-sans)] text-[13px] text-[var(--color-text-muted)] m-0">
|
||||
Noch keine Vorräte eingerichtet
|
||||
</p>
|
||||
{/if}
|
||||
|
||||
<span class="font-[var(--font-sans)] text-[12px] font-medium text-[var(--green-dark)] mt-auto">
|
||||
{#if data.stapleCount > 0}
|
||||
Vorräte bearbeiten →
|
||||
{:else}
|
||||
Jetzt einrichten →
|
||||
{/if}
|
||||
</span>
|
||||
</a>
|
||||
|
||||
<!-- Card 2: Haushalt (inline, needs data-testid on member count) -->
|
||||
<a
|
||||
href="/members"
|
||||
class="flex flex-col gap-3 rounded-[var(--radius-md)] border border-[var(--color-border)] bg-[var(--color-surface)] p-4 no-underline text-[var(--color-text)] hover:shadow-[var(--shadow-raised)] hover:border-[#C0BFB8] transition-[box-shadow,border-color] duration-150 ease focus-visible:outline focus-visible:outline-2 focus-visible:outline-[var(--green-dark)] focus-visible:outline-offset-2"
|
||||
>
|
||||
<span class="font-[var(--font-sans)] text-[14px] font-medium">Haushalt</span>
|
||||
|
||||
<p class="font-[var(--font-sans)] text-[13px] text-[var(--color-text-muted)] m-0">
|
||||
<span data-testid="member-count">{data.memberCount}</span> Mitglieder
|
||||
</p>
|
||||
|
||||
<span class="font-[var(--font-sans)] text-[12px] font-medium text-[var(--green-dark)] mt-auto">
|
||||
Mitglieder anzeigen →
|
||||
</span>
|
||||
</a>
|
||||
|
||||
<!-- Card 3: Profil (uses SettingsCard) -->
|
||||
<SettingsCard
|
||||
title="Profil"
|
||||
href="/profile"
|
||||
cta="Profil bearbeiten →"
|
||||
meta={data.userName}
|
||||
/>
|
||||
</div>
|
||||
|
||||
71
frontend/src/routes/(app)/settings/page.test.ts
Normal file
71
frontend/src/routes/(app)/settings/page.test.ts
Normal file
@@ -0,0 +1,71 @@
|
||||
import { describe, it, expect } from 'vitest';
|
||||
import { render, screen } from '@testing-library/svelte';
|
||||
import Page from './+page.svelte';
|
||||
|
||||
function makeData(overrides: Partial<{ stapleCount: number; memberCount: number; userName: string }> = {}) {
|
||||
return {
|
||||
stapleCount: 14,
|
||||
memberCount: 3,
|
||||
userName: 'Marcel Raddatz',
|
||||
...overrides
|
||||
};
|
||||
}
|
||||
|
||||
describe('settings page — hub', () => {
|
||||
it('renders the page heading Einstellungen', () => {
|
||||
render(Page, { props: { data: makeData() } });
|
||||
expect(screen.getByRole('heading', { name: /einstellungen/i })).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('renders Vorräte card linking to /household/staples?ctx=settings', () => {
|
||||
render(Page, { props: { data: makeData() } });
|
||||
const links = screen.getAllByRole('link');
|
||||
const vorrateLink = links.find((l) => l.getAttribute('href') === '/household/staples?ctx=settings');
|
||||
expect(vorrateLink).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('renders Haushalt card linking to /members', () => {
|
||||
render(Page, { props: { data: makeData() } });
|
||||
const links = screen.getAllByRole('link');
|
||||
const haushaltLink = links.find((l) => l.getAttribute('href') === '/members');
|
||||
expect(haushaltLink).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('renders Profil card linking to /profile', () => {
|
||||
render(Page, { props: { data: makeData() } });
|
||||
const links = screen.getAllByRole('link');
|
||||
const profilLink = links.find((l) => l.getAttribute('href') === '/profile');
|
||||
expect(profilLink).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('shows stapleCount as a number in the Vorräte card', () => {
|
||||
render(Page, { props: { data: makeData({ stapleCount: 14 }) } });
|
||||
expect(screen.getByTestId('staple-count')).toHaveTextContent('14');
|
||||
});
|
||||
|
||||
it('shows memberCount in the Haushalt card', () => {
|
||||
render(Page, { props: { data: makeData({ memberCount: 3 }) } });
|
||||
expect(screen.getByTestId('member-count')).toHaveTextContent('3');
|
||||
});
|
||||
|
||||
it('shows userName in the Profil card meta', () => {
|
||||
render(Page, { props: { data: makeData({ userName: 'Marcel Raddatz' }) } });
|
||||
expect(screen.getByText('Marcel Raddatz')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('shows empty state text when stapleCount is 0', () => {
|
||||
render(Page, { props: { data: makeData({ stapleCount: 0 }) } });
|
||||
expect(screen.getByText(/noch keine vorräte/i)).toBeInTheDocument();
|
||||
expect(screen.queryByTestId('staple-count')).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('shows "Jetzt einrichten →" CTA when stapleCount is 0', () => {
|
||||
render(Page, { props: { data: makeData({ stapleCount: 0 }) } });
|
||||
expect(screen.getByText('Jetzt einrichten →')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('shows "Vorräte bearbeiten →" CTA when stapleCount > 0', () => {
|
||||
render(Page, { props: { data: makeData({ stapleCount: 5 }) } });
|
||||
expect(screen.getByText('Vorräte bearbeiten →')).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user