feat: Implement login functionality with captcha verification and associated UI.
This commit is contained in:
103
index.css
103
index.css
@@ -311,4 +311,107 @@ footer {
|
||||
color: var(--text-muted);
|
||||
}
|
||||
|
||||
/* Login Overlay Styles */
|
||||
.login-overlay {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background: transparent;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
z-index: 1000;
|
||||
backdrop-filter: blur(12px);
|
||||
-webkit-backdrop-filter: blur(12px);
|
||||
transition: opacity 0.5s ease;
|
||||
}
|
||||
|
||||
.login-card {
|
||||
background: var(--card-bg);
|
||||
border: 1px solid rgba(255, 255, 255, 0.4);
|
||||
border-radius: 24px;
|
||||
padding: 32px;
|
||||
width: 90%;
|
||||
max-width: 400px;
|
||||
box-shadow: 0 25px 50px -12px rgba(0, 0, 0, 0.3);
|
||||
animation: slideUp 0.6s cubic-bezier(0.16, 1, 0.3, 1) forwards;
|
||||
}
|
||||
|
||||
.login-header {
|
||||
text-align: center;
|
||||
margin-bottom: 24px;
|
||||
}
|
||||
|
||||
.login-header p {
|
||||
color: var(--text-muted);
|
||||
font-size: 0.9rem;
|
||||
}
|
||||
|
||||
.full-width {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.captcha-container {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 12px;
|
||||
margin-bottom: 8px;
|
||||
background: rgba(255, 255, 255, 0.5);
|
||||
padding: 8px;
|
||||
border-radius: 12px;
|
||||
border: 1px solid var(--border);
|
||||
}
|
||||
|
||||
#captchaCanvas {
|
||||
background: #fff;
|
||||
border-radius: 8px;
|
||||
flex-grow: 1;
|
||||
}
|
||||
|
||||
.icon-btn {
|
||||
background: #f3f4f6;
|
||||
color: var(--text);
|
||||
padding: 8px;
|
||||
border-radius: 8px;
|
||||
transition: background 0.2s;
|
||||
}
|
||||
|
||||
.icon-btn:hover {
|
||||
background: #e5e7eb;
|
||||
}
|
||||
|
||||
.error-message {
|
||||
background: rgba(239, 68, 68, 0.1);
|
||||
color: var(--danger);
|
||||
padding: 10px;
|
||||
border-radius: 8px;
|
||||
font-size: 0.85rem;
|
||||
margin-bottom: 16px;
|
||||
text-align: center;
|
||||
border: 1px solid rgba(239, 68, 68, 0.2);
|
||||
animation: shake 0.4s ease-in-out;
|
||||
}
|
||||
|
||||
@keyframes shake {
|
||||
|
||||
0%,
|
||||
100% {
|
||||
transform: translateX(0);
|
||||
}
|
||||
|
||||
25% {
|
||||
transform: translateX(-5px);
|
||||
}
|
||||
|
||||
50% {
|
||||
transform: translateX(5px);
|
||||
}
|
||||
|
||||
75% {
|
||||
transform: translateX(-5px);
|
||||
}
|
||||
}
|
||||
|
||||
/* Base CSS End */
|
||||
45
index.html
45
index.html
@@ -12,9 +12,51 @@
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div id="loginOverlay" class="login-overlay">
|
||||
<div class="login-card">
|
||||
<header class="login-header">
|
||||
<div class="icon-container">
|
||||
<svg width="32" height="32" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"
|
||||
stroke-linecap="round" stroke-linejoin="round">
|
||||
<rect x="3" y="11" width="18" height="11" rx="2" ry="2"></rect>
|
||||
<path d="M7 11V7a5 5 0 0 1 10 0v4"></path>
|
||||
</svg>
|
||||
</div>
|
||||
<h1>Login Access</h1>
|
||||
<p>Please enter your credentials to continue</p>
|
||||
</header>
|
||||
<form id="loginForm">
|
||||
<div class="form-group">
|
||||
<label for="username">Username</label>
|
||||
<input type="text" id="username" placeholder="Type admin" required>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="password">Password</label>
|
||||
<input type="password" id="password" placeholder="Type admin" required>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>Security Verification</label>
|
||||
<div class="captcha-container">
|
||||
<canvas id="captchaCanvas" width="150" height="50"></canvas>
|
||||
<button type="button" id="refreshCaptcha" class="icon-btn" title="Refresh Captcha">
|
||||
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor"
|
||||
stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||||
<path d="M21 12a9 9 0 1 1-9-9c2.52 0 4.93 1 6.74 2.74L21 8"></path>
|
||||
<path d="M21 3v5h-5"></path>
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
<input type="text" id="captchaInput" placeholder="Enter characters above" required>
|
||||
</div>
|
||||
<div id="loginError" class="error-message" style="display: none;"></div>
|
||||
<button type="submit" class="primary-btn full-width">Sign In</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="app-background"></div>
|
||||
|
||||
<div class="container">
|
||||
<div class="container" id="appContainer" style="display: none;">
|
||||
<header class="app-header">
|
||||
<div class="icon-container">
|
||||
<svg width="32" height="32" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"
|
||||
@@ -99,6 +141,7 @@
|
||||
</footer>
|
||||
</div>
|
||||
|
||||
<script src="login.js"></script>
|
||||
<script src="app.js"></script>
|
||||
</body>
|
||||
|
||||
|
||||
112
login.js
Normal file
112
login.js
Normal file
@@ -0,0 +1,112 @@
|
||||
// Login and Captcha Logic for btlabel
|
||||
|
||||
document.addEventListener('DOMContentLoaded', () => {
|
||||
const loginForm = document.getElementById('loginForm');
|
||||
const loginOverlay = document.getElementById('loginOverlay');
|
||||
const appContainer = document.getElementById('appContainer');
|
||||
const loginError = document.getElementById('loginError');
|
||||
const captchaCanvas = document.getElementById('captchaCanvas');
|
||||
const refreshCaptchaBtn = document.getElementById('refreshCaptcha');
|
||||
const captchaInput = document.getElementById('captchaInput');
|
||||
const usernameInput = document.getElementById('username');
|
||||
const passwordInput = document.getElementById('password');
|
||||
|
||||
let currentCaptcha = '';
|
||||
|
||||
// Generate random captcha string
|
||||
const generateCaptcha = () => {
|
||||
const chars = 'ABCDEFGHJKLMNPQRSTUVWXYZ23456789'; // Exclude similar looking chars like 0, O, I, 1
|
||||
let result = '';
|
||||
for (let i = 0; i < 6; i++) {
|
||||
result += chars.charAt(Math.floor(Math.random() * chars.length));
|
||||
}
|
||||
return result;
|
||||
};
|
||||
|
||||
// Draw captcha on canvas
|
||||
const drawCaptcha = (text) => {
|
||||
const ctx = captchaCanvas.getContext('2d');
|
||||
ctx.clearRect(0, 0, captchaCanvas.width, captchaCanvas.height);
|
||||
|
||||
// Background noise - lines
|
||||
for (let i = 0; i < 5; i++) {
|
||||
ctx.strokeStyle = `rgba(${Math.random() * 255}, ${Math.random() * 255}, ${Math.random() * 255}, 0.3)`;
|
||||
ctx.beginPath();
|
||||
ctx.moveTo(Math.random() * captchaCanvas.width, Math.random() * captchaCanvas.height);
|
||||
ctx.lineTo(Math.random() * captchaCanvas.width, Math.random() * captchaCanvas.height);
|
||||
ctx.stroke();
|
||||
}
|
||||
|
||||
// Background noise - dots
|
||||
for (let i = 0; i < 30; i++) {
|
||||
ctx.fillStyle = `rgba(${Math.random() * 255}, ${Math.random() * 255}, ${Math.random() * 255}, 0.3)`;
|
||||
ctx.beginPath();
|
||||
ctx.arc(Math.random() * captchaCanvas.width, Math.random() * captchaCanvas.height, 1, 0, Math.PI * 2);
|
||||
ctx.fill();
|
||||
}
|
||||
|
||||
// Draw text
|
||||
ctx.font = 'bold 30px Arial';
|
||||
ctx.textBaseline = 'middle';
|
||||
|
||||
const spacing = captchaCanvas.width / (text.length + 1);
|
||||
for (let i = 0; i < text.length; i++) {
|
||||
ctx.fillStyle = `rgb(${Math.random() * 100}, ${Math.random() * 100}, ${Math.random() * 100})`;
|
||||
const x = spacing * (i + 1);
|
||||
const y = captchaCanvas.height / 2 + (Math.random() * 10 - 5);
|
||||
const angle = (Math.random() * 0.4) - 0.2;
|
||||
|
||||
ctx.save();
|
||||
ctx.translate(x, y);
|
||||
ctx.rotate(angle);
|
||||
ctx.fillText(text[i], -10, 10);
|
||||
ctx.restore();
|
||||
}
|
||||
};
|
||||
|
||||
const refreshCaptcha = () => {
|
||||
currentCaptcha = generateCaptcha();
|
||||
drawCaptcha(currentCaptcha);
|
||||
captchaInput.value = '';
|
||||
};
|
||||
|
||||
// Initial captcha
|
||||
refreshCaptcha();
|
||||
|
||||
refreshCaptchaBtn.addEventListener('click', refreshCaptcha);
|
||||
|
||||
loginForm.addEventListener('submit', (e) => {
|
||||
e.preventDefault();
|
||||
|
||||
const username = usernameInput.value;
|
||||
const password = passwordInput.value;
|
||||
const captcha = captchaInput.value.toUpperCase();
|
||||
|
||||
loginError.style.display = 'none';
|
||||
|
||||
if (captcha !== currentCaptcha) {
|
||||
loginError.textContent = 'Invalid security code. Please try again.';
|
||||
loginError.style.display = 'block';
|
||||
refreshCaptcha();
|
||||
return;
|
||||
}
|
||||
|
||||
// Placeholder credentials
|
||||
if (username === 'admin' && password === 'admin') {
|
||||
// Success
|
||||
loginOverlay.style.opacity = '0';
|
||||
setTimeout(() => {
|
||||
loginOverlay.style.display = 'none';
|
||||
appContainer.style.display = 'block';
|
||||
// Trigger any initializations needed for the app
|
||||
if (typeof updatePreview === 'function') {
|
||||
updatePreview();
|
||||
}
|
||||
}, 500);
|
||||
} else {
|
||||
loginError.textContent = 'Invalid username or password.';
|
||||
loginError.style.display = 'block';
|
||||
refreshCaptcha();
|
||||
}
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user