"use client"; import dynamic from "next/dynamic"; import { useState, useEffect, useCallback, useRef } from "react"; import Link from "next/link"; import { useRouter, useSearchParams } from "next/navigation"; import { useAuth } from "@/context/AuthContext"; const API_BASE = ""; const SPECIES_BREEDS = { Dog: ["Beagle", "Boxer", "Bulldog", "Chihuahua", "Dachshund", "German Shepherd", "Golden Retriever", "Labrador Retriever", "Poodle", "Rottweiler", "Shih Tzu", "Siberian Husky", "Yorkshire Terrier", "Mixed / Other"], Cat: ["Abyssinian", "Bengal", "British Shorthair", "Maine Coon", "Persian", "Ragdoll", "Scottish Fold", "Siamese", "Sphynx", "Mixed / Other"], Bird: ["Canary", "Cockatiel", "Cockatoo", "Finch", "Lovebird", "Macaw", "Parakeet", "Parrot", "Other"], Rabbit: ["Dutch", "Flemish Giant", "Holland Lop", "Lionhead", "Mini Rex", "Other"], Hamster: ["Dwarf", "Roborovski", "Syrian", "Other"], "Guinea Pig": ["Abyssinian", "American", "Peruvian", "Teddy", "Other"], Reptile: ["Ball Python", "Bearded Dragon", "Blue-tongued Skink", "Corn Snake", "Leopard Gecko", "Other"], Fish: ["Angelfish", "Betta", "Cichlid", "Clownfish", "Goldfish", "Guppy", "Tetra", "Other"], Other: ["Other"], }; //Services that only apply to specific species, keyed by species name const SPECIES_EXCLUSIVE_SERVICES = { Bird: ["wing clipping", "beak and nail"], Fish: ["aquarium health"], }; //Services that are banned for specific species, keyed by species name const SPECIES_BANNED_SERVICES = { Bird: ["teeth cleaning"], }; //Filters out services that are exclusive to a different species, or banned for the selected species. //When species is unknown, hides all species-exclusive and banned services to avoid invalid options appearing. function getAvailableServices(services, species) { const exclusiveKeywords = Object.values(SPECIES_EXCLUSIVE_SERVICES).flat(); const allBannedKeywords = Object.values(SPECIES_BANNED_SERVICES).flat(); if (!species) { return services.filter((s) => { const name = s.serviceName.toLowerCase(); return !exclusiveKeywords.some((kw) => name.includes(kw)) && !allBannedKeywords.some((kw) => name.includes(kw)); }); } return services.filter((s) => { const name = s.serviceName.toLowerCase(); for (const [exclusiveSpecies, keywords] of Object.entries(SPECIES_EXCLUSIVE_SERVICES)) { if (exclusiveSpecies !== species && keywords.some((kw) => name.includes(kw))) { return false; } } const banned = SPECIES_BANNED_SERVICES[species] ?? []; if (banned.some((kw) => name.includes(kw))) { return false; } return true; }); } const DAYS = ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"]; const MONTHS = ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"]; //Custom calendar date picker that prevents selecting dates in the past function DatePicker({ value, minDate, onChange }) { const today = new Date(); today.setHours(0, 0, 0, 0); const min = minDate ? new Date(minDate + "T00:00:00") : today; const parsed = value ? new Date(value + "T00:00:00") : null; const [viewYear, setViewYear] = useState(parsed ? parsed.getFullYear() : min.getFullYear()); const [viewMonth, setViewMonth] = useState(parsed ? parsed.getMonth() : min.getMonth()); function prevMonth() { if (viewMonth === 0) { setViewMonth(11); setViewYear((y) => y - 1); } else { setViewMonth((m) => m - 1); } } function nextMonth() { if (viewMonth === 11) { setViewMonth(0); setViewYear((y) => y + 1); } else { setViewMonth((m) => m + 1); } } const firstDay = new Date(viewYear, viewMonth, 1).getDay(); const daysInMonth = new Date(viewYear, viewMonth + 1, 0).getDate(); const minYear = min.getFullYear(); const minMonth = min.getMonth(); const isPrevDisabled = viewYear < minYear || (viewYear === minYear && viewMonth <= minMonth); function selectDay(day) { const d = new Date(viewYear, viewMonth, day); if (d < min) return; const iso = `${viewYear}-${String(viewMonth + 1).padStart(2, "0")}-${String(day).padStart(2, "0")}`; onChange(iso); } function isSelected(day) { if (!parsed) return false; return parsed.getFullYear() === viewYear && parsed.getMonth() === viewMonth && parsed.getDate() === day; } function isDisabled(day) { return new Date(viewYear, viewMonth, day) < min; } const cells = []; for (let i = 0; i < firstDay; i++) { cells.push({ key: `empty-${viewYear}-${viewMonth}-${String(i)}`, day: null }); } for (let d = 1; d <= daysInMonth; d++) { cells.push({ key: `day-${viewYear}-${viewMonth}-${String(d)}`, day: d }); } return (
{MONTHS[viewMonth]} {viewYear}
{DAYS.map((d) => ( {d} ))} {cells.map(({ key, day }) => day === null ? ( ) : ( ) )}
{parsed && (
Selected: {MONTHS[parsed.getMonth()]} {parsed.getDate()}, {parsed.getFullYear()}
)}
); } const labelCls = "flex flex-col gap-[0.35rem] text-[0.9rem] font-semibold text-[#444]"; const inputCls = "px-[0.85rem] py-[0.6rem] border border-[#ddd] rounded-lg text-base outline-none transition-all focus:border-[#e68672] focus:shadow-[0_0_0_3px_rgba(230,134,114,0.2)]"; const selectCls = `custom-select ${inputCls} bg-white cursor-pointer`; const errorCls = "bg-[#fff0f0] border border-[#f5c6c6] text-[#c0392b] rounded-lg px-4 py-3 text-[0.9rem]"; const successCls = "bg-[#f0fdf4] border border-[#bbf7d0] text-[#16a34a] rounded-lg px-4 py-3 text-[0.9rem]"; const submitBtnCls = "py-3 bg-[#e68672] text-white border-none rounded-lg text-base font-bold cursor-pointer transition-all hover:bg-[#d4705e] active:scale-[0.98] disabled:opacity-60 disabled:cursor-not-allowed"; //Modal dialog for quickly adding a new pet without leaving the appointments page function AddPetModal({ token, onClose, onAdded }) { const [petName, setPetName] = useState(""); const [species, setSpecies] = useState(""); const [breed, setBreed] = useState(""); const [submitting, setSubmitting] = useState(false); const [petError, setPetError] = useState(null); async function handleSubmit(e) { e.preventDefault(); setPetError(null); setSubmitting(true); try { const res = await fetch(`${API_BASE}/api/v1/my-pets`, { method: "POST", headers: { "Content-Type": "application/json", Authorization: `Bearer ${token}`, }, body: JSON.stringify({ petName, species, breed: breed || null }), }); if (!res.ok) { const data = await res.json().catch(() => null); throw new Error(data?.message || `Request failed (${res.status})`); } onAdded(); onClose(); } catch (err) { setPetError(err.message); } finally { setSubmitting(false); } } return (
e.stopPropagation()}>

