Files
FullStack-Blog-Nestjs-Nextj…/src/views/pages/auth.njk
2026-02-18 21:51:16 +09:00

201 lines
12 KiB
Plaintext

{% extends "layouts/base.njk" %}
{% block body %}
<div class="mx-auto w-full max-w-[42rem] space-y-5">
<section id="auth-tabs" data-initial-tab="{% if resetToken %}reset{% else %}signin{% endif %}" class="rounded-[2rem] border border-zinc-200 bg-zinc-50 p-6 shadow-sm sm:p-8">
<p class="text-center text-xs font-medium tracking-[0.5em] text-slate-500">SIGN IN TO CONTINUE</p>
<div class="mt-6 grid grid-cols-3 gap-1 rounded-full bg-zinc-200 p-1.5">
<button type="button" data-tab-button data-tab="signin" class="rounded-full px-3 py-3 text-center text-sm font-semibold text-slate-500 transition">Sign In</button>
<button type="button" data-tab-button data-tab="signup" class="rounded-full px-3 py-3 text-center text-sm font-semibold text-slate-500 transition">Sign Up</button>
<button type="button" data-tab-button data-tab="reset" class="rounded-full px-3 py-3 text-center text-sm font-semibold text-slate-500 transition">Reset Password</button>
</div>
<section data-tab-panel="signin" class="mt-7 space-y-4">
<form hx-post="/auth/login" hx-target="#flash" hx-swap="innerHTML" class="space-y-4">
<div>
<label for="login-email" class="mb-2 block text-sm font-semibold text-slate-700">Email address</label>
<input id="login-email" name="email" type="email" placeholder="name@email.com" required class="w-full rounded-2xl border border-zinc-300 bg-zinc-100 px-4 py-3 text-base text-zinc-800 placeholder:text-zinc-400 focus:border-blue-500 focus:bg-white focus:outline-none" />
</div>
<div>
<label for="login-password" class="mb-2 block text-sm font-semibold text-slate-700">Password</label>
<div class="flex gap-2">
<input id="login-password" name="password" type="password" placeholder="Enter your password" required class="w-full rounded-2xl border border-zinc-300 bg-zinc-100 px-4 py-3 text-base text-zinc-800 placeholder:text-zinc-400 focus:border-blue-500 focus:bg-white focus:outline-none" />
<button type="button" data-toggle-password data-target="login-password" class="rounded-2xl border border-zinc-300 bg-zinc-100 px-4 py-3 text-sm font-semibold text-slate-600 transition hover:bg-white">Show</button>
</div>
</div>
<button type="submit" class="w-full rounded-2xl bg-blue-600 px-4 py-3 text-lg font-semibold text-white transition hover:bg-blue-700">Sign In</button>
</form>
<form id="magic-link-form" hx-post="/auth/magic-link" hx-target="#flash" hx-swap="innerHTML">
<input id="magic-link-email" type="hidden" name="email" value="" />
<button type="submit" class="w-full rounded-2xl border border-zinc-300 bg-zinc-100 px-4 py-3 text-lg font-semibold text-zinc-500 transition hover:bg-white hover:text-zinc-700">Send magic link</button>
</form>
<a href="/auth/google" class="block w-full rounded-2xl border border-zinc-300 bg-zinc-100 px-4 py-3 text-center text-lg font-semibold text-slate-700 no-underline transition hover:bg-white">Continue with Google</a>
</section>
<section data-tab-panel="signup" class="mt-7 hidden space-y-4">
<form hx-post="/auth/register" hx-target="#flash" hx-swap="innerHTML" class="space-y-4">
<div>
<label for="signup-name" class="mb-2 block text-sm font-semibold text-slate-700">Name</label>
<input id="signup-name" name="name" placeholder="Your full name" required class="w-full rounded-2xl border border-zinc-300 bg-zinc-100 px-4 py-3 text-base text-zinc-800 placeholder:text-zinc-400 focus:border-blue-500 focus:bg-white focus:outline-none" />
</div>
<div>
<label for="signup-email" class="mb-2 block text-sm font-semibold text-slate-700">Email address</label>
<input id="signup-email" name="email" type="email" placeholder="name@email.com" required class="w-full rounded-2xl border border-zinc-300 bg-zinc-100 px-4 py-3 text-base text-zinc-800 placeholder:text-zinc-400 focus:border-blue-500 focus:bg-white focus:outline-none" />
</div>
<div>
<label for="signup-password" class="mb-2 block text-sm font-semibold text-slate-700">Password</label>
<div class="flex gap-2">
<input id="signup-password" name="password" type="password" placeholder="Create a password" required class="w-full rounded-2xl border border-zinc-300 bg-zinc-100 px-4 py-3 text-base text-zinc-800 placeholder:text-zinc-400 focus:border-blue-500 focus:bg-white focus:outline-none" />
<button type="button" data-toggle-password data-target="signup-password" class="rounded-2xl border border-zinc-300 bg-zinc-100 px-4 py-3 text-sm font-semibold text-slate-600 transition hover:bg-white">Show</button>
</div>
</div>
<button type="submit" class="w-full rounded-2xl bg-blue-600 px-4 py-3 text-lg font-semibold text-white transition hover:bg-blue-700">Create Account</button>
</form>
<p class="rounded-2xl border border-blue-100 bg-blue-50 px-4 py-3 text-sm text-blue-700">New sign-ups are assigned the default <strong>MEMBER</strong> role.</p>
</section>
<section data-tab-panel="reset" class="mt-7 hidden space-y-4">
<form hx-post="/auth/password-reset/request" hx-target="#flash" hx-swap="innerHTML" class="space-y-4">
<div>
<label for="reset-email" class="mb-2 block text-sm font-semibold text-slate-700">Email address</label>
<input id="reset-email" name="email" type="email" placeholder="name@email.com" required class="w-full rounded-2xl border border-zinc-300 bg-zinc-100 px-4 py-3 text-base text-zinc-800 placeholder:text-zinc-400 focus:border-blue-500 focus:bg-white focus:outline-none" />
</div>
<button type="submit" class="w-full rounded-2xl border border-zinc-300 bg-zinc-100 px-4 py-3 text-lg font-semibold text-zinc-700 transition hover:bg-white">Request reset token</button>
</form>
<form hx-post="/auth/password-reset/confirm" hx-target="#flash" hx-swap="innerHTML" class="space-y-4 rounded-2xl border border-zinc-200 bg-white p-4">
<p class="text-sm font-semibold text-slate-700">Confirm reset</p>
<input name="token" placeholder="Reset token" value="{{ resetToken }}" required class="w-full rounded-2xl border border-zinc-300 bg-zinc-100 px-4 py-3 text-base text-zinc-800 placeholder:text-zinc-400 focus:border-blue-500 focus:bg-white focus:outline-none" />
<div class="flex gap-2">
<input id="reset-password" name="password" type="password" placeholder="New password" required class="w-full rounded-2xl border border-zinc-300 bg-zinc-100 px-4 py-3 text-base text-zinc-800 placeholder:text-zinc-400 focus:border-blue-500 focus:bg-white focus:outline-none" />
<button type="button" data-toggle-password data-target="reset-password" class="rounded-2xl border border-zinc-300 bg-zinc-100 px-4 py-3 text-sm font-semibold text-slate-600 transition hover:bg-white">Show</button>
</div>
<button type="submit" class="w-full rounded-2xl bg-blue-600 px-4 py-3 text-lg font-semibold text-white transition hover:bg-blue-700">Update password</button>
</form>
</section>
</section>
<section class="rounded-3xl border border-zinc-200 bg-zinc-50 p-6 shadow-sm">
<div class="rounded-2xl border border-zinc-200 bg-zinc-100 p-4">
<h2 class="text-2xl font-semibold text-slate-800">Default Admin Account</h2>
<p class="mt-2 text-lg text-slate-700">Email: <strong>admin@gmail.com</strong></p>
<p class="text-lg text-slate-700">Password: <strong>Whatever123$</strong></p>
<button type="button" data-try-login data-demo-email="admin@gmail.com" data-demo-password="Whatever123$" class="mt-3 rounded-xl border border-blue-200 bg-blue-50 px-4 py-2 text-sm font-semibold text-blue-700 transition hover:bg-blue-100">Use these credentials</button>
</div>
<ul class="mt-4 list-disc space-y-1 pl-5 text-zinc-700">
<li><strong>Sign in with the credentials above to access an <span class="font-bold text-red-600">ADMIN</span> account.</strong></li>
<li><strong>New sign-ups are assigned the default <span class="font-bold text-blue-600">MEMBER</span> role.</strong></li>
</ul>
<h3 class="mt-5 text-2xl font-bold text-zinc-800">Role Permissions Overview</h3>
<div class="mt-4 space-y-4 text-zinc-700">
<section>
<h4 class="text-xl font-bold text-blue-600">MEMBER</h4>
<ul class="list-disc space-y-1 pl-5">
<li>Can view the list of blog posts only.</li>
<li>No permission to create, edit, or delete posts.</li>
</ul>
</section>
<section>
<h4 class="text-xl font-bold text-amber-600">MANAGER</h4>
<ul class="list-disc space-y-1 pl-5">
<li>Can view all blog posts.</li>
<li>Can create new blog posts.</li>
<li>Newly created posts are always saved as Draft by default.</li>
<li>Cannot update or delete any post.</li>
</ul>
</section>
<section>
<h4 class="text-xl font-bold text-red-600">ADMIN</h4>
<ul class="list-disc space-y-1 pl-5">
<li>Full access to the system.</li>
<li>Can create, read, update, and delete any blog post.</li>
<li>Can manage all content without restrictions.</li>
</ul>
</section>
</div>
</section>
</div>
<script>
(function() {
const container = document.getElementById("auth-tabs");
if (!container) return;
const buttons = Array.from(container.querySelectorAll("[data-tab-button]"));
const panels = Array.from(container.querySelectorAll("[data-tab-panel]"));
const magicLinkForm = document.getElementById("magic-link-form");
const magicLinkEmail = document.getElementById("magic-link-email");
function setActiveTab(tabName) {
buttons.forEach((button) => {
const isActive = button.getAttribute("data-tab") === tabName;
button.classList.toggle("bg-white", isActive);
button.classList.toggle("text-zinc-800", isActive);
button.classList.toggle("shadow-sm", isActive);
button.classList.toggle("text-slate-500", !isActive);
});
panels.forEach((panel) => {
panel.classList.toggle("hidden", panel.getAttribute("data-tab-panel") !== tabName);
});
}
const initialTab = container.getAttribute("data-initial-tab") || "signin";
setActiveTab(initialTab);
container.addEventListener("click", function(event) {
const tabButton = event.target.closest("[data-tab-button]");
if (!tabButton) return;
setActiveTab(tabButton.getAttribute("data-tab") || "signin");
});
if (magicLinkForm && magicLinkEmail) {
magicLinkForm.addEventListener("submit", function() {
const emailInput = document.getElementById("login-email");
magicLinkEmail.value = emailInput ? emailInput.value.trim() : "";
});
}
document.addEventListener("click", function(event) {
const toggleButton = event.target.closest("[data-toggle-password]");
if (toggleButton) {
const targetId = toggleButton.getAttribute("data-target");
const input = targetId ? document.getElementById(targetId) : null;
if (input) {
const isPassword = input.getAttribute("type") === "password";
input.setAttribute("type", isPassword ? "text" : "password");
toggleButton.textContent = isPassword ? "Hide" : "Show";
}
return;
}
const demoButton = event.target.closest("[data-try-login]");
if (!demoButton) return;
const emailInput = document.getElementById("login-email");
const passwordInput = document.getElementById("login-password");
if (!emailInput || !passwordInput) return;
setActiveTab("signin");
emailInput.value = demoButton.getAttribute("data-demo-email") || "";
passwordInput.value = demoButton.getAttribute("data-demo-password") || "";
passwordInput.focus();
});
})();
</script>
{% endblock %}