import { useEffect, useState } from "react"; import type { MedicalAlert, PetProfileSummary } from "@groombook/types"; import { PetPhotoDisplay } from "./PetPhotoDisplay.js"; interface Props { petId: string; } type LoadState = | { status: "idle" } | { status: "loading" } | { status: "loaded"; data: PetProfileSummary } | { status: "error"; message: string }; function computeAge(dateOfBirth: string | null): string | null { if (!dateOfBirth) return null; const birth = new Date(dateOfBirth); const now = new Date(); const totalMonths = (now.getFullYear() - birth.getFullYear()) * 12 + (now.getMonth() - birth.getMonth()); if (totalMonths < 1) return "<1 mo"; if (totalMonths < 12) return `${totalMonths} mo`; const years = Math.floor(totalMonths / 12); const months = totalMonths % 12; if (months === 0) return `${years} yr`; return `${years} yr ${months} mo`; } function TemperamentDots({ score }: { score: number }) { return (
{[1, 2, 3, 4, 5].map((n) => (
))}
); } const SEVERITY_STYLES: Record = { high: { bg: "#fef2f2", color: "#dc2626", border: "#fca5a5" }, medium: { bg: "#fffbeb", color: "#d97706", border: "#fde68a" }, low: { bg: "#eff6ff", color: "#2563eb", border: "#bfdbfe" }, }; function MedicalAlertBadge({ alert }: { alert: MedicalAlert }) { const s = SEVERITY_STYLES[alert.severity]; return ( {alert.description} ); } function SectionLabel({ children }: { children: React.ReactNode }) { return (
{children}
); } export function PetProfileCard({ petId }: Props) { const [state, setState] = useState({ status: "idle" }); useEffect(() => { setState({ status: "loading" }); fetch(`/api/pets/${petId}/profile-summary`) .then(async (res) => { if (!res.ok) throw new Error(`HTTP ${res.status}`); return res.json() as Promise; }) .then((data) => setState({ status: "loaded", data })) .catch((e: unknown) => setState({ status: "error", message: e instanceof Error ? e.message : "Unknown error" })); }, [petId]); if (state.status === "idle" || state.status === "loading") { return (
{[1, 2, 3].map((n) =>
)}
); } if (state.status === "error") { return (
Failed to load profile: {state.message}
); } const { data } = state; const age = computeAge(data.dateOfBirth); return (
{/* Header */}
{data.name}
{data.breed ?? "Unknown breed"} {age && · {age}}
{data.weightKg != null && (
{data.weightKg} kg
)}
{/* Coat type */} {data.coatType && (
COAT TYPE {data.coatType}
)} {/* Temperament */} {(data.temperamentScore != null || data.temperamentFlags.length > 0) && (
TEMPERAMENT
{data.temperamentScore != null && } {data.temperamentFlags.map((flag) => ( {flag} ))}
)} {/* Medical alerts — most prominent */} {data.medicalAlerts.length > 0 && (
MEDICAL ALERTS
{data.medicalAlerts.map((alert) => ( ))}
)} {/* Preferred cuts */} {data.preferredCuts.length > 0 && (
PREFERRED CUTS
{data.preferredCuts.map((cut) => ( {cut} ))}
)} {/* Recent visits */} {data.recentVisits.length > 0 && (
RECENT VISITS {data.recentVisits.slice(0, 3).map((log) => (
{new Date(log.groomedAt).toLocaleDateString()} {log.cutStyle && · {log.cutStyle}} {log.staffId && ·}
))}
)} {/* Next appointment */} {data.nextAppointment && (
NEXT APPOINTMENT
{new Date(data.nextAppointment.startTime).toLocaleDateString()} — {data.nextAppointment.serviceName}
)}
); }