From e87b0bd8aa36c7597ece28f84c7e84dce0964c74 Mon Sep 17 00:00:00 2001 From: marsalva Date: Sun, 15 Feb 2026 17:24:54 +0000 Subject: [PATCH] Actualizar usuarios.html --- usuarios.html | 301 +++++++++++++++++--------------------------------- 1 file changed, 101 insertions(+), 200 deletions(-) diff --git a/usuarios.html b/usuarios.html index a82315c..0382d5e 100644 --- a/usuarios.html +++ b/usuarios.html @@ -175,126 +175,50 @@ }); } - // 1. Cargamos municipios AGRUPADOS por nombre -// 1. Carga municipios agrupados para facilitar la selección masiva -async function fetchMunicipios(provName) { - const selMun = document.getElementById('selMunicipio'); - const loader = document.getElementById('loaderMun'); - selMun.innerHTML = ''; - selMun.disabled = true; - if(!provName) return; + async function fetchMunicipios(provName) { + const selMun = document.getElementById('selMunicipio'); + const loader = document.getElementById('loaderMun'); + selMun.innerHTML = ''; + selMun.disabled = true; + if(!provName) return; + if (loader) loader.classList.remove('hidden'); - if (loader) loader.classList.remove('hidden'); - try { - const res = await fetch(`${API_URL}/api/geo/municipios/${provName}`, { - headers: { "Authorization": `Bearer ${localStorage.getItem("token")}` } - }); - const data = await res.json(); - - if (data.ok && data.municipios.length > 0) { - // Agrupamos por nombre de municipio para el selector - const grouped = data.municipios.reduce((acc, m) => { - if(!acc[m.municipio]) acc[m.municipio] = []; - acc[m.municipio].push(m.codigo_postal); - return acc; - }, {}); + try { + const res = await fetch(`${API_URL}/api/geo/municipios/${provName}`, { + headers: { "Authorization": `Bearer ${localStorage.getItem("token")}` } + }); + const data = await res.json(); + if (data.ok && data.municipios.length > 0) { + const grouped = data.municipios.reduce((acc, m) => { + if(!acc[m.municipio]) acc[m.municipio] = []; + acc[m.municipio].push(m.codigo_postal); + return acc; + }, {}); - selMun.innerHTML = ''; - Object.keys(grouped).sort().forEach(mun => { - const opt = document.createElement('option'); - opt.value = mun; - // Guardamos la lista real de CPs para que el sistema los procese - opt.dataset.cps = JSON.stringify(grouped[mun]); - opt.innerText = `${mun.toUpperCase()} (${grouped[mun].length} CPs)`; - selMun.appendChild(opt); - }); - selMun.innerHTML += ''; - selMun.disabled = false; - } - } catch (e) { - showToast("Error al conectar con la base de datos", true); - } finally { - if (loader) loader.classList.add('hidden'); - } -} - -// 2. Añadir zonas al usuario (CORREGIDO: Sin error de validación) -function addZoneToUser() { - const prov = document.getElementById('selProvince').value; - const selMun = document.getElementById('selMunicipio'); - const munName = selMun.value; - - // Validación corregida - if (!prov || !munName) { - showToast("Selecciona Provincia y Municipio primero", true); - return; - } - - if (munName === "OTRO") { - const manualCP = document.getElementById('cpInput').value.trim(); - if(!manualCP) { showToast("Escribe el CP manual en el cuadro de la derecha", true); return; } - tempUserZones.push({ province: prov, city: "MANUAL", cps: manualCP }); - } else { - // Extraemos la lista de CPs reales del municipio - const selectedOption = selMun.options[selMun.selectedIndex]; - const cpList = JSON.parse(selectedOption.dataset.cps); - - let addedCount = 0; - cpList.forEach(cp => { - // Verificamos si el CP individual ya está para no duplicar - const alreadyHasIt = tempUserZones.some(z => z.city === munName && z.cps === cp); - if (!alreadyHasIt) { - tempUserZones.push({ province: prov, city: munName, cps: cp }); - addedCount++; + selMun.innerHTML = ''; + Object.keys(grouped).sort().forEach(mun => { + const opt = document.createElement('option'); + opt.value = mun; + opt.dataset.cps = JSON.stringify(grouped[mun]); + opt.innerText = `${mun.toUpperCase()} (${grouped[mun].length} CPs)`; + selMun.appendChild(opt); + }); + selMun.innerHTML += ''; + selMun.disabled = false; + } else { + selMun.innerHTML = ''; + selMun.disabled = false; + } + } catch (e) { + console.error("Error geo:", e); + showToast("Error base de datos", true); + } finally { + if (loader) loader.classList.add('hidden'); } - }); - - if(addedCount > 0) { - showToast(`✅ Añadidos ${addedCount} códigos de ${munName}`); - } else { - showToast("Esta zona ya estaba completa", true); } - } - renderTempZones(); - document.getElementById('cpInput').value = ""; // Limpiamos campo manual -} - -// 3. Renderizado con agrupación visual (Mantiene la tabla limpia) -function renderTempZones() { - const area = document.getElementById('userZonesArea'); - if (!area) return; - - area.innerHTML = tempUserZones.length === 0 ? '

