use dynamic formset for product action formset

This commit is contained in:
imperosol
2026-03-07 16:21:18 +01:00
parent 7bb3d064ee
commit f17f17d8de
5 changed files with 68 additions and 31 deletions

View File

@@ -21,6 +21,9 @@ document.addEventListener("alpine:init", () => {
* - a button with `@click="addForm"` * - a button with `@click="addForm"`
* - you may also have one or more buttons with `@click="removeForm(element)"`, * - you may also have one or more buttons with `@click="removeForm(element)"`,
* where `element` is the HTML element containing the form. * where `element` is the HTML element containing the form.
*
* For an example of how this is used, you can have a look to
* `counter/templates/counter/product_form.jinja`
*/ */
Alpine.data("dynamicFormSet", (config?: Config) => ({ Alpine.data("dynamicFormSet", (config?: Config) => ({
init() { init() {

View File

@@ -35,8 +35,8 @@
<noscript><link rel="stylesheet" href="{{ static('bundled/fontawesome-index.css') }}"></noscript> <noscript><link rel="stylesheet" href="{{ static('bundled/fontawesome-index.css') }}"></noscript>
<script src="{{ url('javascript-catalog') }}"></script> <script src="{{ url('javascript-catalog') }}"></script>
<script type="module" src={{ static("bundled/core/navbar-index.ts") }}></script> <script type="module" src="{{ static("bundled/core/navbar-index.ts") }}"></script>
<script type="module" src={{ static("bundled/core/components/include-index.ts") }}></script> <script type="module" src="{{ static("bundled/core/components/include-index.ts") }}"></script>
<script type="module" src="{{ static('bundled/alpine-index.js') }}"></script> <script type="module" src="{{ static('bundled/alpine-index.js') }}"></script>
<script type="module" src="{{ static('bundled/htmx-index.js') }}"></script> <script type="module" src="{{ static('bundled/htmx-index.js') }}"></script>
<script type="module" src="{{ static('bundled/country-flags-index.ts') }}"></script> <script type="module" src="{{ static('bundled/country-flags-index.ts') }}"></script>

View File

@@ -291,7 +291,8 @@ ScheduledProductActionFormSet = forms.modelformset_factory(
absolute_max=None, absolute_max=None,
can_delete=True, can_delete=True,
can_delete_extra=False, can_delete_extra=False,
extra=2, extra=0,
min_num=1,
) )

View File

@@ -1,5 +1,44 @@
{% extends "core/base.jinja" %} {% extends "core/base.jinja" %}
{% block additional_js %}
<script type="module" src="{{ static("bundled/core/dynamic-formset-index.ts") }}"></script>
{% endblock %}
{% macro action_form(form) %}
<fieldset x-data="{action: '{{ form.task.initial }}'}">
{{ form.non_field_errors() }}
<div class="row gap-2x margin-bottom">
<div>
{{ form.task.errors }}
{{ form.task.label_tag() }}
{{ form.task|add_attr("x-model=action") }}
</div>
<div>{{ form.trigger_at.as_field_group() }}</div>
</div>
<div x-show="action==='counter.tasks.change_counters'" class="margin-bottom">
{{ form.counters.as_field_group() }}
</div>
{%- if form.DELETE -%}
<div class="row gap">
{{ form.DELETE.as_field_group() }}
</div>
{%- else -%}
<button
class="btn btn-grey"
@click.prevent="removeForm($event.target.closest('fieldset'))"
>
<i class="fa fa-minus"></i>{% trans %}Remove this action{% endtrans %}
</button>
{%- endif -%}
{%- for field in form.hidden_fields() -%}
{{ field }}
{%- endfor -%}
<hr />
</fieldset>
{% endmacro %}
{% block content %} {% block content %}
{% if object %} {% if object %}
<h2>{% trans name=object %}Edit product {{ name }}{% endtrans %}</h2> <h2>{% trans name=object %}Edit product {{ name }}{% endtrans %}</h2>
@@ -25,34 +64,20 @@
</em> </em>
</p> </p>
<div x-data="dynamicFormSet" class="margin-bottom">
{{ form.action_formset.management_form }} {{ form.action_formset.management_form }}
{%- for action_form in form.action_formset.forms -%} <div x-ref="formContainer">
<fieldset x-data="{action: '{{ action_form.task.initial }}'}"> {%- for f in form.action_formset.forms -%}
{{ action_form.non_field_errors() }} {{ action_form(f) }}
<div class="row gap-2x margin-bottom">
<div>
{{ action_form.task.errors }}
{{ action_form.task.label_tag() }}
{{ action_form.task|add_attr("x-model=action") }}
</div>
<div>{{ action_form.trigger_at.as_field_group() }}</div>
</div>
<div x-show="action==='counter.tasks.change_counters'" class="margin-bottom">
{{ action_form.counters.as_field_group() }}
</div>
{%- if action_form.DELETE -%}
<div class="row gap">
{{ action_form.DELETE.as_field_group() }}
</div>
{%- endif -%}
{%- for field in action_form.hidden_fields() -%}
{{ field }}
{%- endfor -%} {%- endfor -%}
</fieldset> </div>
{%- if not loop.last -%} <template x-ref="formTemplate">
<hr class="margin-bottom"> {{ action_form(form.action_formset.empty_form) }}
{%- endif -%} </template>
{%- endfor -%} <button @click.prevent="addForm()" class="btn btn-grey">
<p><input type="submit" value="{% trans %}Save{% endtrans %}" /></p> <i class="fa fa-plus"></i>{% trans %}Add action{% endtrans %}
</button>
</div>
<p><input class="btn btn-blue" type="submit" value="{% trans %}Save{% endtrans %}" /></p>
</form> </form>
{% endblock %} {% endblock %}

View File

@@ -3757,6 +3757,10 @@ msgstr ""
"votre cotisation. Si vous ne renouvelez pas votre cotisation, il n'y aura " "votre cotisation. Si vous ne renouvelez pas votre cotisation, il n'y aura "
"aucune conséquence autre que le retrait de l'argent de votre compte." "aucune conséquence autre que le retrait de l'argent de votre compte."
#: counter/templates/counter/product_form.jinja
msgid "Remove this action"
msgstr "Retirer cette action"
#: counter/templates/counter/product_form.jinja #: counter/templates/counter/product_form.jinja
#, python-format #, python-format
msgid "Edit product %(name)s" msgid "Edit product %(name)s"
@@ -3784,6 +3788,10 @@ msgstr ""
"Les actions automatiques vous permettent de planifier des modifications du " "Les actions automatiques vous permettent de planifier des modifications du "
"produit à l'avance." "produit à l'avance."
#: counter/templates/counter/product_form.jinja
msgid "Add action"
msgstr "Ajouter une action"
#: counter/templates/counter/product_list.jinja #: counter/templates/counter/product_list.jinja
msgid "Product list" msgid "Product list"
msgstr "Liste des produits" msgstr "Liste des produits"