Pet adoption appointments (currently has a small issue)
This commit is contained in:
@@ -294,6 +294,16 @@ function AppointmentsPage() {
|
||||
const router = useRouter();
|
||||
const searchParams = useSearchParams();
|
||||
const preselectedPetId = searchParams.get("petId");
|
||||
|
||||
// Adoption mode — set when arriving from a pet detail page
|
||||
const adoptionMode = searchParams.get("adoptionMode") === "true";
|
||||
const adoptionPetId = searchParams.get("petId");
|
||||
const adoptionPetName = searchParams.get("petName") || "";
|
||||
const adoptionPetSpecies = searchParams.get("petSpecies") || "";
|
||||
const adoptionPetBreed = searchParams.get("petBreed") || "";
|
||||
const adoptionStoreId = searchParams.get("storeId") || "";
|
||||
const adoptionStoreName = searchParams.get("storeName") || "";
|
||||
|
||||
const didPreselectRef = useRef(false);
|
||||
|
||||
const [stores, setStores] = useState([]);
|
||||
@@ -366,9 +376,24 @@ const canBookAppointments = user?.role === "CUSTOMER" || user?.role === "ADMIN";
|
||||
}, [token, loadCustomerPets]);
|
||||
|
||||
useEffect(() => {
|
||||
if (didPreselectRef.current) {
|
||||
if (didPreselectRef.current) return;
|
||||
|
||||
if (adoptionMode) {
|
||||
// Need both the store (so employees load) and a serviceId (so availability slots load)
|
||||
if (adoptionStoreId && services.length > 0) {
|
||||
setStoreId(adoptionStoreId);
|
||||
// Prefer a service named "adopt", fall back to the first available service
|
||||
const adoptionSvc =
|
||||
services.find((s) => s.serviceName.toLowerCase().includes("adopt")) ||
|
||||
services[0];
|
||||
if (adoptionSvc) {
|
||||
setServiceId(String(adoptionSvc.serviceId));
|
||||
didPreselectRef.current = true;
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (!preselectedPetId || services.length === 0 || allPets.length === 0) {
|
||||
return;
|
||||
}
|
||||
@@ -382,7 +407,7 @@ const canBookAppointments = user?.role === "CUSTOMER" || user?.role === "ADMIN";
|
||||
}
|
||||
setSelectedPetIds([Number(preselectedPetId)]);
|
||||
didPreselectRef.current = true;
|
||||
}, [preselectedPetId, services, allPets]);
|
||||
}, [adoptionMode, adoptionStoreId, preselectedPetId, services, allPets]);
|
||||
|
||||
const loadAppointments = useCallback(() => {
|
||||
|
||||
@@ -495,8 +520,9 @@ const canBookAppointments = user?.role === "CUSTOMER" || user?.role === "ADMIN";
|
||||
return d.toISOString().split("T")[0];
|
||||
}
|
||||
|
||||
const formValid =
|
||||
storeId && serviceId && appointmentDate && appointmentTime && selectedPetIds.length > 0;
|
||||
const formValid = adoptionMode
|
||||
? Boolean(employeeId && appointmentDate && appointmentTime)
|
||||
: storeId && serviceId && appointmentDate && appointmentTime && selectedPetIds.length > 0;
|
||||
|
||||
async function handleSubmit(e) {
|
||||
e.preventDefault();
|
||||
@@ -509,7 +535,7 @@ const canBookAppointments = user?.role === "CUSTOMER" || user?.role === "ADMIN";
|
||||
return;
|
||||
}
|
||||
|
||||
if (selectedPetIds.length === 0) {
|
||||
if (!adoptionMode && selectedPetIds.length === 0) {
|
||||
setError(isAdoptionService ? "Please select a pet to adopt." : "Please select at least one pet.");
|
||||
|
||||
return;
|
||||
@@ -518,6 +544,33 @@ const canBookAppointments = user?.role === "CUSTOMER" || user?.role === "ADMIN";
|
||||
setSubmitting(true);
|
||||
|
||||
try {
|
||||
if (adoptionMode) {
|
||||
// Submit an adoption request directly to the adoption table
|
||||
const body = {
|
||||
petId: Number(adoptionPetId),
|
||||
employeeId: employeeId ? Number(employeeId) : undefined,
|
||||
sourceStoreId: adoptionStoreId ? Number(adoptionStoreId) : undefined,
|
||||
};
|
||||
|
||||
const res = await fetch(`${API_BASE}/api/v1/adoptions/request`, {
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
Authorization: `Bearer ${token}`,
|
||||
},
|
||||
body: JSON.stringify(body),
|
||||
});
|
||||
|
||||
if (!res.ok) {
|
||||
const data = await res.json().catch(() => null);
|
||||
throw new Error(data?.message || data?.error || `Request failed (${res.status})`);
|
||||
}
|
||||
|
||||
setSuccess(`Adoption request submitted! ${adoptionPetName} is now marked as Pending. We'll be in touch soon.`);
|
||||
setEmployeeId("");
|
||||
return;
|
||||
}
|
||||
|
||||
const body = {
|
||||
customerId: user.customerId || user.id,
|
||||
storeId: Number(storeId),
|
||||
@@ -609,34 +662,44 @@ const canBookAppointments = user?.role === "CUSTOMER" || user?.role === "ADMIN";
|
||||
|
||||
<label className="appt-label">
|
||||
Store Location
|
||||
<select
|
||||
className="appt-select"
|
||||
value={storeId}
|
||||
onChange={(e) => setStoreId(e.target.value)}
|
||||
required
|
||||
>
|
||||
<option value="">Select a store...</option>
|
||||
{stores.map((s) => (
|
||||
<option key={s.id} value={s.id}>{s.label}</option>
|
||||
))}
|
||||
</select>
|
||||
{adoptionMode ? (
|
||||
<div className="appt-locked-field">{adoptionStoreName || "Pet's store"}</div>
|
||||
) : (
|
||||
<select
|
||||
className="appt-select"
|
||||
value={storeId}
|
||||
onChange={(e) => setStoreId(e.target.value)}
|
||||
required
|
||||
>
|
||||
<option value="">Select a store...</option>
|
||||
{stores.map((s) => (
|
||||
<option key={s.id} value={s.id}>{s.label}</option>
|
||||
))}
|
||||
</select>
|
||||
)}
|
||||
</label>
|
||||
|
||||
<label className="appt-label">
|
||||
Service
|
||||
<select
|
||||
className="appt-select"
|
||||
value={serviceId}
|
||||
onChange={(e) => handleServiceChange(e.target.value)}
|
||||
required
|
||||
>
|
||||
<option value="">Select a service...</option>
|
||||
{services.map((s) => (
|
||||
<option key={s.serviceId} value={s.serviceId}>
|
||||
{s.serviceName} — ${Number(s.servicePrice).toFixed(2)} ({s.serviceDuration} min)
|
||||
</option>
|
||||
))}
|
||||
</select>
|
||||
{adoptionMode ? (
|
||||
<div className="appt-locked-field">
|
||||
Adopting a Pet ({[adoptionPetName, adoptionPetSpecies, adoptionPetBreed].filter(Boolean).join(", ")})
|
||||
</div>
|
||||
) : (
|
||||
<select
|
||||
className="appt-select"
|
||||
value={serviceId}
|
||||
onChange={(e) => handleServiceChange(e.target.value)}
|
||||
required
|
||||
>
|
||||
<option value="">Select a service...</option>
|
||||
{services.map((s) => (
|
||||
<option key={s.serviceId} value={s.serviceId}>
|
||||
{s.serviceName} — ${Number(s.servicePrice).toFixed(2)} ({s.serviceDuration} min)
|
||||
</option>
|
||||
))}
|
||||
</select>
|
||||
)}
|
||||
</label>
|
||||
|
||||
{employees.length > 0 && (
|
||||
@@ -654,7 +717,7 @@ const canBookAppointments = user?.role === "CUSTOMER" || user?.role === "ADMIN";
|
||||
</label>
|
||||
)}
|
||||
|
||||
{selectedService && (
|
||||
{!adoptionMode && selectedService && (
|
||||
<div className="appt-service-info">
|
||||
<p>{selectedService.serviceDesc}</p>
|
||||
</div>
|
||||
@@ -693,7 +756,7 @@ const canBookAppointments = user?.role === "CUSTOMER" || user?.role === "ADMIN";
|
||||
</div>
|
||||
)}
|
||||
|
||||
{serviceId && (
|
||||
{!adoptionMode && serviceId && (
|
||||
<div className="appt-label">
|
||||
<span>{petSectionLabel}</span>
|
||||
{isCustomerPetService && (
|
||||
@@ -762,7 +825,7 @@ const canBookAppointments = user?.role === "CUSTOMER" || user?.role === "ADMIN";
|
||||
className="appt-submit-btn"
|
||||
disabled={!formValid || submitting}
|
||||
>
|
||||
{submitting ? "Booking..." : isAdoptionService ? "Schedule Adoption Visit" : "Book Appointment"}
|
||||
{submitting ? "Booking..." : adoptionMode ? "Schedule Appointment" : isAdoptionService ? "Schedule Adoption Visit" : "Book Appointment"}
|
||||
</button>
|
||||
</form>
|
||||
) : null}
|
||||
|
||||
Reference in New Issue
Block a user