Sin zonas añadidas...

' : ""; - - // Agrupamos solo para mostrarlo bonito en los chips - const visualGroup = tempUserZones.reduce((acc, curr) => { - if(!acc[curr.city]) acc[curr.city] = []; - acc[curr.city].push(curr.cps); - return acc; - }, {}); - - Object.keys(visualGroup).forEach(city => { - const cps = visualGroup[city].join(", "); - area.innerHTML += ` - - ${city} (${visualGroup[city].length} CPs) - - `; - }); - lucide.createIcons(); -} - -// Función auxiliar para borrar un pueblo entero de la lista temporal -function removeTempCity(cityName) { - tempUserZones = tempUserZones.filter(z => z.city !== cityName); - renderTempZones(); -} function autoFillCP(munName) { const sel = document.getElementById('selMunicipio'); - const selectedOpt = sel.options[sel.selectedIndex]; if (munName === "OTRO") { const customMun = prompt("Nombre del Municipio:"); if (customMun) { @@ -303,43 +227,54 @@ function removeTempCity(cityName) { newOpt.innerText = customMun.toUpperCase(); newOpt.selected = true; sel.appendChild(newOpt); - document.getElementById('cpInput').focus(); } - return; - } - if(selectedOpt && selectedOpt.dataset.cp) { - document.getElementById('cpInput').value = selectedOpt.dataset.cp; } } function addZoneToUser() { const prov = document.getElementById('selProvince').value; - const mun = document.getElementById('selMunicipio').value; - const cp = document.getElementById('cpInput').value.trim(); - if (!prov || !mun || !cp || mun === "OTRO") { showToast("Completa los datos de zona", true); return; } + const selMun = document.getElementById('selMunicipio'); + const munName = selMun.value; + + if (!prov || !munName) { showToast("Selecciona Provincia y Municipio", true); return; } - if (tempUserZones.some(z => z.city === mun && z.cps === cp)) { - showToast("Esta zona ya está añadida", true); return; + if (munName === "OTRO" || selMun.options[selMun.selectedIndex].text.includes("MANUAL")) { + const manualCP = document.getElementById('cpInput').value.trim(); + if(!manualCP) { showToast("Escribe el CP manual", true); return; } + tempUserZones.push({ province: prov, city: munName === "OTRO" ? "MANUAL" : munName, cps: manualCP }); + } else { + const cpList = JSON.parse(selMun.options[selMun.selectedIndex].dataset.cps); + cpList.forEach(cp => { + if (!tempUserZones.some(z => z.city === munName && z.cps === cp)) { + tempUserZones.push({ province: prov, city: munName, cps: cp }); + } + }); + showToast(`Añadidos códigos de ${munName}`); } - - tempUserZones.push({ province: prov, city: mun, cps: cp }); renderTempZones(); document.getElementById('cpInput').value = ""; } - function removeTempZone(idx) { - tempUserZones.splice(idx, 1); + function removeTempCity(cityName) { + tempUserZones = tempUserZones.filter(z => z.city !== cityName); renderTempZones(); } function renderTempZones() { const area = document.getElementById('userZonesArea'); area.innerHTML = tempUserZones.length === 0 ? '

Sin zonas añadidas...

