Files
ebook-extension/ebook_backend_admin_panel/admin-frontend/admin_login.js
richardtekula f78c2199e1 Fix: Rename directory to remove & character causing shell issues
Renamed ebook_backend&admin_panel to ebook_backend_admin_panel
  The & character was being interpreted by shell as background
  process operator, causing 'Dockerfile not found' errors in Coolify.
2025-11-11 17:06:39 +01:00

146 lines
4.6 KiB
JavaScript

/**
* Shows a specific form (by ID) and hides others.
* Also clears any existing success or error messages.
*/
function showForm(formId) {
document.querySelectorAll('.form').forEach(form => {
form.classList.remove('active');
});
// Clear all existing error/success messages
document.querySelectorAll('.error, .success').forEach(msg => {
msg.textContent = '';
});
// Add slight delay for smooth CSS transition
setTimeout(() => {
document.getElementById(formId).classList.add('active');
}, 100);
}
/**
* Displays a message in a specified element with optional success styling.
*/
function showMessage(elementId, message, isSuccess = false) {
const element = document.getElementById(elementId);
element.textContent = message;
element.className = isSuccess ? 'success' : 'error';
}
/**
* Sets loading state for a button (e.g., during form submission).
* Disables the button and clears the text when loading, restores after.
*/
function setButtonLoading(button, isLoading) {
if (isLoading) {
button.classList.add('loading');
button.disabled = true;
button.textContent = '';
} else {
button.classList.remove('loading');
button.disabled = false;
button.textContent = button.getAttribute('data-original-text') || 'Submit';
}
}
// Initialize when the DOM is fully loaded
document.addEventListener('DOMContentLoaded', function() {
/**
* Store original button texts (used to restore text after loading).
*/
document.querySelectorAll('button[type="submit"]').forEach(btn => {
btn.setAttribute('data-original-text', btn.textContent);
});
/**
* Handles admin login form submission.
* Sends login data to server and displays result or error messages.
*/
document.getElementById('loginForm').addEventListener('submit', async (e) => {
e.preventDefault();
const submitBtn = e.target.querySelector('button[type="submit"]');
setButtonLoading(submitBtn, true);
const username = document.getElementById('loginUsername').value;
const password = document.getElementById('loginPassword').value;
try {
const response = await fetch('/admin/login', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({ username, password })
});
if (response.ok) {
showMessage('loginMessage', 'Login successful! Redirecting...', true);
setTimeout(() => {
window.location.href = '/';
}, 1500);
} else {
const data = await response.json();
showMessage('loginMessage', data.detail || 'Login failed');
}
} catch (error) {
showMessage('loginMessage', 'An error occurred');
} finally {
setButtonLoading(submitBtn, false);
submitBtn.textContent = 'Login';
}
});
/**
* Adds a ripple click effect to all buttons.
* Creates a circle animation where the button is clicked.
*/
document.querySelectorAll('button').forEach(button => {
button.addEventListener('click', function(e) {
const ripple = document.createElement('div');
const rect = this.getBoundingClientRect();
const size = Math.max(rect.width, rect.height);
const x = e.clientX - rect.left - size / 2;
const y = e.clientY - rect.top - size / 2;
ripple.style.cssText = `
position: absolute;
width: ${size}px;
height: ${size}px;
background: rgba(255,255,255,0.3);
border-radius: 50%;
transform: scale(0);
left: ${x}px;
top: ${y}px;
animation: ripple 0.6s ease-out;
pointer-events: none;
`;
this.appendChild(ripple);
setTimeout(() => {
ripple.remove();
}, 600);
});
});
/**
* Injects keyframe animation styles for ripple effect and ensures buttons are styled to allow overflow for the animation.
*/
const style = document.createElement('style');
style.textContent = `
@keyframes ripple {
to {
transform: scale(2);
opacity: 0;
}
}
button {
position: relative;
overflow: hidden;
}
`;
document.head.appendChild(style);
});