11import { useMemo , useState } from 'react'
2- import { Question , PaperPlaneTilt , ArrowLeft , ArrowRight , Check } from '@phosphor-icons/react'
2+ import { Question , ArrowLeft , ArrowRight , Check } from '@phosphor-icons/react'
33import { useI18n } from '@/lib/i18n'
44import { cn } from '@/lib/utils'
55import { Button } from '@/components/ui/button'
@@ -66,18 +66,37 @@ export function AskUserCard({
6666 if ( multi ) {
6767 setSelected ( selected . includes ( o ) ? selected . filter ( ( x ) => x !== o ) : [ ...selected , o ] )
6868 } else {
69- // Single-select: pick, and for a one-question card that's the whole answer.
7069 setSelected ( [ o ] )
7170 if ( questions . length === 1 ) submit ( { 0 : [ o ] } )
7271 }
7372 }
7473
75- const addCustom = ( ) => {
74+ // Commit the free-text input into the current question's answers.
75+ // Returns the updated answers record.
76+ const commitCustom = ( base : typeof answers ) : typeof answers => {
7677 const v = custom . trim ( )
77- if ( ! v ) return
78- setSelected ( multi ? [ ...selected . filter ( ( x ) => x !== v ) , v ] : [ v ] )
78+ if ( ! v ) return base
79+ const cur = base [ idx ] ?? [ ]
80+ if ( multi ) {
81+ return { ...base , [ idx ] : [ ...cur . filter ( ( x ) => x !== v ) , v ] }
82+ }
83+ return { ...base , [ idx ] : [ v ] }
84+ }
85+
86+ // Next button: attach text first, then advance to the next question.
87+ const handleNext = ( ) => {
88+ const updated = commitCustom ( answers )
89+ setAnswers ( updated )
90+ setCustom ( '' )
91+ setIdx ( ( i ) => Math . min ( questions . length - 1 , i + 1 ) )
92+ }
93+
94+ // Submit button: attach text first, then submit all answers.
95+ const handleSubmit = ( ) => {
96+ const updated = commitCustom ( answers )
97+ setAnswers ( updated )
7998 setCustom ( '' )
80- if ( ! multi && questions . length === 1 ) submit ( { 0 : [ v ] } )
99+ submit ( updated )
81100 }
82101
83102 const answeredCount = questions . filter ( ( _ , i ) => ( answers [ i ] ?. length ?? 0 ) > 0 ) . length
@@ -111,6 +130,8 @@ export function AskUserCard({
111130 )
112131 }
113132
133+ const hasOptions = ( q . options ?? [ ] ) . length > 0
134+
114135 return (
115136 < div className = "fade-up rounded-[var(--radius-md)] border border-primary/40 bg-primary/[0.06] p-3.5" >
116137 < div className = "flex items-start gap-2.5" >
@@ -134,7 +155,7 @@ export function AskUserCard({
134155 < p className = "mt-1.5 whitespace-pre-wrap break-words text-sm font-medium" > { q . question } </ p >
135156
136157 < div className = "mt-3 space-y-2" >
137- { ( q . options ?? [ ] ) . length > 0 ? (
158+ { hasOptions ? (
138159 < div className = "flex flex-wrap gap-2" >
139160 { q . options ! . map ( ( o ) => {
140161 const on = selected . includes ( o )
@@ -155,32 +176,25 @@ export function AskUserCard({
155176 </ div >
156177 ) : null }
157178
158- { /* Free-text answer — always available (this is the "Other"). */ }
159- < div className = "flex items-center gap-2" >
160- < Input
161- value = { custom }
162- onChange = { ( e ) => setCustom ( e . target . value ) }
163- onKeyDown = { ( e ) => {
164- if ( e . key === 'Enter' && ! e . shiftKey ) {
165- e . preventDefault ( )
166- addCustom ( )
179+ { /* Free-text input — no send button; Next/Submit commits it. */ }
180+ < Input
181+ value = { custom }
182+ onChange = { ( e ) => setCustom ( e . target . value ) }
183+ onKeyDown = { ( e ) => {
184+ if ( e . key === 'Enter' && ! e . shiftKey ) {
185+ e . preventDefault ( )
186+ if ( isLast ) {
187+ handleSubmit ( )
188+ } else {
189+ handleNext ( )
167190 }
168- } }
169- placeholder = {
170- ( q . options ?? [ ] ) . length > 0 ? t ( 'ask.otherPlaceholder' ) : t ( 'ask.customPlaceholder' )
171191 }
172- disabled = { disabled }
173- />
174- < Button
175- size = "icon-sm"
176- variant = "outline"
177- disabled = { disabled || ! custom . trim ( ) }
178- onClick = { addCustom }
179- aria-label = { t ( 'ask.add' ) }
180- >
181- < PaperPlaneTilt className = "size-4" />
182- </ Button >
183- </ div >
192+ } }
193+ placeholder = {
194+ hasOptions ? t ( 'ask.otherPlaceholder' ) : t ( 'ask.customPlaceholder' )
195+ }
196+ disabled = { disabled }
197+ />
184198
185199 { multi && selected . length > 0 ? (
186200 < p className = "text-[11px] text-muted-foreground" >
@@ -189,9 +203,8 @@ export function AskUserCard({
189203 ) : null }
190204 </ div >
191205
192- { /* Navigation. A single question with options submits on click, so the
193- nav row only matters for multi-question or free-text cases. */ }
194- { questions . length > 1 || multi || ( q . options ?? [ ] ) . length === 0 ? (
206+ { /* Navigation. Next auto-attaches the typed text. */ }
207+ { questions . length > 1 || multi || ! hasOptions ? (
195208 < div className = "mt-3 flex items-center justify-between gap-2" >
196209 < Button
197210 size = "sm"
@@ -206,8 +219,8 @@ export function AskUserCard({
206219 { isLast ? (
207220 < Button
208221 size = "sm"
209- disabled = { disabled || answeredCount === 0 }
210- onClick = { ( ) => submit ( ) }
222+ disabled = { disabled || ( answeredCount === 0 && ! custom . trim ( ) ) }
223+ onClick = { handleSubmit }
211224 className = "gap-1.5"
212225 >
213226 < Check className = "size-4" />
@@ -218,7 +231,7 @@ export function AskUserCard({
218231 size = "sm"
219232 variant = "secondary"
220233 disabled = { disabled }
221- onClick = { ( ) => setIdx ( ( i ) => Math . min ( questions . length - 1 , i + 1 ) ) }
234+ onClick = { handleNext }
222235 className = "gap-1.5"
223236 >
224237 { t ( 'ask.next' ) }
0 commit comments