216 lines
7.8 KiB
TypeScript
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'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>
|
|
);
|
|
}
|