feat: Reemplazar la página de selección de servidor con una interfaz de selección en línea y ajustar el diseño de la página de inicio de sesión

This commit is contained in:
Shni
2025-10-15 01:56:02 -05:00
parent b92f415f64
commit 4929f4f767
3 changed files with 80 additions and 16 deletions

View File

@@ -1138,14 +1138,10 @@ export const server = createServer(
return;
}
// Select guild page
// Keep /dashboard/select-guild for compatibility: redirect to /dashboard
if (url.pathname === "/dashboard/select-guild") {
await renderTemplate(req, res, "select_guild", {
appName: pkg.name ?? "Amayo Bot",
user,
guilds,
hideNavbar: true,
});
res.writeHead(302, { Location: "/dashboard" });
res.end();
return;
}

View File

@@ -2,17 +2,85 @@
<div class="max-w-3xl mx-auto p-6">
<div class="relative flex justify-center">
<% if (!selectedGuild) { %>
<!-- Card principal (centered) -->
<!-- Show server selection UI inline (replaces /dashboard/select-guild) -->
<div class="w-full">
<div class="mx-auto backdrop-blur-md bg-white/10 border border-white/10 rounded-xl p-6 shadow-lg glass-card">
<h1 class="text-3xl font-bold mb-2"><%= appName %></h1>
<p class="text-sm text-slate-200/80 mb-4">Panel de administración</p>
<div class="mt-4">
<p class="text-sm text-slate-200/80">Selecciona un servidor desde la página principal para administrar sus ajustes.</p>
<a href="/dashboard/select-guild" class="inline-block mt-3 pixel-btn">Seleccionar servidor</a>
<div class="mx-auto backdrop-blur-md bg-white/6 border border-white/10 rounded-xl p-6 shadow-lg glass-card max-w-xl text-center">
<h2 class="text-2xl font-bold mb-2"><%= appName %></h2>
<p class="text-sm text-slate-200/80 mb-4">Selecciona un servidor desde la lista abajo para administrar sus ajustes.</p>
<div class="mt-4 text-left">
<label class="block text-xs text-slate-300 mb-2">Servidor</label>
<div class="relative">
<input id="guildInput" aria-controls="guildList" aria-expanded="false" aria-autocomplete="list" autocomplete="off" placeholder="Busca o selecciona un servidor..." class="w-full rounded-md p-3 bg-white/6 text-white placeholder:text-slate-400 focus:outline-none" />
<button id="clearBtn" aria-label="Limpiar" class="absolute right-2 top-2 text-slate-300 hover:text-white hidden">✕</button>
<div id="guildList" role="listbox" class="mt-2 max-h-64 overflow-auto rounded-md bg-white/4 backdrop-blur divide-y divide-white/6 hidden">
<% if (guilds && guilds.length) { %>
<% guilds.sort((a,b)=> a.name.localeCompare(b.name)).forEach(g => { %>
<div role="option" data-id="<%= g.id %>" class="p-3 cursor-pointer hover:bg-white/6 text-white flex items-center gap-3">
<div class="w-8 h-8 rounded-md bg-white/10 flex items-center justify-center text-sm text-white/80">#</div>
<div class="truncate">
<div class="font-medium"><%= g.name %></div>
<div class="text-xs text-slate-300"><%= g.id %></div>
</div>
</div>
<% }) %>
<% } else { %>
<div class="p-3 text-slate-300">No tienes servidores gestionados</div>
<% } %>
</div>
</div>
</div>
</div>
</div>
<script>
(() => {
const input = document.getElementById('guildInput');
const list = document.getElementById('guildList');
const clearBtn = document.getElementById('clearBtn');
if (!input || !list) return;
const items = Array.from(list.querySelectorAll('[role="option"]'));
function showList() { list.classList.remove('hidden'); input.setAttribute('aria-expanded','true'); }
function hideList() { list.classList.add('hidden'); input.setAttribute('aria-expanded','false'); }
function filter(q) {
const v = String(q || '').toLowerCase().trim();
let any = 0;
items.forEach(it => {
const name = (it.querySelector('.font-medium')?.textContent || '').toLowerCase();
const id = (it.dataset.id || '');
if (!v || name.includes(v) || id.includes(v)) { it.style.display = ''; any++; } else { it.style.display = 'none'; }
});
return any;
}
let focused = -1;
function focusItem(idx) {
items.forEach((it,i)=> it.classList.toggle('ring-2 ring-white/20', i===idx));
focused = idx;
if (idx>=0) { const el = items[idx]; el.scrollIntoView({ block:'nearest' }); }
}
input.addEventListener('input', (e) => { const v = (e.target).value; filter(v); showList(); clearBtn.classList.toggle('hidden', !v); focused = -1; });
input.addEventListener('focus', () => { filter(input.value); showList(); });
input.addEventListener('blur', () => setTimeout(hideList, 150));
clearBtn?.addEventListener('click', (e)=>{ e.preventDefault(); input.value=''; filter(''); input.focus(); clearBtn.classList.add('hidden'); });
// click selection
items.forEach((it)=>{ it.addEventListener('click', ()=>{ const id = it.getAttribute('data-id'); if (id) window.location.href = `/dashboard/${id}/overview`; }); });
// keyboard navigation
input.addEventListener('keydown', (e)=>{
const visible = items.filter(it => it.style.display !== 'none');
if (e.key === 'ArrowDown') { e.preventDefault(); if (visible.length) { focused = Math.min(visible.length-1, (focused+1)); const idx = items.indexOf(visible[focused]); focusItem(idx); } }
else if (e.key === 'ArrowUp') { e.preventDefault(); if (visible.length) { focused = Math.max(0, (focused-1)); const idx = items.indexOf(visible[focused]); focusItem(idx); } }
else if (e.key === 'Enter') { e.preventDefault(); if (focused>=0) { const sel = items[focused]; const id = sel.getAttribute('data-id'); if (id) window.location.href = `/dashboard/${id}/overview`; } }
});
})();
</script>
<% } %>
<% if (typeof selectedGuild !== 'undefined' && selectedGuild) { %>

View File

@@ -1,6 +1,6 @@
<div class="min-h-screen flex items-center justify-center pixel-grid-bg" style="background-image: url('/assets/images/background.svg'); background-size: cover;">
<div class="w-full max-w-3xl p-8">
<div class="mx-auto max-w-lg text-center mb-8">
<div class="mx-auto max-w-lg text-center">
<!-- Animated inline logo -->
<div class="mx-auto mb-4 w-24 logo-anim" aria-hidden="true">
<img src="/assets/images/logo-amayo.svg" alt="logo" class="mx-auto mb-4 rounded-full" />
@@ -8,7 +8,7 @@
<h1 class="text-2xl font-bold text-white">Amayo</h1>
</div>
<div class="mx-auto max-w-md glass-card backdrop-blur-md rounded-xl p-8 entrance-card">
<div class="mx-auto max-w-md glass-card backdrop-blur-md rounded-xl p-4 entrance-card">
<p class="text-sm text-slate-300 mb-6">Log in with Discord to access the dashboard.</p>
<a id="discordLoginBtn" href="/auth/discord" class="inline-flex items-center justify-center gap-3 w-full px-4 py-3 rounded-md bg-indigo-600 hover:bg-indigo-700 text-white font-medium transition-colors relative overflow-hidden">