' : ""; - tempUserZones.forEach((z, i) => { + + const visualGroup = tempUserZones.reduce((acc, curr) => { + if(!acc[curr.city]) acc[curr.city] = []; + acc[curr.city].push(curr.cps); + return acc; + }, {}); + + Object.keys(visualGroup).forEach(city => { area.innerHTML += ` - ${z.city} (${z.cps}) - + ${city} (${visualGroup[city].length} CPs) + `; }); lucide.createIcons(); @@ -349,11 +284,7 @@ function removeTempCity(cityName) { try { const res = await fetch(`${API_URL}/guilds`, { headers: { "Authorization": `Bearer ${localStorage.getItem("token")}` } }); const data = await res.json(); - if (data.ok) { - availableGuilds = data.guilds; - renderGuildsList(); - renderGuildsCheckboxes(); - } + if (data.ok) { availableGuilds = data.guilds; renderGuildsList(); renderGuildsCheckboxes(); } } catch (e) {} } @@ -378,10 +309,7 @@ function removeTempCity(cityName) { try { const res = await fetch(`${API_URL}/admin/users`, { headers: { "Authorization": `Bearer ${localStorage.getItem("token")}` } }); const data = await res.json(); - if (data.ok) { - currentUsers = data.users; - renderUsersTable(); - } + if (data.ok) { currentUsers = data.users; renderUsersTable(); } } catch (e) {} } @@ -411,11 +339,8 @@ function removeTempCity(cityName) { body: JSON.stringify(data) }); const json = await res.json(); - if (json.ok) { - showToast(isEdit ? "Datos actualizados" : "Usuario creado"); - resetUserForm(); - fetchUsers(); - } else { showToast("❌ " + (json.error || "Error"), true); } + if (json.ok) { showToast(isEdit ? "Actualizado" : "Creado"); resetUserForm(); fetchUsers(); } + else { showToast("Error: " + json.error, true); } } catch (e) { showToast("Error conexión", true); } finally { btn.disabled = false; btn.innerText = isEdit ? "Actualizar Datos" : "Guardar Usuario y Zonas"; } } @@ -429,26 +354,20 @@ function removeTempCity(cityName) { document.getElementById('uPhone').value = user.phone; document.getElementById('uRole').value = user.role; document.getElementById('uPass').value = ""; - tempUserZones = user.zones || []; renderTempZones(); - const userGuilds = user.guilds || []; - document.querySelectorAll('.guild-checkbox').forEach(cb => { - cb.checked = userGuilds.includes(parseInt(cb.value)); - }); - + document.querySelectorAll('.guild-checkbox').forEach(cb => { cb.checked = userGuilds.includes(parseInt(cb.value)); }); document.getElementById('formTitle').innerText = "2. Editando Usuario"; - document.getElementById('btnSubmitUser').classList.replace('bg-green-600', 'bg-blue-600'); document.getElementById('btnCancelEdit').classList.remove('hidden'); window.scrollTo({ top: 0, behavior: 'smooth' }); } async function deleteUser(id) { - if(!confirm("¿Seguro que quieres borrar a este usuario?")) return; + if(!confirm("¿Borrar usuario?")) return; try { await fetch(`${API_URL}/admin/users/${id}`, { method: 'DELETE', headers: { "Authorization": `Bearer ${localStorage.getItem("token")}` } }); - fetchUsers(); showToast("Usuario borrado"); + fetchUsers(); showToast("Borrado"); } catch(e) { showToast("Error", true); } } @@ -461,7 +380,6 @@ function removeTempCity(cityName) { tempUserZones = []; renderTempZones(); document.getElementById('formTitle').innerText = "2. Nuevo Usuario"; - document.getElementById('btnSubmitUser').classList.replace('bg-blue-600', 'bg-green-600'); document.getElementById('btnCancelEdit').classList.add('hidden'); document.querySelectorAll('.guild-checkbox').forEach(cb => cb.checked = false); } @@ -470,64 +388,47 @@ function removeTempCity(cityName) { const list = document.getElementById('guildsList'); list.innerHTML = ""; availableGuilds.forEach(g => { - list.innerHTML += `
${g.name}
`; + list.innerHTML += `
${g.name}
`; }); lucide.createIcons(); } function renderGuildsCheckboxes() { const area = document.getElementById('guildsCheckboxArea'); - area.innerHTML = availableGuilds.length === 0 ? '

No hay gremios registrados...

' : ""; + area.innerHTML = availableGuilds.length === 0 ? '

No hay gremios...

' : ""; availableGuilds.forEach(g => { - area.innerHTML += ``; + area.innerHTML += ``; }); } - function renderUsersTable() { - const tbody = document.getElementById('usersListBody'); - tbody.innerHTML = currentUsers.length === 0 ? `Cargando equipo...` : ""; - - currentUsers.forEach(u => { - const uGuildNames = (u.guilds || []).map(gid => availableGuilds.find(ag => ag.id === gid)?.name).filter(Boolean).join(", "); - - // --- LÓGICA DE AGRUPACIÓN POR MUNICIPIO --- - const groupedZones = (u.zones || []).reduce((acc, current) => { - if (!acc[current.city]) { - acc[current.city] = []; - } - // Evitamos duplicados de CP en la misma ciudad - if (!acc[current.city].includes(current.cps)) { - acc[current.city].push(current.cps); - } - return acc; - }, {}); + function renderUsersTable() { + const tbody = document.getElementById('usersListBody'); + tbody.innerHTML = currentUsers.length === 0 ? `Cargando equipo...` : ""; + currentUsers.forEach(u => { + const uGuildNames = (u.guilds || []).map(gid => availableGuilds.find(ag => ag.id === gid)?.name).filter(Boolean).join(", "); + const groupedZones = (u.zones || []).reduce((acc, curr) => { + if (!acc[curr.city]) acc[curr.city] = []; + if (!acc[curr.city].includes(curr.cps)) acc[curr.city].push(curr.cps); + return acc; + }, {}); - // Convertimos el objeto agrupado en HTML elegante - const uZonesHtml = Object.keys(groupedZones).map(city => { - const cps = groupedZones[city].join(", "); - return `
- 📍 ${city} - (${cps}) -
`; - }).join("") || 'Sin zona'; + const uZonesHtml = Object.keys(groupedZones).map(city => { + return `
📍 ${city} (${groupedZones[city].join(", ")})
`; + }).join("") || 'Sin zona'; - tbody.innerHTML += ` - -
${u.full_name}${u.email}
- ${u.phone} - ${u.role}

${uGuildNames || '-'}

- -
- ${uZonesHtml} -
- - - - - - `; - }); -} + tbody.innerHTML += ` + +
${u.full_name}${u.email}
+ ${u.phone} + ${u.role}

${uGuildNames || '-'}

+
${uZonesHtml}
+ + + + + `; + }); + } function showToast(msg, err=false){ const t=document.getElementById('toast'), m=document.getElementById('toastMsg');