add sign up process for backlogger
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful

This commit is contained in:
2026-03-31 21:45:29 +03:00
parent ce3fdfffa1
commit 80f5335886
7 changed files with 217 additions and 2 deletions

View File

@@ -1,4 +1,6 @@
from django.contrib import admin
from django.contrib.auth.admin import UserAdmin
from django.contrib.auth.models import User
from .models import Item
@@ -7,3 +9,14 @@ class ItemAdmin(admin.ModelAdmin):
list_display = ['name', 'category', 'progress_percent', 'favorite', 'created_at']
list_filter = ['category', 'favorite']
search_fields = ['name']
admin.site.unregister(User)
@admin.register(User)
class CustomUserAdmin(UserAdmin):
list_display = ['username', 'email', 'is_active', 'date_joined']
list_editable = ['is_active']
list_filter = ['is_active']
ordering = ['date_joined']

View File

@@ -1,7 +1,17 @@
from django import forms
from django.contrib.auth.forms import UserCreationForm
from django.contrib.auth.models import User
from .models import Item
class SignupForm(UserCreationForm):
email = forms.EmailField(required=True)
class Meta:
model = User
fields = ['username', 'email', 'password1', 'password2']
class ItemForm(forms.ModelForm):
progress_percent = forms.FloatField(
min_value=0,

View File

@@ -89,8 +89,14 @@
<p class="subtitle">Backlogger</p>
{% if form.non_field_errors %}
{% for error in form.non_field_errors %}
{% if 'inactive' in error|lower or 'active' in error|lower %}
<div class="error-banner">Your account is pending approval. You'll be able to log in once it's activated.</div>
{% else %}
<div class="error-banner">Invalid username or password.</div>
{% endif %}
{% endfor %}
{% endif %}
<form method="post">
{% csrf_token %}
@@ -109,6 +115,9 @@
<button type="submit" class="btn">Log in</button>
</form>
<p style="text-align:center; margin-top:1.25rem; font-size:0.83rem; color:#64748b;">
No account? <a href="/accounts/signup/" style="color:#38bdf8; text-decoration:none;">Sign up</a>
</p>
</div>
</body>
</html>

View File

@@ -0,0 +1,117 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Sign up — Backlogger</title>
<style>
*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
body {
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
background: #0f172a;
color: #e2e8f0;
min-height: 100vh;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
}
.card {
background: #0a0f1e;
border: 1px solid #1e293b;
border-radius: 12px;
padding: 2.5rem 2rem;
width: 100%;
max-width: 360px;
}
.brand {
display: block;
text-align: center;
color: #38bdf8;
font-weight: 600;
font-size: 1.1rem;
margin-bottom: 0.4rem;
text-decoration: none;
}
.subtitle {
text-align: center;
color: #64748b;
font-size: 0.85rem;
margin-bottom: 2rem;
}
.field { margin-bottom: 1.1rem; }
.field label {
display: block;
font-size: 0.78rem;
color: #94a3b8;
text-transform: uppercase;
letter-spacing: 0.07em;
margin-bottom: 0.4rem;
}
.field input {
width: 100%;
background: #1e293b;
border: 1px solid #334155;
border-radius: 6px;
color: #e2e8f0;
padding: 0.6rem 0.75rem;
font-size: 0.9rem;
}
.field input:focus { outline: none; border-color: #38bdf8; }
.btn {
width: 100%;
background: #38bdf8;
color: #0f172a;
font-weight: 600;
border: none;
border-radius: 6px;
padding: 0.6rem;
font-size: 0.9rem;
cursor: pointer;
margin-top: 0.5rem;
}
.btn:hover { opacity: 0.88; }
.errorlist { list-style: none; color: #f87171; font-size: 0.8rem; margin-top: 0.3rem; }
.help { font-size: 0.75rem; color: #475569; margin-top: 0.3rem; }
</style>
</head>
<body>
<div class="card">
<a class="brand" href="/">killmybacklog.com</a>
<p class="subtitle">Create an account</p>
<form method="post">
{% csrf_token %}
<div class="field">
<label for="{{ form.username.id_for_label }}">Username</label>
{{ form.username }}
{{ form.username.errors }}
</div>
<div class="field">
<label for="{{ form.email.id_for_label }}">Email</label>
{{ form.email }}
{{ form.email.errors }}
</div>
<div class="field">
<label for="{{ form.password1.id_for_label }}">Password</label>
{{ form.password1 }}
{{ form.password1.errors }}
</div>
<div class="field">
<label for="{{ form.password2.id_for_label }}">Confirm password</label>
{{ form.password2 }}
{{ form.password2.errors }}
</div>
<button type="submit" class="btn">Request account</button>
</form>
<p style="text-align:center; margin-top:1.25rem; font-size:0.83rem; color:#64748b;">
Already have an account? <a href="/accounts/login/" style="color:#38bdf8; text-decoration:none;">Log in</a>
</p>
</div>
</body>
</html>

View File

@@ -0,0 +1,51 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Account requested — Backlogger</title>
<style>
*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
body {
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
background: #0f172a;
color: #e2e8f0;
min-height: 100vh;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
}
.card {
background: #0a0f1e;
border: 1px solid #1e293b;
border-radius: 12px;
padding: 2.5rem 2rem;
width: 100%;
max-width: 360px;
text-align: center;
}
.brand {
display: block;
color: #38bdf8;
font-weight: 600;
font-size: 1.1rem;
margin-bottom: 0.4rem;
text-decoration: none;
}
.icon { font-size: 2rem; margin: 1.25rem 0 0.75rem; }
h2 { font-size: 1rem; font-weight: 600; margin-bottom: 0.6rem; }
p { font-size: 0.85rem; color: #64748b; line-height: 1.5; }
a { color: #38bdf8; text-decoration: none; }
</style>
</head>
<body>
<div class="card">
<a class="brand" href="/">killmybacklog.com</a>
<div class="icon">&#10003;</div>
<h2>Account requested</h2>
<p>Your account is pending approval.<br>You'll receive access once it's activated.</p>
<p style="margin-top:1.5rem;"><a href="/accounts/login/">Back to log in</a></p>
</div>
</body>
</html>

View File

@@ -1,7 +1,20 @@
from django.contrib.auth.decorators import login_required
from django.shortcuts import render, get_object_or_404, redirect
from .models import Item
from .forms import ItemForm
from .forms import ItemForm, SignupForm
def signup(request):
if request.method == 'POST':
form = SignupForm(request.POST)
if form.is_valid():
user = form.save(commit=False)
user.is_active = False
user.save()
return render(request, 'backlogger/signup_pending.html')
else:
form = SignupForm()
return render(request, 'backlogger/signup.html', {'form': form})
SORT_MAP = {

View File

@@ -1,11 +1,13 @@
from django.contrib import admin
from django.contrib.auth import views as auth_views
from django.urls import path, include
from backlogger.views import signup
urlpatterns = [
path('admin/', admin.site.urls),
path('accounts/login/', auth_views.LoginView.as_view(template_name='backlogger/login.html'), name='login'),
path('accounts/logout/', auth_views.LogoutView.as_view(), name='logout'),
path('accounts/signup/', signup, name='signup'),
path('backlogger/', include('backlogger.urls')),
path('daily-stone/', include('dailystone.urls')),
path('', include('core.urls')),