Add autocomplete feature for jury form

Signed-off-by: Emmy D'Anello <emmy.danello@animath.fr>
This commit is contained in:
Emmy D'Anello 2024-03-23 23:04:22 +01:00
parent 40aa2e520f
commit 1dd9a5cf94
Signed by: ynerant
GPG Key ID: 3A75C55819C8CF85
5 changed files with 107 additions and 37 deletions

View File

@ -7,7 +7,7 @@ msgid ""
msgstr "" msgstr ""
"Project-Id-Version: TFJM\n" "Project-Id-Version: TFJM\n"
"Report-Msgid-Bugs-To: \n" "Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2024-03-23 11:22+0100\n" "POT-Creation-Date: 2024-03-23 23:02+0100\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: Emmy D'Anello <emmy.danello@animath.fr>\n" "Last-Translator: Emmy D'Anello <emmy.danello@animath.fr>\n"
"Language-Team: LANGUAGE <LL@li.org>\n" "Language-Team: LANGUAGE <LL@li.org>\n"
@ -490,7 +490,7 @@ msgstr "Ce trigramme est déjà utilisé."
msgid "No team was found with this access code." msgid "No team was found with this access code."
msgstr "Aucune équipe n'a été trouvée avec ce code d'accès." msgstr "Aucune équipe n'a été trouvée avec ce code d'accès."
#: participation/forms.py:85 participation/forms.py:352 #: participation/forms.py:85 participation/forms.py:355
#: registration/forms.py:122 registration/forms.py:144 #: registration/forms.py:122 registration/forms.py:144
#: registration/forms.py:166 registration/forms.py:188 #: registration/forms.py:166 registration/forms.py:188
#: registration/forms.py:237 registration/forms.py:270 #: registration/forms.py:237 registration/forms.py:270
@ -516,7 +516,7 @@ msgstr "Message à adresser à l'équipe :"
msgid "The uploaded file size must be under 5 Mo." msgid "The uploaded file size must be under 5 Mo."
msgstr "Le fichier envoyé doit peser moins de 5 Mo." msgstr "Le fichier envoyé doit peser moins de 5 Mo."
#: participation/forms.py:157 participation/forms.py:354 #: participation/forms.py:157 participation/forms.py:357
msgid "The uploaded file must be a PDF file." msgid "The uploaded file must be a PDF file."
msgstr "Le fichier envoyé doit être au format PDF." msgstr "Le fichier envoyé doit être au format PDF."
@ -530,11 +530,15 @@ msgstr "Le fichier PDF ne doit pas avoir plus de 30 pages."
msgid "Add" msgid "Add"
msgstr "Ajouter" msgstr "Ajouter"
#: participation/forms.py:251 #: participation/forms.py:243
msgid "This user already exists, but is a participant."
msgstr "Cet⋅te utilisateur⋅rice existe déjà, mais en tant que participant⋅e."
#: participation/forms.py:254
msgid "CSV file:" msgid "CSV file:"
msgstr "Tableur au format CSV :" msgstr "Tableur au format CSV :"
#: participation/forms.py:275 #: participation/forms.py:278
msgid "" msgid ""
"This file contains non-UTF-8 and non-ISO-8859-1 content. Please send your " "This file contains non-UTF-8 and non-ISO-8859-1 content. Please send your "
"sheet as a CSV file." "sheet as a CSV file."
@ -542,30 +546,30 @@ msgstr ""
"Ce fichier contient des éléments non-UTF-8 et non-ISO-8859-1. Merci " "Ce fichier contient des éléments non-UTF-8 et non-ISO-8859-1. Merci "
"d'envoyer votre tableur au format CSV." "d'envoyer votre tableur au format CSV."
#: participation/forms.py:290 #: participation/forms.py:293
msgid "Can't determine the pool size. Are you sure your file is correct?" msgid "Can't determine the pool size. Are you sure your file is correct?"
msgstr "" msgstr ""
"Impossible de déterminer la taille de la poule. Êtes-vous sûr⋅e que le " "Impossible de déterminer la taille de la poule. Êtes-vous sûr⋅e que le "
"fichier est correct ?" "fichier est correct ?"
#: participation/forms.py:310 #: participation/forms.py:313
msgid "The following note is higher of the maximum expected value:" msgid "The following note is higher of the maximum expected value:"
msgstr "La note suivante est supérieure au maximum attendu :" msgstr "La note suivante est supérieure au maximum attendu :"
#: participation/forms.py:318 #: participation/forms.py:321
msgid "The following user was not found:" msgid "The following user was not found:"
msgstr "L'utilisateur⋅rice suivant n'a pas été trouvé :" msgstr "L'utilisateur⋅rice suivant n'a pas été trouvé :"
#: participation/forms.py:335 #: participation/forms.py:338
msgid "The defender, the opponent and the reporter must be different." msgid "The defender, the opponent and the reporter must be different."
msgstr "" msgstr ""
"Les équipes défenseuse, opposante et rapportrice doivent être différent⋅es." "Les équipes défenseuse, opposante et rapportrice doivent être différent⋅es."
#: participation/forms.py:339 #: participation/forms.py:342
msgid "This defender did not work on this problem." msgid "This defender did not work on this problem."
msgstr "Ce⋅tte défenseur⋅se ne travaille pas sur ce problème." msgstr "Ce⋅tte défenseur⋅se ne travaille pas sur ce problème."
#: participation/forms.py:358 #: participation/forms.py:361
msgid "The PDF file must not have more than 2 pages." msgid "The PDF file must not have more than 2 pages."
msgstr "Le fichier PDF ne doit pas avoir plus de 2 pages." msgstr "Le fichier PDF ne doit pas avoir plus de 2 pages."
@ -1344,7 +1348,7 @@ msgstr "Modifier la poule"
msgid "Upload notes" msgid "Upload notes"
msgstr "Envoyer les notes" msgstr "Envoyer les notes"
#: participation/templates/participation/pool_jury.html:35 #: participation/templates/participation/pool_jury.html:44
msgid "Back to pool detail" msgid "Back to pool detail"
msgstr "Retour aux détails de la poule" msgstr "Retour aux détails de la poule"
@ -1762,32 +1766,33 @@ msgid "Jury of pool {pool} for {tournament} with teams {teams}"
msgstr "Jury de la poule {pool} pour {tournament} avec les équipes {teams}" msgstr "Jury de la poule {pool} pour {tournament} avec les équipes {teams}"
#: participation/views.py:814 #: participation/views.py:814
msgid "This user already exists, but is a participant." #, python-brace-format
msgstr "Cet⋅te utilisateur⋅rice existe déjà, mais en tant que participant⋅e." msgid "The jury {name} is already in the pool!"
msgstr "{name} est déjà dans la poule !"
#: participation/views.py:833 #: participation/views.py:834
msgid "New TFJM² jury account" msgid "New TFJM² jury account"
msgstr "Nouveau compte de juré⋅e pour le TFJM²" msgstr "Nouveau compte de juré⋅e pour le TFJM²"
#: participation/views.py:850 #: participation/views.py:851
#, python-brace-format #, python-brace-format
msgid "The jury {name} has been successfully added!" msgid "The jury {name} has been successfully added!"
msgstr "{name} a été ajouté⋅e avec succès en tant que juré⋅e !" msgstr "{name} a été ajouté⋅e avec succès en tant que juré⋅e !"
#: participation/views.py:882 #: participation/views.py:883
#, python-brace-format #, python-brace-format
msgid "The jury {name} has been successfully removed!" msgid "The jury {name} has been successfully removed!"
msgstr "{name} a été retiré⋅e avec succès du jury !" msgstr "{name} a été retiré⋅e avec succès du jury !"
#: participation/views.py:910 #: participation/views.py:911
msgid "The following user is not registered as a jury:" msgid "The following user is not registered as a jury:"
msgstr "L'utilisateur⋅rice suivant n'est pas inscrit⋅e en tant que juré⋅e :" msgstr "L'utilisateur⋅rice suivant n'est pas inscrit⋅e en tant que juré⋅e :"
#: participation/views.py:924 #: participation/views.py:925
msgid "Notes were successfully uploaded." msgid "Notes were successfully uploaded."
msgstr "Les notes ont bien été envoyées." msgstr "Les notes ont bien été envoyées."
#: participation/views.py:1588 #: participation/views.py:1589
msgid "You can't upload a synthesis after the deadline." msgid "You can't upload a synthesis after the deadline."
msgstr "Vous ne pouvez pas envoyer de note de synthèse après la date limite." msgstr "Vous ne pouvez pas envoyer de note de synthèse après la date limite."

