Internal
Public Access
Add email verification, admin approval, and user profile enhancements
Implement comprehensive email verification and admin approval system for user registration. Users must verify their email before logging in, and admins must approve new users before they can access the system. Major features: - Email verification with UUID tokens (24hr expiry, one-time use) - Admin approval workflow via Django admin interface - Custom authentication backend enforcing verification and approval - Password change functionality for authenticated users - Enhanced profile page with proper styling and theme support - Collect first name, last name, and timezone during registration - Profile link added to header navigation Email notifications: - Verification email sent after registration - Admin notification when users need approval - Approval notification sent to users Security features: - Cryptographically secure UUID tokens - Token expiration and one-time use enforcement - Email enumeration protection - Custom JWT token validation for API access - Existing users auto-approved via data migration Templates added: - Email templates (verification, approval, admin notification) - Web templates (password change, verification pages) - Enhanced profile page with dark/light mode support 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Sonnet 4.5
parent
87afc4e80c
commit
21d9d01885
@@ -0,0 +1,87 @@
|
||||
{% extends 'base.html' %}
|
||||
|
||||
{% block title %}Change Password - KeepItGoing{% endblock %}
|
||||
|
||||
{% block body %}
|
||||
<div class="app-layout" style="display: flex; min-height: 100vh;">
|
||||
<!-- Simple header for non-dashboard pages -->
|
||||
<div style="position: fixed; top: 0; left: 0; right: 0; background: var(--color-bg-secondary, #fff); border-bottom: 1px solid var(--color-border, #e5e7eb); padding: 1rem 2rem; z-index: 100; display: flex; justify-content: space-between; align-items: center;">
|
||||
<a href="{% url 'dashboard' %}" style="font-size: 1.25rem; font-weight: 600; text-decoration: none; color: var(--color-text, #111);">KeepItGoing</a>
|
||||
<div style="display: flex; gap: 1rem; align-items: center;">
|
||||
<span style="color: var(--color-text-secondary, #666);">{{ user.email }}</span>
|
||||
<a href="{% url 'profile' %}" style="color: var(--color-primary, #3B82F6); text-decoration: none;">Profile</a>
|
||||
<a href="{% url 'logout' %}" style="color: var(--color-text-secondary, #666); text-decoration: none;">Logout</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Main content -->
|
||||
<div style="flex: 1; padding: 5rem 2rem 2rem; max-width: 600px; margin: 0 auto; width: 100%;">
|
||||
<h1 style="font-size: 2rem; font-weight: 700; margin-bottom: 2rem; color: var(--color-text, #111);">Change Password</h1>
|
||||
|
||||
{% if success %}
|
||||
<div style="background-color: #d1fae5; border: 1px solid #6ee7b7; color: #065f46; padding: 1rem; border-radius: 0.5rem; margin-bottom: 1.5rem;">
|
||||
<strong>✓ Success!</strong><br>
|
||||
{{ success }}
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
<form method="post" style="background: var(--color-bg-secondary, #fff); padding: 2rem; border-radius: 0.5rem; box-shadow: 0 1px 3px rgba(0,0,0,0.1);">
|
||||
{% csrf_token %}
|
||||
|
||||
<div style="margin-bottom: 1.5rem;">
|
||||
<label for="old_password" style="display: block; margin-bottom: 0.5rem; font-weight: 500; color: var(--color-text, #111);">Current Password</label>
|
||||
<input type="password"
|
||||
id="old_password"
|
||||
name="old_password"
|
||||
required
|
||||
style="width: 100%; padding: 0.75rem; border: 1px solid var(--color-border, #d1d5db); border-radius: 0.375rem; font-size: 1rem;">
|
||||
{% if errors.old_password %}
|
||||
<p style="color: #dc2626; margin-top: 0.25rem; font-size: 0.875rem;">{{ errors.old_password }}</p>
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
<div style="margin-bottom: 1.5rem;">
|
||||
<label for="new_password" style="display: block; margin-bottom: 0.5rem; font-weight: 500; color: var(--color-text, #111);">New Password</label>
|
||||
<input type="password"
|
||||
id="new_password"
|
||||
name="new_password"
|
||||
required
|
||||
minlength="8"
|
||||
style="width: 100%; padding: 0.75rem; border: 1px solid var(--color-border, #d1d5db); border-radius: 0.375rem; font-size: 1rem;">
|
||||
<p style="color: var(--color-text-secondary, #666); margin-top: 0.25rem; font-size: 0.875rem;">Must be at least 8 characters</p>
|
||||
{% if errors.new_password %}
|
||||
<p style="color: #dc2626; margin-top: 0.25rem; font-size: 0.875rem;">{{ errors.new_password }}</p>
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
<div style="margin-bottom: 1.5rem;">
|
||||
<label for="confirm_password" style="display: block; margin-bottom: 0.5rem; font-weight: 500; color: var(--color-text, #111);">Confirm New Password</label>
|
||||
<input type="password"
|
||||
id="confirm_password"
|
||||
name="confirm_password"
|
||||
required
|
||||
minlength="8"
|
||||
style="width: 100%; padding: 0.75rem; border: 1px solid var(--color-border, #d1d5db); border-radius: 0.375rem; font-size: 1rem;">
|
||||
{% if errors.confirm_password %}
|
||||
<p style="color: #dc2626; margin-top: 0.25rem; font-size: 0.875rem;">{{ errors.confirm_password }}</p>
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
<div style="display: flex; gap: 1rem;">
|
||||
<button type="submit"
|
||||
style="flex: 1; background-color: #3B82F6; color: white; padding: 0.75rem 1.5rem; border: none; border-radius: 0.375rem; font-size: 1rem; font-weight: 600; cursor: pointer;">
|
||||
Change Password
|
||||
</button>
|
||||
<a href="{% url 'profile' %}"
|
||||
style="flex: 1; background-color: var(--color-bg, #f3f4f6); color: var(--color-text, #111); padding: 0.75rem 1.5rem; border: none; border-radius: 0.375rem; font-size: 1rem; font-weight: 600; text-decoration: none; text-align: center; display: flex; align-items: center; justify-content: center;">
|
||||
Cancel
|
||||
</a>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
<div style="margin-top: 2rem; text-align: center;">
|
||||
<a href="{% url 'dashboard' %}" style="color: var(--color-text-secondary, #666); text-decoration: none; font-size: 0.875rem;">← Back to Dashboard</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
@@ -7,14 +7,24 @@
|
||||
<h1 class="auth-title">Login</h1>
|
||||
|
||||
{% if error %}
|
||||
<div class="alert alert-error">{{ error }}</div>
|
||||
<div class="alert alert-error">
|
||||
{{ error }}
|
||||
{% if can_resend %}
|
||||
<p style="margin-top: 10px;">
|
||||
<a href="{% url 'resend-verification' %}" style="color: inherit; text-decoration: underline;">
|
||||
Resend verification email
|
||||
</a>
|
||||
</p>
|
||||
{% endif %}
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
<form method="post">
|
||||
{% csrf_token %}
|
||||
<div class="form-group">
|
||||
<label class="form-label" for="email">Email</label>
|
||||
<input type="email" class="form-input" id="email" name="email" required autofocus>
|
||||
<input type="email" class="form-input" id="email" name="email"
|
||||
value="{{ email|default:'' }}" required autofocus>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="form-label" for="password">Password</label>
|
||||
|
||||
@@ -3,75 +3,125 @@
|
||||
{% block title %}Profile - KeepItGoing{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div class="page-header">
|
||||
<h1>Profile Settings</h1>
|
||||
<div class="page-header" style="margin-bottom: 2rem;">
|
||||
<h1 style="font-size: 1.875rem; font-weight: 700;">Profile Settings</h1>
|
||||
<p style="color: var(--text-secondary); margin-top: 0.5rem;">Manage your account information and preferences</p>
|
||||
</div>
|
||||
|
||||
{% if success %}
|
||||
<div class="alert alert-success">{{ success }}</div>
|
||||
<div class="alert alert-success" style="margin-bottom: 1.5rem;">
|
||||
<strong>✓ Success!</strong> {{ success }}
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
<div class="card">
|
||||
<!-- Personal Information -->
|
||||
<div class="card" style="background: var(--surface); padding: 1.5rem; border-radius: 0.75rem; margin-bottom: 1.5rem; border: 1px solid var(--border);">
|
||||
<h2 style="font-size: 1.25rem; font-weight: 600; margin-bottom: 1.5rem;">Personal Information</h2>
|
||||
|
||||
<form method="post">
|
||||
{% csrf_token %}
|
||||
<div class="form-row">
|
||||
|
||||
<div style="display: grid; grid-template-columns: 1fr 1fr; gap: 1.5rem; margin-bottom: 1.5rem;">
|
||||
<div class="form-group">
|
||||
<label for="first_name">First Name</label>
|
||||
<input type="text" id="first_name" name="first_name" value="{{ user.first_name }}">
|
||||
<label for="first_name" style="display: block; margin-bottom: 0.5rem; font-weight: 500; font-size: 0.875rem;">First Name</label>
|
||||
<input type="text"
|
||||
id="first_name"
|
||||
name="first_name"
|
||||
value="{{ user.first_name }}"
|
||||
style="width: 100%; padding: 0.625rem; background: var(--bg); border: 1px solid var(--border); border-radius: 0.375rem; color: var(--text-primary); font-size: 0.875rem;">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="last_name">Last Name</label>
|
||||
<input type="text" id="last_name" name="last_name" value="{{ user.last_name }}">
|
||||
<label for="last_name" style="display: block; margin-bottom: 0.5rem; font-weight: 500; font-size: 0.875rem;">Last Name</label>
|
||||
<input type="text"
|
||||
id="last_name"
|
||||
name="last_name"
|
||||
value="{{ user.last_name }}"
|
||||
style="width: 100%; padding: 0.625rem; background: var(--bg); border: 1px solid var(--border); border-radius: 0.375rem; color: var(--text-primary); font-size: 0.875rem;">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="email">Email</label>
|
||||
<input type="email" id="email" value="{{ user.email }}" disabled>
|
||||
<small>Email cannot be changed</small>
|
||||
<div class="form-group" style="margin-bottom: 1.5rem;">
|
||||
<label for="email" style="display: block; margin-bottom: 0.5rem; font-weight: 500; font-size: 0.875rem;">Email</label>
|
||||
<input type="email"
|
||||
id="email"
|
||||
value="{{ user.email }}"
|
||||
disabled
|
||||
style="width: 100%; padding: 0.625rem; background: var(--surface); border: 1px solid var(--border); border-radius: 0.375rem; color: var(--text-secondary); font-size: 0.875rem; cursor: not-allowed; opacity: 0.6;">
|
||||
<small style="display: block; margin-top: 0.375rem; color: var(--text-secondary); font-size: 0.75rem;">Email cannot be changed</small>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="timezone">Timezone</label>
|
||||
<select id="timezone" name="timezone">
|
||||
<div class="form-group" style="margin-bottom: 1.5rem;">
|
||||
<label for="timezone" style="display: block; margin-bottom: 0.5rem; font-weight: 500; font-size: 0.875rem;">Timezone</label>
|
||||
<select id="timezone"
|
||||
name="timezone"
|
||||
style="width: 100%; padding: 0.625rem; background: var(--bg); border: 1px solid var(--border); border-radius: 0.375rem; color: var(--text-primary); font-size: 0.875rem;">
|
||||
<option value="UTC" {% if user.timezone == 'UTC' %}selected{% endif %}>UTC</option>
|
||||
<option value="America/New_York" {% if user.timezone == 'America/New_York' %}selected{% endif %}>Eastern Time</option>
|
||||
<option value="America/Chicago" {% if user.timezone == 'America/Chicago' %}selected{% endif %}>Central Time</option>
|
||||
<option value="America/Denver" {% if user.timezone == 'America/Denver' %}selected{% endif %}>Mountain Time</option>
|
||||
<option value="America/Los_Angeles" {% if user.timezone == 'America/Los_Angeles' %}selected{% endif %}>Pacific Time</option>
|
||||
<option value="Europe/London" {% if user.timezone == 'Europe/London' %}selected{% endif %}>London</option>
|
||||
<option value="Europe/Paris" {% if user.timezone == 'Europe/Paris' %}selected{% endif %}>Paris</option>
|
||||
<option value="Asia/Tokyo" {% if user.timezone == 'Asia/Tokyo' %}selected{% endif %}>Tokyo</option>
|
||||
<option value="America/New_York" {% if user.timezone == 'America/New_York' %}selected{% endif %}>Eastern Time (ET)</option>
|
||||
<option value="America/Chicago" {% if user.timezone == 'America/Chicago' %}selected{% endif %}>Central Time (CT)</option>
|
||||
<option value="America/Denver" {% if user.timezone == 'America/Denver' %}selected{% endif %}>Mountain Time (MT)</option>
|
||||
<option value="America/Los_Angeles" {% if user.timezone == 'America/Los_Angeles' %}selected{% endif %}>Pacific Time (PT)</option>
|
||||
<option value="Europe/London" {% if user.timezone == 'Europe/London' %}selected{% endif %}>London (GMT)</option>
|
||||
<option value="Europe/Paris" {% if user.timezone == 'Europe/Paris' %}selected{% endif %}>Paris (CET)</option>
|
||||
<option value="Asia/Tokyo" {% if user.timezone == 'Asia/Tokyo' %}selected{% endif %}>Tokyo (JST)</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="default_reminder_minutes">Default Reminder (minutes before due)</label>
|
||||
<select id="default_reminder_minutes" name="default_reminder_minutes">
|
||||
<div class="form-group" style="margin-bottom: 1.5rem;">
|
||||
<label for="default_reminder_minutes" style="display: block; margin-bottom: 0.5rem; font-weight: 500; font-size: 0.875rem;">Default Reminder</label>
|
||||
<select id="default_reminder_minutes"
|
||||
name="default_reminder_minutes"
|
||||
style="width: 100%; padding: 0.625rem; background: var(--bg); border: 1px solid var(--border); border-radius: 0.375rem; color: var(--text-primary); font-size: 0.875rem;">
|
||||
<option value="0" {% if user.default_reminder_minutes == 0 %}selected{% endif %}>No reminder</option>
|
||||
<option value="5" {% if user.default_reminder_minutes == 5 %}selected{% endif %}>5 minutes</option>
|
||||
<option value="15" {% if user.default_reminder_minutes == 15 %}selected{% endif %}>15 minutes</option>
|
||||
<option value="30" {% if user.default_reminder_minutes == 30 %}selected{% endif %}>30 minutes</option>
|
||||
<option value="60" {% if user.default_reminder_minutes == 60 %}selected{% endif %}>1 hour</option>
|
||||
<option value="1440" {% if user.default_reminder_minutes == 1440 %}selected{% endif %}>1 day</option>
|
||||
<option value="5" {% if user.default_reminder_minutes == 5 %}selected{% endif %}>5 minutes before</option>
|
||||
<option value="15" {% if user.default_reminder_minutes == 15 %}selected{% endif %}>15 minutes before</option>
|
||||
<option value="30" {% if user.default_reminder_minutes == 30 %}selected{% endif %}>30 minutes before</option>
|
||||
<option value="60" {% if user.default_reminder_minutes == 60 %}selected{% endif %}>1 hour before</option>
|
||||
<option value="1440" {% if user.default_reminder_minutes == 1440 %}selected{% endif %}>1 day before</option>
|
||||
</select>
|
||||
<small style="display: block; margin-top: 0.375rem; color: var(--text-secondary); font-size: 0.75rem;">How long before a task is due should you be reminded?</small>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label class="checkbox-label">
|
||||
<input type="checkbox" name="email_notifications" {% if user.email_notifications %}checked{% endif %}>
|
||||
Email Notifications
|
||||
<div style="margin-bottom: 1.5rem;">
|
||||
<label style="display: flex; align-items: center; cursor: pointer; padding: 0.75rem; background: var(--surface); border: 1px solid var(--border); border-radius: 0.375rem; margin-bottom: 0.75rem;">
|
||||
<input type="checkbox"
|
||||
name="email_notifications"
|
||||
{% if user.email_notifications %}checked{% endif %}
|
||||
style="margin-right: 0.75rem; width: 1rem; height: 1rem;">
|
||||
<div>
|
||||
<div style="font-weight: 500; font-size: 0.875rem;">Email Notifications</div>
|
||||
<div style="color: var(--text-secondary); font-size: 0.75rem;">Receive task reminders and updates via email</div>
|
||||
</div>
|
||||
</label>
|
||||
|
||||
<label style="display: flex; align-items: center; cursor: pointer; padding: 0.75rem; background: var(--surface); border: 1px solid var(--border); border-radius: 0.375rem;">
|
||||
<input type="checkbox"
|
||||
name="push_notifications"
|
||||
{% if user.push_notifications %}checked{% endif %}
|
||||
style="margin-right: 0.75rem; width: 1rem; height: 1rem;">
|
||||
<div>
|
||||
<div style="font-weight: 500; font-size: 0.875rem;">Push Notifications</div>
|
||||
<div style="color: var(--text-secondary); font-size: 0.75rem;">Receive push notifications on your devices</div>
|
||||
</div>
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label class="checkbox-label">
|
||||
<input type="checkbox" name="push_notifications" {% if user.push_notifications %}checked{% endif %}>
|
||||
Push Notifications
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<button type="submit" class="btn btn-primary">Save Changes</button>
|
||||
<button type="submit"
|
||||
class="btn btn-primary"
|
||||
style="padding: 0.625rem 1.25rem; font-size: 0.875rem;">
|
||||
Save Changes
|
||||
</button>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<!-- Security Section -->
|
||||
<div class="card" style="background: var(--surface); padding: 1.5rem; border-radius: 0.75rem; border: 1px solid var(--border);">
|
||||
<h2 style="font-size: 1.25rem; font-weight: 600; margin-bottom: 0.5rem;">Security</h2>
|
||||
<p style="color: var(--text-secondary); margin-bottom: 1.25rem; font-size: 0.875rem;">Manage your account security settings</p>
|
||||
|
||||
<a href="{% url 'change-password' %}"
|
||||
class="btn btn-secondary"
|
||||
style="display: inline-block; padding: 0.625rem 1.25rem; text-decoration: none; font-size: 0.875rem;">
|
||||
Change Password
|
||||
</a>
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
||||
@@ -18,11 +18,38 @@
|
||||
{% csrf_token %}
|
||||
<div class="form-group">
|
||||
<label class="form-label" for="email">Email</label>
|
||||
<input type="email" class="form-input" id="email" name="email" required autofocus>
|
||||
<input type="email" class="form-input" id="email" name="email"
|
||||
value="{{ email|default:'' }}" required autofocus>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="form-label" for="username">Username</label>
|
||||
<input type="text" class="form-input" id="username" name="username" required>
|
||||
<input type="text" class="form-input" id="username" name="username"
|
||||
value="{{ username|default:'' }}" required>
|
||||
</div>
|
||||
<div class="form-row">
|
||||
<div class="form-group">
|
||||
<label class="form-label" for="first_name">First Name</label>
|
||||
<input type="text" class="form-input" id="first_name" name="first_name"
|
||||
value="{{ first_name|default:'' }}">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="form-label" for="last_name">Last Name</label>
|
||||
<input type="text" class="form-input" id="last_name" name="last_name"
|
||||
value="{{ last_name|default:'' }}">
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="form-label" for="timezone">Timezone</label>
|
||||
<select class="form-input" id="timezone" name="timezone">
|
||||
<option value="UTC" {% if timezone == 'UTC' or not timezone %}selected{% endif %}>UTC</option>
|
||||
<option value="America/New_York" {% if timezone == 'America/New_York' %}selected{% endif %}>Eastern Time (ET)</option>
|
||||
<option value="America/Chicago" {% if timezone == 'America/Chicago' %}selected{% endif %}>Central Time (CT)</option>
|
||||
<option value="America/Denver" {% if timezone == 'America/Denver' %}selected{% endif %}>Mountain Time (MT)</option>
|
||||
<option value="America/Los_Angeles" {% if timezone == 'America/Los_Angeles' %}selected{% endif %}>Pacific Time (PT)</option>
|
||||
<option value="Europe/London" {% if timezone == 'Europe/London' %}selected{% endif %}>London (GMT)</option>
|
||||
<option value="Europe/Paris" {% if timezone == 'Europe/Paris' %}selected{% endif %}>Paris (CET)</option>
|
||||
<option value="Asia/Tokyo" {% if timezone == 'Asia/Tokyo' %}selected{% endif %}>Tokyo (JST)</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="form-label" for="password">Password</label>
|
||||
|
||||
@@ -0,0 +1,40 @@
|
||||
{% extends "base.html" %}
|
||||
|
||||
{% block title %}Registration Successful - KeepItGoing{% endblock %}
|
||||
|
||||
{% block body %}
|
||||
<div style="min-height: 100vh; display: flex; align-items: center; justify-content: center; background-color: var(--color-bg, #f5f5f5); padding: 20px;">
|
||||
<div style="max-width: 500px; width: 100%; background: white; padding: 40px; border-radius: 8px; box-shadow: 0 2px 10px rgba(0,0,0,0.1);">
|
||||
<div style="text-align: center; margin-bottom: 30px;">
|
||||
<svg style="width: 80px; height: 80px; color: #10B981;" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M3 19v-8.93a2 2 0 01.89-1.664l7-4.666a2 2 0 012.22 0l7 4.666A2 2 0 0121 10.07V19M3 19a2 2 0 002 2h14a2 2 0 002-2M3 19l6.75-4.5M21 19l-6.75-4.5M3 10l6.75 4.5M21 10l-6.75 4.5m0 0l-1.14.76a2 2 0 01-2.22 0l-1.14-.76"></path>
|
||||
</svg>
|
||||
</div>
|
||||
|
||||
<h2 style="text-align: center; color: #2c3e50; margin-bottom: 20px;">Check Your Email!</h2>
|
||||
|
||||
<p style="text-align: center; color: #666; margin-bottom: 15px;">
|
||||
We've sent a verification email to:
|
||||
</p>
|
||||
<p style="text-align: center; font-weight: bold; color: #2c3e50; margin-bottom: 20px;">
|
||||
{{ email }}
|
||||
</p>
|
||||
|
||||
<p style="color: #666; margin-bottom: 15px;">
|
||||
Please click the link in the email to verify your account. After verification,
|
||||
your account will be reviewed by our team for approval.
|
||||
</p>
|
||||
|
||||
<div style="background-color: #e3f2fd; padding: 15px; border-radius: 5px; margin-top: 30px;">
|
||||
<p style="margin: 0; color: #0d47a1; font-size: 14px;">
|
||||
<strong>Didn't receive the email?</strong><br>
|
||||
Check your spam folder or <a href="{% url 'resend-verification' %}" style="color: #1976d2; text-decoration: none; font-weight: bold;">request a new verification email</a>.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div style="text-align: center; margin-top: 30px;">
|
||||
<a href="{% url 'login' %}" style="color: #666; text-decoration: none; font-size: 14px;">Return to Login</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
@@ -0,0 +1,55 @@
|
||||
{% extends "base.html" %}
|
||||
|
||||
{% block title %}Resend Verification Email - KeepItGoing{% endblock %}
|
||||
|
||||
{% block body %}
|
||||
<div style="min-height: 100vh; display: flex; align-items: center; justify-content: center; background-color: var(--color-bg, #f5f5f5); padding: 20px;">
|
||||
<div style="max-width: 500px; width: 100%; background: white; padding: 40px; border-radius: 8px; box-shadow: 0 2px 10px rgba(0,0,0,0.1);">
|
||||
<h2 style="text-align: center; color: #2c3e50; margin-bottom: 30px;">Resend Verification Email</h2>
|
||||
|
||||
{% if success %}
|
||||
<div style="background-color: #d1fae5; padding: 15px; border-radius: 5px; margin-bottom: 20px;">
|
||||
<p style="margin: 0; color: #065f46;">
|
||||
<strong>Success!</strong><br>
|
||||
{{ message }}
|
||||
</p>
|
||||
</div>
|
||||
<div style="text-align: center; margin-top: 30px;">
|
||||
<a href="{% url 'login' %}" style="color: #666; text-decoration: none; font-size: 14px;">Return to Login</a>
|
||||
</div>
|
||||
{% else %}
|
||||
{% if error %}
|
||||
<div style="background-color: #fee2e2; padding: 15px; border-radius: 5px; margin-bottom: 20px;">
|
||||
<p style="margin: 0; color: #991b1b;">
|
||||
<strong>Error:</strong><br>
|
||||
{{ error }}
|
||||
</p>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
<form method="post" style="margin-bottom: 20px;">
|
||||
{% csrf_token %}
|
||||
|
||||
<div style="margin-bottom: 20px;">
|
||||
<label for="email" style="display: block; margin-bottom: 5px; color: #374151; font-weight: 500;">Email Address</label>
|
||||
<input type="email"
|
||||
id="email"
|
||||
name="email"
|
||||
required
|
||||
style="width: 100%; padding: 10px; border: 1px solid #d1d5db; border-radius: 5px; font-size: 16px;"
|
||||
placeholder="Enter your email address">
|
||||
</div>
|
||||
|
||||
<button type="submit"
|
||||
style="width: 100%; background-color: #3B82F6; color: white; padding: 12px; border: none; border-radius: 5px; font-size: 16px; font-weight: bold; cursor: pointer;">
|
||||
Send Verification Email
|
||||
</button>
|
||||
</form>
|
||||
|
||||
<div style="text-align: center; margin-top: 30px;">
|
||||
<a href="{% url 'login' %}" style="color: #666; text-decoration: none; font-size: 14px;">Return to Login</a>
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
@@ -0,0 +1,69 @@
|
||||
{% extends "base.html" %}
|
||||
|
||||
{% block title %}Email Verification - KeepItGoing{% endblock %}
|
||||
|
||||
{% block body %}
|
||||
<div style="min-height: 100vh; display: flex; align-items: center; justify-content: center; background-color: var(--color-bg, #f5f5f5); padding: 20px;">
|
||||
<div style="max-width: 500px; width: 100%; background: white; padding: 40px; border-radius: 8px; box-shadow: 0 2px 10px rgba(0,0,0,0.1);">
|
||||
{% if success %}
|
||||
<div style="text-align: center; margin-bottom: 30px;">
|
||||
<svg style="width: 80px; height: 80px; color: #10B981;" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 12l2 2 4-4m6 2a9 9 0 11-18 0 9 9 0 0118 0z"></path>
|
||||
</svg>
|
||||
</div>
|
||||
|
||||
<h2 style="text-align: center; color: #10B981; margin-bottom: 20px;">Email Verified!</h2>
|
||||
|
||||
<p style="text-align: center; color: #666; margin-bottom: 20px;">
|
||||
Your email has been successfully verified.
|
||||
</p>
|
||||
|
||||
{% if is_approved %}
|
||||
<p style="text-align: center; color: #666; margin-bottom: 30px;">
|
||||
Your account has been approved! You can now log in.
|
||||
</p>
|
||||
<div style="text-align: center;">
|
||||
<a href="{% url 'login' %}"
|
||||
style="display: inline-block; background-color: #3B82F6; color: white; padding: 12px 24px; text-decoration: none; border-radius: 5px; font-weight: bold;">
|
||||
Go to Login
|
||||
</a>
|
||||
</div>
|
||||
{% else %}
|
||||
<div style="background-color: #fff3cd; padding: 15px; border-radius: 5px; margin-bottom: 20px;">
|
||||
<p style="margin: 0; color: #856404;">
|
||||
<strong>Pending Approval</strong><br>
|
||||
Your account is now pending approval from our team.
|
||||
You'll receive an email once your account is approved.
|
||||
</p>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
{% else %}
|
||||
<div style="text-align: center; margin-bottom: 30px;">
|
||||
<svg style="width: 80px; height: 80px; color: #EF4444;" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M10 14l2-2m0 0l2-2m-2 2l-2-2m2 2l2 2m7-2a9 9 0 11-18 0 9 9 0 0118 0z"></path>
|
||||
</svg>
|
||||
</div>
|
||||
|
||||
<h2 style="text-align: center; color: #EF4444; margin-bottom: 20px;">Verification Failed</h2>
|
||||
|
||||
<p style="text-align: center; color: #666; margin-bottom: 30px;">
|
||||
{{ error }}
|
||||
</p>
|
||||
|
||||
{% if can_resend %}
|
||||
<div style="text-align: center;">
|
||||
<a href="{% url 'resend-verification' %}"
|
||||
style="display: inline-block; background-color: #3B82F6; color: white; padding: 12px 24px; text-decoration: none; border-radius: 5px; font-weight: bold;">
|
||||
Request New Verification Email
|
||||
</a>
|
||||
</div>
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
|
||||
<div style="text-align: center; margin-top: 30px;">
|
||||
<a href="{% url 'login' %}" style="color: #666; text-decoration: none; font-size: 14px;">Return to Login</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
Reference in New Issue
Block a user