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 typing import Any
from django.http import HttpRequest from django.http import HttpRequest
@ -10,6 +11,16 @@ def register(fragment):
return 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: class DashboardFragment:
name: str name: str
template: str template: str
@ -18,3 +29,12 @@ class DashboardFragment:
def __init__(self, request: HttpRequest): def __init__(self, request: HttpRequest):
self.request = request 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>
<div class="card"> <div class="card">
<h5 class="card-header">{{ app }}</h5> <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>
</div> </div>
{% endif %} {% endif %}
@ -24,3 +24,9 @@
</div> </div>
</div> </div>
{% endblock %} {% 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"> <div class="accordion accordion-flush" id="{{ app|slugify }}-dash">
{% for text, link in ctx.links.items %} {% for link in ctx.links %}
<li class="list-group-item"> {% if link.permission is None or link.permission in perms %}
<a class="card-link" href="{{ link }}">{{ text }}</a> <div class="accordion-item">
</li> <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 %} {% endfor %}
</ul> </div>

View File

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

View File

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

View File

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

View File

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