Add a New Pet

{petError &&
{petError}
}
); } //Appointments page - book a service or adoption, and view past and active appointments function AppointmentsPage() { const { user, token, loading: authLoading } = useAuth(); const router = useRouter(); const searchParams = useSearchParams(); const preselectedPetId = searchParams.get("petId"); 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 errorRef = useRef(null); const historyRef = useRef(null); const [adoptionVerified, setAdoptionVerified] = useState(!adoptionMode); const [adoptionVerifyError, setAdoptionVerifyError] = useState(null); const [adoptionVerifyLoading, setAdoptionVerifyLoading] = useState(adoptionMode); const [stores, setStores] = useState([]); const [employees, setEmployees] = useState([]); const [services, setServices] = useState([]); const [allPets, setAllPets] = useState([]); const [customerPets, setCustomerPets] = useState([]); const [availableSlots, setAvailableSlots] = useState([]); const [storeId, setStoreId] = useState(""); const [serviceId, setServiceId] = useState(""); const [employeeId, setEmployeeId] = useState(""); const [appointmentDate, setAppointmentDate] = useState(""); const [appointmentTime, setAppointmentTime] = useState(""); const [selectedPetIds, setSelectedPetIds] = useState([]); const [loadingSlots, setLoadingSlots] = useState(false); const [submitting, setSubmitting] = useState(false); const [error, setError] = useState(null); const [success, setSuccess] = useState(null); useEffect(() => { if (error && errorRef.current) { errorRef.current.scrollIntoView({ behavior: "smooth", block: "center" }); } }, [error]); const [appointments, setAppointments] = useState([]); const [loadingAppointments, setLoadingAppointments] = useState(false); const [adoptions, setAdoptions] = useState([]); const [loadingAdoptions, setLoadingAdoptions] = useState(false); const [showAddPetModal, setShowAddPetModal] = useState(false); const [cancellingId, setCancellingId] = useState(null); const [apptSearch, setApptSearch] = useState(""); const [adoptionSearch, setAdoptionSearch] = useState(""); const [showPastAppts, setShowPastAppts] = useState(false); const [showPastAdoptions, setShowPastAdoptions] = useState(false); //Pagination state for each of the four history lists const HISTORY_PAGE_SIZE = 5; const [apptPage, setApptPage] = useState(0); const [pastApptPage, setPastApptPage] = useState(0); const [adoptionPage, setAdoptionPage] = useState(0); const [pastAdoptionPage, setPastAdoptionPage] = useState(0); //Reset appointment page to 0 when the search text changes useEffect(() => { setApptPage(0); }, [apptSearch]); //Reset adoption page to 0 when the search text changes useEffect(() => { setAdoptionPage(0); }, [adoptionSearch]); const canBookAppointments = user?.role === "CUSTOMER" || user?.role === "ADMIN"; useEffect(() => { if (!authLoading && !user) { const target = preselectedPetId ? `/appointments?petId=${encodeURIComponent(preselectedPetId)}` : "/appointments"; router.push(`/login?next=${encodeURIComponent(target)}`); } }, [authLoading, user, router, preselectedPetId]); useEffect(() => { if (!adoptionMode || !adoptionPetId) return; setAdoptionVerifyLoading(true); fetch(`${API_BASE}/api/v1/pets/${adoptionPetId}`) .then((r) => { if (!r.ok) throw new Error("Pet not found. This link may be invalid."); return r.json(); }) .then((pet) => { if (pet.petStatus?.toLowerCase() !== "available") { throw new Error(`${pet.petName || "This pet"} is no longer available for adoption (status: ${pet.petStatus}).`); } if (adoptionStoreId && String(pet.storeId) !== String(adoptionStoreId)) { throw new Error("Store mismatch: this pet is not located at the specified store."); } setAdoptionVerified(true); }) .catch((err) => setAdoptionVerifyError(err.message)) .finally(() => setAdoptionVerifyLoading(false)); }, [adoptionMode, adoptionPetId, adoptionStoreId]); //Loads the user's registered pets for the pet selector const loadCustomerPets = useCallback(() => { if (!token || !canBookAppointments) return; fetch(`${API_BASE}/api/v1/my-pets`, { headers: { Authorization: `Bearer ${token}` }, }) .then((r) => r.json()) .then((data) => setCustomerPets(Array.isArray(data) ? data : [])) .catch(() => {}); }, [token, canBookAppointments]); useEffect(() => { if (!token) return; fetch(`${API_BASE}/api/v1/dropdowns/stores`, { headers: { Authorization: `Bearer ${token}` }, }) .then((r) => r.json()) .then(setStores) .catch(() => setError("Failed to load stores.")); fetch(`${API_BASE}/api/v1/services?size=100`) .then((r) => r.json()) .then((data) => setServices(data.content ?? [])) .catch(() => setError("Failed to load services.")); fetch(`${API_BASE}/api/v1/pets?size=200&sort=id,asc&status=Available`) .then((r) => r.json()) .then((data) => setAllPets(data.content ?? [])) .catch(() => {}); loadCustomerPets(); }, [token, loadCustomerPets]); useEffect(() => { if (didPreselectRef.current) return; if (adoptionMode) { if (adoptionStoreId && services.length > 0) { setStoreId(adoptionStoreId); 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; const adoptionSvc = services.find((s) => s.serviceName.toLowerCase().includes("adopt")); if (adoptionSvc) { setServiceId(String(adoptionSvc.serviceId)); } setSelectedPetIds([Number(preselectedPetId)]); didPreselectRef.current = true; }, [adoptionMode, adoptionStoreId, preselectedPetId, services, allPets]); //Fetches the user's booked appointments const loadAppointments = useCallback(() => { if (!token) return; setLoadingAppointments(true); fetch(`${API_BASE}/api/v1/appointments?size=50&sort=appointmentDate,desc`, { headers: { Authorization: `Bearer ${token}` }, }) .then((r) => r.json()) .then((data) => setAppointments(data.content ?? [])) .catch(() => {}) .finally(() => setLoadingAppointments(false)); }, [token]); //Fetches the user's adoption requests const loadAdoptions = useCallback(() => { if (!token) return; setLoadingAdoptions(true); fetch(`${API_BASE}/api/v1/adoptions?size=50&sort=adoptionDate,desc`, { headers: { Authorization: `Bearer ${token}` }, }) .then((r) => r.json()) .then((data) => setAdoptions(data.content ?? [])) .catch(() => {}) .finally(() => setLoadingAdoptions(false)); }, [token]); useEffect(() => { loadAppointments(); loadAdoptions(); }, [loadAppointments, loadAdoptions]); //Cancels an appointment after asking the user to confirm async function handleCancelAppointment(appointmentId) { if (!confirm("Cancel this appointment?")) return; setCancellingId(appointmentId); try { const res = await fetch(`${API_BASE}/api/v1/appointments/${appointmentId}/cancel`, { method: "PATCH", headers: { Authorization: `Bearer ${token}` }, }); if (!res.ok) { const data = await res.json().catch(() => null); throw new Error(data?.message || `Failed to cancel appointment (${res.status})`); } loadAppointments(); } catch (err) { alert(err.message); } finally { setCancellingId(null); } } //Cancels an adoption request after asking the user to confirm async function handleCancelAdoption(adoptionId) { if (!confirm("Cancel this adoption request?")) return; setCancellingId(adoptionId); try { const res = await fetch(`${API_BASE}/api/v1/adoptions/${adoptionId}/cancel`, { method: "PATCH", headers: { Authorization: `Bearer ${token}` }, }); if (!res.ok) { const data = await res.json().catch(() => null); throw new Error(data?.message || `Failed to cancel adoption (${res.status})`); } loadAdoptions(); } catch (err) { alert(err.message); } finally { setCancellingId(null); } } useEffect(() => { if (!token || !storeId) { setEmployees([]); setEmployeeId(""); return; } fetch(`${API_BASE}/api/v1/dropdowns/stores/${storeId}/employees`, { headers: { Authorization: `Bearer ${token}` }, }) .then((r) => r.json()) .then((data) => setEmployees(Array.isArray(data) ? data : [])) .catch(() => setEmployees([])); }, [token, storeId]); useEffect(() => { if (!employees.length) { setEmployeeId(""); return; } const currentExists = employees.some((employee) => String(employee.id) === String(employeeId)); if (!currentExists) { setEmployeeId(String(employees[0].id)); } }, [employees, employeeId]); useEffect(() => { if (!storeId || !serviceId || !appointmentDate) { setAvailableSlots([]); setAppointmentTime(""); return; } setLoadingSlots(true); setAppointmentTime(""); const params = new URLSearchParams({ storeId, serviceId, date: appointmentDate }); fetch(`${API_BASE}/api/v1/appointments/availability?${params}`) .then((r) => { if (!r.ok) throw new Error("Failed to check availability"); return r.json(); }) .then(setAvailableSlots) .catch(() => setAvailableSlots([])) .finally(() => setLoadingSlots(false)); }, [storeId, serviceId, appointmentDate]); const eligiblePets = customerPets.filter( (p) => p.petStatus?.toLowerCase() === "owned" || p.petStatus?.toLowerCase() === "adopted" ); const selectedService = services.find((s) => s.serviceId === Number(serviceId)); const selectedPet = !adoptionMode ? (eligiblePets.find((p) => p.customerPetId === selectedPetIds[0]) || null) : null; const availableServices = getAvailableServices(services, selectedPet?.species); function handleServiceChange(newServiceId) { setServiceId(newServiceId); } //Selects a pet and clears the chosen service if it is not valid for that species function handlePetSelect(petId) { const newPet = eligiblePets.find((p) => p.customerPetId === petId); setSelectedPetIds([petId]); if (serviceId && newPet) { const newAvailable = getAvailableServices(services, newPet.species); if (!newAvailable.some((s) => String(s.serviceId) === String(serviceId))) { setServiceId(""); setAppointmentTime(""); setAvailableSlots([]); } } } //Converts a 24-hour time string to 12-hour AM/PM format function formatTime(timeStr) { const [h, m] = timeStr.split(":"); const hour = parseInt(h, 10); const ampm = hour >= 12 ? "PM" : "AM"; const display = hour === 0 ? 12 : hour > 12 ? hour - 12 : hour; return `${display}:${m} ${ampm}`; } function getMinDate() { const d = new Date(); return d.toISOString().split("T")[0]; } const formValid = adoptionMode ? Boolean(employeeId && appointmentDate && adoptionVerified) : storeId && serviceId && appointmentDate && appointmentTime && selectedPetIds.length > 0; //Submits either a new appointment or an adoption request depending on the current mode async function handleSubmit(e) { e.preventDefault(); setError(null); setSuccess(null); if (!canBookAppointments) { setError("Only customer accounts can book appointments from the web app."); return; } if (!adoptionMode && selectedPetIds.length === 0) { setError("Please select a pet for your appointment."); return; } if (!adoptionMode && selectedPet && selectedPet.petStatus?.toLowerCase() !== "owned" && selectedPet.petStatus?.toLowerCase() !== "adopted") { setError("The selected pet is no longer eligible for appointments. Please refresh the page."); return; } if (!adoptionMode && selectedPet && serviceId) { const chosenService = services.find((s) => String(s.serviceId) === String(serviceId)); if (chosenService && getAvailableServices([chosenService], selectedPet.species).length === 0) { setError(`"${chosenService.serviceName}" is not available for ${selectedPet.species}s. Please select a valid service.`); return; } } setSubmitting(true); try { if (adoptionMode) { const body = { petId: Number(adoptionPetId), employeeId: employeeId ? Number(employeeId) : undefined, sourceStoreId: adoptionStoreId ? Number(adoptionStoreId) : undefined, adoptionDate: appointmentDate, }; 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(""); loadAdoptions(); setTimeout(() => historyRef.current?.scrollIntoView({ behavior: "smooth", block: "start" }), 300); return; } const body = { customerId: user.customerId || user.id, storeId: Number(storeId), serviceId: Number(serviceId), employeeId: employeeId ? Number(employeeId) : undefined, appointmentDate, appointmentTime: appointmentTime + ":00", appointmentStatus: "Booked", }; if (selectedPetIds.length > 0) { body.petId = selectedPetIds[0]; } const res = await fetch(`${API_BASE}/api/v1/appointments`, { 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("Appointment booked successfully!"); setStoreId(""); setServiceId(""); setAppointmentDate(""); setAppointmentTime(""); setSelectedPetIds([]); setAvailableSlots([]); loadAppointments(); setTimeout(() => historyRef.current?.scrollIntoView({ behavior: "smooth", block: "start" }), 300); } catch (err) { setError(err.message); } finally { setSubmitting(false); } } if (authLoading) { return (

