Improved layout & style, fixed hamburger menu in login page.

This commit is contained in:
Pēteris Caune 2018-10-09 16:12:02 +03:00
parent 160aa191c8
commit a58ce791c0
No known key found for this signature in database
GPG Key ID: E28D7679E9A9EDE2
8 changed files with 205 additions and 103 deletions

View File

@ -6,9 +6,11 @@ All notable changes to this project will be documented in this file.
### Improvements ### Improvements
- Content updates in the "Welcome" page. - Content updates in the "Welcome" page.
- Added "Docs > Third-Party Resources" page. - Added "Docs > Third-Party Resources" page.
- Improved layout and styling in "Login" page.
### Bug Fixes ### Bug Fixes
- Timezones were missing in the "Change Schedule" dialog, fixed - Timezones were missing in the "Change Schedule" dialog, fixed.
- Fix hamburger menu button in "Login" page.
## 1.1.0 - 2018-08-20 ## 1.1.0 - 2018-08-20

View File

@ -1,5 +1,7 @@
from datetime import timedelta as td from datetime import timedelta as td
from django import forms from django import forms
from django.conf import settings
from django.contrib.auth import authenticate
from django.contrib.auth.models import User from django.contrib.auth.models import User
@ -10,9 +12,37 @@ class LowercaseEmailField(forms.EmailField):
return value.lower() return value.lower()
class EmailForm(forms.Form):
email = LowercaseEmailField()
def clean_email(self):
v = self.cleaned_data["email"]
# If registration is not open then validate if an user
# account with this address exists-
if not settings.REGISTRATION_OPEN:
if not User.objects.filter(email=v).exists():
raise forms.ValidationError("Incorrect email address.")
return v
class EmailPasswordForm(forms.Form): class EmailPasswordForm(forms.Form):
identity = LowercaseEmailField() email = LowercaseEmailField()
password = forms.CharField(required=False) password = forms.CharField()
def clean(self):
username = self.cleaned_data.get('email')
password = self.cleaned_data.get('password')
if username and password:
self.user = authenticate(username=username, password=password)
if self.user is None:
raise forms.ValidationError("Incorrect email or password")
if not self.user.is_active:
raise forms.ValidationError("Account is inactive")
return self.cleaned_data
class ReportSettingsForm(forms.Form): class ReportSettingsForm(forms.Form):

View File

@ -10,7 +10,7 @@ from django.conf import settings
class LoginTestCase(TestCase): class LoginTestCase(TestCase):
def test_it_sends_link(self): def test_it_sends_link(self):
form = {"identity": "alice@example.org"} form = {"email": "alice@example.org"}
r = self.client.post("/accounts/login/", form) r = self.client.post("/accounts/login/", form)
assert r.status_code == 302 assert r.status_code == 302
@ -32,10 +32,9 @@ class LoginTestCase(TestCase):
self.client.get("/accounts/login/") self.client.get("/accounts/login/")
assert "bad_link" not in self.client.session assert "bad_link" not in self.client.session
@override_settings(REGISTRATION_OPEN=False) @override_settings(REGISTRATION_OPEN=False)
def test_it_obeys_registration_open(self): def test_it_obeys_registration_open(self):
form = {"identity": "dan@example.org"} form = {"email": "dan@example.org"}
r = self.client.post("/accounts/login/", form) r = self.client.post("/accounts/login/", form)
assert r.status_code == 200 assert r.status_code == 200
@ -45,7 +44,7 @@ class LoginTestCase(TestCase):
alice = User(username="alice", email="alice@example.org") alice = User(username="alice", email="alice@example.org")
alice.save() alice.save()
form = {"identity": "ALICE@EXAMPLE.ORG"} form = {"email": "ALICE@EXAMPLE.ORG"}
r = self.client.post("/accounts/login/", form) r = self.client.post("/accounts/login/", form)
assert r.status_code == 302 assert r.status_code == 302

View File

