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:
parent
63e9c57e87
commit
971f8aad32
@ -1,11 +1,67 @@
|
|||||||
from django.forms import ModelForm
|
from django.forms import ModelForm, ValidationError, TextInput
|
||||||
from django.views.generic import FormView
|
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 EventSubmissionModelForm(ModelForm):
|
||||||
class Meta:
|
class Meta:
|
||||||
model = EventSubmissionForm
|
model = EventSubmissionForm
|
||||||
fields = ["url"]
|
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
|
||||||
|
|
||||||
|
@ -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."
|
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."
|
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."
|
67
src/agenda_culturel/static/js/adjust_datetimes.js
Normal file
67
src/agenda_culturel/static/js/adjust_datetimes.js
Normal 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;
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
};
|
@ -389,4 +389,10 @@ $red-900: #b71c1c !default;
|
|||||||
.footer {
|
.footer {
|
||||||
opacity: 0.7;
|
opacity: 0.7;
|
||||||
font-size: 80%;
|
font-size: 80%;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.errorlist {
|
||||||
|
@extend .message.danger;
|
||||||
|
margin-left: 0;
|
||||||
|
padding-left: 3.7em;
|
||||||
|
}
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
{% extends "agenda_culturel/page.html" %}
|
{% extends "agenda_culturel/page.html" %}
|
||||||
|
{% load static %}
|
||||||
|
|
||||||
{% block title %}Proposer un événement{% endblock %}
|
{% block title %}Proposer un événement{% endblock %}
|
||||||
|
|
||||||
@ -6,8 +7,9 @@
|
|||||||
<script>window.CKEDITOR_BASEPATH = '/static/ckeditor/ckeditor/';</script>
|
<script>window.CKEDITOR_BASEPATH = '/static/ckeditor/ckeditor/';</script>
|
||||||
<script src="/static/admin/js/vendor/jquery/jquery.js"></script>
|
<script src="/static/admin/js/vendor/jquery/jquery.js"></script>
|
||||||
<script src="/static/admin/js/jquery.init.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">
|
<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/django_better_admin_arrayfield.min.js' %}"></script>
|
||||||
|
<script src="{% static 'js/adjust_datetimes.js' %}"></script>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
|
|
||||||
|
@ -1,4 +1,6 @@
|
|||||||
{% extends "agenda_culturel/page.html" %}
|
{% extends "agenda_culturel/page.html" %}
|
||||||
|
{% load static %}
|
||||||
|
|
||||||
|
|
||||||
{% block title %}Éditer {{ object.title }}{% endblock %}
|
{% block title %}Éditer {{ object.title }}{% endblock %}
|
||||||
|
|
||||||
@ -6,8 +8,9 @@
|
|||||||
<script>window.CKEDITOR_BASEPATH = '/static/ckeditor/ckeditor/';</script>
|
<script>window.CKEDITOR_BASEPATH = '/static/ckeditor/ckeditor/';</script>
|
||||||
<script src="/static/admin/js/vendor/jquery/jquery.js"></script>
|
<script src="/static/admin/js/vendor/jquery/jquery.js"></script>
|
||||||
<script src="/static/admin/js/jquery.init.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">
|
<link href="{% static 'js/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/js/django_better_admin_arrayfield.min.js' %}"></script>
|
||||||
|
<script src="{% static 'js/adjust_datetimes.js' %}"></script>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
|
|
||||||
|
@ -7,7 +7,7 @@ from django import forms
|
|||||||
from django.contrib.postgres.search import SearchQuery, SearchHeadline
|
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 .celery import create_event_from_submission
|
||||||
|
|
||||||
from .models import Event, Category, StaticContent
|
from .models import Event, Category, StaticContent
|
||||||
@ -302,6 +302,7 @@ def view_tag(request, t):
|
|||||||
context = {"tag": t, "events": events}
|
context = {"tag": t, "events": events}
|
||||||
return render(request, 'agenda_culturel/tag.html', context)
|
return render(request, 'agenda_culturel/tag.html', context)
|
||||||
|
|
||||||
|
|
||||||
def tag_list(request):
|
def tag_list(request):
|
||||||
def remove_accents(input_str):
|
def remove_accents(input_str):
|
||||||
nfkd_form = unicodedata.normalize('NFKD', 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.')
|
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):
|
class EventCreateView(CreateView):
|
||||||
model = Event
|
model = Event
|
||||||
@ -358,6 +342,15 @@ class EventCreateView(CreateView):
|
|||||||
return kwargs
|
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):
|
class EventUpdateView(SuccessMessageMixin, LoginRequiredMixin, UpdateView):
|
||||||
@ -366,7 +359,6 @@ class EventUpdateView(SuccessMessageMixin, LoginRequiredMixin, UpdateView):
|
|||||||
success_message = _('The event has been successfully modified.')
|
success_message = _('The event has been successfully modified.')
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class EventDeleteView(SuccessMessageMixin, LoginRequiredMixin, DeleteView):
|
class EventDeleteView(SuccessMessageMixin, LoginRequiredMixin, DeleteView):
|
||||||
model = Event
|
model = Event
|
||||||
success_url = reverse_lazy('view_all_events')
|
success_url = reverse_lazy('view_all_events')
|
||||||
|
Loading…
Reference in New Issue
Block a user