From aa993df2bf7c5124b510b4b79e370068534b4c81 Mon Sep 17 00:00:00 2001 From: naielv Date: Mon, 23 Feb 2026 00:23:11 +0100 Subject: [PATCH] =?UTF-8?q?A=C3=B1adir=20funcionalidad=20de=20selecci?= =?UTF-8?q?=C3=B3n=20de=20pictogramas=20en=20el=20formulario=20del=20comed?= =?UTF-8?q?or=20y=20refactorizar=20campos=20de=20entrada?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- assets/static/euskaditech-css/simple.css | 15 + src/app_logic.js | 2 +- src/app_modules.js | 394 +++++++++++++++++++++++ src/page/comedor.js | 90 +++++- 4 files changed, 491 insertions(+), 10 deletions(-) diff --git a/assets/static/euskaditech-css/simple.css b/assets/static/euskaditech-css/simple.css index c4ebdaa..39495ad 100644 --- a/assets/static/euskaditech-css/simple.css +++ b/assets/static/euskaditech-css/simple.css @@ -428,4 +428,19 @@ fieldset legend { } pre { font-size: 15px; +} +.picto { + height: 125px; + width: 100px; + border: 2.5px solid black; + border-radius: 5px; + text-align: center; + background: white; + margin-bottom: 20px; + margin-left: auto; + margin-right: auto; +} +.picto b { + padding-top: 40px; + display: inline-block; } \ No newline at end of file diff --git a/src/app_logic.js b/src/app_logic.js index 1a63322..9fefc42 100644 --- a/src/app_logic.js +++ b/src/app_logic.js @@ -35,7 +35,7 @@ function open_page(params) { PAGES[app].index(); return; } - PAGES[app].edit(path[1]); + PAGES[app].edit(path.slice(1).join(',')); } function setUrlHash(hash) { diff --git a/src/app_modules.js b/src/app_modules.js index 6c3076f..14d6bf1 100644 --- a/src/app_modules.js +++ b/src/app_modules.js @@ -55,6 +55,368 @@ const debounce = (id, callback, wait, args) => { return id; }; +function TS_CreateSearchOverlay(parentEl, options = {}) { + const overlayId = safeuuid(); + const panelId = safeuuid(); + const inputId = safeuuid(); + const closeId = safeuuid(); + const clearId = safeuuid(); + + const overlay = document.createElement('div'); + overlay.id = overlayId; + overlay.style.display = 'none'; + overlay.style.position = 'fixed'; + overlay.style.inset = '0'; + overlay.style.background = 'rgba(0, 0, 0, 0.5)'; + overlay.style.zIndex = '9999'; + overlay.style.alignItems = 'center'; + overlay.style.justifyContent = 'center'; + overlay.style.padding = '16px'; + overlay.innerHTML = html` +
+
+ + + +
+
+ `; + + const targetParent = parentEl || document.body; + targetParent.appendChild(overlay); + + const inputEl = overlay.querySelector('#' + inputId); + const closeEl = overlay.querySelector('#' + closeId); + const clearEl = overlay.querySelector('#' + clearId); + const panelEl = overlay.querySelector('#' + panelId); + + function open() { + overlay.style.display = 'flex'; + inputEl.focus(); + inputEl.select(); + } + + function close() { + overlay.style.display = 'none'; + } + + function setValue(value) { + inputEl.value = value || ''; + } + + function getValue() { + return inputEl.value || ''; + } + + inputEl.addEventListener('input', () => { + if (typeof options.onInput === 'function') { + options.onInput(getValue()); + } + }); + + closeEl.addEventListener('click', close); + clearEl.addEventListener('click', () => { + setValue(''); + if (typeof options.onInput === 'function') { + options.onInput(''); + } + inputEl.focus(); + }); + + overlay.addEventListener('click', (event) => { + if (event.target === overlay && panelEl) { + close(); + } + }); + + return { + open, + close, + setValue, + getValue, + inputEl, + overlayEl: overlay, + }; +} + +function TS_InitOverlaySearch(container, openBtnId, badgeId, options = {}) { + const debounceId = options.debounceId || safeuuid(); + const wait = options.wait || 200; + let currentValue = ''; + const badgeEl = badgeId ? document.getElementById(badgeId) : null; + const overlay = TS_CreateSearchOverlay(container, { + placeholder: options.placeholder || 'Buscar...', + onInput: (value) => { + currentValue = (value || '').toLowerCase().trim(); + if (badgeEl) { + badgeEl.textContent = currentValue ? `Filtro: "${currentValue}"` : ''; + } + if (typeof options.onSearch === 'function') { + debounce(debounceId, options.onSearch, wait, [currentValue]); + } + }, + }); + if (openBtnId) { + const openBtn = document.getElementById(openBtnId); + if (openBtn) { + openBtn.addEventListener('click', () => overlay.open()); + } + } + return { + open: overlay.open, + close: overlay.close, + setValue: overlay.setValue, + getValue: () => currentValue, + getValueRaw: overlay.getValue, + }; +} + +function TS_normalizePictoValue(value) { + if (!value) { + return { text: '', arasaacId: '' }; + } + if (typeof value === 'string') { + return { text: value, arasaacId: '' }; + } + if (typeof value === 'object') { + return { + text: value.text || value.nombre || value.name || '', + arasaacId: value.arasaacId || value.id || '', + }; + } + return { text: String(value), arasaacId: '' }; +} + +function TS_buildArasaacPictogramUrl(id) { + return `https://static.arasaac.org/pictograms/${id}/${id}_300.png`; +} + +function TS_renderPictoPreview(previewEl, value) { + const target = typeof previewEl === 'string' ? document.getElementById(previewEl) : previewEl; + if (!target) return; + target.innerHTML = ''; + if (!value.text && !value.arasaacId) { + const placeholder = document.createElement('b'); + placeholder.textContent = 'Seleccionar Pictograma'; + target.appendChild(placeholder); + } + + if (value.arasaacId) { + const img = document.createElement('img'); + img.src = TS_buildArasaacPictogramUrl(value.arasaacId); + img.alt = value.text || 'Pictograma'; + img.width = 100; + img.height = 100; + img.loading = 'lazy'; + img.style.objectFit = 'contain'; + target.appendChild(img); + } + if (value.text) { + const text = document.createElement('span'); + text.textContent = value.text; + target.appendChild(text); + } +} + +function TS_applyPictoValue(pictoEl, value) { + if (typeof pictoEl === 'string') { + pictoEl = document.getElementById(pictoEl); + } + const plate = TS_normalizePictoValue(value); + pictoEl.dataset.PictoValue = JSON.stringify(plate); + TS_renderPictoPreview(pictoEl, plate); +} + +function TS_getPictoValue(pictoEl) { + if (typeof pictoEl === 'string') { + pictoEl = document.getElementById(pictoEl); + } + if (!pictoEl) return { text: '', arasaacId: '' }; + const plate = pictoEl.dataset.PictoValue ? JSON.parse(pictoEl.dataset.PictoValue) : { text: '', arasaacId: '' }; + return TS_normalizePictoValue(plate); +} + +function TS_CreateArasaacSelector(options) { + let panelEl = options.panelEl; + let searchEl = options.searchEl; + let resultsEl = options.resultsEl; + let statusEl = options.statusEl; + let closeEl = options.closeEl; + const debounceId = options.debounceId || safeuuid(); + const onPick = typeof options.onPick === 'function' ? options.onPick : () => {}; + let activeContext = null; + let overlayEl = null; + + if (options.modal === true && !panelEl) { + const overlayId = safeuuid(); + const panelId = safeuuid(); + const searchId = safeuuid(); + const closeId = safeuuid(); + const statusId = safeuuid(); + const resultsId = safeuuid(); + + overlayEl = document.createElement('div'); + overlayEl.id = overlayId; + overlayEl.style.display = 'none'; + overlayEl.style.position = 'fixed'; + overlayEl.style.inset = '0'; + overlayEl.style.background = 'rgba(0, 0, 0, 0.5)'; + overlayEl.style.zIndex = '10000'; + overlayEl.style.alignItems = 'center'; + overlayEl.style.justifyContent = 'center'; + overlayEl.style.padding = '16px'; + overlayEl.innerHTML = html` +
+
+ + +
+
+
+
+ `; + + document.body.appendChild(overlayEl); + panelEl = overlayEl.querySelector('#' + panelId); + searchEl = overlayEl.querySelector('#' + searchId); + resultsEl = overlayEl.querySelector('#' + resultsId); + statusEl = overlayEl.querySelector('#' + statusId); + closeEl = overlayEl.querySelector('#' + closeId); + + overlayEl.addEventListener('click', (event) => { + if (event.target === overlayEl) { + close(); + } + }); + } + + function open(context) { + activeContext = context || activeContext; + if (overlayEl) { + overlayEl.style.display = 'flex'; + } else if (panelEl) { + panelEl.style.display = 'block'; + } + if (searchEl) searchEl.focus(); + } + + function close() { + if (overlayEl) { + overlayEl.style.display = 'none'; + } else if (panelEl) { + panelEl.style.display = 'none'; + } + } + + function renderResults(items, term) { + if (!resultsEl || !statusEl) return; + resultsEl.innerHTML = ''; + if (!items.length) { + statusEl.textContent = `No se encontraron pictogramas para "${term}"`; + return; + } + statusEl.textContent = `${items.length} pictogramas`; + items.slice(0, 60).forEach((item) => { + const btn = document.createElement('button'); + btn.type = 'button'; + btn.style.display = 'flex'; + btn.style.flexDirection = 'column'; + btn.style.alignItems = 'center'; + btn.style.gap = '4px'; + btn.style.padding = '4px'; + btn.style.border = '1px solid #ddd'; + btn.style.background = 'white'; + btn.style.cursor = 'pointer'; + + const img = document.createElement('img'); + img.src = TS_buildArasaacPictogramUrl(item.id); + img.alt = item.label; + img.width = 64; + img.height = 64; + img.loading = 'lazy'; + img.style.objectFit = 'contain'; + + const label = document.createElement('span'); + label.style.fontSize = '12px'; + label.textContent = item.label; + + btn.appendChild(img); + btn.appendChild(label); + btn.onclick = () => { + if (!activeContext) return; + onPick(activeContext, item); + close(); + }; + resultsEl.appendChild(btn); + }); + } + + function search(term) { + if (!statusEl || !resultsEl) return; + const trimmed = term.trim(); + if (trimmed.length < 2) { + statusEl.textContent = 'Escribe al menos 2 caracteres para buscar.'; + resultsEl.innerHTML = ''; + return; + } + statusEl.textContent = 'Buscando...'; + fetch(`https://api.arasaac.org/api/pictograms/es/search/${encodeURIComponent(trimmed)}`) + .then((res) => res.json()) + .then((items) => { + const pictograms = (Array.isArray(items) ? items : []) + .map((item) => { + if (typeof item === 'string' || typeof item === 'number') { + return { id: item, label: trimmed }; + } + const id = item._id || item.id; + const keywords = Array.isArray(item.keywords) ? item.keywords : []; + const keyword = keywords[0] ? keywords[0].keyword || keywords[0].name : ''; + const label = keyword || item.keyword || item.name || trimmed; + return { id, label }; + }) + .filter((item) => item.id); + renderResults(pictograms, trimmed); + }) + .catch(() => { + statusEl.textContent = 'Error al buscar pictogramas.'; + resultsEl.innerHTML = ''; + }); + } + + if (closeEl) { + closeEl.onclick = close; + } + if (searchEl) { + searchEl.addEventListener('input', () => + debounce(debounceId, search, 300, [searchEl.value]) + ); + } + + return { + open, + close, + }; +} + const wheelcolors = [ // Your original custom colors '#ff0000', @@ -922,6 +1284,11 @@ function TS_IndexElement( if (formattedDate.includes(searchValue)) return true; } break; + case 'picto': { + const plate = TS_normalizePictoValue(value); + if (plate.text && plate.text.toLowerCase().includes(searchValue)) return true; + break; + } default: // For raw and other types, search in the direct value if (String(value).toLowerCase().includes(searchValue)) return true; @@ -1037,6 +1404,33 @@ function TS_IndexElement( new_tr.appendChild(tdFechaISO); break; } + case 'picto': { + const tdPicto = document.createElement('td'); + const plate = TS_normalizePictoValue(data[key.key]); + const wrapper = document.createElement('div'); + wrapper.style.display = 'flex'; + wrapper.style.alignItems = 'center'; + wrapper.style.gap = '8px'; + if (plate.arasaacId) { + const img = document.createElement('img'); + img.src = TS_buildArasaacPictogramUrl(plate.arasaacId); + img.alt = plate.text || 'Pictograma'; + img.width = 48; + img.height = 48; + img.loading = 'lazy'; + img.style.objectFit = 'contain'; + wrapper.appendChild(img); + } + if (plate.text) { + const text = document.createElement('span'); + console.log('Picto data', data, 'normalized', plate); + text.textContent = data[key.labelkey] || plate.text || ''; + wrapper.appendChild(text); + } + tdPicto.appendChild(wrapper); + new_tr.appendChild(tdPicto); + break; + } case 'template': { const tdCustomTemplate = document.createElement('td'); new_tr.appendChild(tdCustomTemplate); diff --git a/src/page/comedor.js b/src/page/comedor.js index 59e2fc6..a23e145 100644 --- a/src/page/comedor.js +++ b/src/page/comedor.js @@ -12,7 +12,14 @@ PAGES.comedor = { } var nameh1 = safeuuid(); var field_fecha = safeuuid(); - var field_platos = safeuuid(); + var field_tipo = safeuuid(); + var field_primero = safeuuid(); + var field_segundo = safeuuid(); + var field_postre = safeuuid(); + var btn_picto_primero = safeuuid(); + var btn_picto_segundo = safeuuid(); + var btn_picto_postre = safeuuid(); + var debounce_picto = safeuuid(); var btn_guardar = safeuuid(); var btn_borrar = safeuuid(); container.innerHTML = html` @@ -24,18 +31,55 @@ PAGES.comedor = {

+ + + `; + const pictogramSelector = TS_CreateArasaacSelector({ + modal: true, + debounceId: debounce_picto, + onPick: (context, item) => { + TS_applyPictoValue(context.pictoId, { + text: item.label, + arasaacId: String(item.id), + }); + }, + }); + document.getElementById(btn_picto_primero).onclick = () => + pictogramSelector.open({ pictoId: btn_picto_primero }); + document.getElementById(btn_picto_segundo).onclick = () => + pictogramSelector.open({ pictoId: btn_picto_segundo }); + document.getElementById(btn_picto_postre).onclick = () => + pictogramSelector.open({ pictoId: btn_picto_postre }); DB.get('comedor', mid).then((data) => { function load_data(data, ENC = '') { document.getElementById(nameh1).innerText = mid; document.getElementById(field_fecha).value = data['Fecha'] || mid || CurrentISODate(); - document.getElementById(field_platos).value = data['Platos'] || ''; + document.getElementById(field_tipo).value = data['Tipo'] || ''; + document.getElementById(field_primero).value = data['Primero'] || ''; + document.getElementById(field_segundo).value = data['Segundo'] || ''; + document.getElementById(field_postre).value = data['Postre'] || ''; + TS_applyPictoValue(btn_picto_primero, data['Primero_Picto'] || ''); + TS_applyPictoValue(btn_picto_segundo, data['Segundo_Picto'] || ''); + TS_applyPictoValue(btn_picto_postre, data['Postre_Picto'] || ''); } if (typeof data == 'string') { TS_decrypt( @@ -60,18 +104,25 @@ PAGES.comedor = { guardarBtn.style.opacity = '0.5'; const newDate = document.getElementById(field_fecha).value; + const newTipo = document.getElementById(field_tipo).value.trim(); var data = { Fecha: newDate, - Platos: document.getElementById(field_platos).value, + Tipo: newTipo, + Primero: document.getElementById(field_primero).value.trim(), + Segundo: document.getElementById(field_segundo).value.trim(), + Postre: document.getElementById(field_postre).value.trim(), + Primero_Picto: TS_getPictoValue(btn_picto_primero), + Segundo_Picto: TS_getPictoValue(btn_picto_segundo), + Postre_Picto: TS_getPictoValue(btn_picto_postre), }; // If the date has changed, we need to delete the old entry - if (mid !== newDate && mid !== '') { + if (mid !== newDate + "," + newTipo && mid !== '') { DB.del('comedor', mid); } document.getElementById('actionStatus').style.display = 'block'; - DB.put('comedor', newDate, data) + DB.put('comedor', newDate + "," + newTipo, data) .then(() => { toastr.success('Guardado!'); setTimeout(() => { @@ -120,10 +171,31 @@ PAGES.comedor = { label: 'Fecha', }, { - key: 'Platos', + key: 'Tipo', type: 'raw', default: '', - label: 'Platos', + label: 'Tipo', + }, + { + key: 'Primero_Picto', + type: 'picto', + default: '', + label: 'Primero', + labelkey: 'Primero', + }, + { + key: 'Segundo_Picto', + type: 'picto', + default: '', + label: 'Segundo', + labelkey: 'Segundo', + }, + { + key: 'Postre_Picto', + type: 'picto', + default: '', + label: 'Postre', + labelkey: 'Postre', }, ], 'comedor',