File size: 10,800 Bytes
7644eac
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
<!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>