Changement du layout de la page d'enregistrement
This commit is contained in:
+30
-11
@@ -205,21 +205,26 @@ def register():
|
|||||||
return redirect(url_for('main.dashboard'))
|
return redirect(url_for('main.dashboard'))
|
||||||
|
|
||||||
if request.method == 'POST':
|
if request.method == 'POST':
|
||||||
|
# Build form data from request to preserve state across re-renders
|
||||||
|
form_data = dict(request.form)
|
||||||
|
form_data['games'] = request.form.getlist('games')
|
||||||
|
|
||||||
# Validate CAPTCHA first
|
# Validate CAPTCHA first
|
||||||
captcha_answer = request.form.get('captcha_answer', '')
|
captcha_answer = request.form.get('captcha_answer', '')
|
||||||
if not verify_captcha(captcha_answer):
|
if not verify_captcha(captcha_answer):
|
||||||
flash('Incorrect CAPTCHA answer. Please try again.', 'danger')
|
flash('Incorrect CAPTCHA answer. Please try again.', 'danger')
|
||||||
captcha = generate_captcha() # Generate new captcha
|
captcha = generate_captcha()
|
||||||
|
# Clear password fields only on CAPTCHA failure
|
||||||
|
form_data.pop('password', None)
|
||||||
|
form_data.pop('confirm_password', None)
|
||||||
return render_template(
|
return render_template(
|
||||||
'pages/register.html',
|
'pages/register.html',
|
||||||
esport_games=ESPORT_GAMES,
|
esport_games=ESPORT_GAMES,
|
||||||
captcha=captcha
|
captcha=captcha,
|
||||||
|
form_data=form_data,
|
||||||
)
|
)
|
||||||
|
|
||||||
# Validate input with marshmallow schema
|
# Validate input with marshmallow schema
|
||||||
# Convert MultiDict to dict with proper lists for checkbox fields
|
|
||||||
form_data = dict(request.form)
|
|
||||||
form_data['games'] = request.form.getlist('games')
|
|
||||||
register_schema = RegisterSchema()
|
register_schema = RegisterSchema()
|
||||||
try:
|
try:
|
||||||
validated = register_schema.load(form_data)
|
validated = register_schema.load(form_data)
|
||||||
@@ -228,10 +233,14 @@ def register():
|
|||||||
for msg in messages:
|
for msg in messages:
|
||||||
flash(f'{field}: {msg}', 'danger')
|
flash(f'{field}: {msg}', 'danger')
|
||||||
captcha = generate_captcha()
|
captcha = generate_captcha()
|
||||||
|
# Clear password fields on validation failure
|
||||||
|
form_data.pop('password', None)
|
||||||
|
form_data.pop('confirm_password', None)
|
||||||
return render_template(
|
return render_template(
|
||||||
'pages/register.html',
|
'pages/register.html',
|
||||||
esport_games=ESPORT_GAMES,
|
esport_games=ESPORT_GAMES,
|
||||||
captcha=captcha
|
captcha=captcha,
|
||||||
|
form_data=form_data,
|
||||||
)
|
)
|
||||||
|
|
||||||
username = validated['username']
|
username = validated['username']
|
||||||
@@ -242,25 +251,30 @@ def register():
|
|||||||
selected_games = validated.get('games', [])
|
selected_games = validated.get('games', [])
|
||||||
discord_username = validated.get('discord_username')
|
discord_username = validated.get('discord_username')
|
||||||
discord_user_id = validated.get('discord_user_id')
|
discord_user_id = validated.get('discord_user_id')
|
||||||
trn_username = request.form.get('trn_username', '').strip() or None
|
|
||||||
league_os_profile = validated.get('league_os_profile')
|
league_os_profile = validated.get('league_os_profile')
|
||||||
|
|
||||||
if User.query.filter_by(username=username).first():
|
if User.query.filter_by(username=username).first():
|
||||||
flash('Username already exists.', 'danger')
|
flash('Username already exists.', 'danger')
|
||||||
captcha = generate_captcha()
|
captcha = generate_captcha()
|
||||||
|
form_data.pop('password', None)
|
||||||
|
form_data.pop('confirm_password', None)
|
||||||
return render_template(
|
return render_template(
|
||||||
'pages/register.html',
|
'pages/register.html',
|
||||||
esport_games=ESPORT_GAMES,
|
esport_games=ESPORT_GAMES,
|
||||||
captcha=captcha
|
captcha=captcha,
|
||||||
|
form_data=form_data,
|
||||||
)
|
)
|
||||||
|
|
||||||
if User.query.filter_by(email=email).first():
|
if User.query.filter_by(email=email).first():
|
||||||
flash('Email already registered.', 'danger')
|
flash('Email already registered.', 'danger')
|
||||||
captcha = generate_captcha()
|
captcha = generate_captcha()
|
||||||
|
form_data.pop('password', None)
|
||||||
|
form_data.pop('confirm_password', None)
|
||||||
return render_template(
|
return render_template(
|
||||||
'pages/register.html',
|
'pages/register.html',
|
||||||
esport_games=ESPORT_GAMES,
|
esport_games=ESPORT_GAMES,
|
||||||
captcha=captcha
|
captcha=captcha,
|
||||||
|
form_data=form_data,
|
||||||
)
|
)
|
||||||
|
|
||||||
hashed_password = hash_password(password)
|
hashed_password = hash_password(password)
|
||||||
@@ -299,9 +313,14 @@ def register():
|
|||||||
flash('Your account has been created! You can now log in.', 'success')
|
flash('Your account has been created! You can now log in.', 'success')
|
||||||
return redirect(url_for('auth.login'))
|
return redirect(url_for('auth.login'))
|
||||||
|
|
||||||
# Generate CAPTCHA only for GET requests
|
# GET request — render empty form
|
||||||
captcha = generate_captcha()
|
captcha = generate_captcha()
|
||||||
return render_template('pages/register.html', esport_games=ESPORT_GAMES, captcha=captcha)
|
return render_template(
|
||||||
|
'pages/register.html',
|
||||||
|
esport_games=ESPORT_GAMES,
|
||||||
|
captcha=captcha,
|
||||||
|
form_data={},
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
@auth_bp.route('/discord/login')
|
@auth_bp.route('/discord/login')
|
||||||
|
|||||||
@@ -3,71 +3,39 @@
|
|||||||
{% block auth_content %}
|
{% block auth_content %}
|
||||||
<form method="POST" action="{{ url_for('auth.register') }}" class="auth-form">
|
<form method="POST" action="{{ url_for('auth.register') }}" class="auth-form">
|
||||||
<input type="hidden" name="csrf_token" value="{{ csrf_token() }}"/>
|
<input type="hidden" name="csrf_token" value="{{ csrf_token() }}"/>
|
||||||
|
|
||||||
|
<!-- ==================== Personal Info ==================== -->
|
||||||
|
<h4 class="section-title"><i class="fas fa-id-card"></i> Personal Information</h4>
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label for="full_name"><i class="fas fa-id-card"></i> Full Name</label>
|
<label for="full_name"><i class="fas fa-id-card"></i> Full Name</label>
|
||||||
<input type="text" id="full_name" name="full_name" placeholder="Enter your full name" required>
|
<input type="text" id="full_name" name="full_name" placeholder="Enter your full name"
|
||||||
|
value="{{ form_data.get('full_name', '') }}" required>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label for="username"><i class="fas fa-user"></i> Username</label>
|
<label for="username"><i class="fas fa-user"></i> Username</label>
|
||||||
<input type="text" id="username" name="username" placeholder="Choose a username" required>
|
<input type="text" id="username" name="username" placeholder="Choose a username"
|
||||||
|
value="{{ form_data.get('username', '') }}" required>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label for="email"><i class="fas fa-envelope"></i> Email</label>
|
<label for="email"><i class="fas fa-envelope"></i> Email</label>
|
||||||
<input type="email" id="email" name="email" placeholder="Enter your email" required>
|
<input type="email" id="email" name="email" placeholder="Enter your email"
|
||||||
|
value="{{ form_data.get('email', '') }}" required>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label for="phone"><i class="fas fa-phone"></i> Phone (Optional)</label>
|
<label for="phone"><i class="fas fa-phone"></i> Phone (Optional)</label>
|
||||||
<input type="tel" id="phone" name="phone" placeholder="Enter your phone number">
|
<input type="tel" id="phone" name="phone" placeholder="Enter your phone number"
|
||||||
|
value="{{ form_data.get('phone', '') }}">
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<hr class="section-divider">
|
<hr class="section-divider">
|
||||||
<h4 class="section-title"><i class="fas fa-gamepad"></i> E-Sports Profile</h4>
|
|
||||||
<p class="text-muted small">Set up your competitive gaming profile for tryouts.</p>
|
|
||||||
|
|
||||||
<div class="form-group">
|
<!-- ==================== Discord Connection (before games) ==================== -->
|
||||||
<label><i class="fas fa-headset"></i> Games You Play</label>
|
<h4 class="section-title"><i class="fab fa-discord"></i> Discord Connection</h4>
|
||||||
<div class="checkbox-grid">
|
<p class="text-muted small">Connect your Discord account to automatically fill your gamertags from your connected game accounts.</p>
|
||||||
{% for game in esport_games %}
|
|
||||||
<label class="checkbox-label">
|
|
||||||
<input type="checkbox" name="games" value="{{ game }}"
|
|
||||||
{% if session.get('discord_oauth') and game in session['discord_oauth'].get('auto_select_games', []) %}checked{% endif %}
|
|
||||||
onchange="toggleGamertagInput(this)">
|
|
||||||
<span>{{ game }}</span>
|
|
||||||
</label>
|
|
||||||
{% endfor %}
|
|
||||||
</div>
|
|
||||||
<small class="form-text text-muted">Select all games you're signing in for.</small>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Gamertag fields per game (shown when game is checked or pre-filled from Discord) -->
|
|
||||||
{% set discord_suggestions = session.get('discord_oauth', {}).get('gamertag_suggestions', {}) %}
|
|
||||||
{% for game in esport_games %}
|
|
||||||
<div class="form-group gamertag-group" id="gamertag_group_{{ game|replace(' ', '_') }}"
|
|
||||||
style="{% if session.get('discord_oauth') and game in discord_suggestions %}display:block{% else %}display:none{% endif %}">
|
|
||||||
<label for="gamertag_{{ game }}"><i class="fas fa-gamepad"></i> {{ game }} Gamertag</label>
|
|
||||||
<input type="text" id="gamertag_{{ game }}" name="gamertag_{{ game }}"
|
|
||||||
placeholder="Enter your {{ game }} username"
|
|
||||||
value="{{ discord_suggestions.get(game, '') }}">
|
|
||||||
<small class="form-text text-muted">
|
|
||||||
Your in-game name or username for {{ game }}.
|
|
||||||
{% if game in discord_suggestions %}
|
|
||||||
<span class="text-success">Pre-filled from Discord connections.</span>
|
|
||||||
{% endif %}
|
|
||||||
</small>
|
|
||||||
</div>
|
|
||||||
{% endfor %}
|
|
||||||
|
|
||||||
<div class="form-group">
|
|
||||||
<label for="trn_username"><i class="fas fa-chart-line"></i> TRN (Tracker Network) Username</label>
|
|
||||||
<input type="text" id="trn_username" name="trn_username" placeholder="e.g. YourTrackerGGUsername">
|
|
||||||
<small class="form-text text-muted">Your public Tracker Network profile name. Others can click it to view your stats.</small>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Discord Connection Section -->
|
|
||||||
{% set discord_data = session.get('discord_oauth') %}
|
{% set discord_data = session.get('discord_oauth') %}
|
||||||
{% if discord_data %}
|
{% if discord_data %}
|
||||||
<div class="form-group discord-connected">
|
<div class="form-group discord-connected">
|
||||||
<label><i class="fab fa-discord"></i> Discord Account</label>
|
|
||||||
<div class="discord-connection-card">
|
<div class="discord-connection-card">
|
||||||
<div class="discord-user-display">
|
<div class="discord-user-display">
|
||||||
{% if discord_data.avatar %}
|
{% if discord_data.avatar %}
|
||||||
@@ -92,27 +60,76 @@
|
|||||||
<input type="hidden" name="discord_username" value="{{ discord_data.username }}">
|
<input type="hidden" name="discord_username" value="{{ discord_data.username }}">
|
||||||
<input type="hidden" name="discord_user_id" value="{{ discord_data.id }}">
|
<input type="hidden" name="discord_user_id" value="{{ discord_data.id }}">
|
||||||
<small class="form-text text-success">
|
<small class="form-text text-success">
|
||||||
<i class="fas fa-check-circle"></i> Your Discord account is connected. Gamertags from your linked game accounts have been pre-filled.
|
<i class="fas fa-check-circle"></i> Discord connected. Game connections have been used to pre-fill your profile below.
|
||||||
</small>
|
</small>
|
||||||
</div>
|
</div>
|
||||||
{% else %}
|
{% else %}
|
||||||
<div class="form-group discord-connect-section">
|
<div class="form-group discord-connect-section">
|
||||||
<label><i class="fab fa-discord"></i> Discord Connection</label>
|
|
||||||
<p class="text-muted small">Connect your Discord account to automatically fill your profile and gamertags from your connected game accounts (Steam, Battle.net, Xbox, etc.).</p>
|
|
||||||
<a href="{{ url_for('auth.discord_login') }}" class="btn btn-discord btn-block">
|
<a href="{{ url_for('auth.discord_login') }}" class="btn btn-discord btn-block">
|
||||||
<i class="fab fa-discord"></i> Connect Discord Account
|
<i class="fab fa-discord"></i> Connect Discord Account
|
||||||
</a>
|
</a>
|
||||||
<small class="form-text text-muted">You can also enter your Discord username manually below.</small>
|
<small class="form-text text-muted">Connect to pre-fill your gamertags from Steam, Battle.net, Xbox, etc.</small>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label for="discord_username"><i class="fab fa-discord"></i> Discord Username (Manual)</label>
|
<label for="discord_username"><i class="fab fa-discord"></i> Discord Username (Manual)</label>
|
||||||
<input type="text" id="discord_username" name="discord_username" placeholder="e.g. YourName">
|
<input type="text" id="discord_username" name="discord_username" placeholder="e.g. YourName"
|
||||||
|
value="{{ form_data.get('discord_username', '') }}">
|
||||||
</div>
|
</div>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
|
<hr class="section-divider">
|
||||||
|
|
||||||
|
<!-- ==================== E-Sports Profile ==================== -->
|
||||||
|
<h4 class="section-title"><i class="fas fa-gamepad"></i> E-Sports Profile</h4>
|
||||||
|
<p class="text-muted small">Set up your competitive gaming profile for tryouts.</p>
|
||||||
|
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label for="league_os_profile"><i class="fas fa-link"></i> League OS Connection</label>
|
<label><i class="fas fa-headset"></i> Games You Play</label>
|
||||||
<input type="text" id="league_os_profile" name="league_os_profile" placeholder="League OS profile link or ID">
|
<div class="checkbox-grid">
|
||||||
|
{% set selected_games = form_data.get('games', []) %}
|
||||||
|
{% set auto_select = session.get('discord_oauth', {}).get('auto_select_games', []) %}
|
||||||
|
{% for game in esport_games %}
|
||||||
|
{% set is_checked = game in selected_games or (not form_data and game in auto_select) %}
|
||||||
|
<label class="checkbox-label">
|
||||||
|
<input type="checkbox" name="games" value="{{ game }}"
|
||||||
|
{% if is_checked %}checked{% endif %}
|
||||||
|
onchange="toggleGamertagInput(this)">
|
||||||
|
<span>{{ game }}</span>
|
||||||
|
</label>
|
||||||
|
{% endfor %}
|
||||||
|
</div>
|
||||||
|
<small class="form-text text-muted">Select all games you're signing in for.</small>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Gamertag fields per game -->
|
||||||
|
{% set discord_suggestions = session.get('discord_oauth', {}).get('gamertag_suggestions', {}) %}
|
||||||
|
{% for game in esport_games %}
|
||||||
|
{% set game_id = game|replace(' ', '_') %}
|
||||||
|
{% set form_gamertag = form_data.get('gamertag_' ~ game, '') if form_data else '' %}
|
||||||
|
{% set suggested = discord_suggestions.get(game, '') %}
|
||||||
|
{% set gamertag_val = form_gamertag if form_gamertag else suggested %}
|
||||||
|
{% set game_checked = (form_data and game in selected_games) or (not form_data and game in auto_select) %}
|
||||||
|
{% set is_visible = gamertag_val or game_checked %}
|
||||||
|
<div class="form-group gamertag-group" id="gamertag_group_{{ game_id }}"
|
||||||
|
style="{% if is_visible %}display:block{% else %}display:none{% endif %}">
|
||||||
|
<label for="gamertag_{{ game }}"><i class="fas fa-gamepad"></i> {{ game }} Gamertag</label>
|
||||||
|
<input type="text" id="gamertag_{{ game }}" name="gamertag_{{ game }}"
|
||||||
|
placeholder="Enter your {{ game }} username"
|
||||||
|
value="{{ gamertag_val }}">
|
||||||
|
<small class="form-text text-muted">
|
||||||
|
Your in-game name for {{ game }}.
|
||||||
|
{% if suggested and not form_gamertag %}
|
||||||
|
<span class="text-success">Pre-filled from Discord connections.</span>
|
||||||
|
{% endif %}
|
||||||
|
</small>
|
||||||
|
</div>
|
||||||
|
{% endfor %}
|
||||||
|
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="league_os_profile"><i class="fas fa-link"></i> League OS Connection (Optional)</label>
|
||||||
|
<input type="text" id="league_os_profile" name="league_os_profile"
|
||||||
|
placeholder="League OS profile link or ID"
|
||||||
|
value="{{ form_data.get('league_os_profile', '') }}">
|
||||||
<small class="form-text text-muted">Connect your League OS profile for organized play.</small>
|
<small class="form-text text-muted">Connect your League OS profile for organized play.</small>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -120,16 +137,18 @@
|
|||||||
<h4 class="section-title"><i class="fas fa-shield-alt"></i> Security</h4>
|
<h4 class="section-title"><i class="fas fa-shield-alt"></i> Security</h4>
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label for="password"><i class="fas fa-lock"></i> Password</label>
|
<label for="password"><i class="fas fa-lock"></i> Password</label>
|
||||||
<input type="password" id="password" name="password" placeholder="Create a password" required minlength="6">
|
<input type="password" id="password" name="password" placeholder="Create a password"
|
||||||
|
required minlength="6">
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label for="confirm_password"><i class="fas fa-check-circle"></i> Confirm Password</label>
|
<label for="confirm_password"><i class="fas fa-check-circle"></i> Confirm Password</label>
|
||||||
<input type="password" id="confirm_password" name="confirm_password" placeholder="Confirm your password" required>
|
<input type="password" id="confirm_password" name="confirm_password" placeholder="Confirm your password"
|
||||||
|
required>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label for="captcha_answer"><i class="fas fa-calculator"></i> {{ captcha.question }}</label>
|
<label for="captcha_answer"><i class="fas fa-calculator"></i> {{ captcha.question }}</label>
|
||||||
<input type="number" id="captcha_answer" name="captcha_answer" placeholder="Answer" required>
|
<input type="number" id="captcha_answer" name="captcha_answer" placeholder="Answer" required autocomplete="off">
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<button type="submit" class="btn btn-primary btn-block">Create Account</button>
|
<button type="submit" class="btn btn-primary btn-block">Create Account</button>
|
||||||
@@ -143,13 +162,6 @@ function toggleGamertagInput(checkbox) {
|
|||||||
var group = document.getElementById('gamertag_group_' + game);
|
var group = document.getElementById('gamertag_group_' + game);
|
||||||
if (group) {
|
if (group) {
|
||||||
group.style.display = checkbox.checked ? 'block' : 'none';
|
group.style.display = checkbox.checked ? 'block' : 'none';
|
||||||
// Clear the input when unchecking (unless it was pre-filled from Discord)
|
|
||||||
if (!checkbox.checked) {
|
|
||||||
var input = group.querySelector('input[type="text"]');
|
|
||||||
if (input && !input.dataset.discordPrefilled) {
|
|
||||||
input.value = '';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -157,16 +169,7 @@ function toggleGamertagInput(checkbox) {
|
|||||||
document.addEventListener('DOMContentLoaded', function() {
|
document.addEventListener('DOMContentLoaded', function() {
|
||||||
var checkboxes = document.querySelectorAll('input[name="games"]');
|
var checkboxes = document.querySelectorAll('input[name="games"]');
|
||||||
checkboxes.forEach(function(checkbox) {
|
checkboxes.forEach(function(checkbox) {
|
||||||
var game = checkbox.value.replace(/ /g, '_');
|
toggleGamertagInput(checkbox);
|
||||||
var group = document.getElementById('gamertag_group_' + game);
|
|
||||||
if (group) {
|
|
||||||
group.style.display = checkbox.checked ? 'block' : 'none';
|
|
||||||
// Mark Discord-prefilled inputs so they aren't cleared on uncheck
|
|
||||||
var input = group.querySelector('input[type="text"]');
|
|
||||||
if (input && input.value) {
|
|
||||||
input.dataset.discordPrefilled = 'true';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -213,10 +213,6 @@ class RegisterSchema(StripMixin):
|
|||||||
allow_none=True,
|
allow_none=True,
|
||||||
load_default=None,
|
load_default=None,
|
||||||
)
|
)
|
||||||
trn_username = fields.String(
|
|
||||||
allow_none=True,
|
|
||||||
load_default=None,
|
|
||||||
)
|
|
||||||
league_os_profile = fields.String(
|
league_os_profile = fields.String(
|
||||||
validate=validate.Length(max=256),
|
validate=validate.Length(max=256),
|
||||||
allow_none=True,
|
allow_none=True,
|
||||||
|
|||||||
Reference in New Issue
Block a user