FEAT-13: Abgabe wizard (/vertraege/neu, 4 steps, ?tiere= prefill), Vertraege list w/ download+delete, /einstellungen Zuchtprofil form, contracts/settings API clients, nav entries, detail-page entry point + replace orphaned contactInfo with email/phone/address across Kontakt pages
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
100
gerbil-manager-web/src/pages/EinstellungenPage.tsx
Normal file
100
gerbil-manager-web/src/pages/EinstellungenPage.tsx
Normal file
@@ -0,0 +1,100 @@
|
||||
/**
|
||||
* FEAT-13: Einstellungen — Zuchtprofil (Verkäufer-Block der Abgabeverträge).
|
||||
* Einzelzeilen-Settings im Backend (/settings/breeder-profile); die Züchterin
|
||||
* pflegt ihre Daten hier statt in einer JSON-Datei.
|
||||
*/
|
||||
import { useState, type FormEvent } from 'react'
|
||||
import { de } from '../strings/de'
|
||||
import {
|
||||
EMPTY_BREEDER_PROFILE,
|
||||
getBreederProfile,
|
||||
isBreederProfileComplete,
|
||||
putBreederProfile,
|
||||
type BreederProfile,
|
||||
} from '../api/settings'
|
||||
import { useApi, useMutation } from '../hooks/useApi'
|
||||
|
||||
export default function EinstellungenPage() {
|
||||
const t = de.pages.einstellungen
|
||||
const tz = t.zuchtprofil
|
||||
|
||||
const [form, setForm] = useState<BreederProfile>(EMPTY_BREEDER_PROFILE)
|
||||
const [initialized, setInitialized] = useState(false)
|
||||
const [saved, setSaved] = useState(false)
|
||||
|
||||
const existing = useApi(() => getBreederProfile(), [])
|
||||
if (existing.data && !initialized) {
|
||||
setInitialized(true)
|
||||
setForm(existing.data)
|
||||
}
|
||||
|
||||
const set = <K extends keyof BreederProfile>(key: K, value: string) => {
|
||||
setSaved(false)
|
||||
setForm((f) => ({ ...f, [key]: value }))
|
||||
}
|
||||
|
||||
const mutation = useMutation((profile: BreederProfile) => putBreederProfile(profile))
|
||||
|
||||
async function onSubmit(e: FormEvent) {
|
||||
e.preventDefault()
|
||||
const result = await mutation.run(form)
|
||||
if (result.ok) setSaved(true)
|
||||
}
|
||||
|
||||
if (existing.loading) return <p className="muted">{de.common.loading}</p>
|
||||
if (existing.error) {
|
||||
return (
|
||||
<section className="page">
|
||||
<h2>{t.title}</h2>
|
||||
<div className="alert alert--error">
|
||||
<span>{existing.error}</span>
|
||||
<button type="button" className="btn" onClick={existing.reload}>
|
||||
{de.common.retry}
|
||||
</button>
|
||||
</div>
|
||||
</section>
|
||||
)
|
||||
}
|
||||
|
||||
const field = (
|
||||
key: keyof BreederProfile,
|
||||
label: string,
|
||||
hint?: string,
|
||||
type: string = 'text',
|
||||
) => (
|
||||
<label className="field">
|
||||
<span>{label}</span>
|
||||
<input className="input" type={type} value={form[key]} onChange={(e) => set(key, e.target.value)} />
|
||||
{hint && <small className="muted">{hint}</small>}
|
||||
</label>
|
||||
)
|
||||
|
||||
return (
|
||||
<section className="page">
|
||||
<h2>{t.title}</h2>
|
||||
|
||||
<h3>{tz.title}</h3>
|
||||
<p className="muted">{tz.intro}</p>
|
||||
{!isBreederProfileComplete(form) && <div className="alert alert--error">{tz.incompleteHint}</div>}
|
||||
|
||||
<form className="form" onSubmit={onSubmit} noValidate>
|
||||
{field('zuchtName', tz.fields.zuchtName)}
|
||||
{field('name', tz.fields.name, tz.fields.nameHint)}
|
||||
{field('address', tz.fields.address, tz.fields.addressHint)}
|
||||
{field('city', tz.fields.city)}
|
||||
{field('phone', tz.fields.phone, undefined, 'tel')}
|
||||
{field('email', tz.fields.email, undefined, 'email')}
|
||||
{field('homepage', tz.fields.homepage, undefined, 'url')}
|
||||
|
||||
{mutation.error && <div className="alert alert--error">{mutation.error}</div>}
|
||||
|
||||
<div className="form-actions">
|
||||
<button type="submit" className="btn btn--primary" disabled={mutation.pending}>
|
||||
{mutation.pending ? tz.saving : tz.save}
|
||||
</button>
|
||||
{saved && <span className="muted">{tz.saved}</span>}
|
||||
</div>
|
||||
</form>
|
||||
</section>
|
||||
)
|
||||
}
|
||||
Reference in New Issue
Block a user