diff --git a/form_cliente.php b/form_cliente.php
index b5ebfae..b9ada7c 100644
--- a/form_cliente.php
+++ b/form_cliente.php
@@ -447,23 +447,25 @@ function renderCampos(esquema, prefilled) {
esquema.forEach(c => {
if (c.tipo === 'separador') {
- html += `
+ html += `
`;
return;
}
+ // Wrapper con data-campo-id para lógica de condiciones
+ const wrapOpen = `
`;
+ const wrapClose = `
`;
+
if (c.tipo === 'firma' || c.tipo === 'firma_profesional') {
- // firma del paciente usa firma_modos global (topbar); profesional usa sus propios modos
const modos = (c.tipo === 'firma' && _firmaModosGlobal)
- ? _firmaModosGlobal
- : (c.modos || ['canvas']);
- html += renderFirmaInline(c.id, modos, c.label, c.required, c.tipo === 'firma_profesional');
+ ? _firmaModosGlobal : (c.modos || ['canvas']);
+ html += wrapOpen + renderFirmaInline(c.id, modos, c.label, c.required, c.tipo === 'firma_profesional') + wrapClose;
return;
}
if (c.tipo === 'parrafo') {
const ws = c.flujoLibre ? 'normal' : 'pre-wrap';
- html += `
${esc(c.contenido||'')}
`;
+ html += wrapOpen + `
${esc(c.contenido||'')}
` + wrapClose;
return;
}
if (c.tipo === 'parrafo_inline') {
@@ -482,7 +484,7 @@ function renderCampos(esquema, prefilled) {
return esc(p);
}).join('');
const inlineHtml = (c.contenido||'').split('\n').map(renderLine).join('
');
- html += `
${inlineHtml}
`;
+ html += wrapOpen + `
${inlineHtml}
` + wrapClose;
return;
}
if (c.tipo === 'lista_marcable') {
@@ -493,14 +495,13 @@ function renderCampos(esquema, prefilled) {
`).join('');
- html += `${lbl}${opts}
`;
+ html += wrapOpen + `${lbl}${opts}
` + wrapClose;
return;
}
const req = c.required ? 'required' : '';
const star = c.required ? '*' : '';
- // Valor pre-llenado (linked usa __paciente.xxx o key directo)
let val = '';
if (c.tipo === 'linked') {
val = pad['__paciente']?.[c.linked_key] || pad[c.linked_key] || '';
@@ -509,7 +510,6 @@ function renderCampos(esquema, prefilled) {
}
const isLinked = c.tipo === 'linked';
- // Linked con valor → solo lectura. Linked sin valor → editable (el paciente puede completarlo)
const isReadOnly = isLinked && !!val;
const linkedNote = isLinked && val
? 'Campo autocompletado'
@@ -518,21 +518,21 @@ function renderCampos(esquema, prefilled) {
const lbl = ``;
if (c.tipo === 'textarea') {
- html += `${lbl}
+ html += wrapOpen + `
${lbl}
${linkedNote}
-
`;
+
` + wrapClose;
} else if (c.tipo === 'select') {
const opts = (c.options||[]).map(o =>
``
).join('');
- html += `${lbl}
+ html += wrapOpen + `
${lbl}
${linkedNote}
-
`;
+
` + wrapClose;
} else if (c.tipo === 'radio') {
const opts = (c.options||[]).map(o => `
@@ -540,7 +540,7 @@ function renderCampos(esquema, prefilled) {
${o===val?'checked':''} ${isReadOnly?'disabled':''}>
`).join('');
- html += `${lbl}${opts}${linkedNote}
`;
+ html += wrapOpen + `${lbl}${opts}${linkedNote}
` + wrapClose;
} else if (c.tipo === 'checkbox') {
const vals = Array.isArray(val) ? val : [];
const opts = (c.options||[]).map(o => `
@@ -549,18 +549,17 @@ function renderCampos(esquema, prefilled) {
${vals.includes(o)?'checked':''} ${isReadOnly?'disabled':''}>
`).join('');
- html += `${lbl}${opts}${linkedNote}
`;
+ html += wrapOpen + `${lbl}${opts}${linkedNote}
` + wrapClose;
} else {
- // texto, numero, fecha, hora, linked
const t = c.tipo === 'numero' ? 'number'
: (c.tipo === 'fecha' || c.linked_key === 'fecha_nacimiento') ? 'date'
: c.tipo === 'hora' ? 'time' : 'text';
- html += `${lbl}
+ html += wrapOpen + `
${lbl}
${linkedNote}
-
`;
+
` + wrapClose;
}
});
@@ -924,6 +923,7 @@ const formCliente = {
const hasInlineFirma = form.esquema_decoded.some(c => c.tipo === 'firma' || c.tipo === 'firma_profesional');
_hasInlineFirma = hasInlineFirma;
renderCampos(form.esquema_decoded, prefilled);
+ iniciarCondiciones(form.esquema_decoded);
// Firma global (solo si NO hay firmas inline y permite_firma está activo)
if (form.permite_firma && !hasInlineFirma) {
@@ -1093,6 +1093,85 @@ const formCliente = {
},
};
+// ══════════════════════════════════════════════════════════════════════
+// LÓGICA DE CONDICIONES — secciones visibles según valor de otro campo
+// ══════════════════════════════════════════════════════════════════════
+function iniciarCondiciones(esquema) {
+ // Agrupar campos en secciones delimitadas por separadores
+ // Una sección = { separadorId, condicion, elementIds[] }
+ const secciones = [];
+ let secActual = null;
+
+ esquema.forEach(c => {
+ if (c.tipo === 'separador') {
+ secActual = { sepId: c.id, condicion: c.condicion || null, elemIds: [] };
+ secciones.push(secActual);
+ } else if (secActual) {
+ secActual.elemIds.push(c.id);
+ }
+ });
+
+ const condicionales = secciones.filter(s => s.condicion);
+ if (!condicionales.length) return;
+
+ // Marcar cada elemento de sección condicional con data-seccion
+ condicionales.forEach(sec => {
+ const sepEl = document.querySelector(`[data-campo-id="${sec.sepId}"]`);
+ if (sepEl) sepEl.dataset.seccionCond = sec.sepId;
+ sec.elemIds.forEach(id => {
+ const el = document.querySelector(`[data-campo-id="${id}"]`);
+ if (el) el.dataset.seccionCond = sec.sepId;
+ });
+ });
+
+ function evaluar() {
+ condicionales.forEach(sec => {
+ const { campo_id, valor } = sec.condicion;
+ // Leer valor del campo controlador
+ const ctrl = document.querySelector(`[data-campo-id="${campo_id}"]`);
+ if (!ctrl) return;
+
+ let activo = false;
+ const checks = ctrl.querySelectorAll('input[type="checkbox"]');
+ if (checks.length) {
+ // checkbox múltiple: activo si alguno con ese valor está checked
+ checks.forEach(cb => { if (cb.value === valor && cb.checked) activo = true; });
+ } else {
+ const radio = ctrl.querySelector(`input[type="radio"]:checked`);
+ const sel = ctrl.querySelector('select');
+ if (radio) activo = radio.value === valor;
+ else if (sel) activo = sel.value === valor;
+ }
+
+ // Mostrar u ocultar toda la sección
+ document.querySelectorAll(`[data-seccion-cond="${sec.sepId}"]`).forEach(el => {
+ el.style.transition = 'opacity .2s, max-height .3s';
+ if (activo) {
+ el.style.maxHeight = '2000px';
+ el.style.opacity = '1';
+ el.style.overflow = '';
+ el.style.pointerEvents = '';
+ } else {
+ el.style.maxHeight = '0';
+ el.style.opacity = '0';
+ el.style.overflow = 'hidden';
+ el.style.pointerEvents = 'none';
+ }
+ });
+ });
+ }
+
+ // Escuchar cambios en los campos controladores
+ const camposCtrl = [...new Set(condicionales.map(s => s.condicion.campo_id))];
+ camposCtrl.forEach(cid => {
+ const el = document.querySelector(`[data-campo-id="${cid}"]`);
+ if (el) el.addEventListener('change', evaluar);
+ });
+
+ // Evaluar estado inicial
+ evaluar();
+}
+
// ══════════════════════════════════════════════════════════════════════
// INIT
// ══════════════════════════════════════════════════════════════════════
diff --git a/lab_formulario_builder.php b/lab_formulario_builder.php
index 2994372..2a017fd 100644
--- a/lab_formulario_builder.php
+++ b/lab_formulario_builder.php
@@ -707,7 +707,10 @@ function renderPreview() {
function renderCampoPreview(c) {
if (c.tipo === 'separador') {
- return `
${esc(c.label)}
`;
+ const condBadge = c.condicion
+ ? ` si: ${esc(c.condicion.valor)}`
+ : '';
+ return `
${esc(c.label)}${condBadge}
`;
}
const req = c.required ? ' *' : '';
const lbl = ``;
@@ -779,7 +782,35 @@ function abrirEditorCampo(idx) {
`;
if (c.tipo === 'separador') {
- // solo label
+ // Campos con opciones que pueden controlar visibilidad
+ const camposOpciones = _campos.filter(f =>
+ ['checkbox','radio','select'].includes(f.tipo) && f.id !== c.id && (f.options||[]).length
+ );
+ const condCampoId = c.condicion?.campo_id || '';
+ const condValor = c.condicion?.valor || '';
+ const campoCtrl = camposOpciones.find(f => f.id === condCampoId);
+ const optsCtrl = campoCtrl ? (campoCtrl.options||[]) : [];
+
+ const optsCampos = camposOpciones.map(f =>
+ ``
+ ).join('');
+ const optsValores = optsCtrl.map(o =>
+ ``
+ ).join('');
+
+ html += `
+
+
+
Esta sección (y los campos hasta el siguiente separador) se muestra solo si se cumple la condición.
+
+
+
`;
} else if (c.tipo === 'linked') {
html += `
Campo vinculado al paciente. Se llenará automáticamente al enviar.
@@ -879,6 +910,17 @@ function abrirEditorCampo(idx) {
setTimeout(() => document.getElementById('ce-label')?.select(), 200);
}
+function actualizarOpcionesCondicion(campoId) {
+ const selValor = document.getElementById('ce-cond-valor');
+ if (!selValor) return;
+ if (!campoId) { selValor.innerHTML = ''; selValor.disabled = true; return; }
+ const campo = _campos.find(f => f.id === campoId);
+ const opts = campo?.options || [];
+ selValor.innerHTML = '' +
+ opts.map(o => ``).join('');
+ selValor.disabled = false;
+}
+
function _insertarKey(key) {
const ta = document.getElementById('ce-contenido');
if (!ta) return;
@@ -921,6 +963,17 @@ function aplicarCampo() {
if (mc.length) c.modos = mc;
}
+ // Condición de visibilidad (solo separadores)
+ const condCampo = document.getElementById('ce-cond-campo');
+ const condValor = document.getElementById('ce-cond-valor');
+ if (condCampo !== null) {
+ if (condCampo.value && condValor.value) {
+ c.condicion = { campo_id: condCampo.value, valor: condValor.value };
+ } else {
+ delete c.condicion;
+ }
+ }
+
_bsCampo.hide();
renderCanvas();
renderPreview();