diff --git a/backend/members/views.py b/backend/members/views.py
index 124f05f..a206ece 100644
--- a/backend/members/views.py
+++ b/backend/members/views.py
@@ -10,12 +10,13 @@ from rules.contrib.views import PermissionRequiredMixin
from members.filters import MemberFilter
from members.models import Member
-
-class MemberListView(PermissionRequiredMixin, FilterView):
+from ..mixins import HTMXViewMixin
+class MemberListView(HTMXViewMixin, PermissionRequiredMixin, FilterView):
filterset_class = MemberFilter
paginate_by = 50
permission_denied_message = _("You do not have permission to view this page.")
permission_required = "members.view_member"
+ partial_name = "members/member_filter.html#content"
def handle_no_permission(self) -> HttpResponseRedirect:
messages.error(self.request, self.get_permission_denied_message())
diff --git a/backend/mixins.py b/backend/mixins.py
new file mode 100644
index 0000000..a798796
--- /dev/null
+++ b/backend/mixins.py
@@ -0,0 +1,93 @@
+from django.http import HttpResponse
+from django.template.loader import render_to_string
+from django_htmx.http import HttpResponseClientRedirect, HttpResponseClientRefresh
+
+class HTMXPartialMixin:
+ """Mixin that automatically switches to a partial template when the request is made via HTMX."""
+
+ partial_template_name = None
+
+ def get_template_names(self):
+ # If HTMX request and a partial is defined, return it
+ if getattr(self.request, "htmx", False) and self.partial_template_name:
+ return [self.partial_template_name]
+
+ return super().get_template_names()
+
+
+class HTMXViewMixin:
+ """
+ A full featured HTMX integration mixin for Django CBVs.
+ Supports:
+ - partial rendering via django-partials
+ - HX-Redirect
+ - HX-Push-URL
+ - HX-Trigger events
+ - HX-Refresh
+ - Graceful fallback to normal DJango rendering
+ """
+
+ # Name of the partial block: template.html#partial_name
+ partial_name = None
+
+ # Optional: automatically push URL on GET
+ htmx_push_url = None
+
+ # Optional: trigger events after rendering
+ htmx_trigger = None
+ htmx_trigger_after_settle = None
+ htmx_trigger_after_swap = None
+
+ # Optional: redirect target for HTMX
+ htmx_redirect_url = None
+
+ # Optional: refresh the page
+ htmx_refresh = False
+
+ def render_partial(self, context):
+ """Render a django-partials block."""
+ request = self.request
+ return render_to_string(self.partial_name, context, request=request)
+
+ def apply_htmx_headers(self, response):
+ """Attach HX-* headers to the response."""
+ request = self.request
+
+ if request.htmx:
+ is_get = request.method == "GET"
+ is_pagination = "page" in request.GET
+
+ if is_get and not is_pagination:
+ # Push the current path unless overridden
+ response.headers["HX-Push-Url"] = self.htmx_push_url or request.get_full_path()
+
+ print(response.headers)
+
+ if self.htmx_trigger:
+ response.headers["HX-Trigger"] = self.htmx_trigger
+
+ if self.htmx_trigger_after_settle:
+ response.headers["HX-Trigger-After-Settle"] = self.htmx_trigger_after_settle
+
+ if self.htmx_trigger_after_swap:
+ response.headers["HX-Trigger-After-Swap"] = self.htmx_trigger_after_swap
+
+ return response
+
+ def render_to_response(self, context, **response_kwargs):
+ """Renders HTMX response, applying headers and handling directives"""
+ request = self.request
+
+ if not request.htmx:
+ response = super().render_to_response(context, **response_kwargs)
+ return self.apply_htmx_headers(response)
+
+ if self.htmx_redirect_url:
+ return HttpResponseClientRedirect(self.htmx_redirect_url)
+
+ if self.htmx_refresh:
+ return HttpResponseClientRefresh()
+
+ html = self.render_partial(context)
+ response = HttpResponse(html, **response_kwargs)
+ return self.apply_htmx_headers(response)
\ No newline at end of file
diff --git a/templates/base.html b/templates/base.html
index 3401887..6f66ee7 100644
--- a/templates/base.html
+++ b/templates/base.html
@@ -101,7 +101,7 @@
-
+
{% block content %}
Welcome!
This is your main content area.
diff --git a/templates/members/member_filter.html b/templates/members/member_filter.html
index 60e10e7..7aba9d5 100644
--- a/templates/members/member_filter.html
+++ b/templates/members/member_filter.html
@@ -6,189 +6,191 @@
{% load pagination %}
{% block content %}
- Members
+ {% partialdef content inline %}
+ Members
-
-
-
{% translate "Filter" %}{% if filter.is_bound %}active{% endif %}
-
+ {% endif %}
+
-
- {% if object_list|length == 0 %}
-
{% translate "No members found" %}
- {% else %}
-
-
- {% if is_paginated %}
-
- {% endif %}
+ {% endpartialdef content %}
{% endblock content %}
\ No newline at end of file