diff --git a/cmsmanage/settings/base.py b/cmsmanage/settings/base.py
index c51636e..ef9fe61 100644
--- a/cmsmanage/settings/base.py
+++ b/cmsmanage/settings/base.py
@@ -40,6 +40,7 @@ INSTALLED_APPS = [
"membershipworks.apps.MembershipworksConfig",
"paperwork.apps.PaperworkConfig",
"doorcontrol.apps.DoorControlConfig",
+ "dashboard.apps.DashboardConfig",
]
MIDDLEWARE = [
diff --git a/cmsmanage/urls.py b/cmsmanage/urls.py
index d46cfd5..4ab64dc 100644
--- a/cmsmanage/urls.py
+++ b/cmsmanage/urls.py
@@ -29,7 +29,7 @@ router.registry.extend(paperwork_router.registry)
router.registry.extend(membershipworks_router.registry)
urlpatterns = [
- path("", lambda request: redirect("/tasks/"), name="root"),
+ path("", include("dashboard.urls")),
path("tasks/", include("tasks.urls")),
path("rentals/", include("rentals.urls")),
path("membershipworks/", include("membershipworks.urls")),
diff --git a/dashboard/__init__.py b/dashboard/__init__.py
new file mode 100644
index 0000000..e54b45e
--- /dev/null
+++ b/dashboard/__init__.py
@@ -0,0 +1,20 @@
+from typing import Any
+
+from django.http import HttpRequest
+
+DASHBOARD_CARDS = {}
+
+
+def register(fragment):
+ DASHBOARD_CARDS[fragment.name] = fragment
+ return fragment
+
+
+class DashboardFragment:
+ name: str
+ template: str
+ context: Any = None
+ visible: bool = True
+
+ def __init__(self, request: HttpRequest):
+ self.request = request
diff --git a/dashboard/apps.py b/dashboard/apps.py
new file mode 100644
index 0000000..d89acb8
--- /dev/null
+++ b/dashboard/apps.py
@@ -0,0 +1,11 @@
+from django.apps import AppConfig
+from django.utils.module_loading import autodiscover_modules
+
+
+class DashboardConfig(AppConfig):
+ default_auto_field = "django.db.models.BigAutoField"
+ name = "dashboard"
+
+ def ready(self):
+ super().ready()
+ autodiscover_modules("dashboard")
diff --git a/dashboard/dashboard.py b/dashboard/dashboard.py
new file mode 100644
index 0000000..e69de29
diff --git a/dashboard/templates/dashboard/dashboard.dj.html b/dashboard/templates/dashboard/dashboard.dj.html
new file mode 100644
index 0000000..b799b9d
--- /dev/null
+++ b/dashboard/templates/dashboard/dashboard.dj.html
@@ -0,0 +1,26 @@
+{% extends "base.dj.html" %}
+
+{% block title %}Claremont MakerSpace Management{% endblock %}
+{% block content %}
+ {% if not user.is_authenticated %}
+
+ You are not logged in. Much of this site is inaccessible until you
log in.
+
+ {% endif %}
+
+
+ {% for app, app_dash in apps.items %}
+ {% if app_dash.visible %}
+
+
+
+ {% include app_dash.template with ctx=app_dash.context %}
+
+
+ {% endif %}
+ {% endfor %}
+
+
+{% endblock %}
diff --git a/dashboard/templates/dashboard/links_card.dj.html b/dashboard/templates/dashboard/links_card.dj.html
new file mode 100644
index 0000000..4dc8ca6
--- /dev/null
+++ b/dashboard/templates/dashboard/links_card.dj.html
@@ -0,0 +1,7 @@
+
+ {% for text, link in ctx.links.items %}
+ -
+ {{ text }}
+
+ {% endfor %}
+
diff --git a/dashboard/tests.py b/dashboard/tests.py
new file mode 100644
index 0000000..7ce503c
--- /dev/null
+++ b/dashboard/tests.py
@@ -0,0 +1,3 @@
+from django.test import TestCase
+
+# Create your tests here.
diff --git a/dashboard/urls.py b/dashboard/urls.py
new file mode 100644
index 0000000..047aecb
--- /dev/null
+++ b/dashboard/urls.py
@@ -0,0 +1,9 @@
+from django.urls import path
+
+from . import views
+
+app_name = "dashboard"
+
+urlpatterns = [
+ path("", views.DashboardView.as_view(), name="dashboard"),
+]
diff --git a/dashboard/views.py b/dashboard/views.py
new file mode 100644
index 0000000..b89f7d5
--- /dev/null
+++ b/dashboard/views.py
@@ -0,0 +1,14 @@
+from django.views.generic.base import TemplateView
+
+from dashboard import DASHBOARD_CARDS
+
+
+class DashboardView(TemplateView):
+ template_name = "dashboard/dashboard.dj.html"
+
+ def get_context_data(self, **kwargs):
+ context = super().get_context_data(**kwargs)
+ context["apps"] = {
+ name: card(self.request) for name, card in DASHBOARD_CARDS.items()
+ }
+ return context
diff --git a/doorcontrol/dashboard.py b/doorcontrol/dashboard.py
new file mode 100644
index 0000000..6050753
--- /dev/null
+++ b/doorcontrol/dashboard.py
@@ -0,0 +1,30 @@
+from typing import Any
+
+from django.urls import reverse
+
+import dashboard
+
+
+@dashboard.register
+class DoorControlDashboardFragment(dashboard.DashboardFragment):
+ name = "Door Controls"
+ template = "dashboard/links_card.dj.html"
+
+ @property
+ def context(self) -> Any:
+ return {
+ "links": {
+ "Access Per Day": reverse(
+ "doorcontrol:access-per-unit-time", kwargs={"unit_time": "day"}
+ ),
+ "Access Per Month": reverse(
+ "doorcontrol:access-per-unit-time", kwargs={"unit_time": "month"}
+ ),
+ "Access Failures": reverse("doorcontrol:denied-access"),
+ "Most Active Members": reverse("doorcontrol:most-active-members"),
+ }
+ }
+
+ @property
+ def visible(self) -> bool:
+ return self.request.user.has_perm("doorcontrol.view_hidevent")
diff --git a/paperwork/dashboard.py b/paperwork/dashboard.py
new file mode 100644
index 0000000..6b01057
--- /dev/null
+++ b/paperwork/dashboard.py
@@ -0,0 +1,44 @@
+from typing import Any
+
+from django.urls import reverse
+
+import dashboard
+from membershipworks.models import Member
+from .models import Department
+
+
+@dashboard.register
+class PaperworkDashboardFragment(dashboard.DashboardFragment):
+ name = "Paperwork"
+ template = "dashboard/links_card.dj.html"
+
+ @property
+ def context(self) -> Any:
+ links = {}
+
+ if hasattr(self.request.user, "ldap_user"):
+ uid = self.request.user.ldap_user.attrs["employeeNumber"][0]
+ links["Member Certifications"] = reverse("paperwork:member_certifications")
+ else:
+ uid = None
+
+ departments = Department.objects.prefetch_related("shop_lead_flag__members")
+ if self.request.user.is_superuser:
+ departments = departments.all()
+ elif uid is not None:
+ user_member = Member.objects.get(uid=uid)
+ # TODO: could be a lot simpler if membershipworks was in the same database
+ # TODO: should also select children
+ member_flags = list(user_member.flags.all().values_list("pk", flat=True))
+ departments = departments.filter(shop_lead_flag__in=member_flags)
+
+ if len(departments) > 0:
+ links["Department Certifications"] = reverse(
+ "paperwork:department_certifications"
+ )
+
+ return {"links": links}
+
+ @property
+ def visible(self) -> bool:
+ return self.request.user.is_authenticated
diff --git a/rentals/dashboard.py b/rentals/dashboard.py
new file mode 100644
index 0000000..47abea2
--- /dev/null
+++ b/rentals/dashboard.py
@@ -0,0 +1,15 @@
+from typing import Any
+
+from django.urls import reverse
+
+import dashboard
+
+
+@dashboard.register
+class RentalsDashboardFragment(dashboard.DashboardFragment):
+ name = "Rentals"
+ template = "dashboard/links_card.dj.html"
+
+ @property
+ def context(self) -> Any:
+ return {"links": {"Locker Index": reverse("rentals:index")}}
diff --git a/templates/base.dj.html b/templates/base.dj.html
index 5cb14a2..c84ef4b 100644
--- a/templates/base.dj.html
+++ b/templates/base.dj.html
@@ -17,7 +17,7 @@