diff --git a/club/templates/club/club_tools.jinja b/club/templates/club/club_tools.jinja
index 651cc9bb..8361e75e 100644
--- a/club/templates/club/club_tools.jinja
+++ b/club/templates/club/club_tools.jinja
@@ -1,25 +1,61 @@
{% extends "core/base.jinja" %}
+{% from "reservation/macros.jinja" import room_detail %}
+
+{% block additional_css %}
+
+{% endblock %}
{% block content %}
-
{% trans %}Communication:{% endtrans %}
+
{% trans %}Reservable rooms{% endtrans %}
+
+ {% trans %}Add a room{% endtrans %}
+
+ {%- if reservable_rooms|length > 0 -%}
+
+ {%- for room in reservable_rooms -%}
+ {{ room_detail(
+ room,
+ can_edit=user.can_edit(room),
+ can_delete=request.user.has_perm("reservation.delete_room")
+ ) }}
+ {%- endfor -%}
+
+ {%- else -%}
+ {% trans %}This club manages no reservable room{% endtrans %}
+ {%- endif -%}
{% trans %}Counters:{% endtrans %}
- {% for c in object.counters.filter(type="OFFICE") %}
- - {{ c }}:
- View
- Edit
+ {% for counter in counters %}
+
- {{ counter }}:
+ View
+ Edit
{% endfor %}
diff --git a/club/views.py b/club/views.py
index 184e64fd..ff9a866c 100644
--- a/club/views.py
+++ b/club/views.py
@@ -241,6 +241,12 @@ class ClubToolsView(ClubTabsMixin, CanEditMixin, DetailView):
template_name = "club/club_tools.jinja"
current_tab = "tools"
+ def get_context_data(self, **kwargs):
+ return super().get_context_data(**kwargs) | {
+ "reservable_rooms": list(self.object.reservable_rooms.all()),
+ "counters": list(self.object.counters.filter(type="OFFICE")),
+ }
+
class ClubMembersView(ClubTabsMixin, CanViewMixin, DetailFormView):
"""View of a club's members."""
diff --git a/core/static/core/components/card.scss b/core/static/core/components/card.scss
index 941b32a5..c2f0ffa3 100644
--- a/core/static/core/components/card.scss
+++ b/core/static/core/components/card.scss
@@ -16,6 +16,13 @@
}
}
+.card-group {
+ display: flex;
+ gap: 15px;
+ margin-bottom: 30px;
+ flex-wrap: wrap;
+}
+
.card {
background-color: $primary-neutral-light-color;
border-radius: 5px;
@@ -92,13 +99,23 @@
}
@media screen and (max-width: 765px) {
- @include row-layout
+ @include row-layout;
}
// When combined with card, card-row display the card in a row layout,
// whatever the size of the screen.
&.card-row {
- @include row-layout
+ @include row-layout;
+
+ &.card-row-m {
+ //width: 50%;
+ max-width: 50%;
+ }
+
+ &.card-row-s {
+ //width: 33%;
+ max-width: 33%;
+ }
}
}
diff --git a/reservation/forms.py b/reservation/forms.py
new file mode 100644
index 00000000..e9eaf5d0
--- /dev/null
+++ b/reservation/forms.py
@@ -0,0 +1,33 @@
+from django import forms
+
+from club.widgets.ajax_select import AutoCompleteSelectClub
+from core.models import User
+from reservation.models import Room
+
+
+class RoomCreateForm(forms.ModelForm):
+ required_css_class = "required"
+ error_css_class = "error"
+
+ class Meta:
+ model = Room
+ fields = ["name", "club", "location", "description"]
+ widgets = {"club": AutoCompleteSelectClub}
+
+
+class RoomUpdateForm(forms.ModelForm):
+ required_css_class = "required"
+ error_css_class = "error"
+
+ class Meta:
+ model = Room
+ fields = ["name", "club", "location", "description"]
+ widgets = {"club": AutoCompleteSelectClub}
+
+ def __init__(self, *args, request_user: User, **kwargs):
+ super().__init__(*args, **kwargs)
+ if not request_user.has_perm("reservation.change_room"):
+ # if the user doesn't have the global edition permission
+ # (i.e. it's a club board member, but not a sith admin)
+ # some fields aren't editable
+ del self.fields["club"]
diff --git a/reservation/templates/reservation/macros.jinja b/reservation/templates/reservation/macros.jinja
new file mode 100644
index 00000000..fb5ae46d
--- /dev/null
+++ b/reservation/templates/reservation/macros.jinja
@@ -0,0 +1,27 @@
+{% macro room_detail(room, can_edit, can_delete) %}
+
+
+
{{ room.name }}
+
{{ room.get_location_display() }}
+
{{ room.description|truncate(250) }}
+
+
+ {% if can_edit %}
+
+
+
+ {% endif %}
+ {% if can_delete %}
+
+
+
+ {% endif %}
+
+
+{% endmacro %}
\ No newline at end of file
diff --git a/reservation/urls.py b/reservation/urls.py
new file mode 100644
index 00000000..cb2f0564
--- /dev/null
+++ b/reservation/urls.py
@@ -0,0 +1,13 @@
+from django.urls import path
+
+from reservation.views import (
+ RoomCreateView,
+ RoomDeleteView,
+ RoomUpdateView,
+)
+
+urlpatterns = [
+ path("room/create/", RoomCreateView.as_view(), name="room_create"),
+ path("room/
/edit", RoomUpdateView.as_view(), name="room_edit"),
+ path("room//delete", RoomDeleteView.as_view(), name="room_delete"),
+]
diff --git a/reservation/views.py b/reservation/views.py
index 60f00ef0..47c1697b 100644
--- a/reservation/views.py
+++ b/reservation/views.py
@@ -1 +1,51 @@
# Create your views here.
+
+from django.contrib.auth.mixins import PermissionRequiredMixin
+from django.contrib.messages.views import SuccessMessageMixin
+from django.urls import reverse_lazy
+from django.utils.translation import gettext_lazy as _
+from django.views.generic import CreateView, DeleteView, TemplateView, UpdateView
+
+from club.models import Club
+from core.auth.mixins import CanEditMixin
+from core.views import UseFragmentsMixin
+from core.views.mixins import FragmentMixin
+from reservation.forms import ReservationForm, RoomCreateForm, RoomUpdateForm
+from reservation.models import ReservationSlot, Room
+
+
+class RoomCreateView(SuccessMessageMixin, PermissionRequiredMixin, CreateView):
+ form_class = RoomCreateForm
+ template_name = "core/create.jinja"
+ success_message = _("%(name)s was created successfully")
+ permission_required = "reservation.add_room"
+
+ def get_initial(self):
+ init = super().get_initial()
+ if "club" in self.request.GET:
+ club_id = self.request.GET["club"]
+ if club_id.isdigit() and int(club_id) > 0:
+ init["club"] = Club.objects.filter(id=int(club_id)).first()
+ return init
+
+
+class RoomUpdateView(SuccessMessageMixin, CanEditMixin, UpdateView):
+ model = Room
+ pk_url_kwarg = "room_id"
+ form_class = RoomUpdateForm
+ template_name = "core/edit.jinja"
+ success_message = _("%(name)s was updated successfully")
+
+ def get_form_kwargs(self):
+ return super().get_form_kwargs() | {"request_user": self.request.user}
+
+ def get_success_url(self):
+ return self.request.path
+
+
+class RoomDeleteView(PermissionRequiredMixin, DeleteView):
+ model = Room
+ pk_url_kwarg = "room_id"
+ template_name = "core/delete_confirm.jinja"
+ success_url = reverse_lazy("reservation:room_list")
+ permission_required = "reservation.delete_room"
diff --git a/sith/settings.py b/sith/settings.py
index 39a88b7e..e6c8f790 100644
--- a/sith/settings.py
+++ b/sith/settings.py
@@ -274,7 +274,7 @@ LOGGING = {
# Internationalization
# https://docs.djangoproject.com/en/1.8/topics/i18n/
-LANGUAGE_CODE = "fr-FR"
+LANGUAGE_CODE = "fr"
LANGUAGES = [("en", _("English")), ("fr", _("French"))]
diff --git a/sith/urls.py b/sith/urls.py
index dd560626..098d0053 100644
--- a/sith/urls.py
+++ b/sith/urls.py
@@ -45,6 +45,10 @@ urlpatterns = [
path("trombi/", include(("trombi.urls", "trombi"), namespace="trombi")),
path("matmatronch/", include(("matmat.urls", "matmat"), namespace="matmat")),
path("pedagogy/", include(("pedagogy.urls", "pedagogy"), namespace="pedagogy")),
+ path(
+ "reservation/",
+ include(("reservation.urls", "reservation"), namespace="reservation"),
+ ),
path("admin/", admin.site.urls),
path("i18n/", include("django.conf.urls.i18n")),
path("jsi18n/", JavaScriptCatalog.as_view(), name="javascript-catalog"),