diff --git a/src/agenda_culturel/forms.py b/src/agenda_culturel/forms.py index e8822eb..f7eac7e 100644 --- a/src/agenda_culturel/forms.py +++ b/src/agenda_culturel/forms.py @@ -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 + diff --git a/src/agenda_culturel/locale/fr/LC_MESSAGES/django.po b/src/agenda_culturel/locale/fr/LC_MESSAGES/django.po index 086e390..fbf71b7 100644 --- a/src/agenda_culturel/locale/fr/LC_MESSAGES/django.po +++ b/src/agenda_culturel/locale/fr/LC_MESSAGES/django.po @@ -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." \ No newline at end of file +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." \ No newline at end of file diff --git a/src/agenda_culturel/static/js/adjust_datetimes.js b/src/agenda_culturel/static/js/adjust_datetimes.js new file mode 100644 index 0000000..f6cb46f --- /dev/null +++ b/src/agenda_culturel/static/js/adjust_datetimes.js @@ -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; + + } + } + +}; \ No newline at end of file diff --git a/src/agenda_culturel/static/style.scss b/src/agenda_culturel/static/style.scss index 8d23d97..14157fb 100644 --- a/src/agenda_culturel/static/style.scss +++ b/src/agenda_culturel/static/style.scss @@ -389,4 +389,10 @@ $red-900: #b71c1c !default; .footer { opacity: 0.7; font-size: 80%; -} \ No newline at end of file +} + +.errorlist { + @extend .message.danger; + margin-left: 0; + padding-left: 3.7em; +} diff --git a/src/agenda_culturel/templates/agenda_culturel/event_create_form.html b/src/agenda_culturel/templates/agenda_culturel/event_create_form.html index b50cdfe..6849611 100644 --- a/src/agenda_culturel/templates/agenda_culturel/event_create_form.html +++ b/src/agenda_culturel/templates/agenda_culturel/event_create_form.html @@ -1,4 +1,5 @@ {% extends "agenda_culturel/page.html" %} +{% load static %} {% block title %}Proposer un événement{% endblock %} @@ -6,8 +7,9 @@ - - + + + {% endblock %} diff --git a/src/agenda_culturel/templates/agenda_culturel/event_form.html b/src/agenda_culturel/templates/agenda_culturel/event_form.html index 3422d68..2faee0e 100644 --- a/src/agenda_culturel/templates/agenda_culturel/event_form.html +++ b/src/agenda_culturel/templates/agenda_culturel/event_form.html @@ -1,4 +1,6 @@ {% extends "agenda_culturel/page.html" %} +{% load static %} + {% block title %}Éditer {{ object.title }}{% endblock %} @@ -6,8 +8,9 @@ - - + + + {% endblock %} diff --git a/src/agenda_culturel/views.py b/src/agenda_culturel/views.py index e37997c..3aef350 100644 --- a/src/agenda_culturel/views.py +++ b/src/agenda_culturel/views.py @@ -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')