EventBuzz_Website/src/components/AskQuestionModal.tsx

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,
);
}