"use client"; import { useEffect, useState, type ReactNode } from "react"; import { AlertTriangle, LinkIcon } from "lucide-react"; import { fetchSharedCheckIn, type SharedCheckIn, } from "@/lib/api/checkin-links"; import { divingFiguresFromWire } from "@/lib/checkin"; import { CheckInPageFrame } from "@/components/checkin/checkin-page-frame"; import { Button } from "@/components/ui/button"; import { PageSpinner } from "@/components/ui/page-spinner"; type Loaded = | { status: "loading" } | { status: "live"; summary: SharedCheckIn } | { status: "gone" } | { status: "failed" }; // The page a check-in link opens, for whoever holds it - a desk's phone, with no // account. It never asks who is signed in: the token is the whole credential, and the // frame it draws is the diver's own sheet with every control that edits taken off. export function SharedCheckInPage({ token }: { token: string }) { const [loaded, setLoaded] = useState({ status: "loading" }); const [attempt, setAttempt] = useState(0); // A promise chain for `react-hooks/set-state-in-effect`, as the other loaders here. useEffect(() => { const controller = new AbortController(); fetchSharedCheckIn(token, controller.signal) .then((summary) => { if (controller.signal.aborted) return; setLoaded(summary ? { status: "live", summary } : { status: "gone" }); }) .catch((error: unknown) => { if (controller.signal.aborted) return; console.error("Failed to load the shared check-in page:", error); setLoaded({ status: "failed" }); }); return () => controller.abort(); }, [token, attempt]); if (loaded.status === "loading") return ; // Expired, revoked and never-existed read the same, because the API answers them // the same: which one it was would tell a stranger something about the diver. if (loaded.status === "gone") { return ( }>

This check-in link is no longer valid

It has expired or was revoked. Ask the diver for a new one.

); } if (loaded.status === "failed") { return ( } >

This check-in page didn’t load

); } const { summary } = loaded; // The link prints names where the diver's own page holds uuids: an // anonymous page can resolve nothing. const contactNames: Record = {}; const instructorNames: Record = {}; for (const card of summary.certifications) { if (card.contact_name) contactNames[card.uuid] = card.contact_name; if (card.instructor_name) instructorNames[card.uuid] = card.instructor_name; } return ( ); } function Notice({ icon, children }: { icon: ReactNode; children: ReactNode }) { return (
{icon} {children}
); }