feat(GRO-1173): add sizeCategory and coatType dropdowns to admin pet form
- PetForm interface: add sizeCategory and coatType fields - EMPTY_PET: initialise new fields as empty strings - openEditPet: pre-populate from pet.petSizeCategory and pet.coatType - submitPet body: include petSizeCategory and coatType in POST/PATCH - Pet form UI: add Size Category and Coat Type dropdowns after Breed field - Size: Small / Medium / Large / X-Large (maps to enum values) - Coat: Smooth / Double / Curly / Wire / Long / Hairless (maps to CoatType union) - Both optional — blank "Not set" option matches API optional semantics Co-Authored-By: Paperclip <noreply@paperclip.ing>
This commit is contained in:
@@ -25,6 +25,8 @@ interface PetForm {
|
|||||||
cutStyle: string;
|
cutStyle: string;
|
||||||
shampooPreference: string;
|
shampooPreference: string;
|
||||||
specialCareNotes: string;
|
specialCareNotes: string;
|
||||||
|
coatType: string;
|
||||||
|
sizeCategory: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface VisitLogForm {
|
interface VisitLogForm {
|
||||||
@@ -38,6 +40,7 @@ const EMPTY_CLIENT: ClientForm = { name: "", email: "", phone: "", address: "",
|
|||||||
const EMPTY_PET: PetForm = {
|
const EMPTY_PET: PetForm = {
|
||||||
name: "", species: "Dog", breed: "", weightStr: "", dob: "",
|
name: "", species: "Dog", breed: "", weightStr: "", dob: "",
|
||||||
healthAlerts: "", groomingNotes: "", cutStyle: "", shampooPreference: "", specialCareNotes: "",
|
healthAlerts: "", groomingNotes: "", cutStyle: "", shampooPreference: "", specialCareNotes: "",
|
||||||
|
coatType: "", sizeCategory: "",
|
||||||
};
|
};
|
||||||
const EMPTY_VISIT_LOG: VisitLogForm = { cutStyle: "", productsUsed: "", notes: "", groomedAt: "" };
|
const EMPTY_VISIT_LOG: VisitLogForm = { cutStyle: "", productsUsed: "", notes: "", groomedAt: "" };
|
||||||
|
|
||||||
@@ -209,6 +212,8 @@ export function ClientsPage() {
|
|||||||
cutStyle: p.cutStyle ?? "",
|
cutStyle: p.cutStyle ?? "",
|
||||||
shampooPreference: p.shampooPreference ?? "",
|
shampooPreference: p.shampooPreference ?? "",
|
||||||
specialCareNotes: p.specialCareNotes ?? "",
|
specialCareNotes: p.specialCareNotes ?? "",
|
||||||
|
coatType: p.coatType ?? "",
|
||||||
|
sizeCategory: p.petSizeCategory ?? "",
|
||||||
});
|
});
|
||||||
setPetFormError(null);
|
setPetFormError(null);
|
||||||
setShowPetForm(true);
|
setShowPetForm(true);
|
||||||
@@ -315,6 +320,8 @@ export function ClientsPage() {
|
|||||||
cutStyle: petForm.cutStyle || undefined,
|
cutStyle: petForm.cutStyle || undefined,
|
||||||
shampooPreference: petForm.shampooPreference || undefined,
|
shampooPreference: petForm.shampooPreference || undefined,
|
||||||
specialCareNotes: petForm.specialCareNotes || undefined,
|
specialCareNotes: petForm.specialCareNotes || undefined,
|
||||||
|
coatType: petForm.coatType || undefined,
|
||||||
|
petSizeCategory: petForm.sizeCategory || undefined,
|
||||||
};
|
};
|
||||||
const res = editingPet
|
const res = editingPet
|
||||||
? await fetch(`/api/pets/${editingPet.id}`, { method: "PATCH", headers: { "Content-Type": "application/json" }, body: JSON.stringify(body) })
|
? await fetch(`/api/pets/${editingPet.id}`, { method: "PATCH", headers: { "Content-Type": "application/json" }, body: JSON.stringify(body) })
|
||||||
@@ -690,6 +697,34 @@ export function ClientsPage() {
|
|||||||
<Field label="Breed (optional)">
|
<Field label="Breed (optional)">
|
||||||
<input value={petForm.breed} onChange={(e) => setPetForm((f) => ({ ...f, breed: e.target.value }))} style={inputStyle} />
|
<input value={petForm.breed} onChange={(e) => setPetForm((f) => ({ ...f, breed: e.target.value }))} style={inputStyle} />
|
||||||
</Field>
|
</Field>
|
||||||
|
<Field label="Size Category (optional)">
|
||||||
|
<select
|
||||||
|
value={petForm.sizeCategory}
|
||||||
|
onChange={(e) => setPetForm((f) => ({ ...f, sizeCategory: e.target.value }))}
|
||||||
|
style={inputStyle}
|
||||||
|
>
|
||||||
|
<option value="">Not set</option>
|
||||||
|
<option value="small">Small</option>
|
||||||
|
<option value="medium">Medium</option>
|
||||||
|
<option value="large">Large</option>
|
||||||
|
<option value="xlarge">X-Large</option>
|
||||||
|
</select>
|
||||||
|
</Field>
|
||||||
|
<Field label="Coat Type (optional)">
|
||||||
|
<select
|
||||||
|
value={petForm.coatType}
|
||||||
|
onChange={(e) => setPetForm((f) => ({ ...f, coatType: e.target.value }))}
|
||||||
|
style={inputStyle}
|
||||||
|
>
|
||||||
|
<option value="">Not set</option>
|
||||||
|
<option value="smooth">Smooth</option>
|
||||||
|
<option value="double">Double</option>
|
||||||
|
<option value="curly">Curly</option>
|
||||||
|
<option value="wire">Wire</option>
|
||||||
|
<option value="long">Long</option>
|
||||||
|
<option value="hairless">Hairless</option>
|
||||||
|
</select>
|
||||||
|
</Field>
|
||||||
<Field label="Weight kg (optional)">
|
<Field label="Weight kg (optional)">
|
||||||
<input type="number" step="0.1" min="0" value={petForm.weightStr} onChange={(e) => setPetForm((f) => ({ ...f, weightStr: e.target.value }))} style={inputStyle} />
|
<input type="number" step="0.1" min="0" value={petForm.weightStr} onChange={(e) => setPetForm((f) => ({ ...f, weightStr: e.target.value }))} style={inputStyle} />
|
||||||
</Field>
|
</Field>
|
||||||
|
|||||||
Reference in New Issue
Block a user