View File

@ -213,17 +213,17 @@ class AddJuryForm(forms.ModelForm):
self.helper.layout = Div( self.helper.layout = Div(
Div( Div(
Div( Div(
Field('first_name', autofocus="autofocus"), Field('email', autofocus="autofocus", list="juries-email"),
css_class='col-md-3',
),
Div(
Field('last_name'),
css_class='col-md-3',
),
Div(
Field('email'),
css_class='col-md-5', css_class='col-md-5',
), ),
Div(
Field('first_name', list="juries-first-name"),
css_class='col-md-3',
),
Div(
Field('last_name', list="juries-last-name"),
css_class='col-md-3',
),
Div( Div(
Submit('submit', _("Add")), Submit('submit', _("Add")),
css_class='col-md-1 py-md-4', css_class='col-md-1 py-md-4',
@ -239,6 +239,9 @@ class AddJuryForm(forms.ModelForm):
email = self.data["email"] email = self.data["email"]
if User.objects.filter(email=email).exists(): if User.objects.filter(email=email).exists():
self.instance = User.objects.get(email=email) self.instance = User.objects.get(email=email)
if self.instance.registration.participates:
self.add_error(None, _("This user already exists, but is a participant."))
return
return email return email
class Meta: class Meta:

View File

@ -8,15 +8,15 @@
{% for jury in pool.juries.all %} {% for jury in pool.juries.all %}
<div class="row my-3"> <div class="row my-3">
<div class="col-md-5">
<input type="email" class="form-control" value="{{ jury.user.email }}" disabled>
</div>
<div class="col-md-3"> <div class="col-md-3">
<input type="text" class="form-control" value="{{ jury.user.first_name }}" disabled> <input type="text" class="form-control" value="{{ jury.user.first_name }}" disabled>
</div> </div>
<div class="col-md-3"> <div class="col-md-3">
<input type="text" class="form-control" value="{{ jury.user.last_name }}" disabled> <input type="text" class="form-control" value="{{ jury.user.last_name }}" disabled>
</div> </div>
<div class="col-md-5">
<input type="email" class="form-control" value="{{ jury.user.email }}" disabled>
</div>
<div class="col-md-1"> <div class="col-md-1">
<a href="{% url 'participation:pool_remove_jury' pk=pool.pk jury_id=jury.id %}" class="btn btn-danger"> <a href="{% url 'participation:pool_remove_jury' pk=pool.pk jury_id=jury.id %}" class="btn btn-danger">
Retirer Retirer
@ -28,6 +28,15 @@
{{ form|as_crispy_errors }} {{ form|as_crispy_errors }}
{% crispy form %} {% crispy form %}
<datalist id="juries-email">
</datalist>
<datalist id="juries-first-name">
</datalist>
<datalist id="juries-last-name">
</datalist>
<hr> <hr>
<div class="row text-center"> <div class="row text-center">
@ -39,8 +48,60 @@
{% block extrajavascript %} {% block extrajavascript %}
<script> <script>
document.addEventListener('DOMContentLoaded', () => { const emailField = document.getElementById('id_email')
initModal("updatePool", "{% url "participation:pool_update" pk=pool.pk %}") const firstNameField = document.getElementById('id_first_name')
const lastNameField = document.getElementById('id_last_name')
const juriesEmailList = document.getElementById('juries-email')
const juriesFirstNameList = document.getElementById('juries-first-name')
const juriesLastNameList = document.getElementById('juries-last-name')
function updateJuries(filter) {
fetch(`/api/registration/volunteers/?search=${filter}`)
.then(response => response.json())
.then(response => response.results)
.then(data => {
juriesEmailList.innerHTML = ''
juriesFirstNameList.innerHTML = ''
juriesLastNameList.innerHTML = ''
data.forEach(jury => {
const optionEmail = document.createElement('option')
optionEmail.value = jury.email
optionEmail.setAttribute('data-id', jury.id)
juriesEmailList.appendChild(optionEmail)
const optionFirstName = document.createElement('option')
optionFirstName.value = jury.first_name
optionFirstName.setAttribute('data-id', jury.id)
juriesFirstNameList.appendChild(optionFirstName)
const optionLastName = document.createElement('option')
optionLastName.value = jury.last_name
optionLastName.setAttribute('data-id', jury.id)
juriesLastNameList.appendChild(optionLastName)
})
})
}
emailField.addEventListener('input', event => {
let emailOption = document.querySelector(`datalist[id="juries-email"] > option[value="${event.target.value}"]`)
if (emailOption) {
let id = emailOption.getAttribute('data-id')
let firstNameOption = document.querySelector(`datalist[id="juries-first-name"] > option[data-id="${id}"]`)
let lastNameOption = document.querySelector(`datalist[id="juries-last-name"] > option[data-id="${id}"]`)
if (firstNameOption && lastNameOption) {
firstNameField.value = firstNameOption.value
lastNameField.value = lastNameOption.value
}
}
updateJuries(event.target.value)
})
firstNameField.addEventListener('input', event => {
updateJuries(event.target.value)
})
lastNameField.addEventListener('input', event => {
updateJuries(event.target.value)
}) })
</script> </script>
{% endblock %} {% endblock %}

View File

@ -810,8 +810,9 @@ class PoolJuryView(VolunteerMixin, FormView, DetailView):
# The user already exists, so we don't recreate it # The user already exists, so we don't recreate it
user.refresh_from_db() user.refresh_from_db()
reg = user.registration reg = user.registration
if reg.participates: if reg in self.object.juries.all():
form.add_error(None, _("This user already exists, but is a participant.")) messages.warning(self.request, _("The jury {name} is already in the pool!")
.format(name=f"{user.first_name} {user.last_name}"))
return self.form_invalid(form) return self.form_invalid(form)
else: else:
# Save the user object first # Save the user object first

View File

@ -50,4 +50,4 @@ class PaymentSerializer(serializers.ModelSerializer):
class BasicUserSerializer(serializers.ModelSerializer): class BasicUserSerializer(serializers.ModelSerializer):
class Meta: class Meta:
model = User model = User
fields = ['first_name', 'last_name', 'email', ] fields = ['id', 'first_name', 'last_name', 'email', ]