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.
This commit is contained in:
145
ebook_backend_admin_panel/admin-frontend/admin_login.js
Normal file
145
ebook_backend_admin_panel/admin-frontend/admin_login.js
Normal file
@@ -0,0 +1,145 @@
|
||||
/**
|
||||
* 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);
|
||||
});
|
||||
Reference in New Issue
Block a user