diff --git a/gerbil-manager-web/e2e/charakter.spec.ts b/gerbil-manager-web/e2e/charakter.spec.ts new file mode 100644 index 0000000..1cfdc50 --- /dev/null +++ b/gerbil-manager-web/e2e/charakter.spec.ts @@ -0,0 +1,21 @@ +/** FEAT-14b: Charakterbogen — Eigenschaft umschalten, persistieren, im Abgabe-Komposer wiederfinden. */ +import { de, expect, skipUnlessMock, test } from './fixtures' + +test('Charakterbogen: Eigenschaft umschalten, speichern und im Abgabe-Komposer wiederfinden', async ({ + page, +}) => { + skipUnlessMock() + await page.goto('/rennmaeuse/kruemel') + await expect(page.getByRole('heading', { name: 'Krümel' })).toBeVisible() + await expect(page.getByText(de.character.sectionTitle)).toBeVisible() + + // Eigenschaft 'neugierig' aktivieren und speichern. + await page.getByRole('checkbox', { name: 'neugierig' }).check() + await page.getByRole('button', { name: de.character.save }).click() + await expect(page.getByRole('checkbox', { name: 'neugierig' })).toBeChecked() + + // Tier zur Abgabe stellen -> erscheint im /abgabe-Komposer mit gesetzter Eigenschaft. + await page.getByRole('button', { name: de.pages.abgabe.markAction }).click() + await page.goto('/abgabe') + await expect(page.getByRole('checkbox', { name: 'neugierig' })).toBeChecked() +}) diff --git a/gerbil-manager-web/src/api/saleAd.ts b/gerbil-manager-web/src/api/saleAd.ts index 766cf34..11027a4 100644 --- a/gerbil-manager-web/src/api/saleAd.ts +++ b/gerbil-manager-web/src/api/saleAd.ts @@ -20,6 +20,9 @@ export interface SaleAdAnimal { farbschlag: string | null dateOfBirth: string | null notes: string | null + /** FEAT-14: German trait LABELS (human-readable for the prompt), not keys. */ + traits?: string[] + characterNote?: string | null } export interface SaleAdRequest { diff --git a/gerbil-manager-web/src/api/types.ts b/gerbil-manager-web/src/api/types.ts index a59aa3e..ebdedff 100644 --- a/gerbil-manager-web/src/api/types.ts +++ b/gerbil-manager-web/src/api/types.ts @@ -34,6 +34,12 @@ export interface Gerbil { /** Compact GEN-1 genotype string, e.g. "Aa CC Dd EE GG Pp Spsp rere" (or "?"-wildcards). */ genotype: string | null notes: string | null + /** + * FEAT-14: Charakterbogen. Selected trait KEYS (see de.character.traits). + * Optional until FEAT-14a backend exposes the fields; treat absent as []. + */ + characterTraits?: string[] | null + characterNote?: string | null } /** Payload for POST /gerbils. */ @@ -52,6 +58,8 @@ export interface CreateGerbil { receiverContactId?: string | null genotype?: string | null notes?: string | null + characterTraits?: string[] | null + characterNote?: string | null } /** Payload for PUT /gerbils/{id} (all optional / partial update). */ diff --git a/gerbil-manager-web/src/components/Charakterbogen.tsx b/gerbil-manager-web/src/components/Charakterbogen.tsx new file mode 100644 index 0000000..f8a7ad1 --- /dev/null +++ b/gerbil-manager-web/src/components/Charakterbogen.tsx @@ -0,0 +1,62 @@ +import { de } from '../strings/de' +import { ALL_TRAITS } from '../format/traits' +import './charakterbogen.css' + +export interface CharakterbogenProps { + /** Selected trait keys. */ + traits: string[] + note: string + onTraitsChange: (traits: string[]) => void + onNoteChange: (note: string) => void +} + +/** + * FEAT-14: character sheet — a checkbox grid of traits + a free note. + * Controlled & reusable: rendered on the animal detail page (persisted) and in + * the Abgabe listing composer (feeds the AI sale-text). German labels from + * de.character.traits; the stored value is the trait KEY. + */ +export default function Charakterbogen({ + traits, + note, + onTraitsChange, + onNoteChange, +}: CharakterbogenProps) { + const t = de.character + const selected = new Set(traits) + + const toggle = (key: string) => { + const next = new Set(selected) + if (next.has(key)) next.delete(key) + else next.add(key) + // Preserve the vocabulary order for stable output. + onTraitsChange(ALL_TRAITS.filter((tr) => next.has(tr.key)).map((tr) => tr.key)) + } + + return ( +
+ +