Refactor code structure for improved readability and maintainability
This commit is contained in:
8
src/server/public/assets/images/logo-amayo.svg
Normal file
8
src/server/public/assets/images/logo-amayo.svg
Normal file
File diff suppressed because one or more lines are too long
|
After Width: | Height: | Size: 2.0 MiB |
@@ -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,
|
||||||
|
|||||||
@@ -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">
|
||||||
|
|||||||
@@ -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>
|
||||||
|
|||||||
Reference in New Issue
Block a user