dashboard: Add more flexible Link Card dashboard fragment

with support for tooltips and accordions
This commit is contained in:
Adam Goldsmith 2024-02-06 00:41:01 -05:00
parent 387767204a
commit 26514e60fb
7 changed files with 104 additions and 46 deletions

View File

@ -1,3 +1,4 @@
import dataclasses
from typing import Any
from django.http import HttpRequest
@ -10,6 +11,16 @@ def register(fragment):
return fragment
@dataclasses.dataclass
class Link:
text: str
href: str
tooltip: str | None = None
body: str | None = None
_: dataclasses.KW_ONLY
permission: str | None
class DashboardFragment:
name: str
template: str
@ -18,3 +29,12 @@ class DashboardFragment:
def __init__(self, request: HttpRequest):
self.request = request
class LinksCardDashboardFragment(DashboardFragment):
template = "dashboard/links_card.dj.html"
links: [Link] = []
@property
def context(self):
return {"links": self.links}

View File

@ -16,7 +16,7 @@
<div>
<div class="card">
<h5 class="card-header">{{ app }}</h5>
{% include app_dash.template with ctx=app_dash.context %}
{% include app_dash.template with app=app ctx=app_dash.context %}
</div>
</div>
{% endif %}
@ -24,3 +24,9 @@
</div>
</div>
{% endblock %}
{% block script %}
<script>
const tooltipTriggerList = document.querySelectorAll('[data-bs-toggle="tooltip"]')
const tooltipList = [...tooltipTriggerList].map(tooltipTriggerEl => new bootstrap.Tooltip(tooltipTriggerEl))
</script>
{% endblock %}

View File

@ -1,7 +1,28 @@
<ul class="list-group list-group-flush">
{% for text, link in ctx.links.items %}
<li class="list-group-item">
<a class="card-link" href="{{ link }}">{{ text }}</a>
</li>
<div class="accordion accordion-flush" id="{{ app|slugify }}-dash">
{% for link in ctx.links %}
{% if link.permission is None or link.permission in perms %}
<div class="accordion-item">
<div class="accordion-header d-flex align-items-center">
<a {% if link.tooltip is not None %}data-bs-toggle="tooltip" title="{{ link.tooltip }}"{% endif %}
href="{{ link.href }}"
class="text-nowrap mx-3 p-1">{{ link.text }}</a>
{% if link.body is not None %}
<button class="accordion-button collapsed bg-transparent shadow-none py-1"
type="button"
data-bs-toggle="collapse"
data-bs-target="#{{ app|slugify }}-{{ forloop.counter }}"
aria-expanded="false"
aria-controls="{{ app|slugify }}-{{ forloop.counter }}"></button>
{% endif %}
</div>
{% if link.body is not None %}
<div id="{{ app|slugify }}-{{ forloop.counter }}"
class="accordion-collapse collapse"
data-bs-parent="#{{ app|slugify }}-dash">
<div class="accordion-body">{{ link.body }}</div>
</div>
{% endif %}
</div>
{% endif %}
{% endfor %}
</ul>
</div>

View File

@ -1,20 +1,20 @@
from typing import Any
import dashboard
from dashboard import Link
from .views import REPORTS
@dashboard.register
class DoorControlDashboardFragment(dashboard.DashboardFragment):
class DoorControlDashboardFragment(dashboard.LinksCardDashboardFragment):
name = "Door Controls"
template = "dashboard/links_card.dj.html"
@property
def context(self) -> Any:
return {
"links": dict(rt for report in REPORTS for rt in report._report_types())
}
def links(self) -> list[Link]:
return [
Link(name, link, permission="doorcontrol.view_hidevent")
for report in REPORTS
for name, link in report._report_types()
]
@property
def visible(self) -> bool:

View File

@ -1,25 +1,31 @@
from typing import Any
from django.urls import reverse
import dashboard
from dashboard import Link
@dashboard.register
class MembershipworksDashboardFragment(dashboard.DashboardFragment):
class MembershipworksDashboardFragment(dashboard.LinksCardDashboardFragment):
name = "MembershipWorks"
template = "dashboard/links_card.dj.html"
@property
def context(self) -> Any:
links = {}
if self.request.user.has_perm("membershipworks.view_event"):
links["Upcoming Events"] = reverse("membershipworks:upcoming-events")
links["Event Report"] = reverse("membershipworks:event-index-report")
links["Event Attendees"] = reverse("membershipworks:event-attendees")
return {"links": links}
links = [
Link(
"Upcoming Events",
reverse("membershipworks:upcoming-events"),
permission="membershipworks.view_event",
tooltip="Generator for Wordpress posts",
),
Link(
"Event Report",
reverse("membershipworks:event-index-report"),
permission="membershipworks.view_event",
),
Link(
"Event Attendees",
reverse("membershipworks:event-attendees"),
permission="membershipworks.view_event",
),
]
@property
def visible(self) -> bool:

View File

@ -1,37 +1,45 @@
from typing import Any
from django.urls import reverse
import dashboard
from dashboard import Link
from membershipworks.models import Member
from .models import Department
@dashboard.register
class PaperworkDashboardFragment(dashboard.DashboardFragment):
class PaperworkDashboardFragment(dashboard.LinksCardDashboardFragment):
name = "Paperwork"
template = "dashboard/links_card.dj.html"
@property
def context(self) -> Any:
links = {}
def links(self) -> list[Link]:
links = []
member = Member.from_user(self.request.user)
if member is not None:
links["Member Certifications"] = reverse(
"paperwork:member_certifications", kwargs={"uid": member.uid}
links.append(
dashboard.Link(
"Member Certifications",
reverse(
"paperwork:member_certifications", kwargs={"uid": member.uid}
),
permission=None,
)
)
if self.request.user.is_superuser or (
member is not None
and Department.objects.filter_by_shop_lead(member).exists()
):
links["Department Certifications"] = reverse(
"paperwork:department_certifications"
links.append(
Link(
"Department Certifications",
reverse("paperwork:department_certifications"),
permission=None,
)
)
return {"links": links}
return links
@property
def visible(self) -> bool:

View File

@ -1,15 +1,12 @@
from typing import Any
from django.urls import reverse
import dashboard
from dashboard import Link
@dashboard.register
class RentalsDashboardFragment(dashboard.DashboardFragment):
class RentalsDashboardFragment(dashboard.LinksCardDashboardFragment):
name = "Rentals"
template = "dashboard/links_card.dj.html"
@property
def context(self) -> Any:
return {"links": {"Locker Index": reverse("rentals:index")}}
links = [Link("Locker Index", reverse("rentals:index"), permission=None)]