This commit is contained in:
pacnpal
2024-10-29 14:55:24 -04:00
parent 66114514c1
commit 3054209646
82 changed files with 241 additions and 174 deletions

View File

@@ -0,0 +1,10 @@
from django.core.management.base import BaseCommand
from django.db import connection
class Command(BaseCommand):
help = 'Fix migration history by removing rides.0001_initial'
def handle(self, *args, **kwargs):
with connection.cursor() as cursor:
cursor.execute("DELETE FROM django_migrations WHERE app='rides' AND name='0001_initial';")
self.stdout.write(self.style.SUCCESS('Successfully removed rides.0001_initial from migration history'))

View File

@@ -2182,6 +2182,10 @@ select {
height: 2rem;
}
.h-6 {
height: 1.5rem;
}
.min-h-\[calc\(100vh-16rem\)\] {
min-height: calc(100vh - 16rem);
}
@@ -2214,6 +2218,10 @@ select {
width: 100%;
}
.w-6 {
width: 1.5rem;
}
.max-w-3xl {
max-width: 48rem;
}

View File

@@ -1,3 +1,3 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 48 48">
<path fill="#7289DA" d="M40.634 11.546c-3.286-2.466-6.824-3.894-10.532-4.546l-.524 1.05c2.898.698 5.574 1.746 8.074 3.142-3.426-1.89-7.144-3.19-11.074-3.89-2.66-.56-5.374-.84-8.074-.84-2.7 0-5.414.28-8.074.84-3.93.7-7.648 2-11.074 3.89 2.5-1.396 5.176-2.444 8.074-3.142l-.524-1.05c-3.708.652-7.246 2.08-10.532 4.546C2.146 17.44 0 24.894 0 32.894c0 .14.006.28.014.42 3.43 4.674 8.25 7.956 13.986 9.384 1.092-.882 2.078-1.87 2.946-2.94-1.642-.614-3.214-1.45-4.678-2.486 1.048.652 2.144 1.218 3.274 1.688 1.994.826 4.118 1.472 6.458 1.87 2.338.398 4.758.598 7.194.598s4.856-.2 7.194-.598c2.34-.398 4.464-1.044 6.458-1.87 1.13-.47 2.226-1.036 3.274-1.688-1.464 1.036-3.036 1.872-4.678 2.486.868 1.07 1.854 2.058 2.946 2.94 5.736-1.428 10.556-4.71 13.986-9.384.008-.14.014-.28.014-.42 0-8-2.146-15.454-6.374-21.348zM16.344 34.394c-2.7 0-4.894-2.52-4.894-5.6s2.194-5.6 4.894-5.6 4.894 2.52 4.894 5.6c0 3.08-2.194 5.6-4.894 5.6zm15.606 0c-2.7 0-4.894-2.52-4.894-5.6s2.194-5.6 4.894-5.6 4.894 2.52 4.894 5.6c0 3.08-2.194 5.6-4.894 5.6z"/>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 245 240">
<path fill="#7289DA" d="M104.4 0c-1.2 0-2.3.5-3.1 1.4-19.7 21.6-31.4 48.5-31.4 77.1 0 1.2.5 2.3 1.4 3.1 19.7 21.6 31.4 48.5 31.4 77.1 0 1.2.5 2.3 1.4 3.1 19.7 21.6 31.4 48.5 31.4 77.1 0 1.2.5 2.3 1.4 3.1 19.7 21.6 31.4 48.5 31.4 77.1 0 1.2.5 2.3 1.4 3.1 19.7 21.6 31.4 48.5 31.4 77.1 0 1.2.5 2.3 1.4 3.1 19.7 21.6 31.4 48.5 31.4 77.1 0 1.2.5 2.3 1.4 3.1 19.7 21.6 31.4 48.5 31.4 77.1 0 1.2.5 2.3 1.4 3.1 19.7 21.6 31.4 48.5 31.4 77.1 0 1.2.5 2.3 1.4 3.1 19.7 21.6 31.4 48.5 31.4 77.1 0 1.2.5 2.3 1.4 3.1 19.7 21.6 31.4 48.5 31.4 77.1 0 1.2.5 2.3 1.4 3.1 19.7 21.6 31.4 48.5 31.4 77.1 0 1.2.5 2.3 1.4 3.1 19.7 21.6 31.4 48.5 31.4 77.1 0 1.2.5 2.3 1.4 3.1 19.7 21.6 31.4 48.5 31.4 77.1 0 1.2.5 2.3 1.4 3.1 8.8 9.6 18.3 18.6 28.6 26.9 1.8 1.6 3.7 2.7 5.7 3.3 1.1.3 2.2.6 3.3.8 20.8 3.8 42.5 5.7 64.9 5.7 22.4 0 44.1-1.9 64.9-5.7 1.1-.3 2.2-.6 3.3-.8 2-0.6 3.9-1.7 5.7-3.3 10.3-8.3 19.8-17.3 28.6-26.9.9-.8 1.4-1.7 1.4-3.1 0-29-9.7-55.5-31.4-77.1-.9-.8-1.8-1.4-3.1-1.4-29 0-55.5 9.7-77.1 31.4-.8.9-1.4 1.8-1.4 3.1 0 29 9.7 55.5 31.4 77.1-.9.8-1.8 1.4-3.1 1.4-29 0-55.5 9.7-77.1 31.4-.8.9-1.4 1.8-1.4 3.1 0 29 9.7 55.5 31.4 77.1-.9.8-1.8 1.4-3.1 1.4-29 0-55.5 9.7-77.1 31.4-.8.9-1.4 1.8-1.4 3.1 0 29 9.7 55.5 31.4 77.1-.9.8-1.8 1.4-3.1 1.4-29 0-55.5 9.7-77.1 31.4-.8.9-1.4 1.8-1.4 3.1 0 29 9.7 55.5 31.4 77.1-.9.8-1.8 1.4-3.1 1.4-29 0-55.5 9.7-77.1 31.4-.8.9-1.4 1.8-1.4 3.1 0 29 9.7 55.5 31.4 77.1-.9.8-1.8 1.4-3.1 1.4-29 0-55.5 9.7-77.1 31.4-.8.9-1.4 1.8-1.4 3.1 0 29 9.7 55.5 31.4 77.1-.9.8-1.8 1.4-3.1 1.4-29 0-55.5 9.7-77.1 31.4-.8.9-1.4 1.8-1.4 3.1 0 29 9.7 55.5 31.4 77.1-.9.8-1.8 1.4-3.1 1.4zM169.7 107.1c-20.8 0-38.5 8.3-52.2 20.8-1.6 1.6-2.6 3.7-3.1 5.9-.7 3.1-1 6.3-1 9.6 0 13.5 10.9 24.4 24.4 24.4 13.5 0 24.4-10.9 24.4-24.4 0-3.3-.4-6.5-1-9.6-.5-2.2-1.5-4.3-3.1-5.9-13.7-12.5-31.4-20.8-52.2-20.8zm82.3 0c-20.8 0-38.5 8.3-52.2 20.8-1.6 1.6-2.6 3.7-3.1 5.9-.7 3.1-1 6.3-1 9.6 0 13.5 10.9 24.4 24.4 24.4 13.5 0 24.4-10.9 24.4-24.4 0-3.3-.4-6.5-1-9.6-.5-2.2-1.5-4.3-3.1-5.9-13.7-12.5-31.4-20.8-52.2-20.8z"/>
</svg>

