EventBuzz_Website/src/pages/EventTrackPage.tsx

231 lines
8.4 KiB
TypeScript

import { useEffect, useState } from "react";
import { Calendar, ChevronDown, Clock, User } from "lucide-react";
import { Link, useSearchParams } from "react-router-dom";
import type { AgendaRowData } from "../components/AgendaRow";
import { AgendaRow } from "../components/AgendaRow";
import { EventBanner } from "../components/EventBanner";
import { HeaderMobile } from "../components/HeaderMobile";
import { HyperNavigationFAB } from "../components/HyperNavigationFAB";
import { MobileShell } from "../components/MobileShell";
import { getVisitorLoginPreview, getVisitorTrackPreview, type VisitorTrackPreview } from "../lib/api";
import { formatEventScheduleRange } from "../lib/sessionDisplay";
import { readStoredVisitorContext } from "../types/visitorContext";
import type { LoginPreviewResponse } from "../types/visitorContext";
const agendaRows: AgendaRowData[] = [
{
time: "06:00 PM",
duration: "45 MINS",
trackTitle: "Asthma",
sessionType: "Symposium",
description:
"Role of GINA Guidelines for Inning and Monitoring Asthma Treatment",
speakers: [
{ name: "Dr. Mansi Gupta", role: "Speaker" },
{ name: "Dr. Soumya Mitra", role: "Speaker" },
{ name: "Dr. Kumar Utsav Samaria", role: "Speaker" },
],
},
{
time: "07:00 PM",
duration: "45 MINS",
trackTitle: "Asthma",
sessionType: "Panel Discussion",
description:
"Role of GINA Guidelines for Iniang and Monitoring Asthma Treatment",
speakers: [
{ name: "Dr. Mansi Gupta", role: "Speaker" },
{ name: "Dr. Soumya Mitra", role: "Speaker" },
{ name: "Dr. Kumar Utsav Samaria", role: "Speaker" },
{ name: "Dr. Agam Vora", role: "Moderator" },
],
},
{
time: "08:00 PM",
duration: "45 MINS",
trackTitle: "Interventional Pulmonology",
sessionType: "Keynote Address",
description:
"Role of GINA Guidelines for Iniang and Monitoring Asthma Treatment",
speakers: [
{ name: "Dr. Mansi Gupta", role: "Speaker" },
{ name: "Dr. Soumya Mitra", role: "Speaker" },
{ name: "Dr. Kumar Utsav Samaria", role: "Speaker" },
],
},
];
export function EventTrackPage() {
const [searchParams] = useSearchParams();
const eventId = searchParams.get("event_id")?.trim() || "";
const trackId = searchParams.get("track_id")?.trim() || "";
const ctx = readStoredVisitorContext();
const [track, setTrack] = useState<VisitorTrackPreview | null>(null);
const [eventInfo, setEventInfo] = useState<LoginPreviewResponse | null>(null);
const [loading, setLoading] = useState(true);
const [err, setErr] = useState<string | null>(null);
useEffect(() => {
if (!eventId || !trackId) {
setErr("Missing track link. Open the track from your event QR code or invite.");
setLoading(false);
setTrack(null);
setEventInfo(null);
return;
}
let cancelled = false;
setLoading(true);
setErr(null);
(async () => {
try {
const tr = await getVisitorTrackPreview(trackId, eventId);
if (cancelled) return;
setTrack(tr);
if (ctx?.eventId === eventId && ctx.event) {
setEventInfo(ctx.event);
} else {
const ev = await getVisitorLoginPreview(eventId);
if (cancelled) return;
setEventInfo(ev);
}
} catch (e) {
if (!cancelled) {
setErr(e instanceof Error ? e.message : "Could not load track.");
setTrack(null);
setEventInfo(null);
}
} finally {
if (!cancelled) setLoading(false);
}
})();
return () => {
cancelled = true;
};
}, [eventId, trackId, ctx?.eventId]);
if (!eventId || !trackId) {
return (
<MobileShell className="relative">
<HeaderMobile eventId={eventId || null} />
<main className="flex flex-1 flex-col items-center justify-center bg-[#f2f2f7] px-4 py-10">
<p className="text-center text-sm text-[#24282f]">{err}</p>
<Link to="/" className="mt-4 text-sm font-semibold text-[#1e72ef] underline">
Home
</Link>
</main>
</MobileShell>
);
}
if (loading) {
return (
<MobileShell className="relative">
<HeaderMobile eventId={eventId || null} />
<main className="flex flex-1 items-center justify-center bg-[#f2f2f7] px-4 py-10">
<p className="text-sm text-[#24282f]">Loading track…</p>
</main>
</MobileShell>
);
}
if (err || !track) {
return (
<MobileShell className="relative">
<HeaderMobile eventId={eventId || null} />
<main className="flex flex-1 flex-col items-center justify-center bg-[#f2f2f7] px-4 py-10">
<p className="text-center text-sm text-red-600">{err ?? "Track not found."}</p>
</main>
</MobileShell>
);
}
const ev = eventInfo;
const eventName = ev?.name ?? "Event";
const schedule =
ev?.startDate && ev?.endDate
? formatEventScheduleRange(
typeof ev.startDate === "string" ? ev.startDate : String(ev.startDate),
typeof ev.endDate === "string" ? ev.endDate : String(ev.endDate),
)
: "";
const trackTypeLabel = track.trackType ? String(track.trackType).replace(/_/g, " ") : "Track";
const headerLogo =
ev?.logoUrl?.trim() ||
(ctx?.eventId === eventId && ctx.event?.logoUrl?.trim() ? ctx.event.logoUrl.trim() : undefined);
return (
<MobileShell className="relative">
<HeaderMobile logoUrl={headerLogo} eventId={eventId || null} />
<EventBanner />
<main className="relative z-0 flex flex-1 flex-col bg-[#f2f2f7] px-4 pb-28 pt-6">
<div className="mx-auto w-full max-w-[370px]">
<div className="mb-6 flex flex-col gap-4">
<div className="inline-flex w-fit rounded bg-[#e5e5ea] px-2 py-0.5">
<span className="text-[10px] tracking-wide text-[#24282f]">{eventName.toUpperCase()}</span>
</div>
<h1 className="text-lg font-semibold tracking-wide text-[#24282f]">{track.name}</h1>
{track.hall?.trim() ? (
<p className="text-xs text-[rgba(36,40,47,0.7)]">{track.hall.trim()}</p>
) : null}
<p className="text-[10px] uppercase tracking-wide text-[#8e8e93]">{trackTypeLabel}</p>
<div className="flex flex-col gap-1 text-xs text-[#24282f]">
<div className="flex items-center gap-2">
<Calendar className="size-4 shrink-0" strokeWidth={1.75} />
<span className="font-inter tracking-wide">{schedule || "—"}</span>
</div>
<div className="flex items-center gap-2">
<Clock className="size-4 shrink-0" strokeWidth={1.75} />
<span className="font-inter tracking-wide">See sessions below</span>
</div>
<div className="flex items-center gap-2">
<User className="size-4 shrink-0" strokeWidth={1.75} />
<span className="font-inter tracking-wide">Track agenda (sample)</span>
</div>
</div>
</div>
<div className="mb-4 flex w-full max-w-[370px] flex-col gap-2">
<h2 className="font-inter text-base font-semibold text-[#24282f]">Sessions</h2>
<button
type="button"
className="flex h-[43px] w-full items-center justify-between rounded-lg border border-[rgba(116,112,112,0.3)] bg-white px-4 py-3"
>
<div className="flex items-center gap-2">
<Calendar className="size-4 shrink-0 text-[#24282f]" strokeWidth={1.75} />
<span className="font-inter text-sm text-[#24282f]">{schedule || "Schedule"}</span>
</div>
<ChevronDown className="size-4 shrink-0 text-[#1d66ef]" strokeWidth={2} />
</button>
</div>
<div className="relative rounded-md bg-white p-4 shadow-[0_1px_4px_rgba(12,12,13,0.05)]">
<div className="flex flex-col gap-6">
{agendaRows.map((row) => (
<AgendaRow key={row.time + row.sessionType} row={row} />
))}
</div>
</div>
<p className="mt-6 text-center text-xs text-[#24282f]/60">
<Link
className="text-[#1e72ef] underline"
to={`/questions?event_id=${encodeURIComponent(eventId)}`}
>
Ask &amp; session questions
</Link>
</p>
</div>
</main>
<HyperNavigationFAB eventId={eventId} />
</MobileShell>
);
}