@ -17,7 +17,7 @@ from django.views.decorators.http import require_POST
from hc.accounts.forms import (ChangeEmailForm, EmailPasswordForm, from hc.accounts.forms import (ChangeEmailForm, EmailPasswordForm,
InviteTeamMemberForm, RemoveTeamMemberForm, InviteTeamMemberForm, RemoveTeamMemberForm,
ReportSettingsForm, SetPasswordForm, ReportSettingsForm, SetPasswordForm,
TeamNameForm) TeamNameForm, EmailForm)
from hc.accounts.models import Profile, Member from hc.accounts.models import Profile, Member
from hc.api.models import Channel, Check from hc.api.models import Channel, Check
from hc.lib.badges import get_badge_url from hc.lib.badges import get_badge_url
@ -57,44 +57,38 @@ def _ensure_own_team(request):
request.profile.save() request.profile.save()
def login(request, show_password=False): def login(request):
bad_credentials = False form = EmailPasswordForm()
magic_form = EmailForm()
if request.method == 'POST': if request.method == 'POST':
form = EmailPasswordForm(request.POST) if request.POST.get("action") == "login":
if form.is_valid(): form = EmailPasswordForm(request.POST)
email = form.cleaned_data["identity"] if form.is_valid():
password = form.cleaned_data["password"] auth_login(request, form.user)
if len(password): return redirect("hc-checks")
user = authenticate(username=email, password=password)
if user is not None and user.is_active: else:
auth_login(request, user) magic_form = EmailForm(request.POST)
return redirect("hc-checks") if magic_form.is_valid():
bad_credentials = True email = magic_form.cleaned_data["email"]
show_password = True
else:
user = None user = None
try: try:
user = User.objects.get(email=email) user = User.objects.get(email=email)
except User.DoesNotExist: except User.DoesNotExist:
if settings.REGISTRATION_OPEN: if settings.REGISTRATION_OPEN:
user = _make_user(email) user = _make_user(email)
else:
bad_credentials = True
if user: if user:
profile = Profile.objects.for_user(user) profile = Profile.objects.for_user(user)
profile.send_instant_login_link() profile.send_instant_login_link()
return redirect("hc-login-link-sent") return redirect("hc-login-link-sent")
else:
form = EmailPasswordForm()
bad_link = request.session.pop("bad_link", None) bad_link = request.session.pop("bad_link", None)
ctx = { ctx = {
"page": "login",
"form": form, "form": form,
"bad_credentials": bad_credentials, "magic_form": magic_form,
"bad_link": bad_link, "bad_link": bad_link
"show_password": show_password
} }
return render(request, "accounts/login.html", ctx) return render(request, "accounts/login.html", ctx)

View File

@ -4,7 +4,7 @@ from django.urls import include, path
from hc.accounts.views import login as hc_login from hc.accounts.views import login as hc_login
urlpatterns = [ urlpatterns = [
path('admin/login/', hc_login, {"show_password": True}), path('admin/login/', hc_login),
path('admin/', admin.site.urls), path('admin/', admin.site.urls),
path('accounts/', include('hc.accounts.urls')), path('accounts/', include('hc.accounts.urls')),
path('', include('hc.api.urls')), path('', include('hc.api.urls')),

74
static/css/login.css Normal file
View File

@ -0,0 +1,74 @@
.page-login h1 {
text-align: center;
margin: 40px 0;
}
.page-login .alert {
margin-bottom: 40px;
}
.page-login form p {
margin-bottom: 20px;
text-align: center;
}
.page-login form input {
margin-bottom: 20px;
}
@media (min-width: 768px) {
#magic-link-form {
margin-right: 50px;
}
#login-form {
margin-left: 50px;
}
}
#link-instruction {
color: #999;
font-style: italic;
padding: 12px 16px;
}
#login-sep {
background:#ddd;
position: absolute;
top: 10%;
right: -1px;
height: 80%;
width: 1px;
}
#login-sep div {
position: absolute;
top: 40%;
width: 40px;
left: -20px;
text-align: center;
background: #fff;
font-style: italic;
color: #666;
font-size: 12px;
padding: 8px 0;
}
#xs-login-sep {
text-align: center;
margin: 40px;
font-style: italic;
color: #666;
font-size: 12px;
height: 1px;
background: #ddd;
}
#xs-login-sep div {
position: relative;
margin: 0 auto;
width: 30px;
top: -9px;
background: #fff;
}

View File

