diff --git a/apps/web/src/portal/CustomerPortal.tsx b/apps/web/src/portal/CustomerPortal.tsx index d8ba8bc..2a5e8e1 100644 --- a/apps/web/src/portal/CustomerPortal.tsx +++ b/apps/web/src/portal/CustomerPortal.tsx @@ -133,7 +133,7 @@ export function CustomerPortal() { case "pets": return ; case "reports": - return ; + return ; case "billing": return ; case "messages": diff --git a/apps/web/src/portal/sections/AccountSettings.tsx b/apps/web/src/portal/sections/AccountSettings.tsx index 2fba3a6..31e5da5 100644 --- a/apps/web/src/portal/sections/AccountSettings.tsx +++ b/apps/web/src/portal/sections/AccountSettings.tsx @@ -72,7 +72,9 @@ function PersonalInfo({ sessionId, readOnly }: { sessionId: string | null; readO const fetchPersonalInfo = async () => { try { setLoading(true); - const response = await fetch("/api/portal/me"); + const response = await fetch("/api/portal/me", { + headers: { "X-Impersonation-Session-Id": sessionId ?? "" }, + }); if (response.ok) { const data: PersonalInfoData = await response.json(); setForm({ @@ -142,6 +144,14 @@ function PersonalInfo({ sessionId, readOnly }: { sessionId: string | null; readO } function PasswordChange({ readOnly }: { readOnly: boolean }) { + const [currentPassword, setCurrentPassword] = useState(""); + const [newPassword, setNewPassword] = useState(""); + const [confirmPassword, setConfirmPassword] = useState(""); + const [error, setError] = useState(null); + + const passwordsMatch = newPassword === confirmPassword; + const canSubmit = currentPassword.length > 0 && newPassword.length > 0 && passwordsMatch; + if (readOnly) { return (
@@ -150,23 +160,56 @@ function PasswordChange({ readOnly }: { readOnly: boolean }) { ); } + function handleSubmit() { + if (!canSubmit) return; + if (newPassword !== confirmPassword) { + setError("Passwords do not match."); + return; + } + // TODO: Wire up to actual password-change API endpoint once backend support exists + setError(null); + setCurrentPassword(""); + setNewPassword(""); + setConfirmPassword(""); + } + return (

Change Password

- + setCurrentPassword(e.target.value)} + className="w-full border border-stone-300 rounded-lg px-3 py-2 text-sm" + />
- + setNewPassword(e.target.value)} + className="w-full border border-stone-300 rounded-lg px-3 py-2 text-sm" + />
- + setConfirmPassword(e.target.value)} + className="w-full border border-stone-300 rounded-lg px-3 py-2 text-sm" + />
-
@@ -185,7 +228,9 @@ function ManagePets({ sessionId, readOnly }: { sessionId: string | null; readOnl const fetchPets = async () => { try { setLoading(true); - const response = await fetch("/api/portal/pets"); + const response = await fetch("/api/portal/pets", { + headers: { "X-Impersonation-Session-Id": sessionId ?? "" }, + }); if (response.ok) { const data = await response.json(); setPets(Array.isArray(data) ? data : []); diff --git a/apps/web/src/portal/sections/Appointments.tsx b/apps/web/src/portal/sections/Appointments.tsx index 03fcad1..2eb6bc1 100644 --- a/apps/web/src/portal/sections/Appointments.tsx +++ b/apps/web/src/portal/sections/Appointments.tsx @@ -118,7 +118,7 @@ export const AppointmentsSection: React.FC = ({ sessio try { const response = await fetch('/api/portal/appointments', { - headers: { Authorization: `Bearer ${sessionId}` }, + headers: { "X-Impersonation-Session-Id": sessionId ?? "" }, }); if (response.ok) { @@ -744,10 +744,10 @@ function BookingFlow({ onClose, sessionId }: BookingFlowProps) { try { const [petsRes, servicesRes] = await Promise.all([ fetch('/api/portal/pets', { - headers: { Authorization: `Bearer ${sessionId}` }, + headers: { "X-Impersonation-Session-Id": sessionId ?? "" }, }), fetch('/api/portal/services', { - headers: { Authorization: `Bearer ${sessionId}` }, + headers: { "X-Impersonation-Session-Id": sessionId ?? "" }, }), ]); diff --git a/apps/web/src/portal/sections/ReportCards.tsx b/apps/web/src/portal/sections/ReportCards.tsx index a8d471b..f8ced2a 100644 --- a/apps/web/src/portal/sections/ReportCards.tsx +++ b/apps/web/src/portal/sections/ReportCards.tsx @@ -1,4 +1,4 @@ -import { useState, useEffect } from "react"; +import { useState, useEffect, useCallback, useRef } from "react"; import { FileText, Share2, Calendar, Smile, Meh, ChevronRight, Loader2 } from "lucide-react"; type MoodKey = "calm" | "cooperative" | "anxious" | "wiggly"; @@ -24,36 +24,44 @@ interface Appointment { reportCardId?: string; } -export function ReportCards() { +interface Props { + sessionId: string | null; +} + +export function ReportCards({ sessionId }: Props) { const [appointments, setAppointments] = useState([]); const [isLoading, setIsLoading] = useState(true); const [error, setError] = useState(null); const [selectedCard, setSelectedCard] = useState(null); - useEffect(() => { - const fetchReportCards = async () => { - try { - const response = await fetch("/api/portal/appointments"); + const fetchReportCardsRef = useRef<() => Promise>(async () => { + setIsLoading(true); + setError(null); + try { + const response = await fetch("/api/portal/appointments", { + headers: { "X-Impersonation-Session-Id": sessionId ?? "" }, + }); - if (response.ok) { - const data = await response.json(); - const allAppointments: Appointment[] = data.appointments || data || []; - const reportCardAppointments = allAppointments.filter( - (appt) => appt.reportCardId - ); - setAppointments(reportCardAppointments); - } else { - setError("Failed to load report cards."); - } - } catch { - setError("Failed to load report cards. Please try again."); - } finally { - setIsLoading(false); + if (response.ok) { + const data = await response.json(); + const allAppointments: Appointment[] = data.appointments || data || []; + const reportCardAppointments = allAppointments.filter( + (appt) => appt.reportCardId + ); + setAppointments(reportCardAppointments); + } else { + setError("Failed to load report cards."); } - }; + } catch { + setError("Failed to load report cards. Please try again."); + } finally { + setIsLoading(false); + } + }); - fetchReportCards(); - }, []); + useEffect(() => { + void fetchReportCardsRef.current(); + }, [sessionId]); if (isLoading) { return ( @@ -69,7 +77,7 @@ export function ReportCards() {

{error}