FEAT-3: litter type+CRUD, Zucht strings, /wuerfe nested routes (retire LittersPage)
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -11,7 +11,9 @@ import HomePage from './pages/HomePage'
|
|||||||
import KontaktePage from './pages/KontaktePage'
|
import KontaktePage from './pages/KontaktePage'
|
||||||
import KontaktDetailPage from './pages/KontaktDetailPage'
|
import KontaktDetailPage from './pages/KontaktDetailPage'
|
||||||
import KontaktFormPage from './pages/KontaktFormPage'
|
import KontaktFormPage from './pages/KontaktFormPage'
|
||||||
import LittersPage from './pages/LittersPage'
|
import WuerfeListPage from './pages/WuerfeListPage'
|
||||||
|
import WurfDetailPage from './pages/WurfDetailPage'
|
||||||
|
import WurfFormPage from './pages/WurfFormPage'
|
||||||
import NotFoundPage from './pages/NotFoundPage'
|
import NotFoundPage from './pages/NotFoundPage'
|
||||||
import StammbaumPage from './pages/StammbaumPage'
|
import StammbaumPage from './pages/StammbaumPage'
|
||||||
|
|
||||||
@@ -28,7 +30,12 @@ export default function App() {
|
|||||||
<Route path=":id/bearbeiten" element={<GerbilFormPage />} />
|
<Route path=":id/bearbeiten" element={<GerbilFormPage />} />
|
||||||
<Route path=":id/stammbaum" element={<StammbaumPage />} />
|
<Route path=":id/stammbaum" element={<StammbaumPage />} />
|
||||||
</Route>
|
</Route>
|
||||||
<Route path="wuerfe" element={<LittersPage />} />
|
<Route path="wuerfe">
|
||||||
|
<Route index element={<WuerfeListPage />} />
|
||||||
|
<Route path="neu" element={<WurfFormPage />} />
|
||||||
|
<Route path=":id" element={<WurfDetailPage />} />
|
||||||
|
<Route path=":id/bearbeiten" element={<WurfFormPage />} />
|
||||||
|
</Route>
|
||||||
{/* FEAT-2: Kontakte ersetzt das Züchter-Platzhalter-Routing (Breeder -> Contact) */}
|
{/* FEAT-2: Kontakte ersetzt das Züchter-Platzhalter-Routing (Breeder -> Contact) */}
|
||||||
<Route path="becken">
|
<Route path="becken">
|
||||||
<Route index element={<BeckenPage />} />
|
<Route index element={<BeckenPage />} />
|
||||||
|
|||||||
@@ -1,7 +1,24 @@
|
|||||||
/** Typed API functions for the Würfe (litters) resource. */
|
/** Typed API functions for the Würfe (litters) resource. */
|
||||||
import { api, resources } from './client'
|
import { api, resources } from './client'
|
||||||
import type { Litter } from './types'
|
import { toQueryString, type GridifyQuery } from './gridify'
|
||||||
|
import type { CreateLitter, Litter, Paged, UpdateLitter } from './types'
|
||||||
|
|
||||||
export function getLitter(id: string): Promise<Litter> {
|
export function getLitter(id: string): Promise<Litter> {
|
||||||
return api.get<Litter>(`${resources.litters}/${id}`)
|
return api.get<Litter>(`${resources.litters}/${id}`)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function listLitters(query: GridifyQuery): Promise<Paged<Litter>> {
|
||||||
|
return api.get<Paged<Litter>>(`${resources.litters}${toQueryString(query)}`)
|
||||||
|
}
|
||||||
|
|
||||||
|
export function createLitter(body: CreateLitter): Promise<Litter> {
|
||||||
|
return api.post<Litter>(resources.litters, body)
|
||||||
|
}
|
||||||
|
|
||||||
|
export function updateLitter(id: string, body: UpdateLitter): Promise<Litter> {
|
||||||
|
return api.put<Litter>(`${resources.litters}/${id}`, body)
|
||||||
|
}
|
||||||
|
|
||||||
|
export function deleteLitter(id: string): Promise<void> {
|
||||||
|
return api.delete(`${resources.litters}/${id}`)
|
||||||
|
}
|
||||||
|
|||||||
@@ -80,12 +80,34 @@ export interface Contact {
|
|||||||
export interface Litter {
|
export interface Litter {
|
||||||
id: string
|
id: string
|
||||||
name: string
|
name: string
|
||||||
|
/** Wurfdatum (birth date). */
|
||||||
date: DateOnlyString
|
date: DateOnlyString
|
||||||
|
/** Wurfstärke — total born count. */
|
||||||
totalBorn: number | null
|
totalBorn: number | null
|
||||||
|
/**
|
||||||
|
* Abgabedatum (auto-suggested ~35 days after birth, editable).
|
||||||
|
* Optional in the type so existing Litter constructors (e.g. pedigree test
|
||||||
|
* factories) keep compiling; the API DTO always includes it (nullable).
|
||||||
|
*/
|
||||||
|
expectedGoHomeDate?: DateOnlyString | null
|
||||||
|
notes?: string | null
|
||||||
fatherId: string | null
|
fatherId: string | null
|
||||||
motherId: string | null
|
motherId: string | null
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Payload for POST /litters. */
|
||||||
|
export interface CreateLitter {
|
||||||
|
name: string
|
||||||
|
date: DateOnlyString
|
||||||
|
totalBorn?: number | null
|
||||||
|
expectedGoHomeDate?: DateOnlyString | null
|
||||||
|
notes?: string | null
|
||||||
|
fatherId?: string | null
|
||||||
|
motherId?: string | null
|
||||||
|
}
|
||||||
|
|
||||||
|
export type UpdateLitter = Partial<CreateLitter>
|
||||||
|
|
||||||
/** Paged list envelope returned by Gridify list endpoints. */
|
/** Paged list envelope returned by Gridify list endpoints. */
|
||||||
export interface Paged<T> {
|
export interface Paged<T> {
|
||||||
items: T[]
|
items: T[]
|
||||||
|
|||||||
@@ -1,10 +0,0 @@
|
|||||||
import { de } from '../strings/de'
|
|
||||||
|
|
||||||
export default function LittersPage() {
|
|
||||||
return (
|
|
||||||
<section>
|
|
||||||
<h2>{de.pages.litters.title}</h2>
|
|
||||||
<p>{de.pages.litters.comingSoon}</p>
|
|
||||||
</section>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
10
gerbil-manager-web/src/pages/WuerfeListPage.tsx
Normal file
10
gerbil-manager-web/src/pages/WuerfeListPage.tsx
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
import { de } from '../strings/de'
|
||||||
|
|
||||||
|
export default function WuerfeListPage() {
|
||||||
|
// Fleshed out in the next FEAT-3 increment (Gridify list, year filter, sort).
|
||||||
|
return (
|
||||||
|
<section className="page">
|
||||||
|
<h2>{de.pages.litters.title}</h2>
|
||||||
|
</section>
|
||||||
|
)
|
||||||
|
}
|
||||||
10
gerbil-manager-web/src/pages/WurfDetailPage.tsx
Normal file
10
gerbil-manager-web/src/pages/WurfDetailPage.tsx
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
import { de } from '../strings/de'
|
||||||
|
|
||||||
|
export default function WurfDetailPage() {
|
||||||
|
// Fleshed out in a later FEAT-3 increment (litter workspace + Jungtier erfassen).
|
||||||
|
return (
|
||||||
|
<section className="page">
|
||||||
|
<h2>{de.pages.litters.title}</h2>
|
||||||
|
</section>
|
||||||
|
)
|
||||||
|
}
|
||||||
10
gerbil-manager-web/src/pages/WurfFormPage.tsx
Normal file
10
gerbil-manager-web/src/pages/WurfFormPage.tsx
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
import { de } from '../strings/de'
|
||||||
|
|
||||||
|
export default function WurfFormPage() {
|
||||||
|
// Fleshed out in a later FEAT-3 increment (parents picker, auto go-home date).
|
||||||
|
return (
|
||||||
|
<section className="page">
|
||||||
|
<h2>{de.pages.litters.form.createTitle}</h2>
|
||||||
|
</section>
|
||||||
|
)
|
||||||
|
}
|
||||||
@@ -117,9 +117,70 @@ export const de = {
|
|||||||
GivenAway: 'Abgegeben',
|
GivenAway: 'Abgegeben',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
// ── FEAT-3 (Kevin): Zucht — Würfe + Zuchtpaare ──
|
||||||
litters: {
|
litters: {
|
||||||
title: 'Würfe',
|
title: 'Würfe',
|
||||||
comingSoon: 'Die Wurf-Verwaltung entsteht gerade. Bald kannst du hier Würfe dokumentieren.',
|
tabs: {
|
||||||
|
litters: 'Würfe',
|
||||||
|
pairs: 'Zuchtpaare',
|
||||||
|
},
|
||||||
|
// Liste
|
||||||
|
newButton: 'Neuer Wurf',
|
||||||
|
empty: 'Keine Würfe gefunden.',
|
||||||
|
countLabel: 'Würfe',
|
||||||
|
filterYear: 'Jahr',
|
||||||
|
allYears: 'Alle Jahre',
|
||||||
|
sort: {
|
||||||
|
dateDesc: 'Wurfdatum (neueste)',
|
||||||
|
dateAsc: 'Wurfdatum (älteste)',
|
||||||
|
},
|
||||||
|
// Felder
|
||||||
|
fields: {
|
||||||
|
name: 'Bezeichnung',
|
||||||
|
date: 'Wurfdatum',
|
||||||
|
father: 'Vater',
|
||||||
|
mother: 'Mutter',
|
||||||
|
totalBorn: 'Wurfstärke',
|
||||||
|
expectedGoHomeDate: 'Voraussichtliches Abgabedatum',
|
||||||
|
notes: 'Notizen',
|
||||||
|
},
|
||||||
|
// Detail / Arbeitsbereich
|
||||||
|
detail: {
|
||||||
|
parents: 'Elterntiere',
|
||||||
|
registered: 'Erfasste Jungtiere',
|
||||||
|
juveniles: 'Jungtiere',
|
||||||
|
registerJuvenile: 'Jungtier erfassen',
|
||||||
|
expectedColors: 'Erwartete Farbschläge dieser Verpaarung',
|
||||||
|
needParentsGenotype:
|
||||||
|
'Für die Farbvorhersage müssen bei Vater und Mutter Genotypen hinterlegt sein.',
|
||||||
|
noJuveniles: 'Noch keine Jungtiere erfasst.',
|
||||||
|
unknownParent: 'Unbekannt',
|
||||||
|
edit: 'Bearbeiten',
|
||||||
|
back: 'Zurück zur Liste',
|
||||||
|
notFound: 'Dieser Wurf wurde nicht gefunden.',
|
||||||
|
},
|
||||||
|
// Formular
|
||||||
|
form: {
|
||||||
|
createTitle: 'Neuen Wurf anlegen',
|
||||||
|
editTitle: 'Wurf bearbeiten',
|
||||||
|
none: '— keine Angabe —',
|
||||||
|
goHomeHint: 'Vorschlag: 35 Tage nach dem Wurfdatum. Anpassbar.',
|
||||||
|
save: 'Speichern',
|
||||||
|
cancel: 'Abbrechen',
|
||||||
|
saving: 'Speichern …',
|
||||||
|
},
|
||||||
|
validation: {
|
||||||
|
nameRequired: 'Bitte eine Bezeichnung eingeben.',
|
||||||
|
dateRequired: 'Bitte ein Wurfdatum angeben.',
|
||||||
|
},
|
||||||
|
// Zuchtpaar-Übersicht
|
||||||
|
pairs: {
|
||||||
|
title: 'Zuchtpaare',
|
||||||
|
empty: 'Noch keine Zuchtpaare — diese ergeben sich aus den erfassten Würfen.',
|
||||||
|
litterCount: 'Würfe',
|
||||||
|
lastLitter: 'Letzter Wurf',
|
||||||
|
testMating: 'Probeverpaarung',
|
||||||
|
},
|
||||||
},
|
},
|
||||||
breeders: {
|
breeders: {
|
||||||
title: 'Züchter',
|
title: 'Züchter',
|
||||||
|
|||||||
Reference in New Issue
Block a user