Loading...

); } if (!user) return null; return (
{showAddPetModal && ( setShowAddPetModal(false)} onAdded={loadCustomerPets} /> )}

{adoptionMode ? "Schedule an Adoption" : "Schedule an Appointment"}

{adoptionMode ? "Schedule a pet adoption visit" : "Book a service for your pet."}

{canBookAppointments ? (

{adoptionMode ? "New Adoption" : "New Appointment"}

{error &&
{error}
} {adoptionMode && adoptionVerifyLoading && (

Verifying pet details…

)} {adoptionMode && adoptionVerifyError && (
{adoptionVerifyError}
)} {(!adoptionMode || adoptionVerified) && (<> {adoptionMode && ( )} {!adoptionMode && (
Select Your Pet {eligiblePets.length === 0 ? (

You have no adopted pets available.{" "} Add a pet on your profile page.

) : (
{eligiblePets.map((p) => ( ))}
)}
)} {(adoptionMode || selectedPetIds.length > 0) && (<> {!adoptionMode && ( )} {employees.length > 0 && ( )} {!adoptionMode && selectedService && (

{selectedService.serviceDesc}

)}
Date
{!adoptionMode && storeId && serviceId && appointmentDate && (
Available Time Slots {loadingSlots ? (

Checking availability...

) : availableSlots.length === 0 ? (

No available slots for this date. Please try another date.

) : (
{availableSlots.map((slot) => ( ))}
)}
)} )} {success &&
{success}
} )}
) : null} {/* History panel */}

