@@ -3086,6 +3174,181 @@
}
});
+ // ============= OSM TAGS =============
+ let osmTags = [];
+ let osmTagSettings = { basePrefixChance: 25, doublePrefixChance: 10 };
+
+ async function loadOsmTags() {
+ try {
+ const response = await api('/api/admin/osm-tags');
+ osmTags = response.osmTags || [];
+ renderOsmTagTable();
+ } catch (e) {
+ console.error('Failed to load OSM tags:', e);
+ showToast('Failed to load OSM tags', 'error');
+ }
+ }
+
+ async function loadOsmTagSettings() {
+ try {
+ const response = await api('/api/admin/osm-tag-settings');
+ osmTagSettings = response;
+ document.getElementById('osm-basePrefixChance').value = osmTagSettings.basePrefixChance || 25;
+ document.getElementById('osm-doublePrefixChance').value = osmTagSettings.doublePrefixChance || 10;
+ } catch (e) {
+ console.error('Failed to load OSM tag settings:', e);
+ }
+ }
+
+ function renderOsmTagTable() {
+ const tbody = document.getElementById('osmTagTableBody');
+ if (osmTags.length === 0) {
+ tbody.innerHTML = '
| No OSM tags configured |
';
+ return;
+ }
+
+ tbody.innerHTML = osmTags.map(tag => {
+ const prefixes = typeof tag.prefixes === 'string' ? JSON.parse(tag.prefixes || '[]') : (tag.prefixes || []);
+ const prefixCount = prefixes.length;
+ const prefixPreview = prefixes.slice(0, 2).join(', ') + (prefixes.length > 2 ? '...' : '');
+ return `
+
+ | ${escapeHtml(tag.id)} |
+ ${escapeHtml(tag.icon)} |
+
+ ${prefixCount > 0 ? `${prefixCount} prefix${prefixCount > 1 ? 'es' : ''}` : 'None'}
+ ${prefixPreview ? ` ${escapeHtml(prefixPreview)}` : ''}
+ |
+ ${tag.visibility_distance}m |
+ ${tag.spawn_radius}m |
+
+
+ ${tag.enabled ? 'Yes' : 'No'}
+
+ |
+
+
+
+ |
+
+ `;
+ }).join('');
+ }
+
+ function openOsmTagModal(tag = null) {
+ const modal = document.getElementById('osmTagModal');
+ const title = document.getElementById('osmTagModalTitle');
+ const form = document.getElementById('osmTagForm');
+ const idInput = document.getElementById('osmTagIdInput');
+
+ form.reset();
+
+ if (tag) {
+ title.textContent = 'Edit OSM Tag';
+ document.getElementById('osmTagIdField').value = tag.id;
+ idInput.value = tag.id;
+ idInput.readOnly = true;
+ document.getElementById('osmTagIcon').value = tag.icon || 'map-marker';
+ document.getElementById('osmTagVisibility').value = tag.visibility_distance || 400;
+ document.getElementById('osmTagSpawnRadius').value = tag.spawn_radius || 400;
+ const prefixes = typeof tag.prefixes === 'string' ? JSON.parse(tag.prefixes || '[]') : (tag.prefixes || []);
+ document.getElementById('osmTagPrefixes').value = prefixes.join('\n');
+ } else {
+ title.textContent = 'Add OSM Tag';
+ document.getElementById('osmTagIdField').value = '';
+ idInput.readOnly = false;
+ document.getElementById('osmTagIcon').value = 'map-marker';
+ document.getElementById('osmTagVisibility').value = 400;
+ document.getElementById('osmTagSpawnRadius').value = 400;
+ document.getElementById('osmTagPrefixes').value = '';
+ }
+
+ modal.classList.add('active');
+ }
+
+ function closeOsmTagModal() {
+ document.getElementById('osmTagModal').classList.remove('active');
+ }
+
+ function editOsmTag(id) {
+ const tag = osmTags.find(t => t.id === id);
+ if (tag) {
+ openOsmTagModal(tag);
+ }
+ }
+
+ async function deleteOsmTag(id) {
+ if (!confirm(`Are you sure you want to delete the OSM tag "${id}"?`)) return;
+
+ try {
+ await api(`/api/admin/osm-tags/${id}`, { method: 'DELETE' });
+ osmTags = osmTags.filter(t => t.id !== id);
+ renderOsmTagTable();
+ showToast('OSM tag deleted');
+ } catch (e) {
+ showToast('Failed to delete OSM tag: ' + e.message, 'error');
+ }
+ }
+
+ document.getElementById('addOsmTagBtn').addEventListener('click', () => {
+ openOsmTagModal(null);
+ });
+
+ document.getElementById('osmTagForm').addEventListener('submit', async (e) => {
+ e.preventDefault();
+
+ const existingId = document.getElementById('osmTagIdField').value;
+ const newId = document.getElementById('osmTagIdInput').value.trim().toLowerCase();
+ const prefixesText = document.getElementById('osmTagPrefixes').value;
+ const prefixes = prefixesText.split('\n').map(p => p.trim()).filter(p => p.length > 0);
+
+ const data = {
+ id: newId,
+ icon: document.getElementById('osmTagIcon').value.trim() || 'map-marker',
+ visibility_distance: parseInt(document.getElementById('osmTagVisibility').value) || 400,
+ spawn_radius: parseInt(document.getElementById('osmTagSpawnRadius').value) || 400,
+ prefixes: prefixes
+ };
+
+ try {
+ if (existingId) {
+ await api(`/api/admin/osm-tags/${existingId}`, {
+ method: 'PUT',
+ body: JSON.stringify(data)
+ });
+ showToast('OSM tag updated');
+ } else {
+ await api('/api/admin/osm-tags', {
+ method: 'POST',
+ body: JSON.stringify(data)
+ });
+ showToast('OSM tag created');
+ }
+ closeOsmTagModal();
+ loadOsmTags();
+ } catch (e) {
+ showToast('Failed to save OSM tag: ' + e.message, 'error');
+ }
+ });
+
+ document.getElementById('saveOsmSettingsBtn').addEventListener('click', async () => {
+ const settings = {
+ basePrefixChance: parseInt(document.getElementById('osm-basePrefixChance').value) || 25,
+ doublePrefixChance: parseInt(document.getElementById('osm-doublePrefixChance').value) || 10
+ };
+
+ try {
+ await api('/api/admin/osm-tag-settings', {
+ method: 'PUT',
+ body: JSON.stringify(settings)
+ });
+ osmTagSettings = settings;
+ showToast('Prefix settings saved');
+ } catch (e) {
+ showToast('Failed to save prefix settings: ' + e.message, 'error');
+ }
+ });
+
// ============= UTILITIES =============
function escapeHtml(str) {
if (!str) return '';
@@ -3098,6 +3361,8 @@
// Initialize
checkAuth();
+ loadOsmTags();
+ loadOsmTagSettings();