From b3a3f8023a79da45db005d7452f0ec08889a6e03 Mon Sep 17 00:00:00 2001 From: "groombook-ci[bot]" Date: Sat, 28 Mar 2026 10:59:22 +0000 Subject: [PATCH 01/13] fix(portal): wire up Edit Pet and Add New Pet buttons in customer portal Enable the Edit Pet button on Manage Pets (Settings) and Pet Profiles, and the Add New Pet button. Add PetForm component for editing. Remove disabled/stub attributes from Reschedule, Cancel, and Add Notes buttons in Dashboard and Appointments. Resolves GRO-167. Co-Authored-By: Paperclip --- .../src/portal/sections/AccountSettings.tsx | 31 ++++--- apps/web/src/portal/sections/Appointments.tsx | 6 +- apps/web/src/portal/sections/Dashboard.tsx | 18 +--- apps/web/src/portal/sections/PetForm.tsx | 87 +++++++++++++++++++ apps/web/src/portal/sections/PetProfiles.tsx | 17 +++- 5 files changed, 126 insertions(+), 33 deletions(-) create mode 100644 apps/web/src/portal/sections/PetForm.tsx diff --git a/apps/web/src/portal/sections/AccountSettings.tsx b/apps/web/src/portal/sections/AccountSettings.tsx index 8b5e9a3..dbd6c01 100644 --- a/apps/web/src/portal/sections/AccountSettings.tsx +++ b/apps/web/src/portal/sections/AccountSettings.tsx @@ -1,6 +1,7 @@ import { useState } from "react"; import { User, Lock, PawPrint, FileCheck, Plus, Archive } from "lucide-react"; import { CUSTOMER, PETS, SIGNED_AGREEMENTS } from "../mockData.js"; +import { PetForm } from "./PetForm.js"; interface Props { readOnly: boolean; @@ -112,6 +113,20 @@ function PasswordChange({ readOnly }: { readOnly: boolean }) { } function ManagePets({ readOnly }: { readOnly: boolean }) { + const [editingPetId, setEditingPetId] = useState(null); + const [showAddForm, setShowAddForm] = useState(false); + const editingPet = editingPetId ? PETS.find(p => p.id === editingPetId) ?? undefined : undefined; + + if (editingPet || showAddForm) { + return ( + { setEditingPetId(null); setShowAddForm(false); }} + onCancel={() => { setEditingPetId(null); setShowAddForm(false); }} + /> + ); + } + return (
{PETS.map(pet => ( @@ -126,17 +141,12 @@ function ManagePets({ readOnly }: { readOnly: boolean }) { {!readOnly && (
-
@@ -145,9 +155,8 @@ function ManagePets({ readOnly }: { readOnly: boolean }) { ))} {!readOnly && ( diff --git a/apps/web/src/portal/sections/Dashboard.tsx b/apps/web/src/portal/sections/Dashboard.tsx index 289d1c7..2f82cc7 100644 --- a/apps/web/src/portal/sections/Dashboard.tsx +++ b/apps/web/src/portal/sections/Dashboard.tsx @@ -77,25 +77,13 @@ export function Dashboard({ onNavigate, readOnly }: Props) {
{!readOnly && (
- - -
diff --git a/apps/web/src/portal/sections/PetForm.tsx b/apps/web/src/portal/sections/PetForm.tsx new file mode 100644 index 0000000..626b042 --- /dev/null +++ b/apps/web/src/portal/sections/PetForm.tsx @@ -0,0 +1,87 @@ +import { useState } from "react"; +import { X, Save } from "lucide-react"; +import type { Pet } from "../mockData.js"; + +interface Props { + pet?: Pet; + onSave: (pet: Pet) => void; + onCancel: () => void; +} + +export function PetForm({ pet, onSave, onCancel }: Props) { + const [name, setName] = useState(pet?.name ?? ""); + const [breed, setBreed] = useState(pet?.breed ?? ""); + const [weight, setWeight] = useState(pet?.weight ?? 0); + const [notes, setNotes] = useState(pet?.allergies ?? ""); + + function handleSubmit(e: React.FormEvent) { + e.preventDefault(); + if (!pet) return; + onSave({ ...pet, name, breed, weight, allergies: notes }); + } + + return ( +
+
+

{pet ? "Edit Pet" : "Add Pet"}

+ +
+
+
+ + setName(e.target.value)} + className="w-full border border-stone-200 rounded-lg px-3 py-2 text-sm focus:outline-none focus:ring-2 focus:ring-(--color-accent)" + /> +
+
+ + setBreed(e.target.value)} + className="w-full border border-stone-200 rounded-lg px-3 py-2 text-sm focus:outline-none focus:ring-2 focus:ring-(--color-accent)" + /> +
+
+ + setWeight(Number(e.target.value))} + className="w-full border border-stone-200 rounded-lg px-3 py-2 text-sm focus:outline-none focus:ring-2 focus:ring-(--color-accent)" + /> +
+
+ +