129 lines
4.3 KiB
TypeScript
129 lines
4.3 KiB
TypeScript
import { useCallback, useEffect, useState, type FormEvent } from "react";
|
|
import { createPortal } from "react-dom";
|
|
import { X } from "lucide-react";
|
|
import { postVisitorSessionQuestion } from "../lib/api";
|
|
import { getAttendeeToken } from "../lib/attendeeAuth";
|
|
import { notifyRewardFromApiData } from "../lib/rewardCelebration";
|
|
|
|
type AskQuestionModalProps = {
|
|
open: boolean;
|
|
onClose: () => void;
|
|
eventId: string;
|
|
sessionId: string;
|
|
idSuffix?: string;
|
|
};
|
|
|
|
export function AskQuestionModal({
|
|
open,
|
|
onClose,
|
|
eventId,
|
|
sessionId,
|
|
idSuffix = "",
|
|
}: AskQuestionModalProps) {
|
|
const [draft, setDraft] = useState("");
|
|
const [submitting, setSubmitting] = useState(false);
|
|
const [submitError, setSubmitError] = useState<string | null>(null);
|
|
const inputId = `session-question-input${idSuffix}`;
|
|
|
|
const close = useCallback(() => {
|
|
onClose();
|
|
setDraft("");
|
|
setSubmitError(null);
|
|
}, [onClose]);
|
|
|
|
useEffect(() => {
|
|
if (!open) return;
|
|
const onKey = (e: KeyboardEvent) => {
|
|
if (e.key === "Escape") close();
|
|
};
|
|
document.addEventListener("keydown", onKey);
|
|
return () => document.removeEventListener("keydown", onKey);
|
|
}, [open, close]);
|
|
|
|
const handleSubmit = async (e: FormEvent) => {
|
|
e.preventDefault();
|
|
const text = draft.trim();
|
|
if (!text) return;
|
|
const eid = eventId.trim();
|
|
const sid = sessionId.trim();
|
|
if (!eid || !sid) return;
|
|
|
|
setSubmitting(true);
|
|
setSubmitError(null);
|
|
try {
|
|
const token = getAttendeeToken();
|
|
const out = await postVisitorSessionQuestion(sid, eid, {
|
|
question: text,
|
|
isAnonymous: !token,
|
|
});
|
|
notifyRewardFromApiData(out);
|
|
close();
|
|
} catch (err) {
|
|
setSubmitError(err instanceof Error ? err.message : "Could not submit question.");
|
|
} finally {
|
|
setSubmitting(false);
|
|
}
|
|
};
|
|
|
|
if (!open) return null;
|
|
|
|
return createPortal(
|
|
<div className="fixed inset-0 z-[200] flex items-end justify-center sm:items-center" role="presentation">
|
|
<button
|
|
type="button"
|
|
className="absolute inset-0 bg-black/45"
|
|
aria-label="Close dialog"
|
|
onClick={close}
|
|
/>
|
|
<div
|
|
role="dialog"
|
|
aria-modal="true"
|
|
aria-labelledby={`ask-question-title${idSuffix}`}
|
|
className="relative z-10 w-full max-w-[min(100%,402px)] rounded-t-2xl bg-white px-5 pb-8 pt-5 shadow-[0_-8px_32px_rgba(0,0,0,0.12)] sm:mx-4 sm:rounded-2xl sm:shadow-xl"
|
|
>
|
|
<div className="mb-4 flex items-start justify-between gap-3">
|
|
<div className="min-w-0">
|
|
<h3 id={`ask-question-title${idSuffix}`} className="text-lg font-semibold text-[#24282f]">
|
|
Ask a Question
|
|
</h3>
|
|
<p className="mt-1 text-sm leading-snug text-[rgba(36,40,47,0.65)]">
|
|
Submit your question to the session moderator.
|
|
</p>
|
|
</div>
|
|
<button
|
|
type="button"
|
|
className="shrink-0 rounded-full p-1.5 text-[#24282f] hover:bg-[#f2f2f7]"
|
|
aria-label="Close"
|
|
onClick={close}
|
|
>
|
|
<X className="size-5" strokeWidth={2} />
|
|
</button>
|
|
</div>
|
|
<form onSubmit={handleSubmit} className="flex flex-col gap-4">
|
|
<label className="sr-only" htmlFor={inputId}>
|
|
Your question
|
|
</label>
|
|
<textarea
|
|
id={inputId}
|
|
value={draft}
|
|
onChange={(e) => setDraft(e.target.value)}
|
|
placeholder="Type your question here..."
|
|
rows={5}
|
|
disabled={submitting}
|
|
className="min-h-[120px] w-full resize-y rounded-xl border border-[#c7c7cc] bg-[#f2f2f7]/40 px-3 py-3 text-sm text-[#24282f] placeholder:text-[rgba(36,40,47,0.45)] focus:border-[#ef8d1d] focus:outline-none focus:ring-2 focus:ring-[#ef8d1d]/30 disabled:opacity-60"
|
|
/>
|
|
{submitError ? <p className="text-sm text-red-600">{submitError}</p> : null}
|
|
<button
|
|
type="submit"
|
|
disabled={submitting}
|
|
className="w-full rounded-xl bg-[#ef8d1d] py-3 text-sm font-semibold text-white shadow-[0_4px_4px_rgba(0,0,0,0.12)] transition hover:bg-[#e0800c] active:scale-[0.99] disabled:opacity-60"
|
|
>
|
|
{submitting ? "Submitting…" : "Submit Question"}
|
|
</button>
|
|
</form>
|
|
</div>
|
|
</div>,
|
|
document.body,
|
|
);
|
|
}
|