Start playing around with HTMX

Hopefully this will allow us to one day remove all the logic around the handling of
modal forms everywhere in the app
This commit is contained in:
Roland Geider
2024-11-14 00:27:14 +01:00
parent 0fc1c35edb
commit e797e11a5c
16 changed files with 128 additions and 174 deletions

View File

@@ -10,6 +10,7 @@
},
"homepage": "https://github.com/wger-project/wger",
"dependencies": {
"htmx.org": "^2.0.3",
"Sortable": "RubaXa/Sortable#1.15.3",
"bootstrap": "5.3.3",
"components-font-awesome": "5.9.0",
@@ -18,7 +19,7 @@
"devbridge-autocomplete": "^1.4.11",
"jquery": "^3.7.1",
"masonry-layout": "^4.2.2",
"popper.js": "^1.16.1",
"@popperjs/core": "^2.11.8",
"yarn": "^1.22.22"
},
"scripts": {

View File

@@ -1,23 +1,10 @@
{% extends extend_template %}
{% load i18n crispy_forms_tags %}
<!--
Title
-->
{% block title %}{{title}}{% endblock %}
<h4>{{ title }}</h4>
<p>{% translate "Are you sure you want to delete this? This action cannot be undone." %}</p>
{% if delete_message %}
<p>{{ delete_message }}</p>
{% endif %}
{% crispy form %}
<!--
Main Content
-->
{% block content %}
<div class="wger-form">
<p>{% translate "Are you sure you want to delete this? This action cannot be undone." %}</p>
{% if delete_message %}
<p>{{ delete_message }}</p>
{% endif %}
{% crispy form %}
</div>
{% endblock %}

View File

@@ -1,29 +1,4 @@
{% extends extend_template %}
{% load i18n static crispy_forms_tags %}
{% load crispy_forms_tags %}
{% block header %}
{{ form.media }}
{% if custom_js %}
<script type="text/javascript">
$(document).ready(function() {
{{custom_js|safe}}
});
</script>
{% endif %}
{% endblock %}
{% block title %}{{title}}{% endblock %}
{% block content %}
{% if form %}
{% crispy form %}
{% else %}
Looks like you clicked on an invalid link. Please try again.
{% endif %}
{% endblock %}
<!--
Side bar
-->
{% block sidebar %}{% if sidebar %}{% include sidebar %}{% endif %}{% endblock %}
<h4>{{ title }}</h4>
{% crispy form %}

View File

@@ -31,7 +31,6 @@
{# Options #}
{# #}
{% block options %}
<a href="{% url 'core:language:add' %}" class="btn btn-success btn-sm wger-modal-dialog">
{% translate "Add" %}
</a>
{% translate 'Add' as text %}
{% modal_link url="core:language:add" text=text %}
{% endblock %}

View File

@@ -61,17 +61,15 @@
{% translate "Options" %}
</button>
<div class="dropdown-menu">
<a href="{% url 'core:language:edit' view_language.id %}"
class="wger-modal-dialog dropdown-item">
<span class="{% fa_class 'edit' %}"></span>
{% translate "Edit" %}
</a>
{% translate 'Edit' as text %}
{% url 'core:language:edit' view_language.id as url %}
{% modal_link url=url text=text css_class="dropdown-item" %}
<div role="separator" class="dropdown-divider"></div>
<a href="{% url 'core:language:delete' view_language.id %}"
class="wger-modal-dialog dropdown-item">
<span class="{% fa_class 'trash' %}"></span>
{% translate "Delete" %}
</a>
{% translate 'Delete' as text %}
{% url 'core:language:delete' view_language.id as url %}
{% modal_link url=url text=text css_class="dropdown-item" %}
</div>
</div>
</div>

View File

@@ -9,14 +9,18 @@
{% for license in license_list %}
<li class="list-group-item list-group-item-action">
<div class="btn-group float-end">
<button type="button" class="btn btn-dark dropdown-toggle btn-sm" data-bs-toggle="dropdown">
<button type="button" class="btn btn-dark dropdown-toggle btn-sm"
data-bs-toggle="dropdown">
<span class="{% fa_class 'cog' %}"></span>
</button>
<div class="dropdown-menu" role="menu">
<a href="{% url 'core:license:edit' license.id %}"
class="dropdown-item wger-modal-dialog">{% translate 'Edit' %}</a>
<a href="{% url 'core:license:delete' license.id %}"
class="dropdown-item wger-modal-dialog">{% translate 'Delete' %}</a>
{% translate 'Edit' as text %}
{% url 'core:license:edit' license.id as url %}
{% modal_link url=url text=text css_class='dropdown-item' %}
{% translate 'Delete' as text %}
{% url 'core:license:delete' license.id as url %}
{% modal_link url=url text=text css_class='dropdown-item' %}
</div>
</div>
{{ license }}
@@ -36,8 +40,7 @@
{# #}
{% block options %}
{% if perms.core.add_license %}
<a href="{% url 'core:license:add' %}" class="btn btn-success btn-sm wger-modal-dialog">
{% translate "Add" %}
</a>
{% translate 'Add' as text %}
{% modal_link url='core:license:add' text=text %}
{% endif %}
{% endblock %}

View File

@@ -1,50 +0,0 @@
% extends "base.html" %}
{% load i18n static wger_extras %}
{% block title %}{% translate "License list" %}{% endblock %}
{% block content %}
<ul class="list-group">
{% for license in license_list %}
<li class="list-group-item ">
<div class="btn-group float-end">
<button type="button" class="btn btn-light dropdown-toggle btn-sm" data-bs-toggle="dropdown">
<span class="{% fa_class 'cog' %}"></span>
</button>
<ul class="dropdown-menu" role="menu">
<li>
<a href="{% url 'core:license:edit' license.id %}"
class="wger-modal-dialog">{% translate 'Edit' %}</a>
</li>
<li>
<a href="{% url 'core:license:delete' license.id %}"
class="wger-modal-dialog">{% translate 'Delete' %}</a>
</li>
</ul>
</div>
{{ license }}
</li>
{% empty %}
<li class="list-group-item">
{% translate "Nothing found" %}
</li>
{% endfor %}
</ul>
{% endblock %}
{% block sidebar %}
{% if perms.core.add_license %}
<p>
<a href="{% url 'core:license:add' %}" class="btn btn-success btn-sm wger-modal-dialog">
{% translate "Add" %}
</a>
</p>
<p>{% blocktranslate %}If a license has been localized, e.g. the Creative Commons
licenses for the different countries, add them as separate entries here.{% endblocktranslate %}</p>
{% endif %}
{% endblock %}

View File

@@ -10,14 +10,18 @@
<li class="list-group-item list-group-item-action">
{% if unit.id != 1 and unit.id != 2 %}
<div class="btn-group float-end">
<button type="button" class="btn btn-dark dropdown-toggle btn-sm" data-bs-toggle="dropdown">
<button type="button" class="btn btn-dark dropdown-toggle btn-sm"
data-bs-toggle="dropdown">
<span class="{% fa_class 'cog' %}"></span>
</button>
<ul class="dropdown-menu" role="menu">
<a href="{% url 'core:repetition-unit:edit' unit.id %}"
class="wger-modal-dialog dropdown-item">{% translate 'Edit' %}</a>
<a href="{% url 'core:repetition-unit:delete' unit.id %}"
class="wger-modal-dialog dropdown-item">{% translate 'Delete' %}</a>
{% translate 'Edit' as text %}
{% url 'core:repetition-unit:edit' unit.id as url %}
{% modal_link url=url text=text css_class='dropdown-item' %}
{% translate 'Delete' as text %}
{% url 'core:repetition-unit:delete' unit.id as url %}
{% modal_link url=url text=text css_class='dropdown-item' %}
</ul>
</div>
{% endif %}
@@ -39,8 +43,7 @@
{# #}
{% block options %}
{% if perms.core.add_license %}
<a href="{% url 'core:repetition-unit:add' %}" class="btn btn-success btn-sm wger-modal-dialog">
{% translate "Add" %}
</a>
{% translate 'Add' as text %}
{% modal_link url='core:repetition-unit:add' text=text %}
{% endif %}
{% endblock %}

View File

@@ -0,0 +1,12 @@
{% load static i18n %}
<a
href="{{ url }}"
class="{{ css_class }}"
hx-get="{{ url }}"
hx-target="#ajax-info-content"
data-bs-toggle="modal"
data-bs-target="#wger-ajax-info"
>
{{ text }}
</a>

View File

@@ -69,6 +69,7 @@
<script src="{% static 'yarn/bootstrap/dist/js/bootstrap.bundle.min.js' %}"></script>
<script src="{% static 'yarn/Sortable/Sortable.min.js' %}"></script>
<script src="{% static 'yarn/d3/dist/d3.js' %}"></script>
<script src="{% static 'yarn/htmx.org/dist/htmx.min.js' %}"></script>
<script src="{% static 'yarn/popper.js/dist/umd/popper.js' %}"></script>
<script src="{% static 'yarn/devbridge-autocomplete/dist/jquery.autocomplete.min.js' %}">
</script>
@@ -116,7 +117,6 @@
<div class="modal-dialog modal-lg">
<div class="modal-content">
<div class="modal-header">
<h4 class="modal-title" id="ajax-info-title">Modal title</h4>
<button type="button" class="btn-close" data-bs-dismiss="modal"
aria-label="Close"></button>
</div>

View File

@@ -1,15 +1,7 @@
{% extends "base.html" %}
{% load i18n static wger_extras crispy_forms_tags %}
{% block title %}{% translate "Delete account" %}{% endblock %}
<h4>{% translate "Delete account" %}</h4>
{% block header %}
{% endblock %}
{% block content %}
<div class="wger-form">
<div class="card bg-light">
<div class="card-header">
<h4 class="card-title">
@@ -26,9 +18,3 @@ and can't be undone. {% endblocktranslate %}{% endwith %}</p>
</div>
<div class="mt-2"></div>
{% crispy form %}
</div>
{% endblock %}
{% block sidebar %}
{% endblock %}

View File

@@ -201,20 +201,38 @@
{% translate "Actions" %} <span class="caret"></span>
</button>
<div class="dropdown-menu" role="menu">
<a href="{% url 'core:user:edit' current_user.pk %}" class="wger-modal-dialog dropdown-item">{% translate "Edit"%}</a>
{% translate 'Edit' as text %}
{% url 'core:user:edit' current_user.pk as url %}
{% modal_link url=url text=text css_class='dropdown-item' %}
<div class="dropdown-divider"></div>
{% if current_user.is_active %}
<a href="{% url 'core:user:deactivate' current_user.pk %}" class="dropdown-item">{% translate "Deactivate user"%}</a>
{% else %}
<a href="{% url 'core:user:activate' current_user.pk %}" class="dropdown-item">{% translate "Activate user"%}</a>
{% endif %}
<a data-url="{% url 'gym:gym:reset-user-password' current_user.pk %}" data-bs-toggle="modal" data-bs-target="#confirmation-modal" class="dropdown-item">{% translate "Reset user password" %}</a>
{% translate 'Deactivate user' as text %}
{% url 'core:user:deactivate' current_user.pk as url %}
{% modal_link url=url text=text css_class='dropdown-item' %}
{% else %}
{% translate 'Activate user' as text %}
{% url 'core:user:activate' current_user.pk as url %}
{% modal_link url=url text=text css_class='dropdown-item' %}
{% endif %}
<a
data-url="{% url 'gym:gym:reset-user-password' current_user.pk %}"
data-bs-toggle="modal"
data-bs-target="#confirmation-modal"
class="dropdown-item">
{% translate "Reset user password" %}
</a>
{% if perms.gym.manage_gym or perms.gym.manage_gyms %}
{% if current_user.userprofile.gym %}
<a href="{% url 'gym:gym:edit-user-permission' current_user.pk %}" class="wger-modal-dialog dropdown-item">{% translate "Roles"%}</a>
{% translate 'Roles' as text %}
{% url 'gym:gym:edit-user-permission' current_user.pk as url %}
{% modal_link url=url text=text css_class='dropdown-item' %}
{% endif %}
{% endif %}
<a href="{% url 'core:user:delete' current_user.pk %}" class="wger-modal-dialog dropdown-item">{% translate "Delete"%}</a>
{% translate 'Delete' as text %}
{% url 'core:user:delete' current_user.pk as url %}
{% modal_link url=url text=text css_class='dropdown-item' %}
</div>
</div>
@@ -362,7 +380,10 @@
</button>
<div class="dropdown-menu" role="menu">
<a href="{% url 'gym:admin_note:list' current_user.pk %}" class="dropdown-item">{% translate "Overview" %}</a>
<a href="{% url 'gym:admin_note:add' current_user.pk %}" class="wger-modal-dialog dropdown-item">{% translate "Add"%}</a>
{% translate 'Add' as text %}
{% url 'gym:admin_note:add' current_user.pk as url %}
{% modal_link url=url text=text css_class='dropdown-item' %}
</div>
</div>
@@ -442,7 +463,10 @@
{% translate "Actions" %} <span class="caret"></span>
</button>
<div class="dropdown-menu" role="menu">
<a href="{% url 'gym:user_config:edit' current_user.gymuserconfig.pk %}" class="wger-modal-dialog dropdown-item">{% translate "Edit"%}</a>
{% translate 'Edit' as text %}
{% url 'gym:user_config:edit' current_user.gymuserconfig.pk %}
{% modal_link url=url text=text css_class='dropdown-item' %}
</div>
</div>
{% endif %}

View File

@@ -27,7 +27,9 @@
</p>
{% endif %}
<p>
<small class="text-muted">{% translate "You need to verify your email to contribute exercises" %}</small>
<small class="text-muted">
{% translate "You need to verify your email to contribute exercises" %}
</small>
</p>
{% endblock %}
@@ -39,8 +41,11 @@
{% block options %}
<div class="btn-group">
<div class="btn-group" role="group">
<button type="button" class="btn btn-primary btn-sm dropdown-toggle" data-bs-toggle="dropdown"
aria-haspopup="true" aria-expanded="false">
<button type="button"
class="btn btn-primary btn-sm dropdown-toggle"
data-bs-toggle="dropdown"
aria-haspopup="true"
aria-expanded="false">
<span class="{% fa_class 'cog' %}"></span>
{% translate "Options" %}
</button>
@@ -54,10 +59,8 @@
{% translate "API key" %}
</a>
<div role="separator" class="dropdown-divider"></div>
<a href="{% url 'core:user:delete' %}" class="wger-modal-dialog dropdown-item">
<span class="{% fa_class 'trash' %}"></span>
{% translate "Delete account" %}
</a>
{% translate 'Delete account' as text %}
{% modal_link url='core:user:delete' text=text css_class='dropdown-item' %}
</div>
</div>
</div>

View File

@@ -10,14 +10,18 @@
<li class="list-group-item list-group-item-action">
{% if unit.id != 1 and unit.id != 2 %}
<div class="btn-group float-end">
<button type="button" class="btn btn-dark dropdown-toggle btn-sm" data-bs-toggle="dropdown">
<button type="button" class="btn btn-dark dropdown-toggle btn-sm"
data-bs-toggle="dropdown">
<span class="{% fa_class 'cog' %}"></span>
</button>
<div class="dropdown-menu" role="menu">
<a href="{% url 'core:weight-unit:edit' unit.id %}"
class="wger-modal-dialog dropdown-item">{% translate 'Edit' %}</a>
<a href="{% url 'core:weight-unit:delete' unit.id %}"
class="wger-modal-dialog dropdown-item">{% translate 'Delete' %}</a>
{% translate 'Edit' as text %}
{% url 'core:weight-unit:edit' unit.id as url %}
{% modal_link url=url text=text css_class='dropdown-item' %}
{% translate 'Delete' as text %}
{% url 'core:weight-unit:delete' unit.id as url %}
{% modal_link url=url text=text css_class='dropdown-item' %}
</div>
</div>
{% endif %}
@@ -39,8 +43,7 @@
{# #}
{% block options %}
{% if perms.core.add_license %}
<a href="{% url 'core:weight-unit:add' %}" class="btn btn-success btn-sm wger-modal-dialog">
{% translate "Add" %}
</a>
{% translate 'Add' as text %}
{% modal_link url='core:weight-unit:add' text=text %}
{% endif %}
{% endblock %}

View File

@@ -26,6 +26,7 @@ from django.utils.translation import (
pgettext,
)
from wger.core.tests.base_testcase import get_reverse
# wger
from wger.manager.models import Day
from wger.utils.constants import (
@@ -34,7 +35,6 @@ from wger.utils.constants import (
)
from wger.utils.language import get_language_data
register = template.Library()
@@ -177,6 +177,11 @@ def fa_class(class_name='', icon_type='fas', fixed_width=True):
return mark_safe(css)
@register.inclusion_tag('tags/modal_link.html')
def modal_link(url: str, text: str, css_class='btn btn-success btn-sm'):
return {'url': get_reverse(url), 'text': text, 'css_class': css_class}
@register.simple_tag
def trans_weight_unit(unit, user=None):
"""

View File

@@ -2,6 +2,11 @@
# yarn lockfile v1
"@popperjs/core@^2.11.8":
version "2.11.8"
resolved "https://registry.yarnpkg.com/@popperjs/core/-/core-2.11.8.tgz#6b79032e760a0899cd4204710beede972a3a185f"
integrity sha512-P1st0aksCrn9sGZhp8GMYwBnQsbvAWsZAX44oXNNvLHGqAOcoVxmjZiohstwQ7SqKnbR47akdNi+uleWD8+g6A==
"@types/jquery@^2.0.32":
version "2.0.54"
resolved "https://registry.yarnpkg.com/@types/jquery/-/jquery-2.0.54.tgz#d7999245f77c3fab5d84e7d32b8a6c20bfd1f072"
@@ -314,6 +319,11 @@ get-size@^2.0.2:
resolved "https://registry.yarnpkg.com/get-size/-/get-size-2.0.3.tgz#54a1d0256b20ea7ac646516756202769941ad2ef"
integrity sha512-lXNzT/h/dTjTxRbm9BXb+SGxxzkm97h/PCIKtlN/CBCxxmkkIVV21udumMS93MuVTDX583gqc94v3RjuHmI+2Q==
htmx.org@^2.0.3:
version "2.0.3"
resolved "https://registry.yarnpkg.com/htmx.org/-/htmx.org-2.0.3.tgz#83f76a53d3fc562fe839f0f57ee7cd5f75fe7b59"
integrity sha512-AeoJUAjkCVVajbfKX+3sVQBTCt8Ct4lif1T+z/tptTXo8+8yyq3QIMQQe/IT+R8ssfrO1I0DeX4CAronzCL6oA==
iconv-lite@0.6:
version "0.6.3"
resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.6.3.tgz#a52f80bf38da1952eb5c681790719871a1a72501"
@@ -348,11 +358,6 @@ outlayer@^2.1.0:
fizzy-ui-utils "^2.0.0"
get-size "^2.0.2"
popper.js@^1.16.1:
version "1.16.1"
resolved "https://registry.yarnpkg.com/popper.js/-/popper.js-1.16.1.tgz#2a223cb3dc7b6213d740e40372be40de43e65b1b"
integrity sha512-Wb4p1J4zyFTbM+u6WuO4XstYx4Ky9Cewe4DWrel7B0w6VVICvPwdOpotjzcf6eD8TsckVnIMNONQyPIUFOUbCQ==
robust-predicates@^3.0.0:
version "3.0.1"
resolved "https://registry.yarnpkg.com/robust-predicates/-/robust-predicates-3.0.1.tgz#ecde075044f7f30118682bd9fb3f123109577f9a"