Before

Width:  |  Height:  |  Size: 1.1 KiB

After

Width:  |  Height:  |  Size: 2.0 KiB

View File

@@ -4,6 +4,9 @@ document.addEventListener('DOMContentLoaded', () => {
const darkMode = localStorage.getItem('darkMode') === 'true';
if (darkMode) {
document.documentElement.classList.add('dark');
toggleIcons(true); // Ensure correct icon is shown
} else {
toggleIcons(false);
}
});
@@ -77,3 +80,24 @@ document.addEventListener('click', (e) => {
}
});
});
// Toggle dark mode icons
function toggleIcons(isDarkMode) {
const sunIcon = document.querySelector('.fa-sun');
const moonIcon = document.querySelector('.fa-moon');
if (isDarkMode) {
sunIcon.classList.remove('hidden');
moonIcon.classList.add('hidden');
} else {
sunIcon.classList.add('hidden');
moonIcon.classList.remove('hidden');
}
}
// Add event listener for theme toggle button
document.getElementById('theme-toggle').addEventListener('click', () => {
const isDarkMode = document.documentElement.classList.toggle('dark');
localStorage.setItem('darkMode', isDarkMode);
toggleIcons(isDarkMode);
});

View File

@@ -22,10 +22,10 @@
tabindex="0"
onkeydown="if(event.key === 'Enter' || event.key === ' ') { this.click(); event.preventDefault(); }">
{% if provider.id == 'google' %}
<img src="{% static 'images/google-icon.svg' %}" alt="Google" class="w-5 h-5 mr-3">
<i class="fab fa-google w-5 h-5 mr-3"></i>
<span>Continue with Google</span>
{% elif provider.id == 'discord' %}
<img src="{% static 'images/discord-icon.svg' %}" alt="Discord" class="w-5 h-5 mr-3">
<i class="fab fa-discord w-5 h-5 mr-3"></i>
<span>Continue with Discord</span>
{% endif %}
</a>

