Amélioration de la gestion des dates et heures dans le formulaire :

- valeur par défaut (Fix #38)
- vérification de la cohérence relative des début et fin
- ajout d'un peu de javascript pour un ajustement de la fin quand on règle le début (un classique)
This commit is contained in:
Jean-Marie Favreau 2023-11-25 14:01:03 +01:00
parent 63e9c57e87
commit 971f8aad32
7 changed files with 166 additions and 28 deletions

View File

@ -1,11 +1,67 @@
from django.forms import ModelForm
from django.forms import ModelForm, ValidationError, TextInput
from django.views.generic import FormView
from .models import EventSubmissionForm
from .models import EventSubmissionForm, Event
from django.utils.translation import gettext_lazy as _
class EventSubmissionModelForm(ModelForm):
class Meta:
model = EventSubmissionForm
fields = ["url"]
class EventForm(ModelForm):
class Meta:
model = Event
fields = '__all__'
widgets = {
'start_day': TextInput(attrs={'type': 'date', 'onchange': 'update_datetimes(event);', "onfocus": "this.oldvalue = this.value;"}),
'start_time': TextInput(attrs={'type': 'time', 'onchange': 'update_datetimes(event);', "onfocus": "this.oldvalue = this.value;"}),
'end_day': TextInput(attrs={'type': 'date'}),
'end_time': TextInput(attrs={'type': 'time'}),
}
def __init__(self, instance, *args, **kwargs):
is_authenticated = kwargs.pop('is_authenticated', False)
super().__init__(instance=instance, *args, **kwargs)
if not is_authenticated:
del self.fields['status']
def clean_start_day(self):
start_day = self.cleaned_data.get("start_day")
if start_day is not None and start_day < date.today():
raise ValidationError(_("The start date cannot be earler than today."))
return start_day
def clean_end_day(self):
start_day = self.cleaned_data.get("start_day")
end_day = self.cleaned_data.get("end_day")
if end_day is not None and start_day is not None and end_day < start_day:
raise ValidationError(_("The end date must be after the start date."))
if end_day is not None and end_day < date.today():
raise ValidationError(_("The end date cannot be earler than today."))
return end_day
def clean_end_time(self):
start_day = self.cleaned_data.get("start_day")
end_day = self.cleaned_data.get("end_day")
start_time = self.cleaned_data.get("start_time")
end_time = self.cleaned_data.get("end_time")
# same day
if start_day is not None and (end_day is None or start_day == end_day):
# both start and end time are defined
if start_time is not None and end_time is not None:
if start_time > end_time:
raise ValidationError(_("The end time cannot be earlier than the start time."))
return end_time

View File

@ -235,4 +235,16 @@ msgid "The URL has been submitted and the associated event will be integrated in
msgstr "L'adresse a été soumise et l'événement associé sera prochainement intégré à l'agenda après validation."
msgid "The static content has been successfully updated."
msgstr "Le contenu statique a été modifié avec succès."
msgstr "Le contenu statique a été modifié avec succès."
msgid "The end date must be after the start date."
msgstr "La date de fin doit être après la date de début."
msgid "The end date cannot be earler than today."
msgstr "La date de fin ne peut pas être avant aujourd'hui."
msgid "The start date cannot be earler than today."
msgstr "La date de début ne peut pas être avant aujourd'hui."
msgid "The end time cannot be earlier than the start time."
msgstr "L'heure de fin ne peut pas être avant l'heure de début."

View File

@ -0,0 +1,67 @@
function formatDate(date = new Date()) {
const year = date.toLocaleString('default', {year: 'numeric'});
const month = date.toLocaleString('default', {
month: '2-digit',
});
const day = date.toLocaleString('default', {day: '2-digit'});
return [year, month, day].join('-');
}
function formatTime(date = new Date()) {
const hour = ("0" + date.getHours()).slice(-2);
const minutes = date.toLocaleString('default', {minute: '2-digit'});
return [hour, minutes].join(':');
}
function isValidDate(d) {
return d instanceof Date && !isNaN(d);
}
const update_datetimes = (event) => {
current = event.currentTarget;
start_day = document.getElementById("id_start_day");
end_day = document.getElementById("id_end_day");
start_time = document.getElementById("id_start_time");
end_time = document.getElementById("id_end_time");
if (current == start_day) {
if (end_day.value) {
console.log("update day ", start_day.oldvalue, " -> ", start_day.value);
new_date = new Date(start_day.value);
old_date = new Date(start_day.oldvalue);
end_date = new Date(end_day.value);
end_date = new Date(end_date.getTime() + new_date.getTime() - old_date.getTime());
if (isValidDate(end_date)) {
end_day.value = formatDate(end_date);
}
start_day.oldvalue = start_day.value;
}
}
else {
if (end_day.value && end_time.value && start_day.value) {
console.log("update time", start_time.oldvalue, " -> ", start_time.value);
new_date = new Date(start_day.value + "T" + start_time.value);
old_date = new Date(start_day.value + "T" + start_time.oldvalue);
end_date = new Date(end_day.value + "T" + end_time.value);
end_date = new Date(end_date.getTime() + new_date.getTime() - old_date.getTime());
if (isValidDate(end_date)) {
end_time.value = formatTime(end_date);
}
start_time.oldvalue = start_time.value;
}
}
};

View File

@ -389,4 +389,10 @@ $red-900: #b71c1c !default;
.footer {
opacity: 0.7;
font-size: 80%;
}
}
.errorlist {
@extend .message.danger;
margin-left: 0;
padding-left: 3.7em;
}