{canBookAppointments ? "Your Appointments" : "Appointments"}

{loadingAppointments ? (

Loading appointments...

) : (() => { const activeAppts = appointments.filter((a) => a.appointmentStatus?.toLowerCase() === "booked"); const pastAppts = appointments.filter((a) => a.appointmentStatus?.toLowerCase() !== "booked"); const q = apptSearch.toLowerCase(); const filteredActive = activeAppts.filter((a) => !q || [a.serviceName, a.storeName, a.petName].some((v) => v?.toLowerCase().includes(q)) ); //Paginate active appointments const apptTotalPages = Math.ceil(filteredActive.length / HISTORY_PAGE_SIZE); const apptSlice = filteredActive.slice(apptPage * HISTORY_PAGE_SIZE, (apptPage + 1) * HISTORY_PAGE_SIZE); //Paginate past appointments const pastApptTotalPages = Math.ceil(pastAppts.length / HISTORY_PAGE_SIZE); const pastApptSlice = pastAppts.slice(pastApptPage * HISTORY_PAGE_SIZE, (pastApptPage + 1) * HISTORY_PAGE_SIZE); return ( <> setApptSearch(e.target.value)} /> {filteredActive.length === 0 ? (

{activeAppts.length === 0 ? "No active appointments." : "No results."}

) : ( <>
{apptSlice.map((a) => (
{a.serviceName} {a.appointmentStatus}
{a.storeName} {a.appointmentDate} at {formatTime(a.appointmentTime)}
{a.petName && (
Pet: {a.petName}
)}
))}
{apptTotalPages > 1 && (
Page {apptPage + 1} of {apptTotalPages}
)} )} {pastAppts.length > 0 && (
{showPastAppts && ( <>
{pastApptSlice.map((a) => (
{a.serviceName} {a.appointmentStatus}
{a.storeName} {a.appointmentDate} at {formatTime(a.appointmentTime)}
{a.petName && (
Pet: {a.petName}
)}
))}
{pastApptTotalPages > 1 && (
Page {pastApptPage + 1} of {pastApptTotalPages}
)} )}
)} ); })()}

