"use client"; import { useEffect, useState, useCallback, useRef } from "react"; import { Button, Input } from "@/components/ui/Primitives"; import { getSurveyClient } from "@/lib/survey-client"; import { toast } from "@/lib/toast"; import type { ActiveSurvey, Question, QuestionAnswer } from "@bytelyst/survey-client"; export function SurveyBanner() { const [survey, setSurvey] = useState(null); const [currentIdx, setCurrentIdx] = useState(0); const [answers, setAnswers] = useState>({}); const [started, setStarted] = useState(false); const pollRef = useRef | null>(null); const fetchSurvey = useCallback(async () => { try { const { survey: active } = await getSurveyClient().getActiveSurvey(); if (active) setSurvey((prev) => prev ?? active); } catch { // non-critical } }, []); useEffect(() => { fetchSurvey(); pollRef.current = setInterval(fetchSurvey, 10 * 60_000); return () => { if (pollRef.current) clearInterval(pollRef.current); }; }, [fetchSurvey]); const dismiss = useCallback(async () => { if (!survey) return; try { await getSurveyClient().dismissSurvey(survey.id); } catch { /* best-effort */ } setSurvey(null); }, [survey]); async function handleStart() { if (!survey) return; try { await getSurveyClient().startSurvey(survey.id); } catch { /* best-effort */ } setStarted(true); } function buildAnswer(question: Question, value: string): QuestionAnswer { switch (question.type) { case "rating": case "nps": case "scale": return { type: "rating", value: Number(value) }; case "single_choice": case "dropdown": return { type: "single_choice", optionId: value }; case "multiple_choice": return { type: "multiple_choice", optionIds: [value] }; case "ranking": return { type: "ranking", rankedOptionIds: [value] }; default: return { type: "text", value }; } } async function handleAnswer(question: Question, value: string) { const answer = buildAnswer(question, value); setAnswers((prev) => ({ ...prev, [question.id]: answer })); try { await getSurveyClient().submitAnswer(survey!.id, question.id, answer); } catch { /* best-effort */ } } async function handleNext() { if (!survey) return; if (currentIdx < survey.questions.length - 1) { setCurrentIdx((i) => i + 1); } else { try { await getSurveyClient().completeSurvey(survey.id); } catch { /* best-effort */ } toast.success("Thanks for your feedback!"); setSurvey(null); } } if (!survey) return null; if (!started) { return (
{survey.title} — Quick survey ({survey.questions.length} questions)
); } const question = survey.questions[currentIdx]; if (!question) return null; const isTextType = question.type === "text_short" || question.type === "text_long"; const isRatingType = question.type === "rating" || question.type === "nps" || question.type === "scale"; const isChoiceType = question.type === "single_choice" || question.type === "multiple_choice" || question.type === "dropdown"; const currentAnswer = answers[question.id]; const hasAnswer = !!currentAnswer; function getTextValue(): string { return currentAnswer?.type === "text" ? currentAnswer.value : ""; } function getRatingValue(): number | null { return currentAnswer?.type === "rating" || currentAnswer?.type === "nps" ? currentAnswer.value : null; } function getChoiceValue(): string | null { if (currentAnswer?.type === "single_choice") return currentAnswer.optionId; return null; } return (
{question.text} {currentIdx + 1}/{survey.questions.length}
{isTextType && ( handleAnswer(question, e.target.value)} aria-label="Survey answer" /> )} {isRatingType && (
{Array.from({ length: (question.maxValue ?? 5) - (question.minValue ?? 1) + 1 }, (_, i) => (question.minValue ?? 1) + i).map((n) => ( ))}
)} {isChoiceType && question.options && (
{question.options.map((opt) => ( ))}
)}
); }