From cf9b5c11a3987f454b4d4612f1e606d7fbc829ba Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89loi=20Rivard?= Date: Thu, 9 Mar 2023 17:41:26 +0100 Subject: [PATCH] Dynamic tables with htmx - Search is triggered with user inputs - Page changes are triggered with clicks --- canaille/account.py | 3 +- canaille/flaskutils.py | 10 ++ canaille/groups.py | 6 +- canaille/oidc/authorizations.py | 3 +- canaille/oidc/clients.py | 3 +- canaille/oidc/tokens.py | 3 +- canaille/templates/group.html | 4 + canaille/templates/groups.html | 2 +- canaille/templates/macro/table.html | 144 +++++++++++------- .../oidc/admin/authorization_list.html | 2 +- .../templates/oidc/admin/client_list.html | 2 +- canaille/templates/oidc/admin/token_list.html | 2 +- canaille/templates/users.html | 2 +- canaille/themes/default/base.html | 5 +- 14 files changed, 122 insertions(+), 69 deletions(-) diff --git a/canaille/account.py b/canaille/account.py index 07407124..3c0b32e0 100644 --- a/canaille/account.py +++ b/canaille/account.py @@ -29,6 +29,7 @@ from .apputils import obj_to_b64 from .apputils import profile_hash from .flaskutils import current_user from .flaskutils import permissions_needed +from .flaskutils import render_htmx_template from .flaskutils import smtp_needed from .flaskutils import user_needed from .forms import FirstLoginForm @@ -187,7 +188,7 @@ def users(user): if request.form and not table_form.validate(): abort(404) - return render_template( + return render_htmx_template( "users.html", menuitem="users", table_form=table_form, diff --git a/canaille/flaskutils.py b/canaille/flaskutils.py index d5f699ae..fc884d40 100644 --- a/canaille/flaskutils.py +++ b/canaille/flaskutils.py @@ -7,6 +7,7 @@ from canaille.models import User from flask import abort from flask import current_app from flask import render_template +from flask import request from flask import session from flask_babel import gettext as _ @@ -84,3 +85,12 @@ def set_parameter_in_url_query(url, **kwargs): parameters = {**parameters, **kwargs} split[3] = "&".join(f"{key}={value}" for key, value in parameters.items()) return urlunsplit(split) + + +def render_htmx_template(template, htmx_template=None, **kwargs): + template = ( + (htmx_template or f"partial/{template}") + if request.headers.get("HX-Request") + else template + ) + return render_template(template, **kwargs) diff --git a/canaille/groups.py b/canaille/groups.py index a43c8f68..179e823b 100644 --- a/canaille/groups.py +++ b/canaille/groups.py @@ -8,6 +8,7 @@ from flask_babel import gettext as _ from flask_themer import render_template from .flaskutils import permissions_needed +from .flaskutils import render_htmx_template from .forms import CreateGroupForm from .forms import EditGroupForm from .forms import TableForm @@ -24,7 +25,7 @@ def groups(user): if request.form and request.form.get("page") and not table_form.validate(): abort(404) - return render_template("groups.html", menuitem="groups", table_form=table_form) + return render_htmx_template("groups.html", menuitem="groups", table_form=table_form) @bp.route("/add", methods=("GET", "POST")) @@ -96,8 +97,9 @@ def edit_group(group): else: flash(_("Group edition failed."), "error") - return render_template( + return render_htmx_template( "group.html", + "partial/users.html", form=form, edited_group=group, table_form=table_form, diff --git a/canaille/oidc/authorizations.py b/canaille/oidc/authorizations.py index a6613eed..27ecbb38 100644 --- a/canaille/oidc/authorizations.py +++ b/canaille/oidc/authorizations.py @@ -1,4 +1,5 @@ from canaille.flaskutils import permissions_needed +from canaille.flaskutils import render_htmx_template from canaille.forms import TableForm from canaille.oidc.models import AuthorizationCode from flask import abort @@ -17,7 +18,7 @@ def index(user): if request.form and request.form.get("page") and not table_form.validate(): abort(404) - return render_template( + return render_htmx_template( "oidc/admin/authorization_list.html", menuitem="admin", table_form=table_form, diff --git a/canaille/oidc/clients.py b/canaille/oidc/clients.py index c116b078..180c5a3e 100644 --- a/canaille/oidc/clients.py +++ b/canaille/oidc/clients.py @@ -1,6 +1,7 @@ import datetime from canaille.flaskutils import permissions_needed +from canaille.flaskutils import render_htmx_template from canaille.forms import TableForm from canaille.oidc.forms import ClientAdd from canaille.oidc.models import Client @@ -25,7 +26,7 @@ def index(user): if request.form and request.form.get("page") and not table_form.validate(): abort(404) - return render_template( + return render_htmx_template( "oidc/admin/client_list.html", menuitem="admin", table_form=table_form ) diff --git a/canaille/oidc/tokens.py b/canaille/oidc/tokens.py index 6bfc8245..45b33f28 100644 --- a/canaille/oidc/tokens.py +++ b/canaille/oidc/tokens.py @@ -1,6 +1,7 @@ import datetime from canaille.flaskutils import permissions_needed +from canaille.flaskutils import render_htmx_template from canaille.forms import TableForm from canaille.models import User from canaille.oidc.models import Client @@ -24,7 +25,7 @@ def index(user): if request.form and request.form.get("page") and not table_form.validate(): abort(404) - return render_template( + return render_htmx_template( "oidc/admin/token_list.html", menuitem="admin", table_form=table_form ) diff --git a/canaille/templates/group.html b/canaille/templates/group.html index 4d1862b9..3c0d7fb0 100644 --- a/canaille/templates/group.html +++ b/canaille/templates/group.html @@ -1,5 +1,6 @@ {% extends theme('base.html') %} {% import 'macro/fomanticui.html' as sui %} +{% import "macro/table.html" as table %} {% block script %} @@ -87,6 +88,9 @@ +
+ {{ table.search(table_form, "table.users") }} +
{% include "partial/users.html" %} {% endif %} diff --git a/canaille/templates/groups.html b/canaille/templates/groups.html index 83e1caf5..52b969d1 100644 --- a/canaille/templates/groups.html +++ b/canaille/templates/groups.html @@ -13,7 +13,7 @@
- {{ table.search(table_form) }} + {{ table.search(table_form, "table.groups") }}
{% include "partial/groups.html" %} diff --git a/canaille/templates/macro/table.html b/canaille/templates/macro/table.html index 5bb22428..172c5fec 100644 --- a/canaille/templates/macro/table.html +++ b/canaille/templates/macro/table.html @@ -1,70 +1,100 @@ -{% macro search(form) %} +{% macro search(form, target) %} {% endmacro %} -{% macro pagination(form) %} -
+{# At the moment we need to build one form per button +# https://github.com/bigskysoftware/htmx/issues/1120 +# when this is fixed we will be able to set the page +# value directly in the submit button and get rid +# of the radius reset +#} +{% macro buttonform(form, page) %} + {{ form.hidden_tag() if form.hidden_tag }} + -
- - {% trans %}Page{% endtrans %} - - {% if form.page.data > 1 %} - - {% else %} - - - - {% endif %} - {% if form.page.data > 1 %} - - {% endif %} - {% if form.page.data > 2 %} - - … - - {% endif %} - - {{ form.page.data }} - - {% if form.page.data < form.page_max - 1 %} - - … - - {% endif %} - {% if form.page.data < form.page_max %} - - {% endif %} - {% if form.page.data < form.page_max %} - - {% else %} - - - - {% endif %} -
-
- - {{ _("%(nb_items)s items", nb_items=form.nb_items) }} - -
+
{% endmacro %} + +{% macro pagination(form) %} +
+ + {% trans %}Page{% endtrans %} + + {% if form.page.data > 1 %} + {% call buttonform(form, form.page.data - 1) %} + + {% endcall %} + {% else %} + + + + {% endif %} + {% if form.page.data > 1 %} + {% call buttonform(form, 1) %} + 1 + {% endcall %} + {% endif %} + {% if form.page.data > 2 %} + + … + + {% endif %} + + {{ form.page.data }} + + {% if form.page.data < form.page_max - 1 %} + + … + + {% endif %} + {% if form.page.data < form.page_max %} + {% call buttonform(form, form.page_max) %} + {{ form.page_max }} + {% endcall %} + {% endif %} + {% if form.page.data < form.page_max %} + {% call buttonform(form, form.page.data + 1) %} + + {% endcall %} + {% else %} + + + + {% endif %} +
+
+ + {{ _("%(nb_items)s items", nb_items=form.nb_items) }} + +
+{% endmacro %} diff --git a/canaille/templates/oidc/admin/authorization_list.html b/canaille/templates/oidc/admin/authorization_list.html index 048621d3..f03e405e 100644 --- a/canaille/templates/oidc/admin/authorization_list.html +++ b/canaille/templates/oidc/admin/authorization_list.html @@ -7,7 +7,7 @@ {% block content %}
- {{ table.search(table_form) }} + {{ table.search(table_form, "table.codes") }}
{% include "partial/oidc/admin/authorization_list.html" %} {% endblock %} diff --git a/canaille/templates/oidc/admin/client_list.html b/canaille/templates/oidc/admin/client_list.html index 17af90ec..6cb5909c 100644 --- a/canaille/templates/oidc/admin/client_list.html +++ b/canaille/templates/oidc/admin/client_list.html @@ -12,7 +12,7 @@
- {{ table.search(table_form) }} + {{ table.search(table_form, "table.clients") }}
{% include "partial/oidc/admin/client_list.html" %} {% endblock %} diff --git a/canaille/templates/oidc/admin/token_list.html b/canaille/templates/oidc/admin/token_list.html index 2062d5a5..c4ce7db8 100644 --- a/canaille/templates/oidc/admin/token_list.html +++ b/canaille/templates/oidc/admin/token_list.html @@ -7,7 +7,7 @@ {% block content %}
- {{ table.search(table_form) }} + {{ table.search(table_form, "table.tokens") }}
{% include "partial/oidc/admin/token_list.html" %} {% endblock %} diff --git a/canaille/templates/users.html b/canaille/templates/users.html index f3caefdc..6c1d96e6 100644 --- a/canaille/templates/users.html +++ b/canaille/templates/users.html @@ -15,7 +15,7 @@
- {{ table.search(table_form) }} + {{ table.search(table_form, "table.users") }}
{% include "partial/users.html" %} {% endblock %} diff --git a/canaille/themes/default/base.html b/canaille/themes/default/base.html index 09f46500..a5993864 100644 --- a/canaille/themes/default/base.html +++ b/canaille/themes/default/base.html @@ -105,10 +105,13 @@ - + + {% block script %}{% endblock %}