@ -3,77 +3,82 @@
{% block content %} {% block content %}
<div class="row"> <div class="row">
<div class="col-sm-6 col-sm-offset-3"> <div class="col-xs-10 col-xs-offset-1 col-sm-12 col-sm-offset-0 col-lg-8 col-lg-offset-2">
<div class="hc-dialog">
{% if bad_link %}
<h1>Incorrect Login Link</h1>
<div class="dialog-body">
<p>The login link you just used is either incorrect or expired.</p>
<p>Please use the form below to request a fresh login link:</p>
</div>
{% else %}
<h1>{% site_name %}</h1>
<div class="dialog-body">
<p>
{% if show_password %}
Please enter your email address and password.
{% else %}
Please enter your email address.
Next, we'll send you an email with log-in instructions!
{% endif %}
</p>
</div>
{% endif %}
{% if bad_credentials %} <h1>Sign In to {% site_name %}</h1>
<p class="alert alert-danger">Incorrect email or password.</p> {% if bad_link %}
{% endif %} <div class="alert alert-warning">
<p>The login link you just used is either incorrect or expired.</p>
<p>Please use the form below to request a fresh login link.</p>
</div>
{% endif %}
<form method="post"> <div class="row">
{% csrf_token %} <div class="col-sm-6">
<form id="magic-link-form" method="post">
{% csrf_token %}
<div class="form-group"> {% if magic_form.email.errors %}
<div class="input-group input-group-lg"> <p class="text-danger">Incorrect email address.</p>
<div class="input-group-addon"> {% else %}
<span class="icon-mail"></span> <p>Enter your <strong>email address</strong>.</p>
</div> {% endif %}
<input
type="text"
class="form-control"
name="identity"
value="{{ form.identity.value|default:"" }}"
placeholder="you@example.org">
</div>
</div>
{% if not show_password %} <input
<div class="checkbox" id="password-toggle"> type="text"
<label> class="form-control input-lg"
<input type="checkbox"> I want to use a password name="email"
</label> value="{{ magic_form.email.value|default:"" }}"
</div> placeholder="you@example.org">
{% endif %}
<p id="link-instruction">
We will email you a magic sign in link.
</p>
<div id="password-block" class="form-group {% if not show_password %} hide {% endif %}"> <button type="submit" class="btn btn-lg btn-primary btn-block">
<div class="input-group input-group-lg"> Email Me a Link
<div class="input-group-addon">
<span class="icon-dots"></span>
</div>
<input
type="password"
class="form-control"
name="password"
placeholder="password">
</div>
</div>
<div class="clearfix">
<button type="submit" class="btn btn-lg btn-primary pull-right">
Log In
</button> </button>
</form>
<div id="login-sep" class="hidden-xs"><div>or</div></div>
</div>
<div class="col-xs-12 visible-xs-block">
<div id="xs-login-sep">
<div>or</div>
</div> </div>
</form> </div>
<div class="col-sm-6">
<form id="login-form" method="post">
{% csrf_token %}
<input type="hidden" name="action" value="login" />
{% if form.non_field_errors %}
<p class="text-danger">Incorrect email or password.</p>
{% else %}
<p>
Enter your <strong>email address</strong> and <strong>password</strong>.
</p>
{% endif %}
<input
type="text"
class="form-control input-lg"
name="email"
value="{{ form.email.value|default:"" }}"
placeholder="you@example.org">
<input
type="password"
class="form-control input-lg"
name="password"
placeholder="your password">
<button type="submit" class="btn btn-lg btn-primary btn-block">
Sign In
</button>
</form>
</div>
</div> </div>
</div> </div>
</div> </div>
@ -82,6 +87,7 @@
{% block scripts %} {% block scripts %}
{% compress js %} {% compress js %}
<script src="{% static 'js/jquery-2.1.4.min.js' %}"></script> <script src="{% static 'js/jquery-2.1.4.min.js' %}"></script>
<script src="{% static 'js/bootstrap.min.js' %}"></script>
<script src="{% static 'js/login.js' %}"></script> <script src="{% static 'js/login.js' %}"></script>
{% endcompress %} {% endcompress %}
{% endblock %} {% endblock %}

View File

@ -42,6 +42,7 @@
<link rel="stylesheet" href="{% static 'css/checkbox.css' %}" type="text/css"> <link rel="stylesheet" href="{% static 'css/checkbox.css' %}" type="text/css">
<link rel="stylesheet" href="{% static 'css/radio.css' %}" type="text/css"> <link rel="stylesheet" href="{% static 'css/radio.css' %}" type="text/css">
<link rel="stylesheet" href="{% static 'css/billing.css' %}" type="text/css"> <link rel="stylesheet" href="{% static 'css/billing.css' %}" type="text/css">
<link rel="stylesheet" href="{% static 'css/login.css' %}" type="text/css">
{% endcompress %} {% endcompress %}
</head> </head>
<body class="page-{{ page }}"> <body class="page-{{ page }}">
@ -109,14 +110,6 @@
</ul> </ul>
{% if request.user.is_authenticated %}
{% else %}
<ul class="nav navbar-nav navbar-right">
<li><a href="{% url 'hc-login' %}">Log In</a></li>
</ul>
{% endif %}
{% if request.user.is_authenticated %} {% if request.user.is_authenticated %}
<ul class="nav navbar-nav navbar-right"> <ul class="nav navbar-nav navbar-right">
<li class="dropdown"> <li class="dropdown">
@ -148,6 +141,10 @@
</ul> </ul>
</li> </li>
</ul> </ul>
{% elif page != "login" %}
<ul class="nav navbar-nav navbar-right">
<li><a href="{% url 'hc-login' %}">Log In</a></li>
</ul>
{% endif %} {% endif %}
</div> </div>