const form = document.getElementById("login-form"); const user = document.getElementById("user"); const pass = document.getElementById("pass"); const errEl = document.getElementById("error"); (async () => { try { const res = await fetch("/api/registration/status"); const data = await res.json(); if (data.enabled) { const el = document.getElementById("register-link"); if (el) el.style.display = ""; } } catch {} })(); if (typeof anime !== "undefined") { const particles = document.querySelectorAll(".particle"); particles.forEach((particle, i) => { anime({ targets: particle, translateX: () => anime.random(-100, 100), translateY: () => anime.random(-100, 100), scale: () => anime.random(0.5, 1.5), opacity: [0, 0.6, 0], duration: () => anime.random(3000, 6000), delay: i * 200, easing: "easeInOutQuad", loop: true, }); }); anime .timeline() .add({ targets: ".login-card", opacity: [0, 1], translateY: [30, 0], duration: 800, easing: "easeOutExpo", }) .add( { targets: ".login-crown-wrapper", scale: [0, 1], rotate: [180, 0], duration: 600, easing: "easeOutBack", }, "-=400", ) .add( { targets: ".login-brand", opacity: [0, 1], translateY: [-10, 0], duration: 500, easing: "easeOutQuad", }, "-=300", ) .add( { targets: ".login-title, .login-subtitle", opacity: [0, 1], translateY: [10, 0], duration: 500, delay: anime.stagger(100), easing: "easeOutQuad", }, "-=300", ) .add( { targets: ".form-group", opacity: [0, 1], translateY: [15, 0], duration: 500, delay: anime.stagger(100), easing: "easeOutQuad", }, "-=300", ) .add( { targets: ".login-btn", opacity: [0, 1], scale: [0.95, 1], duration: 400, easing: "easeOutQuad", }, "-=200", ); anime({ targets: ".login-crown", scale: [1, 1.1, 1], duration: 2000, easing: "easeInOutQuad", loop: true, }); const loginBtn = document.querySelector(".login-btn"); if (loginBtn) { const prefersReducedMotion = window.matchMedia( "(prefers-reduced-motion: reduce)", ).matches; let particleInterval = prefersReducedMotion ? 0 : 220; let currentInterval; function createParticle() { const btnRect = loginBtn.getBoundingClientRect(); const particle = document.createElement("div"); particle.className = "login-btn-particle"; const edge = Math.random(); let startX, startY, moveX, moveY; if (edge < 0.7) { startX = btnRect.left + Math.random() * btnRect.width; startY = btnRect.bottom; moveX = anime.random(-20, 20); moveY = anime.random(40, 80); } else if (edge < 0.8) { startX = btnRect.left; startY = btnRect.top + Math.random() * btnRect.height; moveX = anime.random(-50, -25); moveY = anime.random(-15, 25); } else if (edge < 0.9) { startX = btnRect.right; startY = btnRect.top + Math.random() * btnRect.height; moveX = anime.random(25, 50); moveY = anime.random(-15, 25); } else { startX = btnRect.left + Math.random() * btnRect.width; startY = btnRect.top; moveX = anime.random(-25, 25); moveY = anime.random(-50, -25); } const size = anime.random(3, 7); particle.style.width = size + "px"; particle.style.height = size + "px"; particle.style.left = startX + "px"; particle.style.top = startY + "px"; document.body.appendChild(particle); anime({ targets: particle, translateX: [0, moveX], translateY: [0, moveY], opacity: [0, 0.9, 0.7, 0], scale: [0.5, 1, 0.8, 0.3], rotate: anime.random(-180, 180), duration: anime.random(1200, 2000), easing: "easeOutQuad", complete: () => particle.remove(), }); } function startParticles(interval) { if (currentInterval) clearInterval(currentInterval); if (interval > 0) { currentInterval = setInterval(createParticle, interval); } } startParticles(particleInterval); loginBtn.addEventListener("mouseenter", () => { if (!prefersReducedMotion) { startParticles(120); } }); loginBtn.addEventListener("mouseleave", () => { startParticles(particleInterval); }); } document.querySelectorAll(".input-animated input").forEach((input) => { input.addEventListener("focus", (e) => { anime({ targets: e.target.parentElement, scale: [1, 1.02], duration: 200, easing: "easeOutQuad", }); }); input.addEventListener("blur", (e) => { anime({ targets: e.target.parentElement, scale: [1.02, 1], duration: 200, easing: "easeOutQuad", }); }); }); } form?.addEventListener("submit", async (e) => { e.preventDefault(); errEl.textContent = ""; const submitBtn = e.target.querySelector('button[type="submit"]'); if (typeof anime !== "undefined" && submitBtn) { anime({ targets: submitBtn, scale: [1, 0.95, 1], duration: 300, easing: "easeInOutQuad", }); } try { const res = await fetch("/api/login", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ user: user.value, pass: pass.value }), }); if (!res.ok) { const data = await res.json(); throw new Error(data.error || "Invalid credentials"); } const data = await res.json(); if (data.user && data.user.mustChangePassword) { sessionStorage.setItem("temp_token", data.token); sessionStorage.setItem("temp_user", JSON.stringify(data.user)); window.location.href = "/change-password.html"; } else { window.location.href = "/"; } } catch (err) { errEl.textContent = err.message || "Login failed"; if (typeof anime !== "undefined") { anime({ targets: ".login-card", translateX: [ { value: -10, duration: 100 }, { value: 10, duration: 100 }, { value: -10, duration: 100 }, { value: 10, duration: 100 }, { value: 0, duration: 100 }, ], easing: "easeInOutQuad", }); anime({ targets: "#error", opacity: [0, 1], translateY: [-5, 0], duration: 300, easing: "easeOutQuad", }); } } });