Files
motor-de-encuestas/components/question-renderer.tsx
markosbenitez 3e7109a0f1 feat(wizard): ciudad-select filtra por departamento desde geo json [4F]
- CiudadSelectField: autocomplete sobre Object.keys(geoData[depto]),
  mismo patrón que BarrioAutocompleteField (position:fixed, getBoundingClientRect,
  scroll listener, teclado ARIA). Reutiliza clases CSS .barrio-autocomplete-*.
- Limpia el valor de 2.2 via useEffect cuando cambia departamento (2.1),
  usando un ref estable para onChange para evitar dep cíclica.
- QuestionRenderer: branch code==='2.2' dentro del bloque single_choice;
  pasa answers['2.1:default'] como prop departamento.
- Sin cambios al seed, form-engine ni RPCs.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-26 20:07:16 -03:00

130 lines
3.2 KiB
TypeScript

'use client'
import type { AnswerValue, FormAnswers, Question, QuestionOption } from '@/lib/form-engine.types'
import { RadioGroup } from './widgets/radio-group'
import { CheckboxGroup } from './widgets/checkbox-group'
import { TextAreaField } from './widgets/text-area-field'
import { TextShortField } from './widgets/text-short-field'
import { BarrioAutocompleteField } from './widgets/barrio-autocomplete-field'
import { CiudadSelectField } from './widgets/ciudad-select-field'
interface QuestionRendererProps {
question: Question
value: AnswerValue
onChange: (value: AnswerValue) => void
error?: string
answers?: FormAnswers
}
const YES_NO_OPTIONS: QuestionOption[] = [
{ value: 'true', label: 'Sí' },
{ value: 'false', label: 'No' },
]
export function QuestionRenderer({ question, value, onChange, error, answers }: QuestionRendererProps) {
const { code, type, prompt, required, max_select, options } = question
const legend = (
<>
<span className="q-code">{code} ·</span> {prompt}
</>
)
if (type === 'single_choice' && options) {
if (code === '2.2') {
return (
<CiudadSelectField
name={code}
label={legend}
value={(value as string) ?? ''}
required={required}
departamento={(answers?.['2.1:default'] as string) ?? null}
onChange={onChange}
error={error}
/>
)
}
return (
<RadioGroup
name={code}
legend={legend}
options={options}
value={(value as string) ?? undefined}
required={required}
onChange={onChange}
error={error}
/>
)
}
if (type === 'multiple_choice' && options) {
return (
<CheckboxGroup
name={code}
legend={legend}
options={options}
value={(value as string[]) ?? []}
maxSelect={max_select}
required={required}
onChange={onChange}
error={error}
/>
)
}
// yes_no resuelto con el RadioGroup existente — dos opciones fijas, sin widget nuevo.
if (type === 'yes_no') {
return (
<RadioGroup
name={code}
legend={legend}
options={YES_NO_OPTIONS}
value={(value as string) ?? undefined}
required={required}
onChange={onChange}
error={error}
/>
)
}
if (type === 'text_short') {
if (code === '2.3') {
return (
<BarrioAutocompleteField
name={code}
label={legend}
value={(value as string) ?? ''}
required={required}
departamento={(answers?.['2.1:default'] as string) ?? null}
ciudad={(answers?.['2.2:default'] as string) ?? null}
onChange={onChange}
error={error}
/>
)
}
return (
<TextShortField
name={code}
label={legend}
value={(value as string) ?? ''}
required={required}
onChange={onChange}
error={error}
/>
)
}
if (type === 'text_long') {
return (
<TextAreaField
name={code}
label={legend}
value={(value as string) ?? ''}
required={required}
onChange={onChange}
error={error}
/>
)
}
return null
}