diff --git a/gerbil-manager-web/e2e/hilfe.spec.ts b/gerbil-manager-web/e2e/hilfe.spec.ts new file mode 100644 index 0000000..4374d6a --- /dev/null +++ b/gerbil-manager-web/e2e/hilfe.spec.ts @@ -0,0 +1,36 @@ +/** + * HELP-1: Hilfe-Seite — Smoke-Test (erreichbar + Accordion öffnet sich). + * Kein Mock-API nötig (rein statische Seite). + */ +import { de, expect, gotoSection, test } from './fixtures' + +const th = de.hilfe + +test.describe('Hilfe', () => { + test('Hilfe-Seite ist über die Navigation erreichbar', async ({ page }) => { + await gotoSection(page, de.nav.help) + await expect(page.getByRole('heading', { name: th.title })).toBeVisible() + await expect(page.getByText(th.subtitle)).toBeVisible() + }) + + test('Akkordeon-Abschnitt öffnet sich per Tippen', async ({ page }) => { + await page.goto('/hilfe') + const firstSummary = page.getByText(th.sections.ersteSchritte) + await expect(firstSummary).toBeVisible() + + // Abschnitt ist zunächst geschlossen — Inhalt nicht sichtbar + const sectionBody = page.getByText('Empfohlene Reihenfolge') + await expect(sectionBody).not.toBeVisible() + + // Tippen öffnet den Abschnitt + await firstSummary.click() + await expect(sectionBody).toBeVisible() + }) + + test('SCHECKE-Warnung ist im Genetik-Abschnitt enthalten', async ({ page }) => { + await page.goto('/hilfe') + // Öffne Genetik-Abschnitt + await page.getByText(th.sections.genetik).click() + await expect(page.getByText('Schecke × Schecke', { exact: false })).toBeVisible() + }) +}) diff --git a/gerbil-manager-web/src/App.tsx b/gerbil-manager-web/src/App.tsx index 99b054f..f11a4b0 100644 --- a/gerbil-manager-web/src/App.tsx +++ b/gerbil-manager-web/src/App.tsx @@ -18,6 +18,7 @@ import WurfFormPage from './pages/WurfFormPage' import NotFoundPage from './pages/NotFoundPage' import StammbaumPage from './pages/StammbaumPage' import StatistikPage from './pages/StatistikPage' +import HilfePage from './pages/HilfePage' export default function App() { return ( @@ -54,6 +55,7 @@ export default function App() { } /> } /> } /> + } /> } /> diff --git a/gerbil-manager-web/src/components/AppShell.tsx b/gerbil-manager-web/src/components/AppShell.tsx index dc45d86..3a0859d 100644 --- a/gerbil-manager-web/src/components/AppShell.tsx +++ b/gerbil-manager-web/src/components/AppShell.tsx @@ -23,6 +23,7 @@ const SECONDARY: NavItem[] = [ { to: '/kontakte', label: de.nav.contacts, icon: '📇' }, { to: '/abgabe', label: de.nav.forSale, icon: '🏡' }, { to: '/statistik', label: de.nav.statistics, icon: '📊' }, + { to: '/hilfe', label: de.nav.help, icon: '❓' }, ] const linkClass = ({ isActive }: { isActive: boolean }) => diff --git a/gerbil-manager-web/src/pages/HilfePage.tsx b/gerbil-manager-web/src/pages/HilfePage.tsx new file mode 100644 index 0000000..bfcd958 --- /dev/null +++ b/gerbil-manager-web/src/pages/HilfePage.tsx @@ -0,0 +1,196 @@ +/** + * HELP-1: In-App-Anleitung für die Züchterin (nicht-technisch, du-Form). + * Accordion via
/ — kein JS-State nötig, barrierefrei, mobiltauglich. + * Texte referenzieren nur Bezeichnungen aus de.ts (bleiben bei Umbenennung korrekt). + */ +import { de } from '../strings/de' +import './hilfe.css' + +const t = de.hilfe +const nav = de.nav +const g = de.pages.gerbils +const l = de.pages.litters +const b = de.pages.becken +const st = de.pages.stammbaum +const ge = de.pages.genetik +const ab = de.pages.abgabe +const stat = de.pages.statistik +const scheckeWarning = de.genetics.warnings.SCHECKE_LETHAL +const rexWarning = de.genetics.warnings.REX_SEMI_LETHAL + +interface Section { + id: string + title: string + content: React.ReactNode +} + +const sections: Section[] = [ + { + id: 'erste-schritte', + title: t.sections.ersteSchritte, + content: ( + <> +

Willkommen im {de.app.title}! Diese Anleitung führt dich durch die wichtigsten Bereiche.

+

Die Navigation erreichst du unten (Smartphone) oder links (Laptop). Mit „{nav.more}" öffnest du alle weiteren Bereiche.

+

Empfohlene Reihenfolge beim ersten Mal:

+
    +
  1. Becken anlegen (wo leben deine Tiere?)
  2. +
  3. Rennmäuse anlegen mit Foto
  4. +
  5. Würfe eintragen
  6. +
  7. Genetik erkunden
  8. +
+

Tipp: Lege zuerst die Becken an — dann kannst du beim Erfassen der Tiere direkt das richtige Becken auswählen.

+ + ), + }, + { + id: 'tiere', + title: t.sections.tiere, + content: ( + <> +

Unter „{nav.gerbils}" siehst du alle deine Rennmäuse. Mit „{g.newButton}" legst du ein neues Tier an.

+

Pflichtfelder sind „{g.fields.name}" und „{g.fields.gender}". Optional kannst du Geburtsdatum, Farbschlag, Genotyp, Becken und Herkunft eintragen.

+

Auf der Detailseite findest du drei Tabs:

+
    +
  • „{g.detail.tabs.photos}" — Fotos hochladen; das erste Foto wird als Profilfoto angezeigt.
  • +
  • „{g.detail.tabs.health}" — Tierarztbesuche, Behandlungen und Untersuchungen eintragen.
  • +
  • „{g.detail.tabs.weight}" — Gewicht in Gramm erfassen; nach zwei Einträgen erscheint eine Verlaufskurve.
  • +
+

Mit den Filtern oben in der Liste kannst du nach Status, Geschlecht und Farbschlag filtern oder nach Name suchen.

+ + ), + }, + { + id: 'becken', + title: t.sections.becken, + content: ( + <> +

Unter „{nav.enclosures}" verwaltest du deine Gehege. Mit „{b.newButton}" legst du ein neues Becken an (nur Name ist Pflicht).

+

Die Detailseite zeigt dir die aktuellen „{b.occupancy.title}" — mit Links direkt zur Tierakte. So siehst du auf einen Blick, wer wo lebt.

+

Ein Becken lässt sich nur löschen, wenn es leer ist — das schützt vor versehentlichem Datenverlust.

+

Unter „{nav.contacts}" kannst du Züchter, Käufer und andere Kontakte anlegen. Diese lassen sich dann als Herkunft oder Abnehmer bei Tieren eintragen.

+ + ), + }, + { + id: 'wuerfe', + title: t.sections.wuerfe, + content: ( + <> +

Unter „{nav.litters}" erfasst du Würfe. Mit „{l.newButton}" trägst du Vater, Mutter, Wurfdatum und Wurfstärke ein.

+

Das „{l.fields.expectedGoHomeDate}" wird automatisch auf 35 Tage nach dem Wurfdatum vorgeschlagen — du kannst es anpassen.

+

Auf der Wurf-Detailseite siehst du unter „{l.detail.registered}" die bereits erfassten Jungtiere. Mit „{l.detail.registerJuvenile}" legst du ein neues Tier an, das direkt mit diesem Wurf verknüpft ist.

+

Im Tab „{l.tabs.pairs}" siehst du alle Verpaarungen aus deinen Würfen auf einen Blick — mit Anzahl Würfe und Datum des letzten Wurfes.

+

Tipp: Wenn du auf der Genetik-Seite eine Probeverpaarung berechnest, kannst du mit „Probeverpaarung" direkt zur Genetikseite springen und das Tierpaar vorauswählen.

+ + ), + }, + { + id: 'genetik', + title: t.sections.genetik, + content: ( + <> +

Unter „{nav.genetics}" berechnest du, welche Farbschläge bei einer Verpaarung möglich sind.

+

Wähle „{ge.father}" und „{ge.mother}" aus deinen Tieren oder gib die Genotypen manuell ein. Klicke dann auf „{ge.run}".

+

Das Ergebnis zeigt alle möglichen Farbschläge mit Wahrscheinlichkeit — du kannst zwischen Anzeige nach Farbschlag und nach Genotyp wechseln.

+

Wichtige Warnungen:

+
    +
  • Schecke × Schecke: {scheckeWarning}
  • +
  • Rex × Rex: {rexWarning}
  • +
+

Diese Warnungen erscheinen automatisch, wenn die entsprechende Kombination erkannt wird. Bitte beachte sie beim Zuchtentscheid.

+

Den Genotyp eines Tieres trägst du auf der Tier-Detailseite unter „{g.detail.masterData}" ein. Format: z. B. Aa CC Dd EE GG Pp Spsp rere. Unbekannte Stellen als ?.

+ + ), + }, + { + id: 'stammbaum', + title: t.sections.stammbaum, + content: ( + <> +

Auf der Tier-Detailseite öffnest du mit „{st.openButton}" den interaktiven Stammbaum.

+

Navigieren: {st.tapHint}

+

Zoomen: Mit den Schaltflächen „{st.zoomIn}" / „{st.zoomOut}" oder per Pinch-Geste auf dem Smartphone. „{st.zoomFit}" passt die Ansicht an den Bildschirm an.

+

Drucken / PDF: Mit „{st.print}" öffnest du die Druckansicht (Ahnentafel). Wähle im Druckdialog „Als PDF speichern", um eine Datei zu erzeugen.

+

Der „{st.inbreeding.label}" wird automatisch für das angezeigte Tier berechnet und oben angezeigt.

+

Tipp: Je mehr Elterntiere und Würfe du erfasst hast, desto tiefer reicht der Stammbaum.

+ + ), + }, + { + id: 'abgabe', + title: t.sections.abgabe, + content: ( + <> +

Der Abgabe-Ablauf läuft in mehreren Schritten:

+
    +
  1. + Tier zur Abgabe stellen: Öffne die Tierakte und klicke auf „{ab.markAction}". Das Tier wechselt in den Status „{de.pages.gerbils.statusLabels.ForSale}". +
  2. +
  3. + Gruppen bilden: Auf der „{nav.forSale}"-Seite werden Tiere nach Becken vorgruppiert. Du kannst Tiere zwischen Gruppen verschieben oder neue Gruppen anlegen. +
  4. +
  5. + Inserat erstellen: Fülle für jede Gruppe den emotionalen Einzeiler und den Charakter-Text aus. +
  6. +
  7. + Text kopieren & Fotos herunterladen: Mit „{ab.export.copyText}" kommt der fertige Inseratstext in die Zwischenablage. Mit „{ab.export.downloadPhotos}" lädst du alle Fotos der Gruppe als ZIP herunter. +
  8. +
  9. + KI-Unterstützung (optional): Mit „{ab.ai.improve}" kann der Text automatisch verbessert werden — dazu ist ein API-Schlüssel nötig. +
  10. +
+

Tipp: Bearbeite jede Gruppe einzeln — Text und Fotos für eine Gruppe gleichzeitig zusammenstellen, dann weiter zur nächsten.

+ + ), + }, + { + id: 'statistik', + title: t.sections.statistik, + content: ( + <> +

Unter „{nav.statistics}" findest du Auswertungen über deinen Bestand:

+
    +
  • „{stat.littersPerYear}" — wie viele Würfe pro Jahr.
  • +
  • „{stat.avgLitterSize}" — durchschnittliche Wurfstärke pro Jahr.
  • +
  • „{stat.farbschlag}" — wie viele aktive Tiere welchen Farbschlag haben.
  • +
  • „{stat.population}" — wie sich dein Bestand über die Zeit entwickelt hat.
  • +
  • „{stat.losses}" — verstorbene Tiere nach Jahr.
  • +
+

Die Statistiken werden automatisch aus deinen Daten berechnet. Je vollständiger deine Einträge (Geburtsdaten, Farbschläge, Wurfstärken), desto aussagekräftiger die Auswertungen.

+ + ), + }, + { + id: 'datensicherung', + title: t.sections.datensicherung, + content: ( + <> +

Deine Daten liegen sicher auf Julian's Server zu Hause — kein Cloud-Konto nötig.

+

Julian erstellt automatisch jeden Nacht um 3 Uhr ein Backup der Datenbank und aller Fotos. Bei einem Problem kann er den Stand eines beliebigen Tages wiederherstellen.

+

Wenn du versehentlich Daten gelöscht hast, wende dich an Julian. Er kann aus einem der letzten 7 Tages-Backups wiederherstellen.

+ + ), + }, +] + +export default function HilfePage() { + return ( +
+

{t.title}

+

{t.subtitle}

+ +
+ {sections.map((s) => ( +
+ + {s.title} + + +
{s.content}
+
+ ))} +
+
+ ) +} diff --git a/gerbil-manager-web/src/pages/hilfe.css b/gerbil-manager-web/src/pages/hilfe.css new file mode 100644 index 0000000..ebafb90 --- /dev/null +++ b/gerbil-manager-web/src/pages/hilfe.css @@ -0,0 +1,105 @@ +.hilfe-page { + max-width: 720px; +} + +.hilfe-subtitle { + color: var(--color-muted, #666); + margin-bottom: 1.5rem; +} + +/* Accordion */ +.hilfe-accordion { + display: flex; + flex-direction: column; + gap: 0.5rem; +} + +.hilfe-section { + border: 1px solid var(--color-border, #ddd); + border-radius: 6px; + background: var(--color-surface, #fff); +} + +.hilfe-section-summary { + display: flex; + align-items: center; + justify-content: space-between; + padding: 0.875rem 1rem; + cursor: pointer; + list-style: none; + user-select: none; + gap: 0.5rem; +} + +.hilfe-section-summary::-webkit-details-marker { + display: none; +} + +.hilfe-section-summary:hover { + background: var(--color-hover, #f5f5f5); + border-radius: 6px; +} + +.hilfe-section[open] > .hilfe-section-summary { + border-bottom: 1px solid var(--color-border, #ddd); + border-radius: 6px 6px 0 0; +} + +.hilfe-section-title { + font-weight: 600; + font-size: 1rem; +} + +.hilfe-section-chevron { + font-size: 1.1rem; + transition: transform 0.2s ease; + flex-shrink: 0; +} + +.hilfe-section[open] .hilfe-section-chevron { + transform: rotate(180deg); +} + +.hilfe-section-body { + padding: 1rem 1rem 1.25rem; +} + +.hilfe-section-body p { + margin: 0 0 0.75rem; + line-height: 1.6; +} + +.hilfe-section-body p:last-child { + margin-bottom: 0; +} + +.hilfe-section-body ul, +.hilfe-section-body ol { + margin: 0.25rem 0 0.75rem 1.25rem; + line-height: 1.6; +} + +.hilfe-section-body li { + margin-bottom: 0.4rem; +} + +.hilfe-section-body code { + font-family: monospace; + background: var(--color-hover, #f5f5f5); + padding: 0.1em 0.35em; + border-radius: 3px; + font-size: 0.9em; +} + +/* Hint boxes */ +.hilfe-hint { + background: var(--color-info-bg, #eff6ff); + border-left: 3px solid var(--color-info, #3b82f6); + padding: 0.5rem 0.75rem; + border-radius: 0 4px 4px 0; + font-size: 0.93rem; +} + +.hilfe-warning { + color: var(--color-warning-text, #92400e); +} diff --git a/gerbil-manager-web/src/strings/de.ts b/gerbil-manager-web/src/strings/de.ts index 81a0a36..6dfed61 100644 --- a/gerbil-manager-web/src/strings/de.ts +++ b/gerbil-manager-web/src/strings/de.ts @@ -26,6 +26,8 @@ export const de = { openMenu: 'Menü öffnen', closeMenu: 'Menü schließen', mainNavigation: 'Hauptnavigation', + // HELP-1 + help: 'Hilfe', }, pages: { home: { @@ -473,6 +475,22 @@ export const de = { }, }, }, + // ── HELP-1: In-App-Anleitung ── + hilfe: { + title: 'Hilfe & Anleitung', + subtitle: 'Kurzanleitung für den Rennmaus-Manager', + sections: { + ersteSchritte: 'Erste Schritte', + tiere: 'Rennmäuse anlegen & Fotos', + becken: 'Becken', + wuerfe: 'Würfe & Jungtiere', + genetik: 'Probeverpaarung & Genetik', + stammbaum: 'Stammbaum', + abgabe: 'Abgabe-Ablauf', + statistik: 'Statistik', + datensicherung: 'Datensicherung', + }, + }, api: { errors: { network: 'Verbindung zum Server fehlgeschlagen. Bitte später erneut versuchen.',