“shubhamdhamal”
Deploy Flask app with Docker
7644eac
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Register | Learning Path</title>
<!-- Tailwind CSS via CDN -->
<script src="https://cdn.tailwindcss.com"></script>
<script>
(function () {
const storedTheme = localStorage.getItem('theme');
if (storedTheme === 'light') {
document.documentElement.classList.remove('dark');
} else {
document.documentElement.classList.add('dark');
}
})();
</script>
<!-- Glassmorphic CSS -->
<link rel="stylesheet" href="{{ url_for('static', filename='css/glassmorphic.css') }}">
</head>
<body class="grid-background min-h-screen flex items-center justify-center px-6">
<button id="theme-toggle" class="fixed top-6 right-6 inline-flex items-center justify-center w-10 h-10 rounded-full bg-gray-200 dark:bg-gray-700 text-secondary dark:text-gray-200 focus:outline-none focus:ring-2 focus:ring-magenta transition" aria-label="Toggle dark mode">
<svg id="theme-toggle-light-icon" class="w-6 h-6" fill="currentColor" viewBox="0 0 20 20" xmlns="http://www.w3.org/2000/svg">
<path d="M10 15a5 5 0 100-10 5 5 0 000 10zM10 1a1 1 0 011 1v1a1 1 0 11-2 0V2a1 1 0 011-1zm0 14a1 1 0 011 1v1a1 1 0 11-2 0v-1a1 1 0 011-1zm9-5a1 1 0 01-1 1h-1a1 1 0 110-2h1a1 1 0 011 1zM3 10a1 1 0 01-1 1H1a1 1 0 110-2h1a1 1 0 011 1zm12.364-6.364a1 1 0 010 1.414L14.95 6.464a1 1 0 01-1.414-1.414l1.414-1.414a1 1 0 011.414 0zM5.05 14.95a1 1 0 011.414 0l1.414-1.414a1 1 0 10-1.414-1.414L5.05 13.536a1 1 0 010 1.414zm9.9 0a1 1 0 10-1.414-1.414l-1.414 1.414a1 1 0 101.414 1.414l1.414-1.414zM5.05 5.05a1 1 0 011.414 0L7.878 6.464A1 1 0 116.464 7.878L5.05 6.464A1 1 0 015.05 5.05z" clip-rule="evenodd"></path>
</svg>
<svg id="theme-toggle-dark-icon" class="w-6 h-6 hidden" fill="currentColor" viewBox="0 0 20 20" xmlns="http://www.w3.org/2000/svg">
<path d="M17.293 13.293A8 8 0 016.707 2.707a8 8 0 1010.586 10.586z"></path>
</svg>
</button>
<!-- Registration Card -->
<div class="glass-card p-8 w-full max-w-md z-10 fade-in">
<h2 class="text-4xl font-bold text-gray-900 dark:text-white mb-8 text-center">
Create Account
</h2>
{% with messages = get_flashed_messages(with_categories=true) %}
{% if messages %}
{% for category, message in messages %}
<div class="mb-6 p-4 rounded-lg {% if category == 'success' %}bg-neon-green bg-opacity-20 border border-neon-green text-neon-green{% else %}bg-status-error bg-opacity-20 border border-status-error text-status-error{% endif %}">
{{ message }}
</div>
{% endfor %}
{% endif %}
{% endwith %}
<div class="space-y-4 mb-6">
<a href="{{ url_for('google_auth.start_google_login') }}" class="w-full inline-flex items-center justify-center gap-2 rounded-full border border-gray-300 dark:border-gray-600 px-4 py-3 text-sm font-semibold text-gray-700 dark:text-gray-100 hover:bg-gray-100 dark:hover:bg-gray-700 transition">
<svg class="w-5 h-5" viewBox="0 0 533.5 544.3" aria-hidden="true">
<path fill="#4285f4" d="M533.5 278.4c0-17.4-1.5-34.1-4.3-50.4H272v95.3h147.3c-6.4 34.5-26 63.6-55.3 83.2v68h89.2c52.1-48 80.3-118.7 80.3-196.1z"/>
<path fill="#34a853" d="M272 544.3c74.7 0 137.3-24.7 183.1-66.8l-89.2-68c-24.7 16.9-56.3 27-93.9 27-72.1 0-133.2-48.7-155.1-114.2H25.4v71.9C71.5 497.9 165.3 544.3 272 544.3z"/>
<path fill="#fbbc04" d="M116.9 322.3c-10-29.9-10-62.1 0-92l-71.5-71.9C18.5 206 0 256.2 0 311.4s18.5 105.4 45.4 152.9l71.5-71.9z"/>
<path fill="#ea4335" d="M272 107.7c39.6 0 75.3 13.7 103.3 40.6l77.4-77.4C409.3 24.7 346.7 0 272 0 165.3 0 71.5 46.4 25.4 146.1l71.5 71.9C138.8 156.4 199.9 107.7 272 107.7z"/>
</svg>
Continue with Google
</a>
<div class="flex items-center gap-3 text-xs text-gray-500 dark:text-gray-400">
<span class="flex-1 h-px bg-gray-300 dark:bg-gray-700"></span>
<span>or create an account</span>
<span class="flex-1 h-px bg-gray-300 dark:bg-gray-700"></span>
</div>
</div>
<form method="POST" action="{{ url_for('auth.register') }}" class="space-y-6">
{{ form.hidden_tag() }}
<div>
{{ form.username.label(class="block text-sm font-medium text-gray-700 dark:text-secondary mb-2") }}
{{ form.username(class="glass-input", placeholder="Choose a username") }}
<div id="username-feedback"></div>
{% for error in form.username.errors %}
<p class="text-status-error text-xs mt-1">{{ error }}</p>
{% endfor %}
</div>
<div>
{{ form.email.label(class="block text-sm font-medium text-gray-700 dark:text-secondary mb-2") }}
{{ form.email(class="glass-input", placeholder="you@example.com") }}
{% for error in form.email.errors %}
<p class="text-status-error text-xs mt-1">{{ error }}</p>
{% endfor %}
<button type="button" id="suggest-username-btn" class="text-neon-cyan text-xs mt-1 hover:underline">Suggest username from email</button>
</div>
<div>
{{ form.password.label(class="block text-sm font-medium text-gray-700 dark:text-secondary mb-2") }}
{{ form.password(class="glass-input", placeholder="••••••••") }}
<div id="password-strength"></div>
{% for error in form.password.errors %}
<p class="text-status-error text-xs mt-1">{{ error }}</p>
{% endfor %}
</div>
<div>
{{ form.password2.label(class="block text-sm font-medium text-gray-700 dark:text-secondary mb-2") }}
{{ form.password2(class="glass-input", placeholder="••••••••") }}
{% for error in form.password2.errors %}
<p class="text-status-error text-xs mt-1">{{ error }}</p>
{% endfor %}
</div>
{{ form.submit(class="neon-btn w-full") }}
</form>
<p class="text-center text-muted mt-6">
Already have an account?
<a href="{{ url_for('auth.login') }}" class="text-neon-cyan hover:underline">
Login
</a>
</p>
</div>
<script src="{{ url_for('static', filename='js/theme.js') }}"></script>
<script>
document.addEventListener('DOMContentLoaded', function () {
const usernameInput = document.getElementById('username');
const emailInput = document.getElementById('email');
const passwordInput = document.getElementById('password');
const usernameFeedback = document.getElementById('username-feedback');
const passwordStrength = document.getElementById('password-strength');
const suggestUsernameBtn = document.getElementById('suggest-username-btn');
// 1. Check Username Availability
usernameInput.addEventListener('input', debounce(async function () {
const username = usernameInput.value;
if (username.length < 3) {
usernameFeedback.innerHTML = '';
return;
}
const response = await fetch('/auth/check-username', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ username: username })
});
const data = await response.json();
if (data.available) {
usernameFeedback.innerHTML = `<span class="text-green-500 text-xs">Great choice! '${username}' is available.</span>`;
} else {
usernameFeedback.innerHTML = `<span class="text-red-500 text-xs">Oops! '${username}' is taken. Try another?</span>`;
}
}, 500));
// 2. Suggest Username
suggestUsernameBtn.addEventListener('click', async function () {
const email = emailInput.value;
if (!email) {
usernameFeedback.innerHTML = `<span class="text-yellow-600 text-xs">Please enter your email first.</span>`;
return;
}
const response = await fetch('/auth/suggest-username', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ email: email })
});
const data = await response.json();
if (data.suggestion) {
usernameInput.value = data.suggestion;
usernameFeedback.innerHTML = `<span class="text-blue-500 text-xs">How about this one? You can change it!</span>`;
}
});
// 3. Password Strength Meter
passwordInput.addEventListener('input', async function () {
const password = passwordInput.value;
if (!password) {
passwordStrength.innerHTML = '';
return;
}
const response = await fetch('/auth/check-password-strength', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ password: password })
});
const data = await response.json();
let strengthHTML = `<div class="text-xs">${data.message}</div>`;
strengthHTML += `<div class="w-full bg-gray-200 rounded-full h-2.5 mt-2"><div class="h-2.5 rounded-full ${data.color}" style="width: ${data.strength_percent}%"></div></div>`;
passwordStrength.innerHTML = strengthHTML;
});
// Debounce function to limit API calls
function debounce(func, delay) {
let timeout;
return function(...args) {
const context = this;
clearTimeout(timeout);
timeout = setTimeout(() => func.apply(context, args), delay);
};
}
});
</script>
</body>
</html>