"use client"; // Danışmanın soru formu (DESIGN.md §4.20, §4.33). `soru_sor` aracı // (lib/ai/araclar.ts) liste revizyonu isteği belirsizken 1-3 kısa soruyu bu // formla sorar; sorular tek tek gösterilir (shadcn Questionnaire, // components/ui/questionnaire.tsx). Gönderilince cevaplar hem adayın balonuna // okunur metin olarak (lib/ai/sohbet-meta.ts anketCevabiMetni) hem sunucuya // yapısal olarak gider; bu mesaj kredi harcamaz. Form yalnız sohbetin SON // danışman mesajındayken açıktır: cevaplandıysa ya da aday başka bir şey // yazdıysa kapanır ve tek satırlık durumla kalır (sayfa yenilenince kayıttan // aynı hâlde döner). // // Sorular arası geçiş sihirbaz adımlarıyla birebir aynı yoldan (lib/adim-gecisi.ts): // ileri giderken yeni soru sağdan gelir, eskisi sola çıkar; geri dönerken // tersi; boy ölçülen yüksekliğe yayla uzar. İlkel pasif soruyu `hidden` // yapar; burada `hidden={false}` ile görünürlük bize geçer (mergeProps'ta // kullanıcı prop'u kazanır — @shadcn/react chunk-HBS6WEDP.js `b`), pasif // sorular mutlak konumda saydam durur ve ilkelin verdiği `inert`i korur; // DOM'da kaldıkları için gönderimde FormData önceki cevapları da toplar. import { useId, useState } from "react"; import { Check, MessageSquareText } from "lucide-react"; import { motion, useReducedMotion } from "motion/react"; import useMeasure from "react-use-measure"; import type { Anket, AnketCevaplari } from "@/lib/ai/sohbet-meta"; import { adimGecisi, adimGecisiVaryantlari } from "@/lib/adim-gecisi"; import { Questionnaire, QuestionnaireActions, QuestionnaireChoice, QuestionnaireChoiceDescription, QuestionnaireChoices, QuestionnaireDescription, QuestionnaireError, QuestionnaireInput, QuestionnaireItem, QuestionnaireNext, QuestionnairePrevious, QuestionnaireProgress, QuestionnaireSkip, QuestionnaireSubmit, QuestionnaireTitle, } from "@/components/ui/questionnaire"; export type AnketDurumu = "acik" | "gonderiliyor" | "cevaplandi" | "kapandi"; export function SohbetAnketi({ anket, durum, onGonder, }: { anket: Anket; durum: AnketDurumu; onGonder: (cevaplar: AnketCevaplari) => void; }) { const kimlik = useId(); // Aktif soru bizde (kontrollü ilkel): geçiş yönü sorunun sırasından çıkar. const [aktif, setAktif] = useState(anket.sorular[0]?.ad); const [gecisBasladi, setGecisBasladi] = useState(false); // Yalnız aktif sorunun fieldset'i ölçülür; ref soru değişince ona geçer. const [olcumRef, olculer] = useMeasure(); const hareketiAzalt = !!useReducedMotion(); if (durum === "cevaplandi" || durum === "kapandi") { return (

{durum === "cevaplandi" ? ( ) : ( )} {durum === "cevaplandi" ? "Formu cevapladın." : "Form kapandı; isteğini yazarak söyleyebilirsin."}

); } const items = anket.sorular.map((s) => ({ name: s.ad, required: s.zorunlu !== false, choices: s.secenekler.map((o) => ({ value: o.deger })), })); const aktifIndeks = anket.sorular.findIndex((s) => s.ad === aktif); const yukseklik = gecisBasladi && olculer.height > 0 ? olculer.height : "auto"; const varyantlar = adimGecisiVaryantlari({ hareketiAzalt, yukseklik }); const gecis = adimGecisi(hareketiAzalt); function gonder(e: React.FormEvent) { e.preventDefault(); if (durum === "gonderiliyor") return; const veri = new FormData(e.currentTarget); const cevaplar: AnketCevaplari = {}; for (const s of anket.sorular) { const hepsi = veri .getAll(s.ad) .map((v) => String(v).trim()) .filter(Boolean); if (hepsi.length === 0) continue; cevaplar[s.ad] = s.coklu ? [...new Set(hepsi)] : hepsi[hepsi.length - 1]; } if (Object.keys(cevaplar).length === 0) return; onGonder(cevaplar); } return ( // Sade kutu (§4.4); balonun altında, aynı sütunda durur { setGecisBasladi(true); setAktif(ad); }} items={items} shortcuts="letters" onSubmit={gonder} >

{anket.baslik ?? "Danışmanın sorusu"}

(
{state.current} / {state.total}
)} />
{/* Kayan sorular taşmasın diye kırpılır; 4 px'lik negatif kenar + fieldset'teki p-1 odak halkasına (ring-3) yer bırakır. */}
{anket.sorular.map((s, i) => { const buAktif = s.ad === aktif; return ( ); })}
{durum === "gonderiliyor" ? "Gönderiliyor…" : "Cevapları gönder"}
); }