Swap the muddy #f3ae3e palette for the real OLIVER brand pulled from the master
PPT template: yellow #FFCB05 + near-black #1A1A1A + off-white #F6F7F7, Montserrat
font. White-first page with a brand-yellow highlight rectangle behind page titles,
stat tiles with yellow left-strip, and a short yellow accent line under each
card section title — picks up the template's "01" chapter-marker rhythm.
Fixes two production bugs along the way:
- Nav stays pinned at top while page scrolls. The conflicting
`.navbar { position: relative !important }` rule was removed from nav.html
so the `position: fixed` from style.css can take effect.
- Clicking admin tabs no longer scrolls the page. Converted
`<a href="#users">` to `<button data-bs-target="#users">` (Bootstrap 5's
recommended pattern), so the anchor jump can't happen.
Other refinements: table padding loosened, `transform: scale` row hover
removed (jittery on dense rows), modal headers switched to near-black,
Chart.js palette aligned with brand tokens.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
334 lines
9.6 KiB
HTML
334 lines
9.6 KiB
HTML
{% extends "base.html" %}
|
|
|
|
{% block title %}Register - AgentHub{% endblock %}
|
|
|
|
{% block content %}
|
|
<div class="container my-5">
|
|
<div class="row justify-content-center">
|
|
<div class="col-md-6 col-lg-5">
|
|
<div class="card shadow-lg border-0">
|
|
<div class="card-body p-5">
|
|
<div class="text-center mb-4">
|
|
<div class="register-icon mb-3">
|
|
<i class="fas fa-user-plus"></i>
|
|
</div>
|
|
<h2 class="card-title mb-2">Create Account</h2>
|
|
<p class="text-muted">Join our community today</p>
|
|
</div>
|
|
|
|
<form method="POST" id="registerForm">
|
|
<div class="row">
|
|
<div class="col-md-6 mb-4">
|
|
<label for="firstName" class="form-label">
|
|
<i class="fas fa-user me-2"></i>First Name
|
|
</label>
|
|
<input type="text"
|
|
name="first-name"
|
|
class="form-control form-control-lg"
|
|
id="firstName"
|
|
placeholder="Enter your first name"
|
|
required>
|
|
</div>
|
|
<div class="col-md-6 mb-4">
|
|
<label for="lastName" class="form-label">
|
|
<i class="fas fa-user me-2"></i>Last Name
|
|
</label>
|
|
<input type="text"
|
|
name="last-name"
|
|
class="form-control form-control-lg"
|
|
id="lastName"
|
|
placeholder="Enter your last name"
|
|
required>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="mb-4">
|
|
<label for="userEmail" class="form-label">
|
|
<i class="fas fa-envelope me-2"></i>Email Address
|
|
</label>
|
|
<input type="email"
|
|
name="user-email"
|
|
class="form-control form-control-lg"
|
|
id="userEmail"
|
|
placeholder="Enter your email"
|
|
required>
|
|
<div class="form-text">
|
|
<i class="fas fa-shield-alt me-1"></i>
|
|
We'll never share your email with anyone else.
|
|
</div>
|
|
</div>
|
|
|
|
<div class="mb-4">
|
|
<label for="userPassword" class="form-label">
|
|
<i class="fas fa-lock me-2"></i>Password
|
|
</label>
|
|
<div class="password-input-group">
|
|
<input type="password"
|
|
name="user-password"
|
|
class="form-control form-control-lg"
|
|
id="userPassword"
|
|
placeholder="Create a strong password"
|
|
required
|
|
minlength="8">
|
|
<button type="button" class="password-toggle" onclick="togglePassword('userPassword')">
|
|
<i class="fas fa-eye" id="passwordIcon"></i>
|
|
</button>
|
|
</div>
|
|
<div class="password-strength mt-2">
|
|
<div class="strength-bar">
|
|
<div class="strength-fill" id="strengthFill"></div>
|
|
</div>
|
|
<small class="form-text text-muted" id="strengthText">
|
|
Password should be at least 8 characters long
|
|
</small>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="mb-4">
|
|
<label for="confirmPassword" class="form-label">
|
|
<i class="fas fa-lock me-2"></i>Confirm Password
|
|
</label>
|
|
<div class="password-input-group">
|
|
<input type="password"
|
|
name="confirm-password"
|
|
class="form-control form-control-lg"
|
|
id="confirmPassword"
|
|
placeholder="Confirm your password"
|
|
required
|
|
minlength="8">
|
|
<button type="button" class="password-toggle" onclick="togglePassword('confirmPassword')">
|
|
<i class="fas fa-eye" id="confirmPasswordIcon"></i>
|
|
</button>
|
|
</div>
|
|
<small class="form-text text-muted" id="passwordMatch"></small>
|
|
</div>
|
|
|
|
<div class="mb-4">
|
|
<div class="form-check">
|
|
<input class="form-check-input" type="checkbox" id="agreeTerms" required>
|
|
<label class="form-check-label" for="agreeTerms">
|
|
I agree to the <a href="#" class="text-decoration-none">Terms of Service</a>
|
|
and <a href="#" class="text-decoration-none">Privacy Policy</a>
|
|
</label>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="d-grid mb-4">
|
|
<button type="submit" class="btn btn-primary btn-lg">
|
|
<i class="fas fa-user-plus me-2"></i>Create Account
|
|
</button>
|
|
</div>
|
|
|
|
<div class="text-center">
|
|
<p class="text-muted mb-0">
|
|
Already have an account?
|
|
<a href="{{ base_path }}/login" class="text-decoration-none fw-bold">
|
|
Sign in here
|
|
</a>
|
|
</p>
|
|
</div>
|
|
</form>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<style>
|
|
.register-icon {
|
|
width: 80px;
|
|
height: 80px;
|
|
border-radius: 8px;
|
|
background: var(--brand-yellow);
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
margin: 0 auto;
|
|
font-size: 2rem;
|
|
color: var(--brand-dark);
|
|
}
|
|
|
|
.password-input-group {
|
|
position: relative;
|
|
}
|
|
|
|
.password-toggle {
|
|
position: absolute;
|
|
right: 15px;
|
|
top: 50%;
|
|
transform: translateY(-50%);
|
|
background: none;
|
|
border: none;
|
|
color: var(--text-light);
|
|
cursor: pointer;
|
|
padding: 0;
|
|
width: 20px;
|
|
height: 20px;
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
}
|
|
|
|
.password-toggle:hover {
|
|
color: var(--primary-color);
|
|
}
|
|
|
|
.strength-bar {
|
|
width: 100%;
|
|
height: 4px;
|
|
background-color: var(--border-color);
|
|
border-radius: 2px;
|
|
overflow: hidden;
|
|
margin-bottom: 4px;
|
|
}
|
|
|
|
.strength-fill {
|
|
height: 100%;
|
|
width: 0%;
|
|
transition: all 0.3s ease;
|
|
border-radius: 2px;
|
|
}
|
|
|
|
.strength-weak {
|
|
background-color: #ef4444;
|
|
width: 33%;
|
|
}
|
|
|
|
.strength-medium {
|
|
background-color: #f59e0b;
|
|
width: 66%;
|
|
}
|
|
|
|
.strength-strong {
|
|
background-color: #10b981;
|
|
width: 100%;
|
|
}
|
|
|
|
.form-control-lg {
|
|
padding: 12px 16px;
|
|
font-size: 1rem;
|
|
}
|
|
|
|
.card {
|
|
border-radius: 20px;
|
|
backdrop-filter: blur(10px);
|
|
background: rgba(255, 255, 255, 0.95);
|
|
}
|
|
|
|
.form-check-input:checked {
|
|
background-color: var(--primary-color);
|
|
border-color: var(--primary-color);
|
|
}
|
|
|
|
@media (max-width: 576px) {
|
|
.card-body {
|
|
padding: 2rem 1.5rem !important;
|
|
}
|
|
|
|
.register-icon {
|
|
width: 60px;
|
|
height: 60px;
|
|
font-size: 1.5rem;
|
|
}
|
|
}
|
|
</style>
|
|
|
|
<script>
|
|
function togglePassword(inputId) {
|
|
const passwordInput = document.getElementById(inputId);
|
|
let iconId = 'passwordIcon';
|
|
|
|
if (inputId === 'confirmPassword') {
|
|
iconId = 'confirmPasswordIcon';
|
|
}
|
|
|
|
const icon = document.getElementById(iconId);
|
|
|
|
if (passwordInput.type === 'password') {
|
|
passwordInput.type = 'text';
|
|
icon.className = 'fas fa-eye-slash';
|
|
} else {
|
|
passwordInput.type = 'password';
|
|
icon.className = 'fas fa-eye';
|
|
}
|
|
}
|
|
|
|
// Password strength checker
|
|
document.getElementById('userPassword').addEventListener('input', function() {
|
|
const password = this.value;
|
|
const strengthFill = document.getElementById('strengthFill');
|
|
const strengthText = document.getElementById('strengthText');
|
|
|
|
let strength = 0;
|
|
let text = '';
|
|
|
|
if (password.length >= 8) strength += 1;
|
|
if (password.match(/[a-z]/) && password.match(/[A-Z]/)) strength += 1;
|
|
if (password.match(/[0-9]/)) strength += 1;
|
|
if (password.match(/[^a-zA-Z0-9]/)) strength += 1;
|
|
|
|
strengthFill.className = 'strength-fill';
|
|
|
|
if (password.length === 0) {
|
|
text = 'Password should be at least 8 characters long';
|
|
} else if (strength <= 2) {
|
|
strengthFill.classList.add('strength-weak');
|
|
text = 'Weak password';
|
|
} else if (strength === 3) {
|
|
strengthFill.classList.add('strength-medium');
|
|
text = 'Medium strength password';
|
|
} else {
|
|
strengthFill.classList.add('strength-strong');
|
|
text = 'Strong password';
|
|
}
|
|
|
|
strengthText.textContent = text;
|
|
|
|
// Check password match when password changes
|
|
checkPasswordMatch();
|
|
});
|
|
|
|
// Password confirmation checker
|
|
document.getElementById('confirmPassword').addEventListener('input', checkPasswordMatch);
|
|
|
|
function checkPasswordMatch() {
|
|
const password = document.getElementById('userPassword').value;
|
|
const confirmPassword = document.getElementById('confirmPassword').value;
|
|
const matchText = document.getElementById('passwordMatch');
|
|
|
|
if (confirmPassword.length === 0) {
|
|
matchText.textContent = '';
|
|
return;
|
|
}
|
|
|
|
if (password === confirmPassword) {
|
|
matchText.textContent = '✓ Passwords match';
|
|
matchText.className = 'form-text text-success';
|
|
} else {
|
|
matchText.textContent = '✗ Passwords do not match';
|
|
matchText.className = 'form-text text-danger';
|
|
}
|
|
}
|
|
|
|
// Form validation before submit
|
|
document.getElementById('registerForm').addEventListener('submit', function(e) {
|
|
const password = document.getElementById('userPassword').value;
|
|
const confirmPassword = document.getElementById('confirmPassword').value;
|
|
|
|
if (password !== confirmPassword) {
|
|
e.preventDefault();
|
|
alert('Passwords do not match. Please check your passwords and try again.');
|
|
return false;
|
|
}
|
|
|
|
if (password.length < 8) {
|
|
e.preventDefault();
|
|
alert('Password must be at least 8 characters long.');
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
});
|
|
|
|
</script>
|
|
{% endblock %}
|