"use client";
import Link from "next/link";
import { Download } from "lucide-react";
import { coursesAPI, Course } from "@/lib/api/courses";
import { contactsAPI } from "@/lib/api/contacts";
import { peopleAPI } from "@/lib/api/people";
import { useRecordByUuid } from "@/hooks/useRecordByUuid";
import { useWithReturnTo } from "@/hooks/useReturnTo";
import {
certificationFilePath,
certificationsAPI,
certificationAgencyLabel,
certificationFile,
Certification,
CertificationSide,
CERTIFICATION_SIDES,
CERTIFICATION_SIDE_LABELS,
} from "@/lib/api/certifications";
import {
certificationExpiryBadgeVariant,
certificationExpiryLabel,
certificationExpiryStatus,
} from "@/lib/certification";
import { formatDateOnly } from "@/lib/date-time";
import { getApiErrorMessage } from "@/lib/api/error";
import { saveApiDownload } from "@/lib/download";
import {
Dialog,
DialogContent,
DialogHeader,
DialogTitle,
} from "@/components/ui/dialog";
import { Badge } from "@/components/ui/badge";
import { Button } from "@/components/ui/button";
import { ButtonSpinner } from "@/components/ui/button-spinner";
import { useToast } from "@/components/ui/use-toast";
import { CertificationCardImage } from "./certification-card-image";
import { useEffect, useState } from "react";
interface CertificationViewDialogProps {
certification: Certification | null;
onOpenChange: (open: boolean) => void;
}
function DetailRow({
label,
value,
}: {
label: string;
value: string | null | undefined;
}) {
if (!value) return null;
return (
{label}
{value}
);
}
// The course this card came out of, as a link to it.
//
// The certification carries the course's uuid and nothing else, so the name has
// to be fetched - and the row only appears once it has arrived. A failed lookup
// is non-fatal and leaves the row out, exactly as the dive page's trip link does:
// the rest of the dialog is what the diver opened it for.
function CourseRow({ courseUuid }: { courseUuid: string }) {
const [course, setCourse] = useState(null);
const withReturnTo = useWithReturnTo();
useEffect(() => {
let cancelled = false;
coursesAPI
.getCourse(courseUuid)
.then((data) => {
if (!cancelled) setCourse(data);
})
.catch((error) => console.error("Failed to fetch course:", error));
return () => {
cancelled = true;
};
}, [courseUuid]);
if (!course) return null;
return (
Course
{course.name}
);
}
// Who ran the course the card came out of, on `CourseRow`'s terms: the
// certification carries the contact's uuid and nothing else, so the row appears
// once the name has arrived, and a failed lookup leaves it out.
function ContactRow({ contactUuid }: { contactUuid: string }) {
const contact = useRecordByUuid(contactsAPI.getContact, contactUuid);
if (!contact) return null;
return (
Dive center
{contact.name}
);
}
// Who signed the card, on the same terms: a person, by uuid, named once the
// record has arrived and linked to their page.
function InstructorRow({ personUuid }: { personUuid: string }) {
const person = useRecordByUuid(peopleAPI.getPerson, personUuid);
const withReturnTo = useWithReturnTo();
if (!person) return null;
return (