View File

@ -1,4 +1,5 @@
{% extends "agenda_culturel/page.html" %}
{% load static %}
{% block title %}Proposer un événement{% endblock %}
@ -6,8 +7,9 @@
<script>window.CKEDITOR_BASEPATH = '/static/ckeditor/ckeditor/';</script>
<script src="/static/admin/js/vendor/jquery/jquery.js"></script>
<script src="/static/admin/js/jquery.init.js"></script>
<link href="/static/css/django_better_admin_arrayfield.min.css" type="text/css" media="all" rel="stylesheet">
<script src="/static/js/django_better_admin_arrayfield.min.js"></script>
<link href="{% static 'css/django_better_admin_arrayfield.min.css' %}" type="text/css" media="all" rel="stylesheet">
<script src="{% static 'js/django_better_admin_arrayfield.min.js' %}"></script>
<script src="{% static 'js/adjust_datetimes.js' %}"></script>
{% endblock %}

View File

@ -1,4 +1,6 @@
{% extends "agenda_culturel/page.html" %}
{% load static %}
{% block title %}Éditer {{ object.title }}{% endblock %}
@ -6,8 +8,9 @@
<script>window.CKEDITOR_BASEPATH = '/static/ckeditor/ckeditor/';</script>
<script src="/static/admin/js/vendor/jquery/jquery.js"></script>
<script src="/static/admin/js/jquery.init.js"></script>
<link href="/static/css/django_better_admin_arrayfield.min.css" type="text/css" media="all" rel="stylesheet">
<script src="/static/js/django_better_admin_arrayfield.min.js"></script>
<link href="{% static 'js/css/django_better_admin_arrayfield.min.css' %}" type="text/css" media="all" rel="stylesheet">
<script src="{% static 'js/js/django_better_admin_arrayfield.min.js' %}"></script>
<script src="{% static 'js/adjust_datetimes.js' %}"></script>
{% endblock %}

View File

@ -7,7 +7,7 @@ from django import forms
from django.contrib.postgres.search import SearchQuery, SearchHeadline
from .forms import EventSubmissionModelForm
from .forms import EventSubmissionModelForm, EventForm
from .celery import create_event_from_submission
from .models import Event, Category, StaticContent
@ -302,6 +302,7 @@ def view_tag(request, t):
context = {"tag": t, "events": events}
return render(request, 'agenda_culturel/tag.html', context)
def tag_list(request):
def remove_accents(input_str):
nfkd_form = unicodedata.normalize('NFKD', input_str)
@ -328,23 +329,6 @@ class StaticContentUpdateView(SuccessMessageMixin, LoginRequiredMixin, UpdateVie
success_message = _('The static content has been successfully updated.')
class EventForm(forms.ModelForm):
class Meta:
model = Event
fields = '__all__'
widgets = {
'start_day': forms.TextInput(attrs={'type': 'date'}),
'start_time': forms.TextInput(attrs={'type': 'time'}),
'end_day': forms.TextInput(attrs={'type': 'date'}),
'end_time': forms.TextInput(attrs={'type': 'time'}),
}
def __init__(self, instance, *args, **kwargs):
is_authenticated = kwargs.pop('is_authenticated', False)
super().__init__(instance=instance, *args, **kwargs)
if not is_authenticated:
del self.fields['status']
class EventCreateView(CreateView):
model = Event
@ -358,6 +342,15 @@ class EventCreateView(CreateView):
return kwargs
def get_initial(self):
initial = super().get_initial()
initial = initial.copy()
initial["start_day"] = date.today() + timedelta(days=1)
initial["start_time"] = "20:00"
initial["end_time"] = "22:00"
return initial
class EventUpdateView(SuccessMessageMixin, LoginRequiredMixin, UpdateView):
@ -366,7 +359,6 @@ class EventUpdateView(SuccessMessageMixin, LoginRequiredMixin, UpdateView):
success_message = _('The event has been successfully modified.')
class EventDeleteView(SuccessMessageMixin, LoginRequiredMixin, DeleteView):
model = Event
success_url = reverse_lazy('view_all_events')