121 lines
3.1 KiB
TypeScript
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
|
|
}
|