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')