View File

@@ -7,202 +7,227 @@
{% block content %}
<div class="flex items-center justify-center min-h-[calc(100vh-16rem)]">
<div class="w-full max-w-md">
<div class="auth-card">
<h1 class="auth-title">{% trans "Create Account" %}</h1>
<div class="w-full max-w-md">
<div class="auth-card">
<h1 class="auth-title">{% trans "Create Account" %}</h1>
{% get_providers as socialaccount_providers %}
{% get_providers as socialaccount_providers %}
{% if socialaccount_providers %}
<div class="space-y-3">
{% for provider in socialaccount_providers %}
<a
href="{% provider_login_url provider.id process='signup' %}"
class="btn-social {% if provider.id == 'discord' %}btn-discord{% elif provider.id == 'google' %}btn-google{% endif %}"
role="button"
tabindex="0"
onkeydown="if(event.key === 'Enter' || event.key === ' ') { this.click(); event.preventDefault(); }"
>
{% if provider.id == 'google' %}
<i class="fab fa-google w-5 h-5 mr-3"></i>
<span>Continue with Google</span>
{% elif provider.id == 'discord' %}
<i class="fab fa-discord w-5 h-5 mr-3"></i>
<span>Continue with Discord</span>
{% endif %}
</a>
{% endfor %}
</div>
{% if socialaccount_providers %}
<div class="space-y-3">
{% for provider in socialaccount_providers %}
<a href="{% provider_login_url provider.id process='signup' %}"
class="btn-social {% if provider.id == 'discord' %}btn-discord{% elif provider.id == 'google' %}btn-google{% endif %}"
role="button"
tabindex="0"
onkeydown="if(event.key === 'Enter' || event.key === ' ') { this.click(); event.preventDefault(); }">
{% if provider.id == 'google' %}
<img src="{% static 'images/google-icon.svg' %}" alt="Google" class="w-5 h-5 mr-3">
<span>Continue with Google</span>
{% elif provider.id == 'discord' %}
<img src="{% static 'images/discord-icon.svg' %}" alt="Discord" class="w-5 h-5 mr-3">
<span>Continue with Discord</span>
{% endif %}
</a>
{% endfor %}
</div>
<div class="auth-divider">
<span>Or continue with email</span>
</div>
{% endif %}
<div class="auth-divider">
<span>Or continue with email</span>
</div>
{% endif %}
<form class="space-y-6" method="POST" action="{% url 'account_signup' %}">
{% csrf_token %}
{% if form.non_field_errors %}
<div class="alert alert-error">
<div class="text-sm">{{ form.non_field_errors }}</div>
</div>
{% endif %}
<div>
<label for="id_username" class="form-label">
{% trans "Username" %}
</label>
<input type="text" name="username" id="id_username" required
autocomplete="username"
class="form-input">
{% if form.username.errors %}
<p class="form-error">{{ form.username.errors }}</p>
{% endif %}
</div>
<div>
<label for="id_email" class="form-label">
{% trans "Email" %}
</label>
<input type="email" name="email" id="id_email" required
autocomplete="email"
class="form-input">
{% if form.email.errors %}
<p class="form-error">{{ form.email.errors }}</p>
{% endif %}
</div>
<div>
<label for="id_password1" class="form-label">
{% trans "Password" %}
</label>
<input type="password" name="password1" id="id_password1" required
autocomplete="new-password"
class="form-input"
oninput="validatePassword(this.value)">
{% if form.password1.errors %}
<p class="form-error">{{ form.password1.errors }}</p>
{% endif %}
<div class="mt-3 password-requirements">
<ul id="passwordRequirements">
<li class="invalid" id="req-length">
<i class="text-xs fas fa-circle"></i>
<span>Must be at least 8 characters long</span>
</li>
<li class="invalid" id="req-similar">
<i class="text-xs fas fa-circle"></i>
<span>Can't be too similar to your personal information</span>
</li>
<li class="invalid" id="req-common">
<i class="text-xs fas fa-circle"></i>
<span>Can't be a commonly used password</span>
</li>
<li class="invalid" id="req-numeric">
<i class="text-xs fas fa-circle"></i>
<span>Can't be entirely numeric</span>
</li>
</ul>
</div>
</div>
<div>
<label for="id_password2" class="form-label">
{% trans "Confirm Password" %}
</label>
<input type="password" name="password2" id="id_password2" required
autocomplete="new-password"
class="form-input"
oninput="validatePasswordMatch()">
{% if form.password2.errors %}
<p class="form-error">{{ form.password2.errors }}</p>
{% endif %}
</div>
{% if redirect_field_value %}
<input type="hidden" name="{{ redirect_field_name }}" value="{{ redirect_field_value }}">
{% endif %}
<div>
<button type="submit" class="w-full btn-primary">
<i class="mr-2 fas fa-user-plus"></i>
{% trans "Create Account" %}
</button>
</div>
</form>
<div class="mt-6 text-sm text-center">
<p class="text-gray-600 dark:text-gray-400">
{% trans "Already have an account?" %}
<a href="{% url 'account_login' %}"
class="ml-1 font-medium transition-colors text-primary hover:text-primary/80 focus:outline-none focus:underline"
onkeydown="if(event.key === 'Enter') { this.click(); }">
{% trans "Sign in" %}
</a>
</p>
<form class="space-y-6" method="POST" action="{% url 'account_signup' %}">
{% csrf_token %}
{% if form.non_field_errors %}
<div class="alert alert-error">
<div class="text-sm">{{ form.non_field_errors }}</div>
</div>
{% endif %}
<div>
<label for="id_username" class="form-label">
{% trans "Username" %}
</label>
<input
type="text"
name="username"
id="id_username"
required
autocomplete="username"
class="form-input"
/>
{% if form.username.errors %}
<p class="form-error">{{ form.username.errors }}</p>
{% endif %}
</div>
<div>
<label for="id_email" class="form-label">{% trans "Email" %}</label>
<input
type="email"
name="email"
id="id_email"
required
autocomplete="email"
class="form-input"
/>
{% if form.email.errors %}
<p class="form-error">{{ form.email.errors }}</p>
{% endif %}
</div>
<div>
<label for="id_password1" class="form-label">
{% trans "Password" %}
</label>
<input
type="password"
name="password1"
id="id_password1"
required
autocomplete="new-password"
class="form-input"
oninput="validatePassword(this.value)"
/>
{% if form.password1.errors %}
<p class="form-error">{{ form.password1.errors }}</p>
{% endif %}
<div class="mt-3 password-requirements">
<ul id="passwordRequirements">
<li class="invalid" id="req-length">
<i class="text-xs fas fa-circle"></i>
<span>Must be at least 8 characters long</span>
</li>
<li class="invalid" id="req-similar">
<i class="text-xs fas fa-circle"></i>
<span>Can't be too similar to your personal information</span>
</li>
<li class="invalid" id="req-common">
<i class="text-xs fas fa-circle"></i>
<span>Can't be a commonly used password</span>
</li>
<li class="invalid" id="req-numeric">
<i class="text-xs fas fa-circle"></i>
<span>Can't be entirely numeric</span>
</li>
</ul>
</div>
</div>
<div>
<label for="id_password2" class="form-label">
{% trans "Confirm Password" %}
</label>
<input
type="password"
name="password2"
id="id_password2"
required
autocomplete="new-password"
class="form-input"
oninput="validatePasswordMatch()"
/>
{% if form.password2.errors %}
<p class="form-error">{{ form.password2.errors }}</p>
{% endif %}
</div>
{% if redirect_field_value %}
<input
type="hidden"
name="{{ redirect_field_name }}"
value="{{ redirect_field_value }}"
/>
{% endif %}
<div>
<button type="submit" class="w-full btn-primary">
<i class="mr-2 fas fa-user-plus"></i>
{% trans "Create Account" %}
</button>
</div>
</form>
<div class="mt-6 text-sm text-center">
<p class="text-gray-600 dark:text-gray-400">
{% trans "Already have an account?" %}
<a
href="{% url 'account_login' %}"
class="ml-1 font-medium transition-colors text-primary hover:text-primary/80 focus:outline-none focus:underline"
onkeydown="if(event.key === 'Enter') { this.click(); }"
>
{% trans "Sign in" %}
</a>
</p>
</div>
</div>
</div>
</div>
<script>
function validatePassword(password) {
function validatePassword(password) {
// Length requirement
const lengthReq = document.getElementById('req-length');
const lengthReq = document.getElementById("req-length");
if (password.length >= 8) {
lengthReq.classList.remove('invalid');
lengthReq.classList.add('valid');
lengthReq.querySelector('i').classList.remove('fa-circle');
lengthReq.querySelector('i').classList.add('fa-check');
lengthReq.classList.remove("invalid");
lengthReq.classList.add("valid");
lengthReq.querySelector("i").classList.remove("fa-circle");
lengthReq.querySelector("i").classList.add("fa-check");
} else {
lengthReq.classList.remove('valid');
lengthReq.classList.add('invalid');
lengthReq.querySelector('i').classList.remove('fa-check');
lengthReq.querySelector('i').classList.add('fa-circle');
lengthReq.classList.remove("valid");
lengthReq.classList.add("invalid");
lengthReq.querySelector("i").classList.remove("fa-check");
lengthReq.querySelector("i").classList.add("fa-circle");
}
// Numeric requirement
const numericReq = document.getElementById('req-numeric');
const numericReq = document.getElementById("req-numeric");
if (!/^\d+$/.test(password)) {
numericReq.classList.remove('invalid');
numericReq.classList.add('valid');
numericReq.querySelector('i').classList.remove('fa-circle');
numericReq.querySelector('i').classList.add('fa-check');
numericReq.classList.remove("invalid");
numericReq.classList.add("valid");
numericReq.querySelector("i").classList.remove("fa-circle");
numericReq.querySelector("i").classList.add("fa-check");
} else {
numericReq.classList.remove('valid');
numericReq.classList.add('invalid');
numericReq.querySelector('i').classList.remove('fa-check');
numericReq.querySelector('i').classList.add('fa-circle');
numericReq.classList.remove("valid");
numericReq.classList.add("invalid");
numericReq.querySelector("i").classList.remove("fa-check");
numericReq.querySelector("i").classList.add("fa-circle");
}
// Common password check (basic)
const commonReq = document.getElementById('req-common');
const commonPasswords = ['password', '12345678', 'qwerty', 'letmein'];
const commonReq = document.getElementById("req-common");
const commonPasswords = ["password", "12345678", "qwerty", "letmein"];
if (!commonPasswords.includes(password.toLowerCase())) {
commonReq.classList.remove('invalid');
commonReq.classList.add('valid');
commonReq.querySelector('i').classList.remove('fa-circle');
commonReq.querySelector('i').classList.add('fa-check');
commonReq.classList.remove("invalid");
commonReq.classList.add("valid");
commonReq.querySelector("i").classList.remove("fa-circle");
commonReq.querySelector("i").classList.add("fa-check");
} else {
commonReq.classList.remove('valid');
commonReq.classList.add('invalid');
commonReq.querySelector('i').classList.remove('fa-check');
commonReq.querySelector('i').classList.add('fa-circle');
commonReq.classList.remove("valid");
commonReq.classList.add("invalid");
commonReq.querySelector("i").classList.remove("fa-check");
commonReq.querySelector("i").classList.add("fa-circle");
}
}
}
function validatePasswordMatch() {
const password1 = document.getElementById('id_password1').value;
const password2 = document.getElementById('id_password2').value;
const password2Input = document.getElementById('id_password2');
function validatePasswordMatch() {
const password1 = document.getElementById("id_password1").value;
const password2 = document.getElementById("id_password2").value;
const password2Input = document.getElementById("id_password2");
if (password2.length > 0) {
if (password1 === password2) {
password2Input.classList.remove('border-red-500');
password2Input.classList.add('border-green-500');
} else {
password2Input.classList.remove('border-green-500');
password2Input.classList.add('border-red-500');
}
if (password1 === password2) {
password2Input.classList.remove("border-red-500");
password2Input.classList.add("border-green-500");
} else {
password2Input.classList.remove("border-green-500");
password2Input.classList.add("border-red-500");
}
} else {
password2Input.classList.remove('border-green-500', 'border-red-500');
password2Input.classList.remove("border-green-500", "border-red-500");
}
}
}
</script>
{% endblock %}