Añadir funcionalidad para gestionar puntos de interés en Gest-Aula
This commit is contained in:
@@ -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: '© 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: '© 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;
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user