Añadir funcionalidad para gestionar puntos de interés en Gest-Aula

This commit is contained in:
Naiel
2026-03-04 14:45:04 +00:00
parent a39ecca990
commit 7822e46b61

View File

@@ -1,5 +1,6 @@
PERMS['aulas'] = 'Aulas (Solo docentes!)';
PERMS['aulas:resumen_diario'] = '> Resumen diario';
PERMS['aulas:puntos_interes'] = '> Puntos de interés';
PAGES.aulas = {
//navcss: "btn1",
Title: 'Gest-Aula',
@@ -17,6 +18,7 @@ PAGES.aulas = {
var link_alertas = safeuuid();
var link_diario = safeuuid();
var link_actividades = safeuuid();
var link_puntos_interes = safeuuid();
container.innerHTML = html`
<h1>Gestión del Aula</h1>
<div>
@@ -49,6 +51,9 @@ PAGES.aulas = {
<a class="button" style="font-size: 25px;" href="#aulas,informes">
Informes
</a>
<a class="button btn8" style="font-size: 25px;" href="#aulas,puntos_interes">
Puntos de interés
</a>
</fieldset>
</div>
`;
@@ -417,6 +422,54 @@ PAGES.aulas = {
);
});
},
__leafletPromise: null,
__ensureLeaflet: function () {
if (window.L && typeof window.L.map === 'function') {
return Promise.resolve(window.L);
}
if (PAGES.aulas.__leafletPromise) {
return PAGES.aulas.__leafletPromise;
}
PAGES.aulas.__leafletPromise = new Promise((resolve, reject) => {
try {
if (!document.getElementById('telesec-leaflet-css')) {
var css = document.createElement('link');
css.id = 'telesec-leaflet-css';
css.rel = 'stylesheet';
css.href = 'https://unpkg.com/leaflet@1.9.4/dist/leaflet.css';
css.integrity = 'sha256-p4NxAoJBhIIN+hmNHrzRCf9tD/miZyoHS5obTRR9BMY=';
css.crossOrigin = '';
document.head.appendChild(css);
}
if (window.L && typeof window.L.map === 'function') {
resolve(window.L);
return;
}
var existing = document.getElementById('telesec-leaflet-js');
if (existing) {
existing.addEventListener('load', () => resolve(window.L));
existing.addEventListener('error', () => reject(new Error('No se pudo cargar Leaflet')));
return;
}
var script = document.createElement('script');
script.id = 'telesec-leaflet-js';
script.src = 'https://unpkg.com/leaflet@1.9.4/dist/leaflet.js';
script.integrity = 'sha256-20nQCchB9co0qIjJZRGuk2/Z9VM+kNiyxNV1lvTlZBo=';
script.crossOrigin = '';
script.onload = () => resolve(window.L);
script.onerror = () => reject(new Error('No se pudo cargar Leaflet'));
document.body.appendChild(script);
} catch (e) {
reject(e);
}
});
return PAGES.aulas.__leafletPromise;
},
__getServerNow: async function () {
try {
var couchUrl = (localStorage.getItem('TELESEC_COUCH_URL') || '').replace(/\/$/, '');
@@ -605,6 +658,375 @@ PAGES.aulas = {
await loadData();
};
},
_puntos_interes: function () {
var map_id = safeuuid();
var btn_new = safeuuid();
var btn_my_gps = safeuuid();
container.innerHTML = html`
<a class="button" href="#aulas">← Volver a Gestión de Aulas</a>
<h1>Puntos de interés</h1>
<p>Registra ubicaciones (tiendas, bares, entidades, etc.) y visualízalas en el mapa.</p>
<button id="${btn_new}">Nuevo punto</button>
<button class="btn5" id="${btn_my_gps}">Centrar en mi GPS</button>
<div id="${map_id}" style="height: 380px; border: 2px solid black; margin-top: 8px; border-radius: 8px;"></div>
<div id="cont"></div>
`;
var map = null;
var layer = null;
var markers = {};
function parseCoord(value) {
if (value === null || value === undefined || value === '') return null;
var parsed = parseFloat(String(value).replace(',', '.'));
return isNaN(parsed) ? null : parsed;
}
function updateMarker(data) {
if (!map || !layer || !data || !data._key) return;
var lat = parseCoord(data.Latitud);
var lng = parseCoord(data.Longitud);
if (lat === null || lng === null) {
if (markers[data._key]) {
layer.removeLayer(markers[data._key]);
delete markers[data._key];
}
return;
}
var popup = '<b>' + (data.Nombre || data._key) + '</b>';
if (data.Tipo) popup += '<br>' + data.Tipo;
if (data.Direccion) popup += '<br>' + data.Direccion;
if (markers[data._key]) {
markers[data._key].setLatLng([lat, lng]).bindPopup(popup);
} else {
markers[data._key] = L.marker([lat, lng]).addTo(layer).bindPopup(popup);
}
}
function removeMarker(key) {
if (!layer) return;
if (markers[key]) {
layer.removeLayer(markers[key]);
delete markers[key];
}
}
function focusPoint(data) {
if (!map) return;
var lat = parseCoord(data.Latitud);
var lng = parseCoord(data.Longitud);
if (lat === null || lng === null) {
toastr.error('Este punto no tiene coordenadas válidas');
return;
}
map.setView([lat, lng], 17);
if (markers[data._key]) {
markers[data._key].openPopup();
}
}
TS_IndexElement(
'aulas,puntos_interes',
[
{ key: 'Nombre', type: 'raw', default: '', label: 'Nombre' },
{ key: 'Tipo', type: 'raw', default: '', label: 'Tipo' },
{ key: 'Direccion', type: 'raw', default: '', label: 'Dirección' },
{
key: 'Latitud',
type: 'template',
label: 'Ubicación',
template: (data, td) => {
var lat = parseCoord(data.Latitud);
var lng = parseCoord(data.Longitud);
if (lat === null || lng === null) {
td.innerText = 'Sin coordenadas';
return;
}
var txt = document.createElement('div');
txt.innerText = lat.toFixed(6) + ', ' + lng.toFixed(6);
td.appendChild(txt);
var btn = document.createElement('button');
btn.className = 'btn6';
btn.innerText = 'Ver en mapa';
btn.onclick = (ev) => {
ev.preventDefault();
ev.stopPropagation();
focusPoint(data);
return false;
};
td.appendChild(btn);
},
},
],
'aulas_puntos_interes',
document.querySelector('#cont')
);
document.getElementById(btn_new).onclick = () => {
setUrlHash('aulas,puntos_interes,' + safeuuid(''));
};
document.getElementById(btn_my_gps).onclick = () => {
if (!navigator.geolocation) {
toastr.error('Tu navegador no soporta geolocalización');
return;
}
navigator.geolocation.getCurrentPosition(
(pos) => {
if (!map) return;
map.setView([pos.coords.latitude, pos.coords.longitude], 16);
L.circleMarker([pos.coords.latitude, pos.coords.longitude], {
radius: 8,
color: '#2b8a3e',
fillColor: '#2b8a3e',
fillOpacity: 0.7,
})
.addTo(map)
.bindPopup('Tu ubicación actual')
.openPopup();
},
(err) => {
console.warn('Error obteniendo GPS', err);
toastr.error('No se pudo obtener la ubicación GPS');
},
{ enableHighAccuracy: true, timeout: 10000 }
);
};
(async () => {
try {
await PAGES.aulas.__ensureLeaflet();
map = L.map(map_id).setView([40.4168, -3.7038], 6);
L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
maxZoom: 19,
attribution: '&copy; OpenStreetMap',
}).addTo(map);
layer = L.layerGroup().addTo(map);
EventListeners.DB.push(
DB.map('aulas_puntos_interes', (raw, key) => {
if (raw === null) {
removeMarker(key);
return;
}
PAGES.aulas
.__decryptIfNeeded('aulas_puntos_interes', key, raw)
.then((data) => {
data = data || {};
data._key = key;
updateMarker(data);
})
.catch((e) => {
console.warn('Error cargando punto de interés para mapa', e);
});
})
);
} catch (e) {
console.warn('Leaflet no disponible', e);
toastr.error('No se pudo cargar el mapa');
}
})();
},
_puntos_interes__edit: function (mid) {
var field_nombre = safeuuid();
var field_tipo = safeuuid();
var field_direccion = safeuuid();
var field_descripcion = safeuuid();
var field_lat = safeuuid();
var field_lng = safeuuid();
var btn_gps = safeuuid();
var btn_guardar = safeuuid();
var btn_borrar = safeuuid();
var map_id = safeuuid();
container.innerHTML = html`
<a class="button" href="#aulas,puntos_interes">← Volver a puntos de interés</a>
<h1>Punto de interés <code>${mid}</code></h1>
<fieldset style="float: none; width: calc(100% - 40px); max-width: none;">
<legend>Datos</legend>
<label>Nombre<br /><input type="text" id="${field_nombre}" /></label><br /><br />
<label>Tipo (tienda, bar, entidad...)<br /><input type="text" id="${field_tipo}" /></label><br /><br />
<label>Dirección<br /><input type="text" id="${field_direccion}" style="width: calc(100% - 20px);" /></label><br /><br />
<label>Descripción<br /><textarea id="${field_descripcion}" style="width: 100%; height: 120px;"></textarea></label><br /><br />
<div style="display: flex; gap: 8px; flex-wrap: wrap; align-items: end;">
<label>Latitud<br /><input type="text" id="${field_lat}" placeholder="40.4168" /></label>
<label>Longitud<br /><input type="text" id="${field_lng}" placeholder="-3.7038" /></label>
<button class="btn5" id="${btn_gps}">Usar GPS</button>
</div>
<br />
<div id="${map_id}" style="height: 360px; border: 2px solid black; border-radius: 8px;"></div>
<hr />
<button class="saveico" id="${btn_guardar}">
<img src="static/floppy_disk_green.png" />
<br>Guardar
</button>
<button class="delico" id="${btn_borrar}">
<img src="static/garbage.png" />
<br>Borrar
</button>
</fieldset>
`;
var map = null;
var marker = null;
function parseCoord(value) {
if (value === null || value === undefined || value === '') return null;
var parsed = parseFloat(String(value).replace(',', '.'));
return isNaN(parsed) ? null : parsed;
}
function setCoordInputs(lat, lng) {
document.getElementById(field_lat).value =
lat === null || lat === undefined ? '' : Number(lat).toFixed(6);
document.getElementById(field_lng).value =
lng === null || lng === undefined ? '' : Number(lng).toFixed(6);
}
function refreshMarker(centerMap = false) {
if (!map) return;
var lat = parseCoord(document.getElementById(field_lat).value);
var lng = parseCoord(document.getElementById(field_lng).value);
if (lat === null || lng === null) {
if (marker) {
map.removeLayer(marker);
marker = null;
}
return;
}
if (!marker) {
marker = L.marker([lat, lng], { draggable: true }).addTo(map);
marker.on('dragend', (ev) => {
var ll = ev.target.getLatLng();
setCoordInputs(ll.lat, ll.lng);
});
} else {
marker.setLatLng([lat, lng]);
}
if (centerMap) {
map.setView([lat, lng], 17);
}
}
(async () => {
try {
await PAGES.aulas.__ensureLeaflet();
map = L.map(map_id).setView([40.4168, -3.7038], 6);
L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
maxZoom: 19,
attribution: '&copy; OpenStreetMap',
}).addTo(map);
map.on('click', (ev) => {
setCoordInputs(ev.latlng.lat, ev.latlng.lng);
refreshMarker(false);
});
var raw = await DB.get('aulas_puntos_interes', mid);
var data = await PAGES.aulas.__decryptIfNeeded('aulas_puntos_interes', mid, raw);
data = data || {};
document.getElementById(field_nombre).value = data.Nombre || '';
document.getElementById(field_tipo).value = data.Tipo || '';
document.getElementById(field_direccion).value = data.Direccion || '';
document.getElementById(field_descripcion).value = data.Descripcion || '';
setCoordInputs(parseCoord(data.Latitud), parseCoord(data.Longitud));
refreshMarker(true);
} catch (e) {
console.warn('Error iniciando mapa de punto de interés', e);
toastr.error('No se pudo cargar el mapa del punto de interés');
}
})();
document.getElementById(field_lat).addEventListener('input', () => refreshMarker(false));
document.getElementById(field_lng).addEventListener('input', () => refreshMarker(false));
document.getElementById(btn_gps).onclick = (ev) => {
ev.preventDefault();
if (!navigator.geolocation) {
toastr.error('Tu navegador no soporta geolocalización');
return false;
}
navigator.geolocation.getCurrentPosition(
(pos) => {
setCoordInputs(pos.coords.latitude, pos.coords.longitude);
refreshMarker(true);
toastr.success('Ubicación GPS capturada');
},
(err) => {
console.warn('Error GPS', err);
toastr.error('No se pudo obtener tu ubicación GPS');
},
{ enableHighAccuracy: true, timeout: 10000 }
);
return false;
};
document.getElementById(btn_guardar).onclick = () => {
var guardarBtn = document.getElementById(btn_guardar);
if (guardarBtn.disabled) return;
var lat = parseCoord(document.getElementById(field_lat).value);
var lng = parseCoord(document.getElementById(field_lng).value);
var hasLat = document.getElementById(field_lat).value.trim() !== '';
var hasLng = document.getElementById(field_lng).value.trim() !== '';
if ((hasLat && lat === null) || (hasLng && lng === null) || (hasLat !== hasLng)) {
toastr.error('Introduce latitud y longitud válidas');
return;
}
guardarBtn.disabled = true;
guardarBtn.style.opacity = '0.5';
var data = {
Nombre: document.getElementById(field_nombre).value,
Tipo: document.getElementById(field_tipo).value,
Direccion: document.getElementById(field_direccion).value,
Descripcion: document.getElementById(field_descripcion).value,
Latitud: lat === null ? '' : Number(lat).toFixed(6),
Longitud: lng === null ? '' : Number(lng).toFixed(6),
UpdatedAt: new Date().toISOString(),
Autor: SUB_LOGGED_IN_ID || '',
};
document.getElementById('actionStatus').style.display = 'block';
DB.put('aulas_puntos_interes', mid, data)
.then(() => {
toastr.success('Guardado!');
setTimeout(() => {
document.getElementById('actionStatus').style.display = 'none';
setUrlHash('aulas,puntos_interes');
}, SAVE_WAIT);
})
.catch((e) => {
console.warn('DB.put error', e);
guardarBtn.disabled = false;
guardarBtn.style.opacity = '1';
document.getElementById('actionStatus').style.display = 'none';
toastr.error('Error al guardar el punto de interés');
});
};
document.getElementById(btn_borrar).onclick = () => {
if (confirm('¿Quieres borrar este punto de interés?') == true) {
DB.del('aulas_puntos_interes', mid).then(() => {
toastr.error('Borrado!');
setTimeout(() => {
setUrlHash('aulas,puntos_interes');
}, SAVE_WAIT);
});
}
};
},
_resumen_diario: function () {
var data_Comedor = safeuuid();
var data_Tareas = safeuuid();
@@ -759,6 +1181,9 @@ Cargando...</pre
case 'ordenadores':
this._ordenadores();
break;
case 'puntos_interes':
this._puntos_interes();
break;
case 'resumen_diario':
this._resumen_diario();
break;
@@ -778,6 +1203,9 @@ Cargando...</pre
case 'ordenadores':
this._ordenadores__edit(item);
break;
case 'puntos_interes':
this._puntos_interes__edit(item);
break;
}
}
},