{canBookAppointments ? "Your Adoptions" : "Adoptions"}

{loadingAdoptions ? (

Loading adoptions...

) : (() => { const activeAdoptions = adoptions.filter((a) => a.adoptionStatus?.toLowerCase() === "pending"); const pastAdoptions = adoptions.filter((a) => a.adoptionStatus?.toLowerCase() !== "pending"); const q = adoptionSearch.toLowerCase(); const filteredActive = activeAdoptions.filter((a) => !q || [a.petName, a.sourceStoreName].some((v) => v?.toLowerCase().includes(q)) ); //Paginate active adoptions const adoptionTotalPages = Math.ceil(filteredActive.length / HISTORY_PAGE_SIZE); const adoptionSlice = filteredActive.slice(adoptionPage * HISTORY_PAGE_SIZE, (adoptionPage + 1) * HISTORY_PAGE_SIZE); //Paginate past adoptions const pastAdoptionTotalPages = Math.ceil(pastAdoptions.length / HISTORY_PAGE_SIZE); const pastAdoptionSlice = pastAdoptions.slice(pastAdoptionPage * HISTORY_PAGE_SIZE, (pastAdoptionPage + 1) * HISTORY_PAGE_SIZE); return ( <> setAdoptionSearch(e.target.value)} /> {filteredActive.length === 0 ? (

{activeAdoptions.length === 0 ? "No active adoption requests." : "No results."}

) : ( <>
{adoptionSlice.map((a) => (
{a.petName} {a.adoptionStatus}
{a.sourceStoreName} {a.adoptionDate}
))}
{adoptionTotalPages > 1 && (
Page {adoptionPage + 1} of {adoptionTotalPages}
)} )} {pastAdoptions.length > 0 && (
{showPastAdoptions && ( <>
{pastAdoptionSlice.map((a) => (
{a.petName} {a.adoptionStatus}
{a.sourceStoreName} {a.adoptionDate}
))}
{pastAdoptionTotalPages > 1 && (
Page {pastAdoptionPage + 1} of {pastAdoptionTotalPages}
)} )}
)} ); })()}
); } export default dynamic(() => Promise.resolve(AppointmentsPage), { ssr: false, });