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 %}Club tools{% endtrans %}

+

{% trans %}Club tools{% endtrans %} ({{ club.name }})

{% trans %}Communication:{% endtrans %}

+

{% trans %}Reservable rooms{% endtrans %}

+ + {% trans %}Add a room{% endtrans %} + + {%- if reservable_rooms|length > 0 -%} + + {%- else -%} + {% trans %}This club manages no reservable room{% endtrans %} + {%- endif -%}

{% trans %}Counters:{% endtrans %}

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"),