first commit
This commit is contained in:
230
public_web/templates/auth/register.html
Normal file
230
public_web/templates/auth/register.html
Normal file
@@ -0,0 +1,230 @@
|
||||
{% extends "base.html" %}
|
||||
|
||||
{% block title %}Register - Code of Conquest{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div class="auth-container">
|
||||
<h1 class="page-title">Register</h1>
|
||||
<p class="page-subtitle">Begin your epic journey</p>
|
||||
|
||||
<div class="decorative-line"></div>
|
||||
|
||||
<!-- Error display area for HTMX responses -->
|
||||
<div id="register-errors"></div>
|
||||
|
||||
<form
|
||||
id="register-form"
|
||||
hx-post="{{ api_base_url }}/api/v1/auth/register"
|
||||
hx-ext="json-enc"
|
||||
hx-target="#register-errors"
|
||||
hx-swap="innerHTML"
|
||||
>
|
||||
<div class="form-group">
|
||||
<label class="form-label" for="name">Character Name</label>
|
||||
<input
|
||||
type="text"
|
||||
id="name"
|
||||
name="name"
|
||||
class="form-input"
|
||||
placeholder="Enter your hero's name"
|
||||
required
|
||||
minlength="3"
|
||||
maxlength="50"
|
||||
autocomplete="name"
|
||||
>
|
||||
<span id="name-error" class="field-error"></span>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label class="form-label" for="email">Email Address</label>
|
||||
<input
|
||||
type="email"
|
||||
id="email"
|
||||
name="email"
|
||||
class="form-input"
|
||||
placeholder="adventurer@realm.com"
|
||||
required
|
||||
autocomplete="email"
|
||||
>
|
||||
<span id="email-error" class="field-error"></span>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label class="form-label" for="password">Password</label>
|
||||
<input
|
||||
type="password"
|
||||
id="password"
|
||||
name="password"
|
||||
class="form-input"
|
||||
placeholder="Create a strong passphrase"
|
||||
required
|
||||
autocomplete="new-password"
|
||||
>
|
||||
<div class="password-strength">
|
||||
<div class="strength-bar">
|
||||
<div id="strength-fill" class="strength-fill"></div>
|
||||
</div>
|
||||
<span id="strength-text" class="strength-text">Enter a password to see strength</span>
|
||||
</div>
|
||||
<span id="password-error" class="field-error"></span>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label class="form-label" for="confirm-password">Confirm Password</label>
|
||||
<input
|
||||
type="password"
|
||||
id="confirm-password"
|
||||
name="confirm_password"
|
||||
class="form-input"
|
||||
placeholder="Re-enter your passphrase"
|
||||
required
|
||||
autocomplete="new-password"
|
||||
>
|
||||
<span id="confirm-password-error" class="field-error"></span>
|
||||
</div>
|
||||
|
||||
<button type="submit" class="btn btn-primary">
|
||||
Begin Adventure
|
||||
<span class="htmx-indicator loading-spinner"></span>
|
||||
</button>
|
||||
</form>
|
||||
|
||||
<div class="form-links">
|
||||
<a href="{{ url_for('auth_views.login') }}" class="form-link">Already have an account? Login here</a>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
||||
{% block scripts %}
|
||||
<script>
|
||||
// Password strength indicator
|
||||
function updatePasswordStrength(password) {
|
||||
const fill = document.getElementById('strength-fill');
|
||||
const text = document.getElementById('strength-text');
|
||||
|
||||
if (!password) {
|
||||
fill.className = 'strength-fill';
|
||||
text.textContent = 'Enter a password to see strength';
|
||||
text.style.color = 'var(--text-muted)';
|
||||
return;
|
||||
}
|
||||
|
||||
let strength = 0;
|
||||
|
||||
// Check length
|
||||
if (password.length >= 8) strength++;
|
||||
if (password.length >= 12) strength++;
|
||||
|
||||
// Check for uppercase
|
||||
if (/[A-Z]/.test(password)) strength++;
|
||||
|
||||
// Check for lowercase
|
||||
if (/[a-z]/.test(password)) strength++;
|
||||
|
||||
// Check for numbers
|
||||
if (/[0-9]/.test(password)) strength++;
|
||||
|
||||
// Check for special characters
|
||||
if (/[^A-Za-z0-9]/.test(password)) strength++;
|
||||
|
||||
if (strength <= 2) {
|
||||
fill.className = 'strength-fill strength-weak';
|
||||
text.textContent = 'Weak - Add more complexity';
|
||||
text.style.color = 'var(--accent-red)';
|
||||
} else if (strength <= 4) {
|
||||
fill.className = 'strength-fill strength-medium';
|
||||
text.textContent = 'Medium - Almost there';
|
||||
text.style.color = 'var(--accent-gold)';
|
||||
} else {
|
||||
fill.className = 'strength-fill strength-strong';
|
||||
text.textContent = 'Strong - Excellent password';
|
||||
text.style.color = 'var(--accent-green)';
|
||||
}
|
||||
}
|
||||
|
||||
// Attach password strength checker
|
||||
document.getElementById('password').addEventListener('input', function() {
|
||||
updatePasswordStrength(this.value);
|
||||
});
|
||||
|
||||
// Validate password confirmation
|
||||
document.getElementById('confirm-password').addEventListener('input', function() {
|
||||
const password = document.getElementById('password').value;
|
||||
const confirmPassword = this.value;
|
||||
const errorSpan = document.getElementById('confirm-password-error');
|
||||
|
||||
if (confirmPassword && password !== confirmPassword) {
|
||||
errorSpan.textContent = 'Passwords do not match';
|
||||
} else {
|
||||
errorSpan.textContent = '';
|
||||
}
|
||||
});
|
||||
|
||||
// Handle HTMX response
|
||||
document.body.addEventListener('htmx:afterRequest', function(event) {
|
||||
// Only handle register form
|
||||
if (!event.detail.elt || event.detail.elt.id !== 'register-form') return;
|
||||
|
||||
try {
|
||||
const xhr = event.detail.xhr;
|
||||
|
||||
// Check if registration was successful
|
||||
if (xhr.status === 201) {
|
||||
const data = JSON.parse(xhr.responseText);
|
||||
|
||||
// Show success message
|
||||
document.getElementById('register-errors').innerHTML = '<div class="success-message">' +
|
||||
'Registration successful! Please check your email to verify your account.' +
|
||||
'</div>';
|
||||
|
||||
// Clear form
|
||||
document.getElementById('register-form').reset();
|
||||
updatePasswordStrength('');
|
||||
|
||||
// Redirect to login after delay
|
||||
setTimeout(function() {
|
||||
window.location.href = '{{ url_for("auth_views.login") }}';
|
||||
}, 2000);
|
||||
} else {
|
||||
// Handle error
|
||||
const data = JSON.parse(xhr.responseText);
|
||||
let errorHtml = '<div class="error-message">';
|
||||
|
||||
if (data.error && data.error.details) {
|
||||
for (const [field, message] of Object.entries(data.error.details)) {
|
||||
errorHtml += `<strong>${field}:</strong> ${message}<br>`;
|
||||
// Also show inline error
|
||||
const errorSpan = document.getElementById(field + '-error');
|
||||
if (errorSpan) {
|
||||
errorSpan.textContent = message;
|
||||
}
|
||||
}
|
||||
} else if (data.error && data.error.message) {
|
||||
errorHtml += data.error.message;
|
||||
} else {
|
||||
errorHtml += 'An error occurred. Please try again.';
|
||||
}
|
||||
errorHtml += '</div>';
|
||||
document.getElementById('register-errors').innerHTML = errorHtml;
|
||||
}
|
||||
} catch (e) {
|
||||
console.error('Error parsing response:', e);
|
||||
document.getElementById('register-errors').innerHTML =
|
||||
'<div class="error-message">An unexpected error occurred.</div>';
|
||||
}
|
||||
});
|
||||
|
||||
// Clear errors when user starts typing
|
||||
document.querySelectorAll('.form-input').forEach(input => {
|
||||
input.addEventListener('input', function() {
|
||||
const fieldName = this.name;
|
||||
const errorSpan = document.getElementById(fieldName + '-error');
|
||||
if (errorSpan) {
|
||||
errorSpan.textContent = '';
|
||||
}
|
||||
// Clear general errors
|
||||
document.getElementById('register-errors').innerHTML = '';
|
||||
});
|
||||
});
|
||||
</script>
|
||||
{% endblock %}
|
||||
Reference in New Issue
Block a user