Skip to content

Commit

Permalink
Allow to upload an arbitrary number of legal files
Browse files Browse the repository at this point in the history
  • Loading branch information
brmzkw committed Aug 4, 2023
1 parent 69fa125 commit 1c0e790
Show file tree
Hide file tree
Showing 5 changed files with 87 additions and 52 deletions.
12 changes: 10 additions & 2 deletions mesads/app/forms.py
Original file line number Diff line number Diff line change
Expand Up @@ -140,7 +140,11 @@ def _should_delete_form(self, form):


ADSLegalFileFormSet = inlineformset_factory(
ADS, ADSLegalFile, fields=("file",), can_delete=True, extra=25, max_num=25
ADS,
ADSLegalFile,
fields=("file",),
can_delete=True,
extra=0,
)


Expand All @@ -164,7 +168,11 @@ class ADSSearchForm(forms.Form):


ADSManagerDecreeFormSet = inlineformset_factory(
ADSManager, ADSManagerDecree, fields=("file",), can_delete=True, extra=5, max_num=5
ADSManager,
ADSManagerDecree,
fields=("file",),
can_delete=True,
extra=0,
)


Expand Down
72 changes: 22 additions & 50 deletions mesads/templates/webpack/formset_files.html
Original file line number Diff line number Diff line change
@@ -1,49 +1,13 @@
{% comment %}Render a formset to upload one or several files.{% endcomment %}

<script type="text/javascript">
// Workaround, because array.findLastIndex is only available for firefox>104
function findLastIndex(arr, func) {
for (i = arr.length; i > 0; --i) {
if (func(arr[i - 1])) {
return i - 1;
}
}
return -1;
}

function x_data() {
return {
numberOfFormsToDisplay: (() => {
const allForms = [...document.getElementsByClassName('formset-file')];
// list of booleans with one entry per form, true if the form is empty, false is the form is filled or has an error
const filled = allForms.map((e) => !!(e.querySelector('input[type="hidden"]') || e.querySelector('p.fr-error-text')));
// Index of the first filled form
const firstFilled = findLastIndex(filled, (e) => e === true);
// All the forms are empty, display one form
if (firstFilled < 0) {
return 1;
}
return firstFilled + 1;
})(),
};
}
</script>

<div
class="fr-input-group"
x-data="x_data()"
>
<div id="formset-files" class="fr-input-group" x-data="{extra: 0}">
{{ formset.management_form }}

{% for form in formset %}
<div
class="fr-download fr-download--card formset-file"
x-show="{{ forloop.counter0 }} < numberOfFormsToDisplay"
x-data="{deleteFile: false}"
x-bind:style="deleteFile ? {'background-color': 'rgb(253,222,222)'} : {'background-color': '#fff'}"
x-transition
>
{% comment %}Existing document {% endcomment %}
{% if form.instance.file %}
{{ form.id.as_hidden }}
{{ form.file.as_hidden }}
Expand Down Expand Up @@ -90,15 +54,6 @@
Annuler la suppression
</label>
</p>
{% comment %}New document {% endcomment %}
{% else %}
<div class="fr-upload-group">
<label class="fr-label" for="file-upload">
<strong>Nouveau document</strong>
<span class="fr-hint-text">Taille maximale : 10 Mo. Format préféré : pdf.</span>
</label>
<input class="fr-upload" type="file" id="{{ form.file.id_for_label }}" name="{{ form.file.html_name }}">
</div>
{% endif %}

{% comment %}Form errors {% endcomment %}
Expand All @@ -107,10 +62,27 @@
{{ error }}
</p>
{% endfor %}

</div>
{% endfor %}

{% comment %}Extra forms to select new document{% endcomment %}
<template x-for="i in extra">
<div
class="fr-download fr-download--card formset-file"
x-data="{deleteFile: false}"
x-bind:style="deleteFile ? {'background-color': 'rgb(253,222,222)'} : {'background-color': '#fff'}"
x-transition
>
<div class="fr-upload-group">
<label class="fr-label" for="file-upload">
<strong>Nouveau document</strong>
<span class="fr-hint-text">Taille maximale : 10 Mo. Format préféré : pdf.</span>
</label>
<input class="fr-upload" type="file" id="{{ formset.empty_form.file.id_for_label }}" name="{{ formset.empty_form.file.html_name }}">
</div>
</div>
</template>

{% for error in formset.non_form_errors %}
<p class="fr-error-text">
{{ error }}
Expand All @@ -120,10 +92,10 @@
<button
type="button"
class="fr-btn fr-btn--sm fr-icon-add-circle-line fr-btn--icon-left fr-btn--secondary"
x-on:click="numberOfFormsToDisplay++"
x-show="numberOfFormsToDisplay < {{ formset.extra }}"
@click="extra++"
x-add-file-button
{% if disabled %}disabled{% endif %}
>
Ajouter un autre document
Ajouter un document
</button>
</div>
49 changes: 49 additions & 0 deletions mesads/templates/webpack/formset_files.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
import Alpine from "alpinejs";

// This function must be called by the javascript page using formset_files.html
// to setup the directive "add-file-button".
export default function SetupFormsetFiles() {
Alpine.directive("add-file-button", (el, { expression }, { evaluate }) => {
el.addEventListener("click", () => {
const form = document.getElementById("formset-files");
if (!form) {
throw new Error("formset-files not found");
}

// Initial number of forms in the formset
const initialForms = Array.from(
form.querySelectorAll("input[type=hidden]")
).find((form) => form.id.match(/INITIAL_FORMS/)) as HTMLInputElement;

const numInitialForms = parseInt(initialForms.value, 10);

// Retrieve the value of the "extra" x-data attribute
const extra = evaluate("extra") as number;

// For each file input (ie. for each new form), replace the string
// __prefix__ present in formset.empty_form.file.id_for_label and
// formset.empty_form.file.html_name by the form index. If the forms don't
// have a correct index, the files will be ignored by django.
el.parentElement
?.querySelectorAll("input[type=file]")
.forEach((value) => {
const input = value as HTMLInputElement;

input.id = input.id.replace(
/__prefix__/g,
(extra + numInitialForms - 1).toString()
);
input.name = input.name.replace(
/__prefix__/,
(extra + numInitialForms - 1).toString()
);
});

const totalInput = Array.from(
form.querySelectorAll("input[type=hidden]")
).find((form) => form.id.match(/TOTAL_FORMS/)) as HTMLInputElement;

totalInput.value = (parseInt(totalInput.value, 10) + 1).toString();
});
});
}
3 changes: 3 additions & 0 deletions mesads/templates/webpack/pages/ads.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
import Alpine from "alpinejs";
import SetupFormsetFiles from "../formset_files";

SetupFormsetFiles();

// array.findLastIndex is only available for firefox>104
function findLastIndex(arr: any[], func: (e: any) => boolean) {
Expand Down
3 changes: 3 additions & 0 deletions mesads/templates/webpack/pages/ads_manager_decree.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
import Alpine from "alpinejs";
import SetupFormsetFiles from "../formset_files";

SetupFormsetFiles();

Alpine.start();

0 comments on commit 1c0e790

Please sign in to comment.