Close chat #169
@@ -615,15 +615,7 @@ function AppointmentsPage() {
|
||||
{submitting ? "Booking..." : isAdoptionService ? "Schedule Adoption Visit" : "Book Appointment"}
|
||||
</button>
|
||||
</form>
|
||||
) : (
|
||||
<div className="appt-form">
|
||||
<h2 className="appt-form-title">Appointment Booking</h2>
|
||||
<div className="appt-service-info">
|
||||
<p>Web appointment booking is currently available for customer accounts only.</p>
|
||||
<p>Admin and staff accounts can still review appointment activity below.</p>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
) : null}
|
||||
|
||||
<div className="appt-history">
|
||||
<h2 className="appt-form-title">{canBookAppointments ? "Your Appointments" : "Appointments"}</h2>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
"use client";
|
||||
|
||||
import { useEffect, useState, useCallback } from "react";
|
||||
import { useEffect, useState, useCallback, useRef } from "react";
|
||||
import { useRouter } from "next/navigation";
|
||||
import { useAuth } from "@/context/AuthContext";
|
||||
|
||||
@@ -9,6 +9,7 @@ const API_BASE = "";
|
||||
export default function ProfilePage() {
|
||||
const {user, token, loading, logout, refreshUser} = useAuth();
|
||||
const router = useRouter();
|
||||
const petImageObjectUrlsRef = useRef([]);
|
||||
|
||||
const [pets, setPets] = useState([]);
|
||||
const [loadingPets, setLoadingPets] = useState(false);
|
||||
@@ -25,6 +26,13 @@ export default function ProfilePage() {
|
||||
const [profileSuccess, setProfileSuccess] = useState(null);
|
||||
const [avatarSubmitting, setAvatarSubmitting] = useState(false);
|
||||
|
||||
const clearPetImageObjectUrls = useCallback(() => {
|
||||
for (const objectUrl of petImageObjectUrlsRef.current) {
|
||||
URL.revokeObjectURL(objectUrl);
|
||||
}
|
||||
petImageObjectUrlsRef.current = [];
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
if (!loading && !user) {
|
||||
router.replace(`/login?next=${encodeURIComponent("/profile")}`);
|
||||
@@ -40,17 +48,64 @@ export default function ProfilePage() {
|
||||
});
|
||||
}, [user]);
|
||||
|
||||
const loadPets = useCallback(() => {
|
||||
const loadPets = useCallback(async () => {
|
||||
if (!token) return;
|
||||
setLoadingPets(true);
|
||||
fetch(`${API_BASE}/api/v1/my-pets`, {
|
||||
headers: { Authorization: `Bearer ${token}` },
|
||||
})
|
||||
.then((r) => r.json())
|
||||
.then(setPets)
|
||||
.catch(() => {})
|
||||
.finally(() => setLoadingPets(false));
|
||||
}, [token]);
|
||||
|
||||
try {
|
||||
const response = await fetch(`${API_BASE}/api/v1/my-pets`, {
|
||||
headers: { Authorization: `Bearer ${token}` },
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error(`Request failed (${response.status})`);
|
||||
}
|
||||
|
||||
const petData = await response.json();
|
||||
clearPetImageObjectUrls();
|
||||
|
||||
const petsWithResolvedImages = await Promise.all(
|
||||
(Array.isArray(petData) ? petData : []).map(async (pet) => {
|
||||
if (!pet.imageUrl) {
|
||||
return pet;
|
||||
}
|
||||
|
||||
try {
|
||||
const imageResponse = await fetch(`${API_BASE}${pet.imageUrl}`, {
|
||||
headers: { Authorization: `Bearer ${token}` },
|
||||
});
|
||||
|
||||
if (!imageResponse.ok) {
|
||||
return { ...pet, imageUrl: null };
|
||||
}
|
||||
|
||||
const blob = await imageResponse.blob();
|
||||
const objectUrl = URL.createObjectURL(blob);
|
||||
petImageObjectUrlsRef.current.push(objectUrl);
|
||||
|
||||
return { ...pet, imageUrl: objectUrl };
|
||||
} catch {
|
||||
return { ...pet, imageUrl: null };
|
||||
}
|
||||
})
|
||||
);
|
||||
|
||||
setPets(petsWithResolvedImages);
|
||||
}
|
||||
|
||||
catch {
|
||||
}
|
||||
|
||||
finally {
|
||||
setLoadingPets(false);
|
||||
}
|
||||
}, [token, clearPetImageObjectUrls]);
|
||||
|
||||
useEffect(() => {
|
||||
return () => {
|
||||
clearPetImageObjectUrls();
|
||||
};
|
||||
}, [clearPetImageObjectUrls]);
|
||||
|
||||
useEffect(() => {
|
||||
if (user?.role === "CUSTOMER") {
|
||||
|
||||
Reference in New Issue
Block a user