Files
formularios_front/src/containers/create-formulario.tsx
T

365 lines
11 KiB
TypeScript

import React from 'react';
import {
FormularioCreacion,
TipoPregunta,
TiposValidacion,
} from '@/types/create-formulario';
import SimpleInput from '@/components/input';
import Select from '@/components/select';
import Button from '@/components/button';
interface Props {
formulario: FormularioCreacion;
onChange: (formularioActualizado: FormularioCreacion) => void;
}
const tipoPreguntaOpciones: TipoPregunta[] = [
'Abierta (Respuesta corta)',
'Abierta (Parrafo)',
'Cerrada',
'Multiple',
];
const tiposValidacion: TiposValidacion[] = [
'correo',
'correo_institucional',
'telefono',
'nombre',
'apellidos',
'genero',
'entero',
'decimal',
'comunidad_alumno',
'cuenta_alumno',
'comunidad_trabajador',
'cuenta_trabajador',
'rfc'
];
type CampoPreguntaSimple =
| 'titulo'
| 'tipo'
| 'validacion'
| 'obligatoria'
| 'limite';
type CampoPreguntaOpciones = 'opciones';
const tiposCuestionario = [
{ label: 'Registro', value: 1 },
{ label: 'Evaluación', value: 2 },
];
export default function FormularioEditor({ formulario, onChange }: Props) {
function actualizarCampo<K extends keyof FormularioCreacion>(
campo: K,
valor: FormularioCreacion[K]
): void {
const actualizado = { ...formulario, [campo]: valor };
onChange(actualizado);
}
function handleUpdatePregunta(
seccionIdx: number,
preguntaIdx: number,
campo: CampoPreguntaSimple | CampoPreguntaOpciones,
valor:
| string
| boolean
| TipoPregunta
| TiposValidacion
| number
| { valor: string }[]
| undefined
) {
const actualizado = { ...formulario };
const pregunta = actualizado.secciones[seccionIdx].preguntas[preguntaIdx];
if (campo === 'opciones') {
pregunta.opciones = valor as { valor: string }[];
} else if (campo === 'titulo' && typeof valor === 'string') {
pregunta.titulo = valor;
} else if (campo === 'tipo' && typeof valor === 'string') {
pregunta.tipo = valor as TipoPregunta;
} else if (campo === 'validacion' || valor === undefined) {
pregunta.validacion = valor as TiposValidacion | undefined;
} else if (campo === 'obligatoria' && typeof valor === 'boolean') {
pregunta.obligatoria = valor;
} else if (campo === 'limite' && typeof valor === 'number') {
pregunta.limite = valor;
}
onChange(actualizado);
}
const handleUpdateSeccion = (
index: number,
campo: 'titulo' | 'descripcion',
valor: string
) => {
const actualizado = { ...formulario };
actualizado.secciones = actualizado.secciones.map((seccion, idx) =>
idx === index ? { ...seccion, [campo]: valor } : seccion
);
onChange(actualizado);
};
const agregarSeccion = () => {
const nuevaSeccion = {
titulo: '',
descripcion: '',
preguntas: [],
};
onChange({
...formulario,
secciones: [...formulario.secciones, nuevaSeccion],
});
};
const eliminarSeccion = (index: number) => {
const nuevas = [...formulario.secciones];
nuevas.splice(index, 1);
onChange({ ...formulario, secciones: nuevas });
};
const agregarPregunta = (seccionIdx: number) => {
const nuevaPregunta = {
titulo: '',
tipo: 'Abierta (Respuesta corta)' as TipoPregunta,
obligatoria: false,
opciones: [],
};
const actualizado = { ...formulario };
actualizado.secciones[seccionIdx].preguntas.push(nuevaPregunta);
onChange(actualizado);
};
const eliminarPregunta = (seccionIdx: number, preguntaIdx: number) => {
const actualizado = { ...formulario };
actualizado.secciones[seccionIdx].preguntas.splice(preguntaIdx, 1);
onChange(actualizado);
};
return (
<div className="border p-3 rounded my-4">
<h2 className="mb-3">Editar Formulario</h2>
<SimpleInput
label="Nombre del Formulario"
value={formulario.nombre_form}
onChange={(e) => actualizarCampo('nombre_form', e.target.value)}
/>
<SimpleInput
label="Descripción del Formulario"
value={formulario.descripcion}
onChange={(e) => actualizarCampo('descripcion', e.target.value)}
/>
<div className="row">
<div className="col-md-6">
<SimpleInput
label="Fecha de Inicio"
type="date"
value={formulario.fecha_inicio.slice(0, 16)}
onChange={(e) => actualizarCampo('fecha_inicio', e.target.value)}
/>
</div>
<div className="col-md-6">
<SimpleInput
label="Fecha de Fin"
type="date"
value={formulario.fecha_fin.slice(0, 16)}
onChange={(e) => actualizarCampo('fecha_fin', e.target.value)}
/>
</div>
</div>
<Select
label="Tipo de Cuestionario"
value={formulario.id_tipo_cuestionario}
onChange={(e) =>
actualizarCampo('id_tipo_cuestionario', Number(e.target.value))
}
options={tiposCuestionario}
placeholder="Selecciona un tipo"
/>
<hr className="my-4" />
{/* Secciones */}
{formulario.secciones.map((seccion, seccionIdx) => (
<div key={seccionIdx} className="mb-4 p-3 border rounded bg-light">
<div className="d-flex justify-content-between align-items-center mb-2">
<h4 className="mb-0">Sección {seccionIdx + 1}</h4>
<Button
variant="danger"
icon="trash"
size="sm"
outline
onClick={() => eliminarSeccion(seccionIdx)}
>
Eliminar sección
</Button>
</div>
<SimpleInput
label="Título de la sección"
value={seccion.titulo}
onChange={(e) =>
handleUpdateSeccion(seccionIdx, 'titulo', e.target.value)
}
/>
<SimpleInput
label="Descripción de la sección"
value={seccion.descripcion}
onChange={(e) =>
handleUpdateSeccion(seccionIdx, 'descripcion', e.target.value)
}
/>
{/* Preguntas */}
{seccion.preguntas.map((pregunta, preguntaIdx) => (
<div key={preguntaIdx} className="mb-3 p-3 border rounded bg-white">
<div className="d-flex justify-content-between align-items-center mb-2">
<h6 className="mb-0">Pregunta {preguntaIdx + 1}</h6>
<Button
variant="danger"
icon="trash"
size="sm"
outline
onClick={() => eliminarPregunta(seccionIdx, preguntaIdx)}
>
Eliminar
</Button>
</div>
<SimpleInput
label="Título de la pregunta"
value={pregunta.titulo}
onChange={(e) =>
handleUpdatePregunta(
seccionIdx,
preguntaIdx,
'titulo',
e.target.value
)
}
/>
<div className="row">
<div className="col-md-6">
<Select
label="Tipo de pregunta"
value={pregunta.tipo}
onChange={(e) =>
handleUpdatePregunta(
seccionIdx,
preguntaIdx,
'tipo',
e.target.value
)
}
options={tipoPreguntaOpciones.map((tipo) => ({
label: tipo,
value: tipo,
}))}
/>
</div>
<div className="col-md-6">
<Select
label="Validación"
value={pregunta.validacion || ''}
onChange={(e) =>
handleUpdatePregunta(
seccionIdx,
preguntaIdx,
'validacion',
e.target.value || undefined
)
}
options={[
{ label: 'Ninguna', value: '' },
...tiposValidacion.map((v) => ({
label: v,
value: v,
})),
]}
/>
</div>
</div>
<div className="form-check mb-2">
<input
className="form-check-input"
type="checkbox"
checked={pregunta.obligatoria}
id={`obligatoria-${seccionIdx}-${preguntaIdx}`}
onChange={(e) =>
handleUpdatePregunta(
seccionIdx,
preguntaIdx,
'obligatoria',
e.target.checked
)
}
/>
<label
className="form-check-label"
htmlFor={`obligatoria-${seccionIdx}-${preguntaIdx}`}
>
¿Es obligatoria?
</label>
</div>
{(pregunta.tipo === 'Cerrada' ||
pregunta.tipo === 'Multiple') && (
<div className="mb-2">
<label className="form-label">Opciones</label>
{(pregunta.opciones || []).map((op, opIdx) => (
<SimpleInput
key={opIdx}
value={op.valor}
onChange={(e) => {
const nuevasOpciones = [...(pregunta.opciones || [])];
nuevasOpciones[opIdx].valor = e.target.value;
handleUpdatePregunta(
seccionIdx,
preguntaIdx,
'opciones',
nuevasOpciones
);
}}
placeholder={`Opción ${opIdx + 1}`}
/>
))}
</div>
)}
</div>
))}
<Button
outline
className="mt-2"
size="sm"
icon="plus"
onClick={() => agregarPregunta(seccionIdx)}
>
Agregar pregunta
</Button>
</div>
))}
<div className="text-center mt-4">
<Button
className="btn btn-success"
onClick={agregarSeccion}
icon="plus"
>
Agregar sección
</Button>
</div>
</div>
);
}