Add timezone support, scss, and bem
This commit is contained in:
parent
515967766a
commit
c97613e486
3
.gitignore
vendored
3
.gitignore
vendored
@ -251,3 +251,6 @@ dist
|
|||||||
.yarn/build-state.yml
|
.yarn/build-state.yml
|
||||||
.yarn/install-state.gz
|
.yarn/install-state.gz
|
||||||
.pnp.*
|
.pnp.*
|
||||||
|
|
||||||
|
# CACHES
|
||||||
|
CACHE/
|
||||||
|
|||||||
3
Pipfile
3
Pipfile
@ -9,6 +9,9 @@ django-compressor = "*"
|
|||||||
qrcode = "*"
|
qrcode = "*"
|
||||||
pillow = "*"
|
pillow = "*"
|
||||||
django-qr-code = "*"
|
django-qr-code = "*"
|
||||||
|
pytz = "*"
|
||||||
|
django-easy-timezones = "*"
|
||||||
|
django-libsass = "*"
|
||||||
|
|
||||||
[dev-packages]
|
[dev-packages]
|
||||||
|
|
||||||
|
|||||||
81
Pipfile.lock
generated
81
Pipfile.lock
generated
@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"_meta": {
|
"_meta": {
|
||||||
"hash": {
|
"hash": {
|
||||||
"sha256": "d1dd018056bda89f00de6e3fbdd88cfa2fc56b756047e88153a4f8e99171e1bb"
|
"sha256": "fa23d606797893ecd178aaeda5c8301bcff6f72d7b79fcdb1bf4947207b7a9bc"
|
||||||
},
|
},
|
||||||
"pipfile-spec": 6,
|
"pipfile-spec": 6,
|
||||||
"requires": {
|
"requires": {
|
||||||
@ -26,11 +26,11 @@
|
|||||||
},
|
},
|
||||||
"django": {
|
"django": {
|
||||||
"hashes": [
|
"hashes": [
|
||||||
"sha256:2d78425ba74c7a1a74b196058b261b9733a8570782f4e2828974777ccca7edf7",
|
"sha256:169e2e7b4839a7910b393eec127fd7cbae62e80fa55f89c6510426abf673fe5f",
|
||||||
"sha256:efa2ab96b33b20c2182db93147a0c3cd7769d418926f9e9f140a60dca7c64ca9"
|
"sha256:c6c0462b8b361f8691171af1fb87eceb4442da28477e12200c40420176206ba7"
|
||||||
],
|
],
|
||||||
"index": "pypi",
|
"index": "pypi",
|
||||||
"version": "==3.1.5"
|
"version": "==3.1.6"
|
||||||
},
|
},
|
||||||
"django-appconf": {
|
"django-appconf": {
|
||||||
"hashes": [
|
"hashes": [
|
||||||
@ -47,6 +47,22 @@
|
|||||||
"index": "pypi",
|
"index": "pypi",
|
||||||
"version": "==2.4"
|
"version": "==2.4"
|
||||||
},
|
},
|
||||||
|
"django-easy-timezones": {
|
||||||
|
"hashes": [
|
||||||
|
"sha256:1c90f8a9731bf0762b6c7d239beb5b9434f45e8c9bbdc3586faa69851d00c434",
|
||||||
|
"sha256:d28a5e60263de39eeef72240dac01090a11f7212d942dfbd3b4ba4be7b2bf77e"
|
||||||
|
],
|
||||||
|
"index": "pypi",
|
||||||
|
"version": "==0.8.0"
|
||||||
|
},
|
||||||
|
"django-libsass": {
|
||||||
|
"hashes": [
|
||||||
|
"sha256:38fab4ce1245542f3afd7248dc48f8a0b261f5f6c61e7cc43969a9c9079b5ffd",
|
||||||
|
"sha256:3e74fd8e75ac0e6ebc0443efc3e530167981bf279fecc2294094c820ae266fbb"
|
||||||
|
],
|
||||||
|
"index": "pypi",
|
||||||
|
"version": "==0.8"
|
||||||
|
},
|
||||||
"django-qr-code": {
|
"django-qr-code": {
|
||||||
"hashes": [
|
"hashes": [
|
||||||
"sha256:5fdd84f4c02127c262e6987bd1a9396a47f2b806be04aa0bebfcb786a9f6a0b7",
|
"sha256:5fdd84f4c02127c262e6987bd1a9396a47f2b806be04aa0bebfcb786a9f6a0b7",
|
||||||
@ -55,6 +71,31 @@
|
|||||||
"index": "pypi",
|
"index": "pypi",
|
||||||
"version": "==2.1.0"
|
"version": "==2.1.0"
|
||||||
},
|
},
|
||||||
|
"ipaddress": {
|
||||||
|
"hashes": [
|
||||||
|
"sha256:5a3182b322a706525c46282ca6f064d27a02cffbd449f9f47416f1dc96aa71b0",
|
||||||
|
"sha256:935712800ce4760701d89ad677666cd52691fd2f6f0b340c8b4239a3c17988a5"
|
||||||
|
],
|
||||||
|
"version": "==1.0.16"
|
||||||
|
},
|
||||||
|
"libsass": {
|
||||||
|
"hashes": [
|
||||||
|
"sha256:1521d2a8d4b397c6ec90640a1f6b5529077035efc48ef1c2e53095544e713d1b",
|
||||||
|
"sha256:1b2d415bbf6fa7da33ef46e549db1418498267b459978eff8357e5e823962d35",
|
||||||
|
"sha256:25ebc2085f5eee574761ccc8d9cd29a9b436fc970546d5ef08c6fa41eb57dff1",
|
||||||
|
"sha256:2ae806427b28bc1bb7cb0258666d854fcf92ba52a04656b0b17ba5e190fb48a9",
|
||||||
|
"sha256:4a246e4b88fd279abef8b669206228c92534d96ddcd0770d7012088c408dff23",
|
||||||
|
"sha256:553e5096414a8d4fb48d0a48f5a038d3411abe254d79deac5e008516c019e63a",
|
||||||
|
"sha256:697f0f9fa8a1367ca9ec6869437cb235b1c537fc8519983d1d890178614a8903",
|
||||||
|
"sha256:a8fd4af9f853e8bf42b1425c5e48dd90b504fa2e70d7dac5ac80b8c0a5a5fe85",
|
||||||
|
"sha256:c9411fec76f480ffbacc97d8188322e02a5abca6fc78e70b86a2a2b421eae8a2",
|
||||||
|
"sha256:daa98a51086d92aa7e9c8871cf1a8258124b90e2abf4697852a3dca619838618",
|
||||||
|
"sha256:e0e60836eccbf2d9e24ec978a805cd6642fa92515fbd95e3493fee276af76f8a",
|
||||||
|
"sha256:e64ae2587f1a683e831409aad03ba547c245ef997e1329fffadf7a866d2510b8",
|
||||||
|
"sha256:f6852828e9e104d2ce0358b73c550d26dd86cc3a69439438c3b618811b9584f5"
|
||||||
|
],
|
||||||
|
"version": "==0.20.1"
|
||||||
|
},
|
||||||
"pillow": {
|
"pillow": {
|
||||||
"hashes": [
|
"hashes": [
|
||||||
"sha256:165c88bc9d8dba670110c689e3cc5c71dbe4bfb984ffa7cbebf1fac9554071d6",
|
"sha256:165c88bc9d8dba670110c689e3cc5c71dbe4bfb984ffa7cbebf1fac9554071d6",
|
||||||
@ -93,12 +134,31 @@
|
|||||||
"index": "pypi",
|
"index": "pypi",
|
||||||
"version": "==8.1.0"
|
"version": "==8.1.0"
|
||||||
},
|
},
|
||||||
|
"pygeoip": {
|
||||||
|
"hashes": [
|
||||||
|
"sha256:1938b9dac7b00d77f94d040b9465ea52c938f3fcdcd318b5537994f3c16aef96",
|
||||||
|
"sha256:f22c4e00ddf1213e0fae36dc60b46ee7c25a6339941ec1a975539014c1f9a96d"
|
||||||
|
],
|
||||||
|
"version": "==0.3.2"
|
||||||
|
},
|
||||||
"pytz": {
|
"pytz": {
|
||||||
"hashes": [
|
"hashes": [
|
||||||
"sha256:16962c5fb8db4a8f63a26646d8886e9d769b6c511543557bc84e9569fb9a9cb4",
|
"sha256:0ca2c5966a10e8044db98bac372ed238f323d8d526bbd9a601c3520b98a4bbd3",
|
||||||
"sha256:180befebb1927b16f6b57101720075a984c019ac16b1b7575673bea42c6c3da5"
|
"sha256:1ce2d13b6503a7260c950a25fa6d26ba8d4da1a7077e062aa9e82c74949aaf4b",
|
||||||
|
"sha256:35b5a38d6ffda82220a9e6b97b7b7162887b2d7ff82a1b601979e60d13062718",
|
||||||
|
"sha256:4e9d04cb71d5db26123a1602eb402dab9a7b3686e2786d1e170858e31b59850f",
|
||||||
|
"sha256:51ee2cbb7309df4c2f7c1d2c418aeb869ad48928ed590da7689c034dda7c912f",
|
||||||
|
"sha256:58f594447ca62397c7be6c40fc9406c1d60836cb40974f279058062e58752cea",
|
||||||
|
"sha256:62fd4e22635f609cf1f5da3037612214758ff5f510f4e877ec31d3bbbb995802",
|
||||||
|
"sha256:7dd7accd1690ed32fbd90ad9d273c84e8e09178cdc190b195347d27daebd40df",
|
||||||
|
"sha256:8eba058ab00830a8e3c0a8b81fe5d7b367f0fd18277c94afc9d674a99eae5d42",
|
||||||
|
"sha256:937f30580a1ef85c28fea2672871c2e3df9a22fb9070bc77c20d26ab99ea6369",
|
||||||
|
"sha256:b1f51ed44fe5c25f088bc8c7c189060dc1258b157b5bb6d46d08cc033fca6d1a",
|
||||||
|
"sha256:de72bec3c2f2c86bea4c0c83a921031613976b05ff7f14e1bae1149f177b7746",
|
||||||
|
"sha256:f25c9941d5e80865bfab0f49627e7e199e81ac2f47497b19bebb498dc494b7c9"
|
||||||
],
|
],
|
||||||
"version": "==2020.5"
|
"index": "pypi",
|
||||||
|
"version": "==2016.6"
|
||||||
},
|
},
|
||||||
"qrcode": {
|
"qrcode": {
|
||||||
"hashes": [
|
"hashes": [
|
||||||
@ -155,6 +215,13 @@
|
|||||||
],
|
],
|
||||||
"markers": "python_version >= '3.5'",
|
"markers": "python_version >= '3.5'",
|
||||||
"version": "==0.4.1"
|
"version": "==0.4.1"
|
||||||
|
},
|
||||||
|
"wheel": {
|
||||||
|
"hashes": [
|
||||||
|
"sha256:1ebb8ad7e26b448e9caa4773d2357849bf80ff9e313964bcaf79cbf0201a1648",
|
||||||
|
"sha256:ea8033fc9905804e652f75474d33410a07404c1a78dd3c949a66863bd1050ebd"
|
||||||
|
],
|
||||||
|
"version": "==0.29.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"develop": {}
|
"develop": {}
|
||||||
|
|||||||
@ -14,6 +14,7 @@ class Department(models.Model):
|
|||||||
class Instructor(models.Model):
|
class Instructor(models.Model):
|
||||||
user = models.OneToOneField(User, on_delete=models.CASCADE)
|
user = models.OneToOneField(User, on_delete=models.CASCADE)
|
||||||
department = models.ForeignKey(Department, on_delete=models.CASCADE)
|
department = models.ForeignKey(Department, on_delete=models.CASCADE)
|
||||||
|
timezone = models.CharField(max_length=20)
|
||||||
|
|
||||||
created = models.DateTimeField(auto_now_add=True)
|
created = models.DateTimeField(auto_now_add=True)
|
||||||
modified = models.DateTimeField(auto_now=True)
|
modified = models.DateTimeField(auto_now=True)
|
||||||
@ -30,6 +31,7 @@ class Student(models.Model):
|
|||||||
department = models.ForeignKey(Department, on_delete=models.CASCADE)
|
department = models.ForeignKey(Department, on_delete=models.CASCADE)
|
||||||
is_clocked_in = models.BooleanField(default=False)
|
is_clocked_in = models.BooleanField(default=False)
|
||||||
current_period_id = models.IntegerField(blank=True, null=True)
|
current_period_id = models.IntegerField(blank=True, null=True)
|
||||||
|
timezone = models.CharField(max_length=20)
|
||||||
|
|
||||||
created = models.DateTimeField(auto_now_add=True)
|
created = models.DateTimeField(auto_now_add=True)
|
||||||
modified = models.DateTimeField(auto_now=True)
|
modified = models.DateTimeField(auto_now=True)
|
||||||
|
|||||||
23
accounts/templates/accounts/timezone.html
Normal file
23
accounts/templates/accounts/timezone.html
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
{% extends 'application.html' %}
|
||||||
|
{% load tz %}
|
||||||
|
{% get_current_timezone as TIME_ZONE %}
|
||||||
|
|
||||||
|
{% block content %}
|
||||||
|
<section class="panel">
|
||||||
|
<h1>Set timezone</h1>
|
||||||
|
<form action="{% url 'set_timezone' %}" method="POST">
|
||||||
|
{% csrf_token %}
|
||||||
|
<p>
|
||||||
|
<label for="timezone">Please select your timezone:</label><br>
|
||||||
|
<select name="timezone">
|
||||||
|
{% for tz in timezones %}
|
||||||
|
<option value="{{ tz }}"{% if tz == TIME_ZONE %} selected{% elif tz == 'US/Mountain' %} selected{% endif %}>{{ tz }}</option>
|
||||||
|
{% endfor %}
|
||||||
|
</select>
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
<input type="submit" value="Set timezone">
|
||||||
|
</p>
|
||||||
|
</form>
|
||||||
|
</section>
|
||||||
|
{% endblock %}
|
||||||
@ -1,13 +1,14 @@
|
|||||||
from django.urls import include, path
|
from django.urls import include, path
|
||||||
from . import views
|
from . import views
|
||||||
|
|
||||||
# list/ /users/
|
# list/ /accounts/
|
||||||
# create/ /users/new/
|
# create/ /accounts/new/
|
||||||
# detail/ /users/1/
|
# detail/ /accounts/1/
|
||||||
# update/ /users/1/update/ (update shift preferences)
|
# update/ /accounts/1/update/
|
||||||
# delete/ /users/1/delete/
|
# delete/ /accounts/1/delete/
|
||||||
|
|
||||||
urlpatterns = [
|
urlpatterns = [
|
||||||
|
path('timezone/', views.set_timezone, name='set_timezone'),
|
||||||
path('', views.AccountListView.as_view(), name='account-list'),
|
path('', views.AccountListView.as_view(), name='account-list'),
|
||||||
path('new/', views.AccountCreateView.as_view(), name='account-create'),
|
path('new/', views.AccountCreateView.as_view(), name='account-create'),
|
||||||
path('<int:pk>/', include([
|
path('<int:pk>/', include([
|
||||||
|
|||||||
@ -1,4 +1,5 @@
|
|||||||
from django.shortcuts import render, reverse
|
import pytz
|
||||||
|
from django.shortcuts import render, reverse, redirect
|
||||||
from django.urls import reverse_lazy
|
from django.urls import reverse_lazy
|
||||||
from django.views.generic.edit import FormView, CreateView, UpdateView, DeleteView
|
from django.views.generic.edit import FormView, CreateView, UpdateView, DeleteView
|
||||||
from django.views.generic.detail import DetailView
|
from django.views.generic.detail import DetailView
|
||||||
@ -8,6 +9,13 @@ from django.contrib.auth.mixins import LoginRequiredMixin
|
|||||||
from django.contrib.auth.models import User
|
from django.contrib.auth.models import User
|
||||||
from django.contrib.auth.forms import PasswordChangeForm
|
from django.contrib.auth.forms import PasswordChangeForm
|
||||||
|
|
||||||
|
def set_timezone(request):
|
||||||
|
if request.method == 'POST':
|
||||||
|
request.session['django_timezone'] = request.POST['timezone']
|
||||||
|
return redirect('attendance-overview')
|
||||||
|
else:
|
||||||
|
return render(request, 'accounts/timezone.html', {'timezones': pytz.common_timezones})
|
||||||
|
|
||||||
class AccountListView(LoginRequiredMixin, ListView):
|
class AccountListView(LoginRequiredMixin, ListView):
|
||||||
model = User
|
model = User
|
||||||
template_name = 'accounts/account_list.html'
|
template_name = 'accounts/account_list.html'
|
||||||
|
|||||||
@ -1,4 +1,3 @@
|
|||||||
{% load l10n %}
|
|
||||||
<section>
|
<section>
|
||||||
<header>
|
<header>
|
||||||
<h1>{{ user.instructor.department.name }}</h1>
|
<h1>{{ user.instructor.department.name }}</h1>
|
||||||
@ -9,7 +8,7 @@
|
|||||||
|
|
||||||
<h3>Active sessions</h3>
|
<h3>Active sessions</h3>
|
||||||
<p>
|
<p>
|
||||||
<a href="{% url 'period-create' %}">+ Add new session</a>
|
<a class="action-button" href="{% url 'period-create' %}">+ Add new session</a>
|
||||||
</p>
|
</p>
|
||||||
<table>
|
<table>
|
||||||
<thead>
|
<thead>
|
||||||
@ -26,9 +25,9 @@
|
|||||||
<tr>
|
<tr>
|
||||||
<td>{{ period.student }}</td>
|
<td>{{ period.student }}</td>
|
||||||
<td>{{ period.station_number }}</td>
|
<td>{{ period.station_number }}</td>
|
||||||
<td>{{ period.clocked_in|localize }}</td>
|
<td>{{ period.clocked_in }}</td>
|
||||||
{% if period.clocked_out %}
|
{% if period.clocked_out %}
|
||||||
<td>{{ period.clocked_out|localize }}</td>
|
<td>{{ period.clocked_out }}</td>
|
||||||
<td>{{ period.clocked_in|timesince:period.clocked_out }}</td>
|
<td>{{ period.clocked_in|timesince:period.clocked_out }}</td>
|
||||||
{% else %}
|
{% else %}
|
||||||
<td>Current sesson: {{ period.clocked_in|timesince }}</td>
|
<td>Current sesson: {{ period.clocked_in|timesince }}</td>
|
||||||
@ -61,9 +60,9 @@
|
|||||||
<tr>
|
<tr>
|
||||||
<td>{{ period.student }}</td>
|
<td>{{ period.student }}</td>
|
||||||
<td>{{ period.station_number }}</td>
|
<td>{{ period.station_number }}</td>
|
||||||
<td>{{ period.clocked_in|localize }}</td>
|
<td>{{ period.clocked_in }}</td>
|
||||||
{% if period.clocked_out %}
|
{% if period.clocked_out %}
|
||||||
<td>{{ period.clocked_out|localize }}</td>
|
<td>{{ period.clocked_out }}</td>
|
||||||
<td>{{ period.clocked_in|timesince:period.clocked_out }}</td>
|
<td>{{ period.clocked_in|timesince:period.clocked_out }}</td>
|
||||||
{% else %}
|
{% else %}
|
||||||
<td colspan="2">Current sesson: {{ period.clocked_in|timesince }}</td>
|
<td colspan="2">Current sesson: {{ period.clocked_in|timesince }}</td>
|
||||||
|
|||||||
@ -1,30 +1,37 @@
|
|||||||
<section>
|
<section class="student panel">
|
||||||
<header class="header">
|
<h1 class="student__department">{{ user.student.department.name }}</h1>
|
||||||
<h1>{{ user.first_name }} {{ user.last_name }}</h1>
|
|
||||||
<a href="{% url 'account-update' user.id %}">Update profile</a>
|
|
||||||
</header>
|
|
||||||
<h5>{{ user.student.department.name }}</h5>
|
|
||||||
{% if user.student.is_clocked_in and user.student.code_set.last %}
|
{% if user.student.is_clocked_in and user.student.code_set.last %}
|
||||||
<p>You are currently clocked in.<br>
|
<p><span class="student__clocked-in">You are currently clocked in.</span><br>
|
||||||
Current sesson: {{ current_period.clocked_in|timesince }}</p>
|
Clocked in at: <strong>{{ current_period.clocked_in|date:"P" }}</strong><br>
|
||||||
<a href="{% url 'code-update' user.student.code_set.last.id %}">Clock out →</a>
|
|
||||||
{% elif user.student.is_clocked_in and not user.student.code_set.last %}
|
|
||||||
<p><span class="message info">You are currently clocked in.</span><br>
|
|
||||||
Current sesson: <strong>{{ current_period.clocked_in|timesince }}</strong></p>
|
Current sesson: <strong>{{ current_period.clocked_in|timesince }}</strong></p>
|
||||||
<a href="{% url 'code-create' %}">Clock out →</a>
|
<p>
|
||||||
|
<a class="action-button" href="{% url 'code-update' user.student.code_set.last.id %}">Clock out →</a>
|
||||||
|
</p>
|
||||||
|
{% elif user.student.is_clocked_in and not user.student.code_set.last %}
|
||||||
|
<p><span class="student__clocked-in">You are currently clocked in.</span><br>
|
||||||
|
Clocked in at: {{ current_period.clocked_in }}<br>
|
||||||
|
Current sesson: <strong>{{ current_period.clocked_in|timesince }}</strong></p>
|
||||||
|
<p>
|
||||||
|
<a class="action-button" href="{% url 'code-create' %}">Clock out →</a>
|
||||||
|
</p>
|
||||||
{% elif user.student.code_set.last %}
|
{% elif user.student.code_set.last %}
|
||||||
<p>You are not clocked in.</p>
|
<p>You are not clocked in.</p>
|
||||||
<a href="{% url 'code-update' user.student.code_set.last.id %}">Clock in →</a>
|
<p>
|
||||||
|
<a class="action-button" href="{% url 'code-update' user.student.code_set.last.id %}">Clock in →</a>
|
||||||
|
</p>
|
||||||
{% else %}
|
{% else %}
|
||||||
<p>You are not clocked in.</p>
|
<p>You are not clocked in.</p>
|
||||||
<a href="{% url 'code-create' %}">Clock in →</a>
|
<p>
|
||||||
|
<a class="action-button" href="{% url 'code-create' %}">Clock in →</a>
|
||||||
|
</p>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</section>
|
</section>
|
||||||
<article>
|
<section class="attendance">
|
||||||
<h2>Attendance log</h2>
|
<h2 class="attendance__title">Attendance log</h2>
|
||||||
<p>Total hours for the month: {{period_total}}<br>
|
<p class="attendance__total">Total hours for the month: <strong>{{period_total}}</strong><br>
|
||||||
<small>(Does not include current session.)</small></p>
|
<small>(Does not include current session.)</small></p>
|
||||||
<table>
|
<table class="attendance__log">
|
||||||
|
<caption class="attendance__month">{% now "F Y" %}</caption>
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<th>Station</th>
|
<th>Station</th>
|
||||||
@ -38,9 +45,9 @@
|
|||||||
{% if period.clocked_out %}
|
{% if period.clocked_out %}
|
||||||
<tr>
|
<tr>
|
||||||
<td>{{ period.station_number }}</td>
|
<td>{{ period.station_number }}</td>
|
||||||
<td>{{ period.clocked_in|localize }}</td>
|
<td>{{ period.clocked_in }}</td>
|
||||||
{% if period.clocked_out %}
|
{% if period.clocked_out %}
|
||||||
<td>{{ period.clocked_out|localize }}</td>
|
<td>{{ period.clocked_out }}</td>
|
||||||
<td>{{ period.clocked_in|timesince:period.clocked_out }}</td>
|
<td>{{ period.clocked_in|timesince:period.clocked_out }}</td>
|
||||||
{% else %}
|
{% else %}
|
||||||
<td colspan="2">Current sesson: {{ period.clocked_in|timesince }}</td>
|
<td colspan="2">Current sesson: {{ period.clocked_in|timesince }}</td>
|
||||||
@ -52,4 +59,4 @@
|
|||||||
{% endfor %}
|
{% endfor %}
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
</article>
|
</section>
|
||||||
|
|||||||
@ -4,11 +4,9 @@
|
|||||||
{% block content %}
|
{% block content %}
|
||||||
<h1>Clock in</h1>
|
<h1>Clock in</h1>
|
||||||
|
|
||||||
<section>
|
<section class="code_detail">
|
||||||
<p>
|
|
||||||
{% qr_from_text code.qr_code_str size=24 version=2 %}
|
|
||||||
</p>
|
|
||||||
<p><em>Scan code at clock-in station</em></p>
|
<p><em>Scan code at clock-in station</em></p>
|
||||||
<a href="{% url 'attendance-overview' %}">Done</a>
|
<span>{% qr_from_text code.qr_code_str size=24 version=2 %}</span>
|
||||||
|
<span><a class="action-button" href="{% url 'attendance-overview' %}">Done</a></span>
|
||||||
</section>
|
</section>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|||||||
@ -62,6 +62,7 @@ hr {
|
|||||||
display: flex;
|
display: flex;
|
||||||
align-items: baseline;
|
align-items: baseline;
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
|
margin: 0 1rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.header {
|
.header {
|
||||||
@ -104,13 +105,23 @@ label {
|
|||||||
font-weight: 700;
|
font-weight: 700;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
select {
|
||||||
|
text-align: left;
|
||||||
|
border: 4px solid #bdc3c7;
|
||||||
|
padding: 0.5em;
|
||||||
|
outline: 0;
|
||||||
|
|
||||||
|
-webkit-box-sizing: border-box;
|
||||||
|
-moz-box-sizing: border-box;
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
|
||||||
input[type=text],
|
input[type=text],
|
||||||
input[type=email],
|
input[type=email],
|
||||||
input[type=number],
|
input[type=number],
|
||||||
input[type=password],
|
input[type=password],
|
||||||
select[multiple=multiple],
|
select[multiple=multiple],
|
||||||
textarea,
|
textarea,
|
||||||
.action-button,
|
|
||||||
.datepickr {
|
.datepickr {
|
||||||
text-align: left;
|
text-align: left;
|
||||||
color: #2c3e50;
|
color: #2c3e50;
|
||||||
@ -178,6 +189,10 @@ textarea {
|
|||||||
font-style: oblique;
|
font-style: oblique;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
article {
|
||||||
|
margin: 0 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
section {
|
section {
|
||||||
padding: 1em;
|
padding: 1em;
|
||||||
@ -212,9 +227,31 @@ th {
|
|||||||
border-color: #bdc3c7;
|
border-color: #bdc3c7;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.action-button {
|
||||||
|
|
||||||
|
font-weight: 900;
|
||||||
|
padding: 0.5em 1em;
|
||||||
|
border: none;
|
||||||
|
max-width: 12rem;
|
||||||
|
background-color: #2980B9;
|
||||||
|
color: #ffffff;
|
||||||
|
cursor: pointer;
|
||||||
|
text-decoration: none;
|
||||||
|
|
||||||
|
box-shadow: 0 4px 0 #2980B9;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/* Messages */
|
/* Messages */
|
||||||
.info {color: green;}
|
.info {color: green;}
|
||||||
|
|
||||||
|
|
||||||
|
.code_detail {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
justify-content: center;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|||||||
349
static/css/normalize.css
vendored
Normal file
349
static/css/normalize.css
vendored
Normal file
@ -0,0 +1,349 @@
|
|||||||
|
/*! normalize.css v8.0.1 | MIT License | github.com/necolas/normalize.css */
|
||||||
|
|
||||||
|
/* Document
|
||||||
|
========================================================================== */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 1. Correct the line height in all browsers.
|
||||||
|
* 2. Prevent adjustments of font size after orientation changes in iOS.
|
||||||
|
*/
|
||||||
|
|
||||||
|
html {
|
||||||
|
line-height: 1.15; /* 1 */
|
||||||
|
-webkit-text-size-adjust: 100%; /* 2 */
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Sections
|
||||||
|
========================================================================== */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove the margin in all browsers.
|
||||||
|
*/
|
||||||
|
|
||||||
|
body {
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Render the `main` element consistently in IE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
main {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Correct the font size and margin on `h1` elements within `section` and
|
||||||
|
* `article` contexts in Chrome, Firefox, and Safari.
|
||||||
|
*/
|
||||||
|
|
||||||
|
h1 {
|
||||||
|
font-size: 2em;
|
||||||
|
margin: 0.67em 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Grouping content
|
||||||
|
========================================================================== */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 1. Add the correct box sizing in Firefox.
|
||||||
|
* 2. Show the overflow in Edge and IE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
hr {
|
||||||
|
box-sizing: content-box; /* 1 */
|
||||||
|
height: 0; /* 1 */
|
||||||
|
overflow: visible; /* 2 */
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 1. Correct the inheritance and scaling of font size in all browsers.
|
||||||
|
* 2. Correct the odd `em` font sizing in all browsers.
|
||||||
|
*/
|
||||||
|
|
||||||
|
pre {
|
||||||
|
font-family: monospace, monospace; /* 1 */
|
||||||
|
font-size: 1em; /* 2 */
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Text-level semantics
|
||||||
|
========================================================================== */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove the gray background on active links in IE 10.
|
||||||
|
*/
|
||||||
|
|
||||||
|
a {
|
||||||
|
background-color: transparent;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 1. Remove the bottom border in Chrome 57-
|
||||||
|
* 2. Add the correct text decoration in Chrome, Edge, IE, Opera, and Safari.
|
||||||
|
*/
|
||||||
|
|
||||||
|
abbr[title] {
|
||||||
|
border-bottom: none; /* 1 */
|
||||||
|
text-decoration: underline; /* 2 */
|
||||||
|
text-decoration: underline dotted; /* 2 */
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add the correct font weight in Chrome, Edge, and Safari.
|
||||||
|
*/
|
||||||
|
|
||||||
|
b,
|
||||||
|
strong {
|
||||||
|
font-weight: bolder;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 1. Correct the inheritance and scaling of font size in all browsers.
|
||||||
|
* 2. Correct the odd `em` font sizing in all browsers.
|
||||||
|
*/
|
||||||
|
|
||||||
|
code,
|
||||||
|
kbd,
|
||||||
|
samp {
|
||||||
|
font-family: monospace, monospace; /* 1 */
|
||||||
|
font-size: 1em; /* 2 */
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add the correct font size in all browsers.
|
||||||
|
*/
|
||||||
|
|
||||||
|
small {
|
||||||
|
font-size: 80%;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Prevent `sub` and `sup` elements from affecting the line height in
|
||||||
|
* all browsers.
|
||||||
|
*/
|
||||||
|
|
||||||
|
sub,
|
||||||
|
sup {
|
||||||
|
font-size: 75%;
|
||||||
|
line-height: 0;
|
||||||
|
position: relative;
|
||||||
|
vertical-align: baseline;
|
||||||
|
}
|
||||||
|
|
||||||
|
sub {
|
||||||
|
bottom: -0.25em;
|
||||||
|
}
|
||||||
|
|
||||||
|
sup {
|
||||||
|
top: -0.5em;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Embedded content
|
||||||
|
========================================================================== */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove the border on images inside links in IE 10.
|
||||||
|
*/
|
||||||
|
|
||||||
|
img {
|
||||||
|
border-style: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Forms
|
||||||
|
========================================================================== */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 1. Change the font styles in all browsers.
|
||||||
|
* 2. Remove the margin in Firefox and Safari.
|
||||||
|
*/
|
||||||
|
|
||||||
|
button,
|
||||||
|
input,
|
||||||
|
optgroup,
|
||||||
|
select,
|
||||||
|
textarea {
|
||||||
|
font-family: inherit; /* 1 */
|
||||||
|
font-size: 100%; /* 1 */
|
||||||
|
line-height: 1.15; /* 1 */
|
||||||
|
margin: 0; /* 2 */
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Show the overflow in IE.
|
||||||
|
* 1. Show the overflow in Edge.
|
||||||
|
*/
|
||||||
|
|
||||||
|
button,
|
||||||
|
input { /* 1 */
|
||||||
|
overflow: visible;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove the inheritance of text transform in Edge, Firefox, and IE.
|
||||||
|
* 1. Remove the inheritance of text transform in Firefox.
|
||||||
|
*/
|
||||||
|
|
||||||
|
button,
|
||||||
|
select { /* 1 */
|
||||||
|
text-transform: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Correct the inability to style clickable types in iOS and Safari.
|
||||||
|
*/
|
||||||
|
|
||||||
|
button,
|
||||||
|
[type="button"],
|
||||||
|
[type="reset"],
|
||||||
|
[type="submit"] {
|
||||||
|
-webkit-appearance: button;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove the inner border and padding in Firefox.
|
||||||
|
*/
|
||||||
|
|
||||||
|
button::-moz-focus-inner,
|
||||||
|
[type="button"]::-moz-focus-inner,
|
||||||
|
[type="reset"]::-moz-focus-inner,
|
||||||
|
[type="submit"]::-moz-focus-inner {
|
||||||
|
border-style: none;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Restore the focus styles unset by the previous rule.
|
||||||
|
*/
|
||||||
|
|
||||||
|
button:-moz-focusring,
|
||||||
|
[type="button"]:-moz-focusring,
|
||||||
|
[type="reset"]:-moz-focusring,
|
||||||
|
[type="submit"]:-moz-focusring {
|
||||||
|
outline: 1px dotted ButtonText;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Correct the padding in Firefox.
|
||||||
|
*/
|
||||||
|
|
||||||
|
fieldset {
|
||||||
|
padding: 0.35em 0.75em 0.625em;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 1. Correct the text wrapping in Edge and IE.
|
||||||
|
* 2. Correct the color inheritance from `fieldset` elements in IE.
|
||||||
|
* 3. Remove the padding so developers are not caught out when they zero out
|
||||||
|
* `fieldset` elements in all browsers.
|
||||||
|
*/
|
||||||
|
|
||||||
|
legend {
|
||||||
|
box-sizing: border-box; /* 1 */
|
||||||
|
color: inherit; /* 2 */
|
||||||
|
display: table; /* 1 */
|
||||||
|
max-width: 100%; /* 1 */
|
||||||
|
padding: 0; /* 3 */
|
||||||
|
white-space: normal; /* 1 */
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add the correct vertical alignment in Chrome, Firefox, and Opera.
|
||||||
|
*/
|
||||||
|
|
||||||
|
progress {
|
||||||
|
vertical-align: baseline;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove the default vertical scrollbar in IE 10+.
|
||||||
|
*/
|
||||||
|
|
||||||
|
textarea {
|
||||||
|
overflow: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 1. Add the correct box sizing in IE 10.
|
||||||
|
* 2. Remove the padding in IE 10.
|
||||||
|
*/
|
||||||
|
|
||||||
|
[type="checkbox"],
|
||||||
|
[type="radio"] {
|
||||||
|
box-sizing: border-box; /* 1 */
|
||||||
|
padding: 0; /* 2 */
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Correct the cursor style of increment and decrement buttons in Chrome.
|
||||||
|
*/
|
||||||
|
|
||||||
|
[type="number"]::-webkit-inner-spin-button,
|
||||||
|
[type="number"]::-webkit-outer-spin-button {
|
||||||
|
height: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 1. Correct the odd appearance in Chrome and Safari.
|
||||||
|
* 2. Correct the outline style in Safari.
|
||||||
|
*/
|
||||||
|
|
||||||
|
[type="search"] {
|
||||||
|
-webkit-appearance: textfield; /* 1 */
|
||||||
|
outline-offset: -2px; /* 2 */
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove the inner padding in Chrome and Safari on macOS.
|
||||||
|
*/
|
||||||
|
|
||||||
|
[type="search"]::-webkit-search-decoration {
|
||||||
|
-webkit-appearance: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 1. Correct the inability to style clickable types in iOS and Safari.
|
||||||
|
* 2. Change font properties to `inherit` in Safari.
|
||||||
|
*/
|
||||||
|
|
||||||
|
::-webkit-file-upload-button {
|
||||||
|
-webkit-appearance: button; /* 1 */
|
||||||
|
font: inherit; /* 2 */
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Interactive
|
||||||
|
========================================================================== */
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Add the correct display in Edge, IE 10+, and Firefox.
|
||||||
|
*/
|
||||||
|
|
||||||
|
details {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Add the correct display in all browsers.
|
||||||
|
*/
|
||||||
|
|
||||||
|
summary {
|
||||||
|
display: list-item;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Misc
|
||||||
|
========================================================================== */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add the correct display in IE 10+.
|
||||||
|
*/
|
||||||
|
|
||||||
|
template {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add the correct display in IE 10.
|
||||||
|
*/
|
||||||
|
|
||||||
|
[hidden] {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
0
static/scss/_codes.scss
Normal file
0
static/scss/_codes.scss
Normal file
91
static/scss/_forms.scss
Normal file
91
static/scss/_forms.scss
Normal file
@ -0,0 +1,91 @@
|
|||||||
|
button,
|
||||||
|
input,
|
||||||
|
optgroup,
|
||||||
|
select,
|
||||||
|
textarea {
|
||||||
|
color: inherit;
|
||||||
|
font: inherit;
|
||||||
|
margin: 0;
|
||||||
|
max-width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
input {
|
||||||
|
text-align: left;
|
||||||
|
outline: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
label {
|
||||||
|
text-align: left;
|
||||||
|
font-weight: 700;
|
||||||
|
}
|
||||||
|
|
||||||
|
select {
|
||||||
|
text-align: left;
|
||||||
|
border: 4px solid $grey;
|
||||||
|
padding: 0.5em;
|
||||||
|
outline: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
input[type=text],
|
||||||
|
input[type=email],
|
||||||
|
input[type=number],
|
||||||
|
input[type=password],
|
||||||
|
select[multiple=multiple],
|
||||||
|
textarea {
|
||||||
|
text-align: left;
|
||||||
|
color: $primary-color;
|
||||||
|
border: 4px solid $grey;
|
||||||
|
padding: 0.5rem;
|
||||||
|
outline: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
input[type=submit],
|
||||||
|
.action-button {
|
||||||
|
font-weight: 900;
|
||||||
|
padding: 0.5em 1em;
|
||||||
|
border: none;
|
||||||
|
background-color: $blue;
|
||||||
|
color: white;
|
||||||
|
cursor: pointer;
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
input:focus,
|
||||||
|
textarea:focus {
|
||||||
|
border-color: $blue;
|
||||||
|
}
|
||||||
|
|
||||||
|
select[multiple=multiple] {
|
||||||
|
height: 125px;
|
||||||
|
}
|
||||||
|
|
||||||
|
input[type=number] {
|
||||||
|
max-width: 6.25rem;
|
||||||
|
padding: 0 1rem;
|
||||||
|
// height: 3.32em;
|
||||||
|
}
|
||||||
|
|
||||||
|
input[type=checkbox] {
|
||||||
|
width: 1em;
|
||||||
|
vertical-align: text-top;
|
||||||
|
}
|
||||||
|
|
||||||
|
textarea {
|
||||||
|
width: 100%;
|
||||||
|
height: 6.25rem;
|
||||||
|
line-height: 1.45;
|
||||||
|
}
|
||||||
|
|
||||||
|
::-webkit-input-placeholder {
|
||||||
|
font-style: oblique;
|
||||||
|
}
|
||||||
|
::-moz-input-placeholder {
|
||||||
|
font-style: oblique;
|
||||||
|
}
|
||||||
|
::-ms-input-placeholder {
|
||||||
|
font-style: oblique;
|
||||||
|
}
|
||||||
|
|
||||||
|
.action-button {
|
||||||
|
border-radius: 3.75rem;
|
||||||
|
}
|
||||||
81
static/scss/_globals.scss
Normal file
81
static/scss/_globals.scss
Normal file
@ -0,0 +1,81 @@
|
|||||||
|
html {
|
||||||
|
font-size: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
body {
|
||||||
|
background-color: $bg-color;
|
||||||
|
color: $primary-color;
|
||||||
|
font-family: 'Lato', sans-serif;
|
||||||
|
font-weight: 400;
|
||||||
|
line-height: 1.75;
|
||||||
|
text-rendering: optimizeLegibility;
|
||||||
|
}
|
||||||
|
p {
|
||||||
|
margin-bottom: 1rem;
|
||||||
|
}
|
||||||
|
h1, h2, h3, h4, h5 {
|
||||||
|
margin: 3rem 0 1.38rem;
|
||||||
|
font-family: 'Lato', sans-serif;
|
||||||
|
line-height: 1.3;
|
||||||
|
text-rendering: optimizeLegibility;
|
||||||
|
}
|
||||||
|
h1 {
|
||||||
|
margin-top: 0;
|
||||||
|
font-size: 2.488rem;
|
||||||
|
}
|
||||||
|
h2 {
|
||||||
|
font-size: 2.074rem;
|
||||||
|
}
|
||||||
|
h3 {
|
||||||
|
font-size: 1.728rem;
|
||||||
|
}
|
||||||
|
h4 {
|
||||||
|
font-size: 1.44rem;
|
||||||
|
}
|
||||||
|
h5 {
|
||||||
|
font-size: 1.2rem;
|
||||||
|
}
|
||||||
|
small {
|
||||||
|
font-size: 0.833rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
a {
|
||||||
|
color: $blue;
|
||||||
|
cursor: pointer;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
hr {
|
||||||
|
margin: 0;
|
||||||
|
border: 0.8px solid $grey;
|
||||||
|
}
|
||||||
|
|
||||||
|
// BLOCK ELEMENTS
|
||||||
|
main {
|
||||||
|
max-width: 58rem;
|
||||||
|
margin: 0 auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// TABLES
|
||||||
|
|
||||||
|
table {
|
||||||
|
border-collapse: collapse;
|
||||||
|
border-spacing: 0;
|
||||||
|
width: 100%;
|
||||||
|
margin-bottom: 1.3em;
|
||||||
|
border: 1px solid $grey;
|
||||||
|
}
|
||||||
|
|
||||||
|
table a {
|
||||||
|
white-space: normal;
|
||||||
|
}
|
||||||
|
|
||||||
|
td,
|
||||||
|
th {
|
||||||
|
text-align: left;
|
||||||
|
font-size: 0.85em;
|
||||||
|
padding: 2px 10px;
|
||||||
|
border-bottom: 1px solid;
|
||||||
|
border-color: $grey;
|
||||||
|
}
|
||||||
14
static/scss/_helpers.scss
Normal file
14
static/scss/_helpers.scss
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
.panel {
|
||||||
|
max-width: 58rem;
|
||||||
|
padding: 1rem;
|
||||||
|
background-color: white;
|
||||||
|
border-top: 1px solid $grey;
|
||||||
|
border-bottom: 1px solid $grey;
|
||||||
|
margin-bottom: 1.5rem;
|
||||||
|
}
|
||||||
|
@media screen and (min-width: 58rem) {
|
||||||
|
.panel {
|
||||||
|
border: 1px solid $grey;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
0
static/scss/_instructors.scss
Normal file
0
static/scss/_instructors.scss
Normal file
27
static/scss/_nav.scss
Normal file
27
static/scss/_nav.scss
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
.navbar {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
margin: 1rem;
|
||||||
|
|
||||||
|
&__logo {
|
||||||
|
font-weight: bold;
|
||||||
|
font-size: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__nav {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
.nav {
|
||||||
|
&nav__user {
|
||||||
|
|
||||||
|
}
|
||||||
|
&nav__logout {
|
||||||
|
|
||||||
|
}
|
||||||
|
&nav__login {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
0
static/scss/_periods.scss
Normal file
0
static/scss/_periods.scss
Normal file
0
static/scss/_registration.scss
Normal file
0
static/scss/_registration.scss
Normal file
27
static/scss/_students.scss
Normal file
27
static/scss/_students.scss
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
.student {
|
||||||
|
|
||||||
|
&__header {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: baseline;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__department {
|
||||||
|
margin-top: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__clocked-in {
|
||||||
|
background-color: $green;
|
||||||
|
color: white;
|
||||||
|
padding: 0.5rem;
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.attendance {
|
||||||
|
margin: 0 1rem;
|
||||||
|
|
||||||
|
&__month {
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
}
|
||||||
16
static/scss/_variables.scss
Normal file
16
static/scss/_variables.scss
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
/*bg (gray) white
|
||||||
|
primary (black) #323834
|
||||||
|
faded (light-black) #747a7c
|
||||||
|
accent 1 (red) #2980B9
|
||||||
|
accent 2 (orange) #9b6f45
|
||||||
|
accent 3 (blue) #242e34
|
||||||
|
*/
|
||||||
|
|
||||||
|
$bg-color: #f7f7f7;
|
||||||
|
$primary-color: #323834;
|
||||||
|
$faded-color: #747a7c;
|
||||||
|
$blue: #2980B9;
|
||||||
|
$orange: #9b6f45;
|
||||||
|
$darkblue: #242e34;
|
||||||
|
$grey: #bdc3c7;
|
||||||
|
$green: green;
|
||||||
11
static/scss/main.scss
Normal file
11
static/scss/main.scss
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
@import "variables";
|
||||||
|
@import "globals";
|
||||||
|
@import "helpers";
|
||||||
|
@import "forms";
|
||||||
|
@import "nav";
|
||||||
|
@import "registration";
|
||||||
|
@import "students";
|
||||||
|
@import "instructors";
|
||||||
|
@import "periods";
|
||||||
|
@import "codes";
|
||||||
|
|
||||||
@ -1,4 +1,5 @@
|
|||||||
{% load static %}
|
{% load static %}
|
||||||
|
{% load compress %}
|
||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
<html lang="en">
|
<html lang="en">
|
||||||
<head>
|
<head>
|
||||||
@ -10,25 +11,33 @@
|
|||||||
{% block head %}
|
{% block head %}
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
<link rel="preconnect" href="https://fonts.gstatic.com">
|
<link rel="preconnect" href="https://fonts.gstatic.com">
|
||||||
<link href="https://fonts.googleapis.com/css2?family=Lato:ital,wght@0,400;0,700;1,400;1,700&display=swap" rel="stylesheet">
|
<link href="https://fonts.googleapis.com/css2?family=Lato:ital,wght@0,400;0,700;1,400;1,700&display=swap" rel="stylesheet">
|
||||||
<link rel="stylesheet" type="text/css" href="{% static 'css/base.css' %}">
|
{% compress css %}
|
||||||
|
<link href="{% static 'css/normalize.css' %}" rel="stylesheet" media="screen">
|
||||||
|
<link type="text/x-scss" href="{% static 'scss/main.scss' %}" rel="stylesheet" media="screen">
|
||||||
|
{% endcompress %}
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<header class="navigation">
|
<header class="navbar">
|
||||||
<p><strong>BTech Time Tracker</strong></p>
|
<span class="navbar__logo">BTech Time Tracker</span>
|
||||||
|
<nav class="navbar__nav nav">
|
||||||
{% if user.is_authenticated %}
|
{% if user.is_authenticated %}
|
||||||
<nav>
|
<a class="nav__user" href="{% url 'account-update' user.id %}">{{ user.first_name }} {{ user.last_name }}</a> /
|
||||||
<a href="{% url 'account-update' user.id %}">{{ user.first_name }} {{ user.last_name }}</a> /
|
<a class="nav__logout" class="logout" href="{% url 'logout' %}">Logout</a>
|
||||||
<a class="logout" href="{% url 'logout' %}">Logout</a>
|
|
||||||
</nav>
|
|
||||||
{% else %}
|
{% else %}
|
||||||
<nav>
|
<a class="nav__login" class="login" href="{% url 'login' %}">Login</a>
|
||||||
<a class="login" href="{% url 'login' %}">Login</a>
|
|
||||||
</nav>
|
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
</nav>
|
||||||
</header>
|
</header>
|
||||||
|
|
||||||
<aside>
|
<aside>
|
||||||
|
{% if messages %}
|
||||||
|
<div class="messages">
|
||||||
|
{% for message in messages %}
|
||||||
|
<span {% if message.tags %} class="{{ message.tags }} messages__message"{% endif %}>{{ message }}</span>
|
||||||
|
{% endfor %}
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
{% block aside %}
|
{% block aside %}
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
</aside>
|
</aside>
|
||||||
@ -36,13 +45,6 @@
|
|||||||
<main>
|
<main>
|
||||||
{% block content %}
|
{% block content %}
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
{% if messages %}
|
|
||||||
<div class="messages">
|
|
||||||
{% for message in messages %}
|
|
||||||
<p {% if message.tags %} class="{{ message.tags }}"{% endif %}>{{ message }}</p>
|
|
||||||
{% endfor %}
|
|
||||||
</div>
|
|
||||||
{% endif %}
|
|
||||||
</main>
|
</main>
|
||||||
|
|
||||||
<footer>
|
<footer>
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
{% extends 'application.html' %}
|
{% extends 'application.html' %}
|
||||||
|
|
||||||
{% block content %}
|
{% block content %}
|
||||||
<section>
|
<section class="panel">
|
||||||
<p>You have been logged out. <a href="{% url 'login' %}">Log in</a></p>
|
<p>You have been logged out. <a href="{% url 'login' %}">Log in</a></p>
|
||||||
</section>
|
</section>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
{% extends 'application.html' %}
|
{% extends 'application.html' %}
|
||||||
|
|
||||||
{% block content %}
|
{% block content %}
|
||||||
<section>
|
<section class="panel">
|
||||||
<h1>Log in</h1>
|
<h1>Log in</h1>
|
||||||
{% if form.errors %}
|
{% if form.errors %}
|
||||||
<p class="error">Your username and password didn't match. Please try again.</p>
|
<p class="error">Your username and password didn't match. Please try again.</p>
|
||||||
@ -19,6 +19,7 @@
|
|||||||
{{ form.password.label_tag }}
|
{{ form.password.label_tag }}
|
||||||
<br>
|
<br>
|
||||||
{{ form.password }}
|
{{ form.password }}
|
||||||
|
<br>
|
||||||
<small>
|
<small>
|
||||||
Forgot your password? <a href="{% url 'password_reset' %}">Reset your password here</a>.
|
Forgot your password? <a href="{% url 'password_reset' %}">Reset your password here</a>.
|
||||||
</small>
|
</small>
|
||||||
|
|||||||
@ -1,12 +1,12 @@
|
|||||||
{% extends 'application.html' %}
|
{% extends 'application.html' %}
|
||||||
|
|
||||||
{% block content %}
|
{% block content %}
|
||||||
<section>
|
<section class="panel">
|
||||||
<form method="post" action="{% url 'password_change' %}">
|
<form method="post" action="{% url 'password_change' %}">
|
||||||
{% csrf_token %}
|
{% csrf_token %}
|
||||||
{{ form.as_p }}
|
{{ form.as_p }}
|
||||||
|
|
||||||
<input type="submit" value="Change my password" />
|
<input type="submit" value="Change my password">
|
||||||
</form>
|
</form>
|
||||||
</section>
|
</section>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
{% extends 'application.html' %}
|
{% extends 'application.html' %}
|
||||||
|
|
||||||
{% block content %}
|
{% block content %}
|
||||||
<sectin>
|
<section class="panel">
|
||||||
<p>Password was reset successfully.</p>
|
<p>Password was reset successfully.</p>
|
||||||
</section>
|
</section>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|||||||
@ -3,7 +3,7 @@
|
|||||||
{% block content %}
|
{% block content %}
|
||||||
|
|
||||||
{% if validlink %}
|
{% if validlink %}
|
||||||
<section>
|
<section class="panel">
|
||||||
<h1>Reset password</h1>
|
<h1>Reset password</h1>
|
||||||
<p>Enter a <em>new</em> password below.</p>
|
<p>Enter a <em>new</em> password below.</p>
|
||||||
<form method="post" action=".">
|
<form method="post" action=".">
|
||||||
@ -15,7 +15,7 @@
|
|||||||
</section>
|
</section>
|
||||||
{% else %}
|
{% else %}
|
||||||
|
|
||||||
<section>
|
<section class="panel">
|
||||||
<p>Password reset failed</p>
|
<p>Password reset failed</p>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
{% extends 'application.html' %}
|
{% extends 'application.html' %}
|
||||||
|
|
||||||
{% block content %}
|
{% block content %}
|
||||||
<section>
|
<section class="panel">
|
||||||
<p>An email with password reset instructions has been sent.</p>
|
<p>An email with password reset instructions has been sent.</p>
|
||||||
</section>
|
</section>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
{% extends 'application.html' %}
|
{% extends 'application.html' %}
|
||||||
|
|
||||||
{% block content %}
|
{% block content %}
|
||||||
<section>
|
<section class="panel">
|
||||||
<h1>Reset your password</h1>
|
<h1>Reset your password</h1>
|
||||||
<p>Enter your email address below and we'll send you instructions.</p>
|
<p>Enter your email address below and we'll send you instructions.</p>
|
||||||
<form method="post" action="{% url 'password_reset' %}">
|
<form method="post" action="{% url 'password_reset' %}">
|
||||||
|
|||||||
18
tracker/middleware.py
Normal file
18
tracker/middleware.py
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
import pytz
|
||||||
|
|
||||||
|
from django.utils import timezone
|
||||||
|
|
||||||
|
class TimezoneMiddleware:
|
||||||
|
def __init__(self, get_response):
|
||||||
|
self.get_response = get_response
|
||||||
|
|
||||||
|
def __call__(self, request):
|
||||||
|
tzname = request.session.get('django_timezone')
|
||||||
|
print(tzname)
|
||||||
|
if tzname:
|
||||||
|
timezone.activate(pytz.timezone(tzname))
|
||||||
|
print("tz activated")
|
||||||
|
else:
|
||||||
|
timezone.deactivate()
|
||||||
|
print("tz deactivated")
|
||||||
|
return self.get_response(request)
|
||||||
@ -52,6 +52,7 @@ MIDDLEWARE = [
|
|||||||
'django.contrib.auth.middleware.AuthenticationMiddleware',
|
'django.contrib.auth.middleware.AuthenticationMiddleware',
|
||||||
'django.contrib.messages.middleware.MessageMiddleware',
|
'django.contrib.messages.middleware.MessageMiddleware',
|
||||||
'django.middleware.clickjacking.XFrameOptionsMiddleware',
|
'django.middleware.clickjacking.XFrameOptionsMiddleware',
|
||||||
|
'tracker.middleware.TimezoneMiddleware',
|
||||||
]
|
]
|
||||||
|
|
||||||
ROOT_URLCONF = 'tracker.urls'
|
ROOT_URLCONF = 'tracker.urls'
|
||||||
@ -110,7 +111,7 @@ AUTH_PASSWORD_VALIDATORS = [
|
|||||||
|
|
||||||
LANGUAGE_CODE = 'en-us'
|
LANGUAGE_CODE = 'en-us'
|
||||||
|
|
||||||
TIME_ZONE = 'US/Mountain'
|
TIME_ZONE = 'UTC'
|
||||||
|
|
||||||
USE_I18N = True
|
USE_I18N = True
|
||||||
|
|
||||||
@ -130,13 +131,22 @@ STATICFILES_FINDERS = (
|
|||||||
)
|
)
|
||||||
|
|
||||||
STATIC_URL = '/static/'
|
STATIC_URL = '/static/'
|
||||||
STATICFILES_DIRS = ['static']
|
|
||||||
STATIC_ROOT = '/var/www/static/'
|
STATIC_ROOT = '/var/www/static/'
|
||||||
|
COMPRESS_ROOT = BASE_DIR / 'static'
|
||||||
|
STATICFILES_DIRS = [BASE_DIR / 'static']
|
||||||
|
STATICFILES_FINDERS = (
|
||||||
|
'django.contrib.staticfiles.finders.FileSystemFinder',
|
||||||
|
'django.contrib.staticfiles.finders.AppDirectoriesFinder',
|
||||||
|
'compressor.finders.CompressorFinder',
|
||||||
|
)
|
||||||
|
|
||||||
|
COMPRESS_PRECOMPILERS = (
|
||||||
|
('text/x-scss', 'django_libsass.SassCompiler'),
|
||||||
|
)
|
||||||
# Media file storage
|
# Media file storage
|
||||||
|
|
||||||
MEDIA_URL = '/media/'
|
MEDIA_URL = '/media/'
|
||||||
MEDIA_ROOT = BASE_DIR / 'media'
|
MEDIA_ROOT = BASE_DIR / 'media'
|
||||||
|
|
||||||
|
|
||||||
LOGIN_REDIRECT_URL = '/attendance'
|
LOGIN_REDIRECT_URL = '/accounts/timezone/'
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user