Refactor code structure for improved readability and maintainability

This commit is contained in:
Shni
2025-10-15 01:39:05 -05:00
parent 7dc556e16f
commit 0490fc71ad
4 changed files with 171 additions and 20 deletions

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 2.0 MiB

View File

@@ -1155,6 +1155,8 @@ export const server = createServer(
if (parts.length >= 2) { if (parts.length >= 2) {
const guildId = parts[1]; const guildId = parts[1];
const page = parts[2] || "overview"; const page = parts[2] || "overview";
const fragment =
url.searchParams.get("fragment") || url.searchParams.get("ajax");
// find a nicer display name for selected guild // find a nicer display name for selected guild
const found = guilds.find((g) => String(g.id) === String(guildId)); const found = guilds.find((g) => String(g.id) === String(guildId));
const selectedGuildName = found ? found.name : guildId; const selectedGuildName = found ? found.name : guildId;
@@ -1193,10 +1195,9 @@ export const server = createServer(
} }
// Render dashboard with selected guild context; show dashboard nav // Render dashboard with selected guild context; show dashboard nav
// If caller requested a fragment, render only the page template (no layout) // If caller requested a fragment, render only the page template (no layout)
const fragment =
url.searchParams.get("fragment") || url.searchParams.get("ajax");
if (fragment) { if (fragment) {
const pageFile = path.join(viewsDir, "pages", `${page}.ejs`); // Render the dashboard page and extract the inner #dashContent fragment
const dashPage = path.join(viewsDir, "pages", `dashboard.ejs`);
const pageLocals = { const pageLocals = {
appName: pkg.name ?? "Amayo Bot", appName: pkg.name ?? "Amayo Bot",
user, user,
@@ -1211,9 +1212,16 @@ export const server = createServer(
useDashboardNav: true, useDashboardNav: true,
}; };
try { try {
const fragmentHtml = await ejs.renderFile(pageFile, pageLocals, { const fullPageHtml = await ejs.renderFile(dashPage, pageLocals, {
async: true, async: true,
}); });
// extract content inside the first <div id="dashContent"> ... </div>
const match =
/<div\s+id=["']dashContent["']\b[^>]*>([\s\S]*?)<\/div>/.exec(
fullPageHtml
);
if (match && match[1] != null) {
const fragmentHtml = match[1];
res.writeHead( res.writeHead(
200, 200,
applySecurityHeadersForRequest(req, { applySecurityHeadersForRequest(req, {
@@ -1222,12 +1230,12 @@ export const server = createServer(
); );
res.end(fragmentHtml); res.end(fragmentHtml);
return; return;
}
// if extraction failed, fall through to full render
} catch (err) { } catch (err) {
console.warn("Failed rendering page fragment:", err); console.warn("Failed rendering dashboard fragment:", err);
// fallthrough to full render
} }
} }
await renderTemplate(req, res, "dashboard", { await renderTemplate(req, res, "dashboard", {
appName: pkg.name ?? "Amayo Bot", appName: pkg.name ?? "Amayo Bot",
user, user,

View File

@@ -45,6 +45,67 @@
<% } %> <% } %>
<div id="dashContent"> <div id="dashContent">
<% if (typeof page !== 'undefined' && page === 'overview' && selectedGuild) { %>
<div class="w-full max-w-7xl mx-auto mt-6 px-4">
<div class="grid grid-cols-1 lg:grid-cols-12 gap-6">
<!-- Left sidebar -->
<aside class="lg:col-span-3">
<div class="bg-white/4 border border-white/6 rounded-xl p-4 sticky top-20">
<h3 class="text-slate-200 font-semibold mb-3">Menú</h3>
<ul class="space-y-2 text-slate-200">
<li><a href="/dashboard/<%= selectedGuild %>/overview" class="block p-2 rounded hover:bg-white/5">Overview</a></li>
<li><a href="/dashboard/<%= selectedGuild %>/members" class="block p-2 rounded hover:bg-white/5">Miembros</a></li>
<li><a href="/dashboard/<%= selectedGuild %>/settings" class="block p-2 rounded hover:bg-white/5">Ajustes</a></li>
<li><a href="/dashboard/<%= selectedGuild %>/areas" class="block p-2 rounded hover:bg-white/5">Game Areas</a></li>
<li><a href="/dashboard/<%= selectedGuild %>/mobs" class="block p-2 rounded hover:bg-white/5">Mobs</a></li>
</ul>
</div>
</aside>
<!-- Main content -->
<main class="lg:col-span-6">
<div class="flex items-center justify-between mb-6">
<div>
<h1 class="text-3xl font-bold text-white">Welcome <span class="text-indigo-400"><%= user?.username || 'Admin' %></span></h1>
<p class="text-slate-300">find commonly used dashboard pages below.</p>
</div>
</div>
<div class="grid grid-cols-1 sm:grid-cols-2 gap-4">
<div class="bg-white/6 rounded-xl p-4">
<h4 class="text-white font-semibold">Custom messages</h4>
<p class="text-slate-300 text-sm mt-2">Create fully customized messages called templates and pack them with your very own embeds, buttons and select menus.</p>
<div class="mt-3"><a href="#" class="inline-block pixel-btn">Create template</a></div>
</div>
<div class="bg-white/6 rounded-xl p-4">
<h4 class="text-white font-semibold">Moderation cases</h4>
<p class="text-slate-300 text-sm mt-2">View and edit all moderation cases using the dashboard.</p>
<div class="mt-3"><a href="#" class="inline-block pixel-btn">View cases</a></div>
</div>
<div class="bg-white/6 rounded-xl p-4">
<h4 class="text-white font-semibold">User reports</h4>
<p class="text-slate-300 text-sm mt-2">Allow users to report others and fully customize how to handle them.</p>
<div class="mt-3"><a href="#" class="inline-block pixel-btn">Configure reports</a></div>
</div>
<div class="bg-white/6 rounded-xl p-4">
<h4 class="text-white font-semibold">Role greetings</h4>
<p class="text-slate-300 text-sm mt-2">Welcome users to their new role by using role assignment messages.</p>
<div class="mt-3"><a href="#" class="inline-block pixel-btn">Show role messages</a></div>
</div>
</div>
</main>
<!-- Right column (info / notices) -->
<aside class="lg:col-span-3">
<div class="bg-white/4 border border-white/6 rounded-xl p-4 sticky top-20">
<h4 class="text-white font-semibold mb-2">Notices</h4>
<div class="text-slate-300 text-sm">You are not in the support server. It is recommended to join for updates and support.</div>
</div>
</aside>
</div>
</div>
<% } %>
<% if (typeof page !== 'undefined' && page === 'settings' && selectedGuild) { %> <% if (typeof page !== 'undefined' && page === 'settings' && selectedGuild) { %>
<div class="w-full max-w-3xl mt-6"> <div class="w-full max-w-3xl mt-6">
<div class="backdrop-blur-md bg-white/6 border border-white/8 rounded-xl p-6 glass-card"> <div class="backdrop-blur-md bg-white/6 border border-white/8 rounded-xl p-6 glass-card">

View File

@@ -1,7 +1,81 @@
<div class="min-h-screen flex items-center justify-center"> <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-md p-6 glass-card backdrop-blur-md rounded-xl"> <div class="w-full max-w-3xl p-8">
<h2 class="text-2xl font-bold mb-4">Inicia sesión con Discord</h2> <div class="mx-auto max-w-lg text-center mb-8">
<p class="text-sm mb-6">Para administrar servidores y usar el dashboard debes autenticarte con Discord.</p> <!-- Animated inline logo -->
<a class="inline-block px-4 py-3 rounded-md bg-indigo-600 hover:bg-indigo-700 text-white" href="/auth/discord">Continuar con Discord</a> <div class="mx-auto w-14 h-14 mb-4 logo-anim" aria-hidden="true">
<img src="/assets/images/logo-amayo.svg" alt="logo" class="mx-auto w-12 h-12 mb-4" />
</div>
<h1 class="text-2xl font-bold text-white">Sapphire</h1>
</div>
<div class="mx-auto max-w-md glass-card backdrop-blur-md rounded-xl p-8 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">
<svg xmlns="http://www.w3.org/2000/svg" class="w-5 h-5" viewBox="0 0 24 24" fill="none"><path d="M8.25 15.5c1.1 0 1.99-.9 1.99-2s-.89-2-1.99-2-2 .9-2 2 .9 2 2 2zm7.5 0c1.1 0 1.99-.9 1.99-2s-.89-2-1.99-2-2 .9-2 2 .9 2 2 2z" fill="#fff"/></svg>
<span id="discordLoginText">Log in with Discord</span>
<!-- spinner -->
<svg id="discordSpinner" class="w-5 h-5 ml-2 hidden spinner" viewBox="0 0 50 50">
<circle cx="25" cy="25" r="18" stroke="white" stroke-width="4" stroke-linecap="round" fill="none" stroke-dasharray="80" stroke-dashoffset="60"></circle>
</svg>
</a>
<div class="mt-4 text-sm text-slate-300">
<label class="inline-flex items-center gap-2">
<input id="autoLogin" type="checkbox" checked class="form-checkbox h-4 w-4 text-indigo-500" />
<span>Automatically log in next time</span>
</label>
</div> </div>
</div> </div>
</div>
</div>
<style>
/* simple entrance animation */
@keyframes floatIn { from { transform: translateY(8px) scale(0.98); opacity: 0 } to { transform: translateY(0) scale(1); opacity: 1 } }
.entrance-card { animation: floatIn 420ms cubic-bezier(.2,.9,.3,1) both; }
.logo-anim { animation: floatIn 520ms cubic-bezier(.2,.9,.3,1) both; }
.logo-triangle { transform-origin: 50% 50%; animation: logo-spin 3s linear infinite; }
@keyframes logo-spin { from { transform: rotate(0deg) } to { transform: rotate(360deg) } }
/* spinner */
.spinner { animation: spinner-rot 1s linear infinite; }
@keyframes spinner-rot { to { transform: rotate(360deg) } }
/* button disabled look when spinner active */
.btn-busy { pointer-events: none; opacity: 0.85; }
</style>
<script>
(function(){
const btn = document.getElementById('discordLoginBtn');
const spinner = document.getElementById('discordSpinner');
const text = document.getElementById('discordLoginText');
if(!btn) return;
btn.addEventListener('click', function(ev){
// prevent default to show spinner animation briefly
ev.preventDefault();
if(btn.classList.contains('btn-busy')) return;
btn.classList.add('btn-busy');
spinner.classList.remove('hidden');
if(text) text.textContent = 'Redirigiendo...';
// store preference (optional) — sync to server could be added later
const auto = document.getElementById('autoLogin');
try { localStorage.setItem('amayo:autoLogin', auto && auto.checked ? '1' : '0'); } catch(e) {}
// give the animation a short moment so user sees the spinner
setTimeout(()=> {
window.location.href = btn.getAttribute('href');
}, 220);
});
// small UX: if user has previously enabled autoLogin, optionally focus/animate
try {
const pref = localStorage.getItem('amayo:autoLogin');
if(pref === '1') {
// subtle pulsing to suggest quick login
btn.animate([{ transform: 'scale(1)' }, { transform: 'scale(1.02)' }, { transform: 'scale(1)' }], { duration: 1400, iterations: 1 });
}
} catch(e){}
})();
</script>