Con los números de preguntas y secciones

This commit is contained in:
markosbenitez
2026-06-08 20:25:23 -03:00
parent 307a54dabc
commit 52295a78d0
12 changed files with 367 additions and 33 deletions

View File

@@ -32,6 +32,7 @@ export function QuestionRenderer({
const error = errors[code]
// A1 (Departamento) renders a combined geo widget that also handles A2 (Ciudad)
// Code badges for A1/A2 are rendered inside GeoSelectWidget.
if (code === 'A1') {
return (
<GeoSelectWidget
@@ -47,11 +48,13 @@ export function QuestionRenderer({
// A2 is rendered inside the GeoSelectWidget above — skip here to avoid duplication
if (code === 'A2') return null
const legend = <><span className="q-code">{code} ·</span> {prompt}</>
if (type === 'single_choice' && isArrayOptions(options)) {
return (
<RadioGroup
name={code}
legend={prompt}
legend={legend}
options={options}
value={answers[code] as string | undefined}
required={required}
@@ -65,7 +68,7 @@ export function QuestionRenderer({
return (
<CheckboxGroup
name={code}
legend={prompt}
legend={legend}
options={options}
value={(answers[code] as string[]) ?? []}
maxSelect={max_select}
@@ -80,7 +83,7 @@ export function QuestionRenderer({
return (
<RatingScale
name={code}
legend={prompt}
legend={legend}
ratingOptions={options}
value={answers[code] as number | undefined}
required={required}
@@ -94,7 +97,7 @@ export function QuestionRenderer({
return (
<TextShortField
name={code}
label={prompt}
label={legend}
value={(answers[code] as string) ?? ''}
required={required}
onChange={(v) => onChange(code, v)}
@@ -107,7 +110,7 @@ export function QuestionRenderer({
return (
<TextAreaField
name={code}
label={prompt}
label={legend}
value={(answers[code] as string) ?? ''}
required={required}
onChange={(v) => onChange(code, v)}

View File

@@ -9,9 +9,9 @@ import { ProgressBar } from './progress-bar'
import { QuestionRenderer } from './question-renderer'
// Section metadata (code prefix → display title)
const SECTIONS: { prefix: string; title: string; sensitive?: boolean }[] = [
const SECTIONS: { prefix: string; title: string }[] = [
{ prefix: 'A', title: 'Datos Generales' },
{ prefix: '1', title: 'Prevalencia: cuidado y discapacidad', sensitive: true },
{ prefix: '1', title: 'Prevalencia: cuidado y discapacidad' },
{ prefix: '2', title: 'Intensidad y rol del cuidado' },
{ prefix: '3', title: 'Impacto económico del cuidado' },
{ prefix: '4', title: 'Tiempo y descanso' },
@@ -190,13 +190,13 @@ export function SurveyForm({ survey, questions }: SurveyFormProps) {
return v !== null && v !== undefined && v !== '' && !(Array.isArray(v) && v.length === 0)
}).length
type Section = { prefix: string; title: string; sensitive?: boolean; questions: Question[] }
type Section = { prefix: string; title: string; questions: Question[] }
const sectionMap = new Map<string, Section>()
for (const q of visibleQuestions) {
const prefix = getSectionPrefix(q.code)
if (!sectionMap.has(prefix)) {
const meta = SECTIONS.find((s) => s.prefix === prefix)
sectionMap.set(prefix, { prefix, title: meta?.title ?? prefix, sensitive: meta?.sensitive, questions: [] })
sectionMap.set(prefix, { prefix, title: meta?.title ?? prefix, questions: [] })
}
sectionMap.get(prefix)!.questions.push(q)
}
@@ -242,13 +242,7 @@ export function SurveyForm({ survey, questions }: SurveyFormProps) {
aria-labelledby={`section-${section.prefix}`}
>
<div className="section-header">
<h2 id={`section-${section.prefix}`} className="section-title">{section.title}</h2>
{section.sensitive && (
<p className="section-sensitive-note" role="note">
Esta sección contiene preguntas sensibles sobre salud o discapacidad.
Responder es completamente voluntario.
</p>
)}
<h2 id={`section-${section.prefix}`} className="section-title">Sección {section.prefix} · {section.title}</h2>
</div>
{section.questions.map((q) => (
<div key={q.id} className="question-wrapper" data-code={q.code}>

View File

@@ -1,9 +1,10 @@
'use client'
import type { ReactNode } from 'react'
import type { QuestionOption } from '@/lib/types'
interface CheckboxGroupProps {
name: string
legend: string
legend: ReactNode
options: QuestionOption[]
value: string[]
maxSelect: number | null

View File

@@ -31,7 +31,7 @@ export function GeoSelectWidget({
{/* Departamento */}
<div className="geo-select-group">
<label className="question-legend" htmlFor="A1">
Departamento
<span className="q-code">A1 ·</span> Departamento
</label>
<select
id="A1"
@@ -58,7 +58,7 @@ export function GeoSelectWidget({
className={`question-legend${cityDisabled ? ' geo-label-disabled' : ''}`}
htmlFor="A2"
>
Ciudad
<span className="q-code">A2 ·</span> Ciudad
{cityDisabled && (
<span className="geo-disabled-hint"> (seleccioná un departamento primero)</span>
)}

View File

@@ -1,9 +1,10 @@
'use client'
import type { ReactNode } from 'react'
import type { QuestionOption } from '@/lib/types'
interface RadioGroupProps {
name: string
legend: string
legend: ReactNode
options: QuestionOption[]
value: string | undefined
required: boolean

View File

@@ -1,9 +1,10 @@
'use client'
import type { ReactNode } from 'react'
import type { RatingOptions } from '@/lib/types'
interface RatingScaleProps {
name: string
legend: string
legend: ReactNode
ratingOptions: RatingOptions
value: number | undefined
required: boolean

View File

@@ -1,8 +1,9 @@
'use client'
import type { ReactNode } from 'react'
interface TextAreaFieldProps {
name: string
label: string
label: ReactNode
value: string
required: boolean
onChange: (value: string) => void

View File

@@ -1,8 +1,9 @@
'use client'
import type { ReactNode } from 'react'
interface TextShortFieldProps {
name: string
label: string
label: ReactNode
value: string
required: boolean
placeholder?: string