EventBuzz_Website/src/pages/RatingsPage.tsx

216 lines
7.8 KiB
TypeScript

import { useEffect, useState } from "react";
import { Star } from "lucide-react";
import { Link, useSearchParams } from "react-router-dom";
import { HeaderMobile } from "../components/HeaderMobile";
import { LegalFooterLinks } from "../components/marketing/LegalFooterLinks";
import { MobileShell } from "../components/MobileShell";
import { DynamicFeedbackForm } from "../components/DynamicFeedbackForm";
import { useEventRegistrationSettings } from "../hooks/useEventRegistrationSettings";
import { getVisitorFeedbackFormSchema, getVisitorLoginPreview, type VisitorPublicFeedbackForm } from "../lib/api";
import { readStoredVisitorContext } from "../types/visitorContext";
function StarRow({
label,
value,
onChange,
}: {
label: string;
value: number;
onChange: (n: number) => void;
}) {
return (
<div className="flex w-full flex-col gap-3">
<p className="font-inter text-base font-semibold text-[#24282f]">{label}</p>
<div className="flex gap-4">
{Array.from({ length: 5 }, (_, i) => (
<button
key={i}
type="button"
onClick={() => onChange(i + 1)}
className="text-[#ef8d1d]"
aria-label={`${i + 1} stars`}
>
<Star
className={`size-6 ${i < value ? "fill-[#ef8d1d]" : "fill-transparent"}`}
strokeWidth={1.75}
/>
</button>
))}
</div>
</div>
);
}
export function RatingsPage() {
const [searchParams] = useSearchParams();
const formId = searchParams.get("form_id")?.trim() || "";
const urlEventId = searchParams.get("event_id")?.trim() || "";
const ctx = readStoredVisitorContext();
const eventId = urlEventId || ctx?.eventId || "";
const [eventSnapshot, setEventSnapshot] = useState(
ctx?.eventId === eventId && ctx.event ? ctx.event : null,
);
const [schema, setSchema] = useState<VisitorPublicFeedbackForm | null>(null);
const [schemaLoading, setSchemaLoading] = useState(false);
const [schemaErr, setSchemaErr] = useState<string | null>(null);
const [a, setA] = useState(0);
const [b, setB] = useState(0);
const [c, setC] = useState(0);
useEffect(() => {
if (!eventId) {
setEventSnapshot(null);
return;
}
if (ctx?.eventId === eventId && ctx.event) {
setEventSnapshot(ctx.event);
return;
}
let cancelled = false;
getVisitorLoginPreview(eventId)
.then((ev) => {
if (!cancelled) setEventSnapshot(ev);
})
.catch(() => {
if (!cancelled) setEventSnapshot(null);
});
return () => {
cancelled = true;
};
}, [eventId, ctx?.eventId, ctx?.event]);
useEffect(() => {
if (!formId || !eventId) {
setSchema(null);
setSchemaErr(null);
setSchemaLoading(false);
return;
}
let cancelled = false;
setSchemaLoading(true);
setSchemaErr(null);
getVisitorFeedbackFormSchema(eventId, formId)
.then((s) => {
if (!cancelled) setSchema(s);
})
.catch((e) => {
if (!cancelled) {
setSchema(null);
setSchemaErr(e instanceof Error ? e.message : "Could not load form.");
}
})
.finally(() => {
if (!cancelled) setSchemaLoading(false);
});
return () => {
cancelled = true;
};
}, [formId, eventId]);
const reg = useEventRegistrationSettings(eventSnapshot);
if (!eventId) {
return (
<MobileShell className="relative">
<HeaderMobile logoUrl={ctx?.event?.logoUrl} eventId={null} />
<main className="flex flex-1 flex-col bg-[#f2f2f7] px-4 pb-10 pt-6">
<div className="mx-auto w-full max-w-[370px]">
<h1 className="font-inter text-xl font-semibold text-[#24282f]">Ratings</h1>
<p className="mt-3 text-sm text-[rgba(36,40,47,0.67)]">Open ratings from your event or session link.</p>
<Link to="/" className="mt-4 inline-block text-sm font-semibold text-[#1e72ef] underline">
Home
</Link>
</div>
</main>
</MobileShell>
);
}
if (!reg.enableRatings) {
return (
<MobileShell className="relative">
<HeaderMobile logoUrl={eventSnapshot?.logoUrl} eventId={eventId || null} />
<main className="flex flex-1 flex-col bg-[#f2f2f7] px-4 pb-10 pt-6">
<div className="mx-auto w-full max-w-[370px]">
<h1 className="font-inter text-xl font-semibold text-[#24282f]">Ratings</h1>
<p className="mt-3 text-sm text-[rgba(36,40,47,0.67)]">Ratings are disabled for this event.</p>
<Link to="/event" className="mt-4 inline-block text-sm font-semibold text-[#1e72ef] underline">
Back to event
</Link>
</div>
</main>
</MobileShell>
);
}
if (formId) {
return (
<MobileShell className="relative">
<HeaderMobile logoUrl={eventSnapshot?.logoUrl} eventId={eventId || null} />
<main className="flex flex-1 flex-col bg-[#f2f2f7] px-4 pb-10 pt-6">
<div className="mx-auto w-full max-w-[370px]">
<h1 className="font-inter text-xl font-semibold text-[#24282f]">{schema?.name || "Ratings"}</h1>
{schemaLoading ? (
<p className="mt-4 text-sm text-[rgba(36,40,47,0.67)]">Loading form…</p>
) : schemaErr ? (
<div className="mt-4">
<p className="text-sm text-red-600">{schemaErr}</p>
<Link to="/event" className="mt-4 inline-block text-sm font-semibold text-[#1e72ef] underline">
Back to event
</Link>
</div>
) : schema ? (
<div className="mt-6">
<DynamicFeedbackForm eventId={eventId} schema={schema} variant="light" />
</div>
) : null}
</div>
</main>
</MobileShell>
);
}
return (
<MobileShell className="relative">
<HeaderMobile logoUrl={eventSnapshot?.logoUrl} eventId={eventId || null} />
<div className="eb-gradient-auth flex flex-1 flex-col pb-10 pt-4">
<h1 className="text-center font-inter text-xl font-semibold text-white">Event Ratings</h1>
<div className="mx-auto mt-6 w-full max-w-[354px] flex-1 px-4">
<div className="rounded-xl bg-white p-6 shadow-[0_8px_12px_6px_rgba(0,0,0,0.15),0_4px_4px_rgba(0,0,0,0.3)]">
<h2 className="text-center font-inter text-base font-semibold text-[#24282f]">Your Rating</h2>
<p className="mt-3 text-center text-base leading-[22px] text-[rgba(36,40,47,0.67)]">
We&apos;d love to hear your thoughts about this event. Your rating helps us improve future
events.
</p>
<div className="mt-6 flex flex-col gap-6">
<StarRow label="How will you rate the speakers?" value={a} onChange={setA} />
<StarRow label="How will you rate the quality of content?" value={b} onChange={setB} />
<StarRow
label="Probability to recommend to your friends and colleagues"
value={c}
onChange={setC}
/>
</div>
<button
type="button"
className="mt-6 w-full rounded-full border border-[rgba(214,200,200,0.6)] bg-[#ef8d1d] py-3 text-xs font-semibold uppercase tracking-wide text-white shadow-[0_4px_4px_rgba(0,0,0,0.15)]"
>
Submit Ratings
</button>
<div className="mt-4 text-center">
<Link to="/event" className="font-inter font-semibold text-[#1e72ef] underline">
Cancel
</Link>
</div>
</div>
</div>
<footer className="mx-auto mt-6 text-sm font-light">
<LegalFooterLinks variant="light" />
</footer>
</div>
</MobileShell>
);
}