Files
motor-de-encuestas/components/question-renderer.tsx

121 lines
3.1 KiB
TypeScript

'use client'
import type { FormAnswers, Question, QuestionOption, RatingOptions } from '@/lib/types'
import { RadioGroup } from './widgets/radio-group'
import { CheckboxGroup } from './widgets/checkbox-group'
import { RatingScale } from './widgets/rating-scale'
import { TextAreaField } from './widgets/text-area-field'
import { TextShortField } from './widgets/text-short-field'
import { GeoSelectWidget } from './widgets/geo-select-widget'
interface QuestionRendererProps {
question: Question
answers: FormAnswers
onChange: (code: string, value: FormAnswers[string]) => void
errors: Record<string, string>
}
function isArrayOptions(opts: unknown): opts is QuestionOption[] {
return Array.isArray(opts)
}
function isRatingOptions(opts: unknown): opts is RatingOptions {
return typeof opts === 'object' && opts !== null && 'min' in opts && 'max' in opts
}
export function QuestionRenderer({
question,
answers,
onChange,
errors,
}: QuestionRendererProps) {
const { code, type, prompt, required, max_select, options } = question
const error = errors[code]
// A1 (Departamento) renders a combined geo widget that also handles A2 (Ciudad)
if (code === 'A1') {
return (
<GeoSelectWidget
departmentValue={(answers['A1'] as string) ?? ''}
cityValue={(answers['A2'] as string) ?? ''}
onDepartmentChange={(v) => onChange('A1', v)}
onCityChange={(v) => onChange('A2', v)}
departmentError={errors['A1']}
cityError={errors['A2']}
/>
)
}
// A2 is rendered inside the GeoSelectWidget above — skip here to avoid duplication
if (code === 'A2') return null
if (type === 'single_choice' && isArrayOptions(options)) {
return (
<RadioGroup
name={code}
legend={prompt}
options={options}
value={answers[code] as string | undefined}
required={required}
onChange={(v) => onChange(code, v)}
error={error}
/>
)
}
if (type === 'multiple_choice' && isArrayOptions(options)) {
return (
<CheckboxGroup
name={code}
legend={prompt}
options={options}
value={(answers[code] as string[]) ?? []}
maxSelect={max_select}
required={required}
onChange={(v) => onChange(code, v)}
error={error}
/>
)
}
if (type === 'rating' && isRatingOptions(options)) {
return (
<RatingScale
name={code}
legend={prompt}
ratingOptions={options}
value={answers[code] as number | undefined}
required={required}
onChange={(v) => onChange(code, v)}
error={error}
/>
)
}
if (type === 'text_short') {
return (
<TextShortField
name={code}
label={prompt}
value={(answers[code] as string) ?? ''}
required={required}
onChange={(v) => onChange(code, v)}
error={error}
/>
)
}
if (type === 'text_long') {
return (
<TextAreaField
name={code}
label={prompt}
value={(answers[code] as string) ?? ''}
required={required}
onChange={(v) => onChange(code, v)}
error={error}
/>
)
}
return null
}