diff --git a/accounts/templates/accounts/account_detail.html b/accounts/templates/accounts/account_detail.html
index 4e6c6e3..0e277e6 100755
--- a/accounts/templates/accounts/account_detail.html
+++ b/accounts/templates/accounts/account_detail.html
@@ -1,11 +1,13 @@
{% extends 'base.html' %}
{% block content %}
-
+
+
{% endblock %}
diff --git a/accounts/templates/accounts/profile.html b/accounts/templates/accounts/profile.html
index 927dec5..acf3d50 100644
--- a/accounts/templates/accounts/profile.html
+++ b/accounts/templates/accounts/profile.html
@@ -6,5 +6,59 @@
+
+ Today, {% now "D, M j" %}
+
+ {% for event in today %}
+
+
{{event.date|date:"D, M j"}}
+
{{event.name}}
+ {% if event.employee %} for
+ {{event.employee}}
+ {% endif %}
+
+
{{event.time|time:"TIME_FORMAT"}}
+
Edit
+
+ {% endfor %}
+
+
+
Add event
+
+ Upcoming
+
+ {% for event in upcoming_events %}
+
+
{{event.date|date:"D, M j"}}
+
{{event.name}}
+ {% if event.employee %} for
+ {{event.employee}}
+ {% endif %}
+
+
{{event.time|time:"TIME_FORMAT"}}
+
Edit
+
+ {% endfor %}
+
+ See all events
+
+
+ Recent Activity
+ {% regroup latest_activity by created_at.date as latest_activity_re %}
+ {% for date in latest_activity_re %}
+ {{date.grouper|date:"D, M j"}}
+ {% for entry in date.list %}
+
+ {{entry.notes}}
+ for
+ {{entry.employee}}
+
+ {% endfor %}
+ {% endfor %}
+
{% endblock %}
+
+
+
+
diff --git a/accounts/views.py b/accounts/views.py
index b13a258..1d0ffad 100755
--- a/accounts/views.py
+++ b/accounts/views.py
@@ -1,5 +1,6 @@
import pytz
from django.utils import timezone
+from datetime import date, timedelta
from django.shortcuts import render, reverse, redirect
from django.urls import reverse_lazy
from django.views.generic.base import TemplateView
@@ -12,14 +13,25 @@ from django.contrib.auth.models import User
from .models import Profile
from .forms import AccountUpdateForm, ProfileUpdateForm
+from board.models import LogEntry, Event
+
class ProfileView(LoginRequiredMixin, TemplateView):
template_name = 'accounts/profile.html'
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
+ today = date.today()
+ tomorrow = today + timedelta(days=1)
+ enddate = today + timedelta(days=6)
context['profile'] = self.request.user.profile
-
+ context['latest_activity'] = LogEntry.objects.all()[:10]
+ context['today'] = Event.objects.filter(
+ date=today
+ )
+ context['upcoming_events'] = Event.objects.filter(
+ date__range=[tomorrow, enddate]
+ )
return context
class ProfileUpdateView(LoginRequiredMixin, UpdateView):
diff --git a/board/admin.py b/board/admin.py
index fa58b84..2bbcd67 100644
--- a/board/admin.py
+++ b/board/admin.py
@@ -4,8 +4,10 @@ from .models import (
Employee,
LogEntry,
Todo,
+ Event,
)
admin.site.register(Employee)
admin.site.register(LogEntry)
admin.site.register(Todo)
+admin.site.register(Event)
diff --git a/board/forms.py b/board/forms.py
index d32ebe2..0305e3c 100644
--- a/board/forms.py
+++ b/board/forms.py
@@ -1,6 +1,7 @@
from datetime import datetime
from django import forms
-from .models import Employee, LogEntry, Todo
+from django.utils import timezone
+from .models import Employee, LogEntry, Todo, Event
from .regex import process_regex
class EmployeePreCreateForm(forms.Form):
@@ -89,3 +90,25 @@ class TodoCreateForm(forms.ModelForm):
'autofocus': 'autofocus'
})
}
+
+class EventForm(forms.ModelForm):
+ class Meta:
+ model = Event
+ fields = (
+ 'name',
+ 'date',
+ 'time',
+ 'employee',
+ )
+ widgets = {
+ 'name': forms.TextInput(attrs = {
+ 'autofocus': 'autofocus'
+ }),
+ 'date': forms.DateInput(attrs = {
+ 'type': 'date',
+ 'value': timezone.now().date(),
+ }),
+ 'time': forms.DateInput(attrs = {
+ 'type': 'time',
+ }),
+ }
diff --git a/board/migrations/0003_auto_20210727_0006.py b/board/migrations/0003_auto_20210727_0006.py
new file mode 100644
index 0000000..07ee0d7
--- /dev/null
+++ b/board/migrations/0003_auto_20210727_0006.py
@@ -0,0 +1,70 @@
+# Generated by Django 3.2.5 on 2021-07-27 00:06
+
+from django.db import migrations, models
+import django.db.models.deletion
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ('board', '0002_auto_20210709_0103'),
+ ]
+
+ operations = [
+ migrations.AlterModelOptions(
+ name='employee',
+ options={'ordering': ('-hire_date',)},
+ ),
+ migrations.AlterModelOptions(
+ name='logentry',
+ options={'ordering': ('-created_at',)},
+ ),
+ migrations.AlterField(
+ model_name='employee',
+ name='department',
+ field=models.CharField(blank=True, max_length=64, null=True),
+ ),
+ migrations.AlterField(
+ model_name='employee',
+ name='first_name',
+ field=models.CharField(max_length=64),
+ ),
+ migrations.AlterField(
+ model_name='employee',
+ name='last_name',
+ field=models.CharField(max_length=64),
+ ),
+ migrations.AlterField(
+ model_name='employee',
+ name='manager',
+ field=models.CharField(blank=True, max_length=64, null=True),
+ ),
+ migrations.AlterField(
+ model_name='employee',
+ name='title',
+ field=models.CharField(blank=True, max_length=64, null=True),
+ ),
+ migrations.AlterField(
+ model_name='logentry',
+ name='notes',
+ field=models.CharField(max_length=500),
+ ),
+ migrations.AlterField(
+ model_name='todo',
+ name='description',
+ field=models.CharField(max_length=64),
+ ),
+ migrations.CreateModel(
+ name='Event',
+ fields=[
+ ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
+ ('description', models.CharField(max_length=64)),
+ ('date', models.DateField()),
+ ('time', models.TimeField(blank=True, null=True)),
+ ('employee', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='board.employee')),
+ ],
+ options={
+ 'ordering': ('date', 'time'),
+ },
+ ),
+ ]
diff --git a/board/migrations/0004_rename_description_event_name.py b/board/migrations/0004_rename_description_event_name.py
new file mode 100644
index 0000000..dc8a6d3
--- /dev/null
+++ b/board/migrations/0004_rename_description_event_name.py
@@ -0,0 +1,18 @@
+# Generated by Django 3.2.5 on 2021-07-27 01:53
+
+from django.db import migrations
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ('board', '0003_auto_20210727_0006'),
+ ]
+
+ operations = [
+ migrations.RenameField(
+ model_name='event',
+ old_name='description',
+ new_name='name',
+ ),
+ ]
diff --git a/board/models.py b/board/models.py
index 1d3ac53..b56eeba 100644
--- a/board/models.py
+++ b/board/models.py
@@ -57,3 +57,19 @@ class Todo(models.Model):
def get_absolute_url(self):
return reverse('todo-detail', kwargs={'pk': self.employee.pk, 'todo_pk': self.pk})
+
+
+class Event(models.Model):
+ class Meta:
+ ordering = ('date', 'time')
+
+ name = models.CharField(max_length=64)
+ date = models.DateField()
+ time = models.TimeField(blank=True, null=True)
+ employee = models.ForeignKey(Employee, on_delete=models.CASCADE, blank=True, null=True)
+
+ def __str__(self):
+ return f"{self.description} on {self.date} @ {self.time}"
+
+ def get_absolute_url(self):
+ return reverse('event-detail', kwargs={'pk': self.pk})
diff --git a/board/templates/board/employee_detail.html b/board/templates/board/employee_detail.html
index 4874dd1..f5eb452 100644
--- a/board/templates/board/employee_detail.html
+++ b/board/templates/board/employee_detail.html
@@ -31,6 +31,17 @@
Initial comments:
{{employee.initial_comments|linebreaksbr}}
+
+ Employee Events
+ {% for event in employee.event_set.all %}
+
+
{{event.date|date:"D, M j"}}
+
{{event.name}}
+
{{event.time|time:"TIME_FORMAT"}}
+
Edit
+
+ {% endfor %}
+
To-do's
{% include "board/todo_list.html" with todo_list=employee.todo_set.all %}
@@ -45,11 +56,14 @@
Add an Entry
- {% for entry in employee.logentry_set.all %}
-
- {{entry.created_at|date:"SHORT_DATE_FORMAT"}}—
- {{entry.notes}}
-
+ {% regroup employee.logentry_set.all by created_at.date as activity %}
+ {% for date in activity %}
+ {{date.grouper|date:"D, M j"}}
+ {% for entry in date.list %}
+
+ {{entry.created_at|time:"H:i"}}
+ {{entry.notes}}
+ {% endfor %}
{% endfor %}
diff --git a/board/templates/board/event_confirm_delete.html b/board/templates/board/event_confirm_delete.html
new file mode 100644
index 0000000..831a651
--- /dev/null
+++ b/board/templates/board/event_confirm_delete.html
@@ -0,0 +1,13 @@
+{% extends "base.html" %}
+
+{% block content %}
+
+ Delete {{event}}
+
+
+{% endblock %}
\ No newline at end of file
diff --git a/board/templates/board/event_create_form.html b/board/templates/board/event_create_form.html
new file mode 100644
index 0000000..698740d
--- /dev/null
+++ b/board/templates/board/event_create_form.html
@@ -0,0 +1,16 @@
+{% extends "base.html" %}
+
+{% block content %}
+
+ Create Event
+
+
+{% endblock %}
\ No newline at end of file
diff --git a/board/templates/board/event_detail.html b/board/templates/board/event_detail.html
new file mode 100644
index 0000000..c1934b8
--- /dev/null
+++ b/board/templates/board/event_detail.html
@@ -0,0 +1,17 @@
+{% extends "base.html" %}
+
+{% block content %}
+
+ Event
+
+
+
{{event.name}}
+ {% if event.employee %} for
+ {{event.employee}}
+ {% endif %}
+
+
{{event.time|time:"TIME_FORMAT"}}
+
+
+
+{% endblock %}
\ No newline at end of file
diff --git a/board/templates/board/event_form.html b/board/templates/board/event_form.html
new file mode 100644
index 0000000..5ca737b
--- /dev/null
+++ b/board/templates/board/event_form.html
@@ -0,0 +1,16 @@
+{% extends "base.html" %}
+
+{% block content %}
+
+ Update Event
+
+
+{% endblock %}
\ No newline at end of file
diff --git a/board/templates/board/event_list.html b/board/templates/board/event_list.html
new file mode 100644
index 0000000..5e8efb0
--- /dev/null
+++ b/board/templates/board/event_list.html
@@ -0,0 +1,24 @@
+{% extends "base.html" %}
+
+{% block content %}
+
+ Schedule
+ Add event
+
+ {% for event in event_list %}
+
+
{{event.date|date:"D, M j"}}
+
{{event.name}}
+ {% if event.employee %} for
+ {{event.employee}}
+ {% endif %}
+
+
{{event.time|time:"TIME_FORMAT"}}
+
Edit
+
+ {% empty %}
+ There are no events yet.
+ {% endfor %}
+
+
+{% endblock %}
diff --git a/board/urls.py b/board/urls.py
index 0da3ec1..0ac94c6 100644
--- a/board/urls.py
+++ b/board/urls.py
@@ -32,4 +32,12 @@ urlpatterns = [
])),
])),
+
+ path('events/', views.EventListView.as_view(), name='event-list'),
+ path('events/new/', views.EventCreateView.as_view(), name='event-create'),
+ path('events//', include([
+ path('', views.EventDetailView.as_view(), name='event-detail'),
+ path('update/', views.EventUpdateView.as_view(), name='event-update'),
+ path('delete/', views.EventDeleteView.as_view(), name='event-delete'),
+ ])),
]
diff --git a/board/views.py b/board/views.py
index d0be241..0cbd418 100644
--- a/board/views.py
+++ b/board/views.py
@@ -7,14 +7,15 @@ from django.views.generic.list import ListView
from django.contrib.auth.mixins import LoginRequiredMixin
from django.db.models import Q
-from .models import Employee, LogEntry, Todo
+from .models import Employee, LogEntry, Todo, Event
from .forms import (
EmployeeForm,
EmployeePreCreateForm,
EmployeeArchiveForm,
LogEntryForm,
TodoForm,
- TodoCreateForm
+ TodoCreateForm,
+ EventForm,
)
class SearchResultsView(ListView):
@@ -170,4 +171,27 @@ class TodoDeleteView(LoginRequiredMixin, DeleteView):
return reverse('todo-deleted', kwargs={'pk': self.kwargs['pk']})
class TodoDeleteDoneView(LoginRequiredMixin, TemplateView):
- template_name = 'board/item_deleted.html'
\ No newline at end of file
+ template_name = 'board/item_deleted.html'
+
+
+# Events
+class EventListView(LoginRequiredMixin, ListView):
+ model = Event
+
+class EventCreateView(LoginRequiredMixin, CreateView):
+ model = Event
+ template_name_suffix = '_create_form'
+ form_class = EventForm
+ success_url = reverse_lazy('profile-detail')
+
+class EventDetailView(LoginRequiredMixin, DetailView):
+ model = Event
+
+class EventUpdateView(LoginRequiredMixin, UpdateView):
+ model = Event
+ form_class = EventForm
+
+class EventDeleteView(LoginRequiredMixin, DeleteView):
+ model = Event
+ success_url = reverse_lazy('profile-detail')
+
diff --git a/onboard/middleware.py b/onboard/middleware.py
new file mode 100644
index 0000000..8260705
--- /dev/null
+++ b/onboard/middleware.py
@@ -0,0 +1,17 @@
+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('timezone')
+ if request.user.is_authenticated:
+ tzname = request.user.profile.timezone
+ if tzname:
+ timezone.activate(pytz.timezone(tzname))
+ else:
+ timezone.deactivate()
+ return self.get_response(request)
diff --git a/onboard/settings.py b/onboard/settings.py
index 6d2e0bb..086bd93 100644
--- a/onboard/settings.py
+++ b/onboard/settings.py
@@ -53,6 +53,7 @@ MIDDLEWARE = [
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware',
+ 'onboard.middleware.TimezoneMiddleware',
]
ROOT_URLCONF = 'onboard.urls'
diff --git a/onboard/settings_dev.py b/onboard/settings_dev.py
index e8b8f9e..8e9fb55 100644
--- a/onboard/settings_dev.py
+++ b/onboard/settings_dev.py
@@ -25,7 +25,7 @@ SECRET_KEY = 'django-insecure-fdq1(f%l*__obb*zvb3gqnlki!ryr@_1_5om(_2ju3mu70mi4%
# SECURITY WARNING: don't run with debug turned on in production!
DEBUG = True
-ALLOWED_HOSTS = []
+ALLOWED_HOSTS = ['localhost', '127.0.0.1', 'xcarbon.lan']
# Application definition
@@ -51,6 +51,7 @@ MIDDLEWARE = [
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware',
+ 'onboard.middleware.TimezoneMiddleware',
]
ROOT_URLCONF = 'onboard.urls'
diff --git a/static/styles/main.css b/static/styles/main.css
index 07c66e4..a82652e 100644
--- a/static/styles/main.css
+++ b/static/styles/main.css
@@ -2,7 +2,7 @@
--white: #fdfdff;
--black: #393d3f;
--grey: #a7b4bb;
- --blue: #2288a2;
+ --blue: #10638c;
}
html {
@@ -63,7 +63,7 @@ blockquote {
border-left: 2px solid var(--blue);
}
-header {
+body > header {
padding: 1rem;
}
@@ -95,6 +95,7 @@ article {
.navbar__mobile {
display: none;
+ font-weight: 900;
}
.navbar__menu {
@@ -203,6 +204,8 @@ article {
/* FORMS */
input[type=text]:not([name=description]),
input[type=number],
+input[type=date],
+input[type=time],
input[type=password],
input[type=search],
textarea, select {
@@ -223,10 +226,6 @@ input[name=description] {
box-sizing: border-box;
}
-input:focus, textarea:focus, select:focus {
- border-color: var(--blue);
-}
-
input[type=radio], input[type=checkbox] {
cursor: pointer;
width: 1.5rem;
@@ -256,6 +255,10 @@ li label {
display: inline-block;
}
+input:focus, textarea:focus, select:focus {
+ border-color: var(--blue) !important;
+}
+
@@ -300,4 +303,27 @@ hgroup {
right: 0;
text-align: left;
}
+}
+
+
+
+.log_entry {
+ display: grid;
+ grid-template-columns: 1fr 2fr 1fr;
+ column-gap: 0.5rem;
+ margin-bottom: 1rem;
+}
+
+.log_entry--employee {
+ grid-template-columns: 1fr auto;
+}
+
+
+.today__event {
+ display: grid;
+ grid-template-columns: 1fr 2fr 0.5fr 0.5fr;
+ column-gap: 1rem;
+ margin-bottom: 2rem;
+ padding-bottom: 0.5rem;
+ border-bottom: 0.046rem solid var(--grey);
}
\ No newline at end of file
diff --git a/templates/base.html b/templates/base.html
index 61f2fc5..4e7d227 100644
--- a/templates/base.html
+++ b/templates/base.html
@@ -26,6 +26,8 @@
{% else %}
+ OnBoard
Login
{% endif %}
@@ -51,12 +54,17 @@
+ Today
+
+ Events
+
Employees
Logout
{% else %}
+ OnBoard
Login
{% endif %}