diff --git a/.python-version b/.python-version
index 3a4f41ef..fdcfcfdf 100644
--- a/.python-version
+++ b/.python-version
@@ -1 +1 @@
-3.13
\ No newline at end of file
+3.12
\ No newline at end of file
diff --git a/com/api.py b/com/api.py
index 9dd70606..79ff9c34 100644
--- a/com/api.py
+++ b/com/api.py
@@ -1,6 +1,7 @@
from typing import Literal
from django.http import HttpResponse
+from django.utils.cache import add_never_cache_headers
from ninja import Query
from ninja_extra import ControllerBase, api_controller, paginate, route
from ninja_extra.pagination import PageNumberPaginationExtra
@@ -18,7 +19,9 @@ from core.views.files import send_raw_file
class CalendarController(ControllerBase):
@route.get("/internal.ics", url_name="calendar_internal")
def calendar_internal(self):
- return send_raw_file(IcsCalendar.get_internal())
+ response = send_raw_file(IcsCalendar.get_internal())
+ add_never_cache_headers(response)
+ return response
@route.get(
"/unpublished.ics",
@@ -26,10 +29,12 @@ class CalendarController(ControllerBase):
url_name="calendar_unpublished",
)
def calendar_unpublished(self):
- return HttpResponse(
+ response = HttpResponse(
IcsCalendar.get_unpublished(self.context.request.user),
content_type="text/calendar",
)
+ add_never_cache_headers(response)
+ return response
@api_controller("/news")
diff --git a/core/models.py b/core/models.py
index ce4b2102..b71f5408 100644
--- a/core/models.py
+++ b/core/models.py
@@ -341,8 +341,8 @@ class User(AbstractUser):
return reverse("core:user_profile", kwargs={"user_id": self.pk})
def promo_has_logo(self) -> bool:
- return Path(
- settings.BASE_DIR / f"core/static/core/img/promo_{self.promo}.png"
+ return (
+ settings.BASE_DIR / f"core/static/core/img/promo_{self.promo:02d}.png"
).exists()
@cached_property
diff --git a/core/static/bundled/core/accordion-index.ts b/core/static/bundled/core/accordion-index.ts
new file mode 100644
index 00000000..39e37564
--- /dev/null
+++ b/core/static/bundled/core/accordion-index.ts
@@ -0,0 +1,25 @@
+const setMaxHeight = (element: HTMLDetailsElement) => {
+ element.setAttribute("style", `max-height: ${element.scrollHeight}px`);
+};
+
+// Initialize max-height at load
+window.addEventListener("DOMContentLoaded", () => {
+ for (const el of document.querySelectorAll("details.accordion")) {
+ setMaxHeight(el as HTMLDetailsElement);
+ }
+});
+
+// Accordion opened
+new MutationObserver((mutations: MutationRecord[]) => {
+ for (const mutation of mutations) {
+ const target = mutation.target as HTMLDetailsElement;
+ if (target.tagName !== "DETAILS" || !target.classList.contains("accordion")) {
+ continue;
+ }
+ setMaxHeight(target);
+ }
+}).observe(document.body, {
+ attributes: true,
+ attributeFilter: ["open"],
+ subtree: true,
+});
diff --git a/core/static/core/accordion.scss b/core/static/core/accordion.scss
new file mode 100644
index 00000000..07793a7a
--- /dev/null
+++ b/core/static/core/accordion.scss
@@ -0,0 +1,55 @@
+details.accordion>summary {
+ margin: 2px 0 0 0;
+ padding: .5em .5em .5em .7em;
+ cursor: pointer;
+ user-select: none;
+ display: block;
+
+ border-top-right-radius: 3px;
+ border-top-left-radius: 3px;
+}
+
+details[open].accordion>summary {
+ border: 1px solid #003eff;
+ background: #007fff;
+ color: #ffffff;
+}
+
+
+details:not([open]).accordion>summary {
+ border-bottom-right-radius: 3px;
+ border-bottom-left-radius: 3px;
+
+ border: 1px solid #c5c5c5;
+ background: #f6f6f6;
+ color: #454545;
+}
+
+details.accordion>summary::before {
+ font-family: FontAwesome;
+ content: '\f0da';
+ margin-right: 5px;
+ transition: 700ms;
+ font-size: 0.8em;
+}
+
+details[open]>summary::before {
+ font-family: FontAwesome;
+ content: '\f0d7';
+}
+
+// ::details-content isn't available on firefox yet
+// we use .accordion-content as a workaround
+details.accordion>.accordion-content {
+ background: #ffffff;
+ color: #333333;
+ padding: 1em 2.2em;
+ overflow: auto;
+ border: 1px solid #dddddd;
+ border-bottom-right-radius: 3px;
+ border-bottom-left-radius: 3px;
+}
+
+details.accordion {
+ transition: max-height 300ms ease-in-out;
+}
\ No newline at end of file
diff --git a/core/static/core/navbar.scss b/core/static/core/navbar.scss
index 9c9bf4df..9b77bf77 100644
--- a/core/static/core/navbar.scss
+++ b/core/static/core/navbar.scss
@@ -1,5 +1,7 @@
@import "colors";
+$desktop-size: 500px;
+
nav.navbar {
background-color: $primary-dark-color;
margin: 1em;
@@ -7,15 +9,24 @@ nav.navbar {
border-radius: 0.6em;
min-height: 40px;
- @media (max-width: 500px) {
+ @media (max-width: $desktop-size) {
position: relative;
flex-direction: column;
align-items: flex-start;
gap: 0;
margin: .2em;
+
+ >.content[mobile-display="hidden"] {
+ display: none;
+ }
+
+ >.content[mobile-display="revealed"] {
+ display: block;
+ }
}
- > .expand-button {
+
+ >.expand-button {
background-color: transparent;
display: none;
position: relative;
@@ -27,27 +38,27 @@ nav.navbar {
align-items: center;
margin: 0;
- > i {
+ >i {
font-size: 1.5em;
color: white;
}
- @media (max-width: 500px) {
+ @media (max-width: $desktop-size) {
display: flex;
}
}
- > .content {
- @media (min-width: 500px) {display: flex;
+ >.content {
+ @media (min-width: $desktop-size) {
flex-direction: row;
flex-wrap: wrap;
align-items: center;
justify-content: center;
- display: flex !important;
+ display: flex;
}
- > .menu,
- > .link {
+ >.menu,
+ >.link {
box-sizing: border-box;
width: 130px;
height: 52px;
@@ -56,7 +67,7 @@ nav.navbar {
justify-content: center;
cursor: pointer;
- @media (max-width: 500px) {
+ @media (max-width: $desktop-size) {
width: 100%;
height: auto;
justify-content: flex-start;
@@ -64,80 +75,75 @@ nav.navbar {
&:last-child {
border-radius: 0 0 .6em .6em;
- > .content {
+ >.content {
box-shadow: 3px 3px 3px 0 #dfdfdf;
}
}
}
}
- > .menu > .head,
- > .link {
+ >.menu>.head,
+ >.link {
color: white;
- padding: 10px 20px;
- box-sizing: border-box;
+ }
- @media (max-width: 500px) {
+ >.menu>summary,
+ >.link {
+ @media (max-width: $desktop-size) {
padding: 10px;
}
}
+ >.link {
+ padding: 10px 20px;
+ box-sizing: border-box;
+ }
+
+
.link:hover,
.menu:hover {
background-color: rgba(0, 0, 0, .2);
}
- > .menu > .head,
- > .link {
- color: white;
- padding: 10px 20px;
- box-sizing: border-box;
+ details.menu {
+ cursor: pointer;
+ user-select: none;
+ z-index: 10;
+ align-items: center;
+ display: inline-block;
- @media (max-width: 500px) {
- padding: 10px;
+ summary {
+ list-style: none;
+
+ display: flex;
+ align-items: center;
+ height: 100%;
+ padding-left: 20px;
+ padding-right: 20px;
+
+ @media (min-width: $desktop-size) {
+ justify-content: center;
+ }
}
- }
- .link:hover,
- .menu:hover {
- background-color: rgba(0, 0, 0, .2);
- }
-
- > .menu:hover > .content,
- > .menu > .head:hover + .content,
- > .menu > .content:hover {
- display: flex;
- }
-
- > .menu {
- display: flex;
- position: relative;
-
- > .content {
- z-index: 10;
+ summary::-webkit-details-marker {
display: none;
- position: absolute;
- top: 100%;
- background-color: white;
- margin: 0;
- list-style-type: none;
- width: 130px;
- box-shadow: 3px 3px 3px 0 #dfdfdf;
- flex-direction: column;
+ }
- @media (max-width: 500px) {
- position: absolute;
- flex-direction: row;
- flex-wrap: wrap;
- width: 100%;
- box-shadow: inset 3px 3px 3px 0 #dfdfdf;
+ ul.content {
+ list-style-type: none;
+ background: white;
+ margin: 0;
+
+ @media (min-width: $desktop-size) {
+ box-shadow: 3px 3px 3px 0 #dfdfdf;
}
- > li > a {
+ >li>a {
display: flex;
padding: 15px 20px;
- @media (max-width: 500px) {
+ @media (max-width: $desktop-size) {
padding: 10px;
}
diff --git a/core/templates/core/base.jinja b/core/templates/core/base.jinja
index 6479e833..0d476689 100644
--- a/core/templates/core/base.jinja
+++ b/core/templates/core/base.jinja
@@ -12,6 +12,7 @@
+
{% block jquery_css %}
{# Thile file is quite heavy (around 250kb), so declaring it in a block allows easy removal #}
@@ -26,6 +27,7 @@
+
@@ -122,10 +124,27 @@
{% block script %}
-{% endblock %}
-
-
diff --git a/core/templates/core/user_detail.jinja b/core/templates/core/user_detail.jinja
index 4ad1f5d9..57eb3bf7 100644
--- a/core/templates/core/user_detail.jinja
+++ b/core/templates/core/user_detail.jinja
@@ -254,13 +254,5 @@
keys.shift();
}
});
-
- $(function () {
- $("#drop_gifts").accordion({
- heightStyle: "content",
- collapsible: true,
- active: false
- });
- });
{% endblock %}
\ No newline at end of file
diff --git a/core/tests/test_user.py b/core/tests/test_user.py
index e19c98f4..5d4cb734 100644
--- a/core/tests/test_user.py
+++ b/core/tests/test_user.py
@@ -335,3 +335,10 @@ class TestRedirectMe:
def test_anonymous_user(self, client: Client):
url = reverse("core:user_me_redirect")
assertRedirects(client.get(url), reverse("core:login", query={"next": url}))
+
+
+@pytest.mark.parametrize("promo", [7, 22])
+@pytest.mark.django_db
+def test_promo_has_logo(promo):
+ user = baker.make(User, promo=promo)
+ assert user.promo_has_logo()
diff --git a/counter/static/bundled/counter/counter-click-index.ts b/counter/static/bundled/counter/counter-click-index.ts
index 5b593532..296c6003 100644
--- a/counter/static/bundled/counter/counter-click-index.ts
+++ b/counter/static/bundled/counter/counter-click-index.ts
@@ -103,7 +103,7 @@ document.addEventListener("alpine:init", () => {
this.customerBalance += Number.parseFloat(
(event.detail.target.querySelector("#id_amount") as HTMLInputElement).value,
);
- document.getElementById("selling-accordion").click();
+ document.getElementById("selling-accordion").setAttribute("open", "");
this.codeField.widget.focus();
},
@@ -139,12 +139,6 @@ document.addEventListener("alpine:init", () => {
});
$(() => {
- /* Accordion UI between basket and refills */
- // biome-ignore lint/suspicious/noExplicitAny: dealing with legacy jquery
- ($("#click-form") as any).accordion({
- heightStyle: "content",
- activate: () => $(".focus").focus(),
- });
// biome-ignore lint/suspicious/noExplicitAny: dealing with legacy jquery
($("#products") as any).tabs();
});
diff --git a/counter/templates/counter/counter_click.jinja b/counter/templates/counter/counter_click.jinja
index 0054f35d..24e1c6ae 100644
--- a/counter/templates/counter/counter_click.jinja
+++ b/counter/templates/counter/counter_click.jinja
@@ -51,185 +51,192 @@
- {% if object.type == "BAR" %}
- {% trans %}Refilling{% endtrans %}
- {% if refilling_fragment %}
-
- {{ refilling_fragment }}
+
+
+ {% if not products %}
+
+ {% trans %}No products available on this counter for this user{% endtrans %}
{% else %}
-
-
- {% trans trimmed %}
- As a barman, you are not able to refill any account on your own.
- An admin should be connected on this counter for that.
- The customer can refill by using the eboutic.
- {% endtrans %}
-
-
- {% endif %}
- {% if student_card_fragment %}
-
{% trans %}Student card{% endtrans %}
-
- {{ student_card_fragment }}
-
- {% endif %}
-
- {% endif %}
-
-
-
- {% if not products %}
-
- {% trans %}No products available on this counter for this user{% endtrans %}
-
- {% else %}
-
+
+ {% for category in categories.keys() -%}
+ {{ category }}
+ {%- endfor %}
+
{% for category in categories.keys() -%}
- {{ category }}
- {%- endfor %}
-
- {% for category in categories.keys() -%}
-
-
{{ category }}
-
- {% for product in categories[category] -%}
-
-
-
- {{ product.name }}
- {{ product.price }} € {{ product.code }}
-
-
- {%- endfor %}
+
+
{{ category }}
+
+ {% for product in categories[category] -%}
+
+
+
+ {{ product.name }}
+ {{ product.price }} € {{ product.code }}
+
+
+ {%- endfor %}
+
-
- {%- endfor %}
- {% endif %}
+ {%- endfor %}
+ {% endif %}
+
-
{% endblock content %}
{% block script %}
diff --git a/docs/tutorial/structure.md b/docs/tutorial/structure.md
index b687d25b..1421cddc 100644
--- a/docs/tutorial/structure.md
+++ b/docs/tutorial/structure.md
@@ -42,46 +42,44 @@ sith/
│ └── ...
├── galaxy/ (11)
│ └── ...
-├── launderette/ (12)
+├── locale/ (12)
│ └── ...
-├── locale/ (13)
+├── matmat/ (13)
│ └── ...
-├── matmat/ (14)
+├── pedagogy/ (14)
│ └── ...
-├── pedagogy/ (15)
+├── rootplace/ (15)
│ └── ...
-├── rootplace/ (16)
+├── sas/ (16)
│ └── ...
-├── sas/ (17)
+├── sith/ (17)
│ └── ...
-├── sith/ (18)
+├── subscription/ (18)
│ └── ...
-├── subscription/ (19)
+├── trombi/ (19)
│ └── ...
-├── trombi/ (20)
+├── antispam/ (20)
│ └── ...
-├── antispam/ (21)
+├── staticfiles/ (21)
│ └── ...
-├── staticfiles/ (22)
-│ └── ...
-├── processes/ (23)
+├── processes/ (22)
│ └── ...
│
-├── .coveragerc (24)
-├── .envrc (25)
+├── .coveragerc (23)
+├── .envrc (24)
├── .gitattributes
├── .gitignore
├── .mailmap
-├── .env (26)
-├── .env.example (27)
-├── manage.py (28)
-├── mkdocs.yml (29)
+├── .env (25)
+├── .env.example (26)
+├── manage.py (27)
+├── mkdocs.yml (28)
├── uv.lock
-├── pyproject.toml (30)
-├── .venv/ (31)
-├── .python-version (32)
-├── Procfile.static (33)
-├── Procfile.service (34)
+├── pyproject.toml (29)
+├── .venv/ (30)
+├── .python-version (31)
+├── Procfile.static (32)
+├── Procfile.service (33)
└── README.md
```
@@ -108,40 +106,39 @@ sith/
11. Application de gestion de la galaxie ; la galaxie
est un graphe des niveaux de proximité entre les différents
étudiants.
-12. Gestion des machines à laver de l'AE
-13. Dossier contenant les fichiers de traduction.
-14. Fonctionnalités de recherche d'utilisateurs.
-15. Le guide des UEs du site, sur lequel les utilisateurs
+12. Dossier contenant les fichiers de traduction.
+13. Fonctionnalités de recherche d'utilisateurs.
+14. Le guide des UEs du site, sur lequel les utilisateurs
peuvent également laisser leurs avis.
-16. Fonctionnalités utiles aux utilisateurs root.
-17. Le SAS, où l'on trouve toutes les photos de l'AE.
-18. Application principale du projet, contenant sa configuration.
-19. Gestion des cotisations des utilisateurs du site.
-20. Outil pour faciliter la fabrication des trombinoscopes de promo.
-21. Fonctionnalités pour gérer le spam.
-22. Gestion des statics du site. Override le système de statics de Django.
+15. Fonctionnalités utiles aux utilisateurs root.
+16. Le SAS, où l'on trouve toutes les photos de l'AE.
+17. Application principale du projet, contenant sa configuration.
+18. Gestion des cotisations des utilisateurs du site.
+19. Outil pour faciliter la fabrication des trombinoscopes de promo.
+20. Fonctionnalités pour gérer le spam.
+21. Gestion des statics du site. Override le système de statics de Django.
Ajoute l'intégration du scss et du bundler js
de manière transparente pour l'utilisateur.
-23. Module de gestion des services externes.
+22. Module de gestion des services externes.
Offre une API simple pour utiliser les fichiers `Procfile.*`.
-24. Fichier de configuration de coverage.
-25. Fichier de configuration de direnv.
-26. Contient les variables d'environnement, qui sont susceptibles
+23. Fichier de configuration de coverage.
+24. Fichier de configuration de direnv.
+25. Contient les variables d'environnement, qui sont susceptibles
de varier d'une machine à l'autre.
-27. Contient des valeurs par défaut pour le `.env`
+26. Contient des valeurs par défaut pour le `.env`
pouvant convenir à un environnment de développement local
-28. Fichier généré automatiquement par Django. C'est lui
+27. Fichier généré automatiquement par Django. C'est lui
qui permet d'appeler des commandes de gestion du projet
avec la syntaxe `python ./manage.py `
-29. Le fichier de configuration de la documentation,
+28. Le fichier de configuration de la documentation,
avec ses plugins et sa table des matières.
-30. Le fichier où sont déclarés les dépendances et la configuration
+29. Le fichier où sont déclarés les dépendances et la configuration
de certaines d'entre elles.
-31. Dossier d'environnement virtuel généré par uv
-32. Fichier qui contrôle quelle version de python utiliser pour le projet
-33. Fichier qui contrôle les commandes à lancer pour gérer la compilation
+30. Dossier d'environnement virtuel généré par uv
+31. Fichier qui contrôle quelle version de python utiliser pour le projet
+32. Fichier qui contrôle les commandes à lancer pour gérer la compilation
automatique des static et autres services nécessaires à la command runserver.
-34. Fichier qui contrôle les services tiers nécessaires au fonctionnement
+33. Fichier qui contrôle les services tiers nécessaires au fonctionnement
du Sith tel que redis.
## L'application principale
diff --git a/launderette/__init__.py b/launderette/__init__.py
deleted file mode 100644
index f4445e69..00000000
--- a/launderette/__init__.py
+++ /dev/null
@@ -1,14 +0,0 @@
-#
-# Copyright 2023 © AE UTBM
-# ae@utbm.fr / ae.info@utbm.fr
-#
-# This file is part of the website of the UTBM Student Association (AE UTBM),
-# https://ae.utbm.fr.
-#
-# You can find the source code of the website at https://github.com/ae-utbm/sith
-#
-# LICENSED UNDER THE GNU GENERAL PUBLIC LICENSE VERSION 3 (GPLv3)
-# SEE : https://raw.githubusercontent.com/ae-utbm/sith/master/LICENSE
-# OR WITHIN THE LOCAL FILE "LICENSE"
-#
-#
diff --git a/launderette/migrations/0001_initial.py b/launderette/migrations/0001_initial.py
deleted file mode 100644
index 2c1d70d1..00000000
--- a/launderette/migrations/0001_initial.py
+++ /dev/null
@@ -1,181 +0,0 @@
-from __future__ import unicode_literals
-
-import django.db.models.deletion
-from django.db import migrations, models
-
-
-class Migration(migrations.Migration):
- dependencies = [("subscription", "0001_initial"), ("counter", "0001_initial")]
-
- operations = [
- migrations.CreateModel(
- name="Launderette",
- fields=[
- (
- "id",
- models.AutoField(
- primary_key=True,
- serialize=False,
- verbose_name="ID",
- auto_created=True,
- ),
- ),
- ("name", models.CharField(max_length=30, verbose_name="name")),
- (
- "counter",
- models.OneToOneField(
- on_delete=django.db.models.deletion.CASCADE,
- related_name="launderette",
- verbose_name="counter",
- to="counter.Counter",
- ),
- ),
- ],
- options={"verbose_name": "Launderette"},
- ),
- migrations.CreateModel(
- name="Machine",
- fields=[
- (
- "id",
- models.AutoField(
- primary_key=True,
- serialize=False,
- verbose_name="ID",
- auto_created=True,
- ),
- ),
- ("name", models.CharField(max_length=30, verbose_name="name")),
- (
- "type",
- models.CharField(
- choices=[("WASHING", "Washing"), ("DRYING", "Drying")],
- max_length=10,
- verbose_name="type",
- ),
- ),
- (
- "is_working",
- models.BooleanField(verbose_name="is working", default=True),
- ),
- (
- "launderette",
- models.ForeignKey(
- on_delete=django.db.models.deletion.CASCADE,
- verbose_name="launderette",
- to="launderette.Launderette",
- related_name="machines",
- ),
- ),
- ],
- options={"verbose_name": "Machine"},
- ),
- migrations.CreateModel(
- name="Slot",
- fields=[
- (
- "id",
- models.AutoField(
- primary_key=True,
- serialize=False,
- verbose_name="ID",
- auto_created=True,
- ),
- ),
- ("start_date", models.DateTimeField(verbose_name="start date")),
- (
- "type",
- models.CharField(
- choices=[("WASHING", "Washing"), ("DRYING", "Drying")],
- max_length=10,
- verbose_name="type",
- ),
- ),
- (
- "machine",
- models.ForeignKey(
- on_delete=django.db.models.deletion.CASCADE,
- verbose_name="machine",
- to="launderette.Machine",
- related_name="slots",
- ),
- ),
- ],
- options={"verbose_name": "Slot", "ordering": ["start_date"]},
- ),
- migrations.CreateModel(
- name="Token",
- fields=[
- (
- "id",
- models.AutoField(
- primary_key=True,
- serialize=False,
- verbose_name="ID",
- auto_created=True,
- ),
- ),
- ("name", models.CharField(max_length=5, verbose_name="name")),
- (
- "type",
- models.CharField(
- choices=[("WASHING", "Washing"), ("DRYING", "Drying")],
- max_length=10,
- verbose_name="type",
- ),
- ),
- (
- "borrow_date",
- models.DateTimeField(
- null=True, verbose_name="borrow date", blank=True
- ),
- ),
- (
- "launderette",
- models.ForeignKey(
- on_delete=django.db.models.deletion.CASCADE,
- verbose_name="launderette",
- to="launderette.Launderette",
- related_name="tokens",
- ),
- ),
- (
- "user",
- models.ForeignKey(
- on_delete=django.db.models.deletion.CASCADE,
- null=True,
- related_name="tokens",
- verbose_name="user",
- to="core.User",
- blank=True,
- ),
- ),
- ],
- options={"verbose_name": "Token", "ordering": ["type", "name"]},
- ),
- migrations.AddField(
- model_name="slot",
- name="token",
- field=models.ForeignKey(
- on_delete=django.db.models.deletion.CASCADE,
- null=True,
- related_name="slots",
- verbose_name="token",
- to="launderette.Token",
- blank=True,
- ),
- ),
- migrations.AddField(
- model_name="slot",
- name="user",
- field=models.ForeignKey(
- on_delete=django.db.models.deletion.CASCADE,
- verbose_name="user",
- to="core.User",
- related_name="slots",
- ),
- ),
- migrations.AlterUniqueTogether(
- name="token", unique_together={("name", "launderette", "type")}
- ),
- ]
diff --git a/launderette/migrations/0002_remove_token_launderette_remove_machine_launderette_and_more.py b/launderette/migrations/0002_remove_token_launderette_remove_machine_launderette_and_more.py
deleted file mode 100644
index cb63fa26..00000000
--- a/launderette/migrations/0002_remove_token_launderette_remove_machine_launderette_and_more.py
+++ /dev/null
@@ -1,14 +0,0 @@
-# Generated by Django 5.2 on 2025-04-15 19:37
-
-from django.db import migrations
-
-
-class Migration(migrations.Migration):
- dependencies = [("launderette", "0001_initial")]
-
- operations = [
- migrations.DeleteModel(name="Launderette"),
- migrations.DeleteModel(name="Machine"),
- migrations.DeleteModel(name="Slot"),
- migrations.DeleteModel(name="Token"),
- ]
diff --git a/launderette/migrations/__init__.py b/launderette/migrations/__init__.py
deleted file mode 100644
index e69de29b..00000000
diff --git a/locale/fr/LC_MESSAGES/django.po b/locale/fr/LC_MESSAGES/django.po
index b76ba3e3..c7c4d34f 100644
--- a/locale/fr/LC_MESSAGES/django.po
+++ b/locale/fr/LC_MESSAGES/django.po
@@ -6,7 +6,7 @@
msgid ""
msgstr ""
"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2025-04-15 23:39+0200\n"
+"POT-Creation-Date: 2025-06-04 09:58+0200\n"
"PO-Revision-Date: 2016-07-18\n"
"Last-Translator: Maréchal \n"
@@ -55,8 +55,7 @@ msgstr "S'abonner"
msgid "Remove"
msgstr "Retirer"
-#: club/forms.py launderette/views.py
-#: pedagogy/templates/pedagogy/moderation.jinja
+#: club/forms.py pedagogy/templates/pedagogy/moderation.jinja
msgid "Action"
msgstr "Action"
@@ -121,7 +120,6 @@ msgid "You do not have the permission to do that"
msgstr "Vous n'avez pas la permission de faire cela"
#: club/models.py com/models.py counter/models.py forum/models.py
-#: launderette/models.py
msgid "name"
msgstr "nom"
@@ -162,7 +160,7 @@ msgid "You can not make loops in clubs"
msgstr "Vous ne pouvez pas faire de boucles dans les clubs"
#: club/models.py core/models.py counter/models.py eboutic/models.py
-#: election/models.py launderette/models.py sas/models.py trombi/models.py
+#: election/models.py sas/models.py trombi/models.py
msgid "user"
msgstr "utilisateur"
@@ -170,7 +168,7 @@ msgstr "utilisateur"
msgid "club"
msgstr "club"
-#: club/models.py counter/models.py election/models.py launderette/models.py
+#: club/models.py counter/models.py election/models.py
msgid "start date"
msgstr "date de début"
@@ -231,7 +229,6 @@ msgstr "Liste de diffusion"
#: club/templates/club/club_old_members.jinja club/templates/club/mailing.jinja
#: counter/templates/counter/cash_summary_list.jinja
#: counter/templates/counter/stats.jinja
-#: launderette/templates/launderette/launderette_admin.jinja
msgid "User"
msgstr "Utilisateur"
@@ -283,7 +280,6 @@ msgid "Description"
msgstr "Description"
#: club/templates/club/club_members.jinja core/templates/core/user_clubs.jinja
-#: launderette/templates/launderette/launderette_admin.jinja
#: rootplace/templates/rootplace/userban.jinja
msgid "Since"
msgstr "Depuis"
@@ -293,8 +289,7 @@ msgid "There are no members in this club."
msgstr "Il n'y a pas de membres dans ce club."
#: club/templates/club/club_members.jinja core/templates/core/file_detail.jinja
-#: core/views/forms.py launderette/views.py
-#: trombi/templates/trombi/detail.jinja
+#: core/views/forms.py trombi/templates/trombi/detail.jinja
msgid "Add"
msgstr "Ajouter"
@@ -425,9 +420,7 @@ msgstr "Méthode de paiement"
#: counter/templates/counter/fragments/create_student_card.jinja
#: counter/templates/counter/last_ops.jinja
#: election/templates/election/election_detail.jinja
-#: forum/templates/forum/macros.jinja
-#: launderette/templates/launderette/launderette_admin.jinja
-#: launderette/views.py pedagogy/templates/pedagogy/guide.jinja
+#: forum/templates/forum/macros.jinja pedagogy/templates/pedagogy/guide.jinja
#: pedagogy/templates/pedagogy/uv_detail.jinja sas/templates/sas/album.jinja
#: sas/templates/sas/moderation.jinja sas/templates/sas/picture.jinja
#: trombi/templates/trombi/detail.jinja
@@ -468,10 +461,6 @@ msgstr "Affiches"
msgid "Counters:"
msgstr "Comptoirs : "
-#: club/templates/club/club_tools.jinja
-msgid "Manage launderettes"
-msgstr "Gestion des laveries"
-
#: club/templates/club/edit_club.jinja core/templates/core/edit.jinja
#, python-format
msgid "Edit %(name)s"
@@ -605,9 +594,7 @@ msgstr "Outils"
#: counter/templates/counter/cash_summary_list.jinja
#: counter/templates/counter/counter_list.jinja
#: election/templates/election/election_detail.jinja
-#: forum/templates/forum/macros.jinja
-#: launderette/templates/launderette/launderette_list.jinja
-#: pedagogy/templates/pedagogy/guide.jinja
+#: forum/templates/forum/macros.jinja pedagogy/templates/pedagogy/guide.jinja
#: pedagogy/templates/pedagogy/uv_detail.jinja sas/templates/sas/album.jinja
#: trombi/templates/trombi/detail.jinja
#: trombi/templates/trombi/edit_profile.jinja
@@ -1133,7 +1120,7 @@ msgid "Delete from weekmail"
msgstr "Supprimer du Weekmail"
#: com/templates/com/weekmail_preview.jinja
-#: core/templates/core/user_account_detail.jinja launderette/views.py
+#: core/templates/core/user_account_detail.jinja
#: pedagogy/templates/pedagogy/uv_detail.jinja
#: trombi/templates/trombi/comment_moderation.jinja
#: trombi/templates/trombi/export.jinja
@@ -1636,7 +1623,7 @@ msgstr "url"
msgid "param"
msgstr "param"
-#: core/models.py launderette/models.py
+#: core/models.py
msgid "type"
msgstr "type"
@@ -1774,13 +1761,6 @@ msgstr "Eboutic"
msgid "Services"
msgstr "Services"
-#: core/templates/core/base/navbar.jinja launderette/models.py
-#: launderette/templates/launderette/launderette_book.jinja
-#: launderette/templates/launderette/launderette_book_choose.jinja
-#: launderette/templates/launderette/launderette_main.jinja
-msgid "Launderette"
-msgstr "Laverie"
-
#: core/templates/core/base/navbar.jinja core/templates/core/file.jinja
#: core/views/files.py
msgid "Files"
@@ -2016,12 +1996,7 @@ msgstr "Cotisant jusqu'au %(subscription_end)s"
msgid "Account number: "
msgstr "Numéro de compte : "
-#: core/templates/core/macros.jinja launderette/models.py
-msgid "Slot"
-msgstr "Créneau"
-
#: core/templates/core/macros.jinja
-#: launderette/templates/launderette/launderette_admin.jinja
msgid "Tokens"
msgstr "Jetons"
@@ -2858,7 +2833,7 @@ msgstr "Espèces"
msgid "Credit card"
msgstr "Carte bancaire"
-#: counter/apps.py counter/models.py launderette/models.py
+#: counter/apps.py counter/models.py
msgid "counter"
msgstr "comptoir"
@@ -3054,7 +3029,7 @@ msgstr "Bureau"
msgid "sellers"
msgstr "vendeurs"
-#: counter/models.py launderette/models.py
+#: counter/models.py
msgid "token"
msgstr "jeton"
@@ -3266,7 +3241,6 @@ msgid "There is no cash register summary in this website."
msgstr "Il n'y a pas de relevé de caisse dans ce site web."
#: counter/templates/counter/counter_click.jinja
-#: launderette/templates/launderette/launderette_admin.jinja
msgid "Selling"
msgstr "Vente"
@@ -3290,8 +3264,6 @@ msgstr "Annuler (ANN)"
#: counter/templates/counter/fragments/create_refill.jinja
#: counter/templates/counter/fragments/create_student_card.jinja
#: counter/templates/counter/invoices_call.jinja
-#: launderette/templates/launderette/launderette_admin.jinja
-#: launderette/templates/launderette/launderette_click.jinja
#: sas/templates/sas/picture.jinja
#: subscription/templates/subscription/stats.jinja
msgid "Go"
@@ -3359,7 +3331,6 @@ msgid "There is no counters in this website."
msgstr "Il n'y a pas de comptoirs dans ce site web."
#: counter/templates/counter/counter_main.jinja
-#: launderette/templates/launderette/launderette_click.jinja
#, python-format
msgid "%(counter_name)s counter"
msgstr "Comptoir %(counter_name)s"
@@ -4257,117 +4228,6 @@ msgstr "Galaxie de %(user_name)s"
msgid "This citizen has not yet joined the galaxy"
msgstr "Ce citoyen n'a pas encore rejoint la galaxie"
-#: launderette/models.py
-msgid "launderette"
-msgstr "laverie"
-
-#: launderette/models.py
-msgid "is working"
-msgstr "fonctionne"
-
-#: launderette/models.py
-msgid "Machine"
-msgstr "Machine"
-
-#: launderette/models.py
-msgid "borrow date"
-msgstr "date d'emprunt"
-
-#: launderette/models.py
-msgid "Token"
-msgstr "Jeton"
-
-#: launderette/models.py launderette/views.py
-msgid "Token name can not be blank"
-msgstr "Le nom du jeton ne peut pas être vide"
-
-#: launderette/models.py
-msgid "machine"
-msgstr "machine"
-
-#: launderette/templates/launderette/launderette_admin.jinja
-msgid "Launderette admin"
-msgstr "Gestion de la laverie"
-
-#: launderette/templates/launderette/launderette_admin.jinja
-msgid "Sell"
-msgstr "Vendre"
-
-#: launderette/templates/launderette/launderette_admin.jinja
-msgid "Machines"
-msgstr "Machines"
-
-#: launderette/templates/launderette/launderette_admin.jinja
-msgid "New machine"
-msgstr "Nouvelle machine"
-
-#: launderette/templates/launderette/launderette_admin.jinja
-#: launderette/views.py
-msgid "Type"
-msgstr "Type"
-
-#: launderette/templates/launderette/launderette_admin.jinja
-msgid "Name"
-msgstr "Nom"
-
-#: launderette/templates/launderette/launderette_book.jinja
-msgid "Choose"
-msgstr "Choisir"
-
-#: launderette/templates/launderette/launderette_book.jinja
-msgid "Washing and drying"
-msgstr "Lavage et séchage"
-
-#: launderette/templates/launderette/launderette_book.jinja sith/settings.py
-msgid "Washing"
-msgstr "Lavage"
-
-#: launderette/templates/launderette/launderette_book.jinja sith/settings.py
-msgid "Drying"
-msgstr "Séchage"
-
-#: launderette/templates/launderette/launderette_list.jinja
-msgid "Launderette admin list"
-msgstr "Liste des laveries"
-
-#: launderette/templates/launderette/launderette_list.jinja
-msgid "New launderette"
-msgstr "Nouvelle laverie"
-
-#: launderette/templates/launderette/launderette_list.jinja
-msgid "There is no launderette in this website."
-msgstr "Il n'y a pas de laverie dans ce site web."
-
-#: launderette/templates/launderette/launderette_main.jinja
-msgid "Edit presentation page"
-msgstr "Éditer la page de présentation"
-
-#: launderette/templates/launderette/launderette_main.jinja
-msgid "Book launderette slot"
-msgstr "Réserver un créneau de laverie"
-
-#: launderette/views.py
-msgid "Tokens, separated by spaces"
-msgstr "Jetons, séparés par des espaces"
-
-#: launderette/views.py
-#, python-format
-msgid "Token %(token_name)s does not exists"
-msgstr "Le jeton %(token_name)s n'existe pas"
-
-#: launderette/views.py
-#, python-format
-msgid "Token %(token_name)s already exists"
-msgstr "Un jeton %(token_name)s existe déjà"
-
-#: launderette/views.py
-msgid "User has booked no slot"
-msgstr "L'utilisateur n'a pas réservé de créneau"
-
-#: launderette/views.py
-msgid "Token not found"
-msgstr "Jeton non trouvé"
-
#: matmat/templates/matmat/search_form.jinja
msgid "Search user"
msgstr "Rechercher un utilisateur"
diff --git a/pedagogy/static/pedagogy/css/pedagogy.scss b/pedagogy/static/pedagogy/css/pedagogy.scss
index 51656615..ae09b122 100644
--- a/pedagogy/static/pedagogy/css/pedagogy.scss
+++ b/pedagogy/static/pedagogy/css/pedagogy.scss
@@ -17,6 +17,7 @@ $large-devices: 992px;
margin-bottom: 0;
margin-top: 0;
}
+
&.star-checked {
color: $pedagogy-orange;
margin-bottom: 0;
@@ -32,6 +33,7 @@ $large-devices: 992px;
margin-left: 5px;
margin-right: 5px;
}
+
&.star-checked {
margin-left: 5px;
margin-right: 5px;
@@ -42,6 +44,7 @@ $large-devices: 992px;
&.grade-without-star {
display: block;
}
+
&.grade-with-star {
display: none;
}
@@ -51,10 +54,12 @@ $large-devices: 992px;
font-size: 1.1em;
overflow-wrap: break-word;
+
.closed td.title {
color: lighten($black-color, 10%);
font-style: italic;
}
+
td {
text-align: center;
border: none;
@@ -131,10 +136,12 @@ $large-devices: 992px;
input[type="checkbox"] {
display: none;
}
+
.radio-guide {
margin-top: 10px;
color: white;
}
+
.radio-guide label {
display: inline-block;
background-color: $pedagogy-blue;
@@ -143,12 +150,15 @@ $large-devices: 992px;
font-size: 16px;
border-radius: 4px;
}
- .radio-guide input[type="radio"]:checked + label {
+
+ .radio-guide input[type="radio"]:checked+label {
background-color: $pedagogy-orange;
}
- .radio-guide input[type="checkbox"]:checked + label {
+
+ .radio-guide input[type="checkbox"]:checked+label {
background-color: $pedagogy-orange;
}
+
.radio-guide label:hover {
background-color: $pedagogy-hover-blue;
}
@@ -219,12 +229,7 @@ $large-devices: 992px;
"stars"
"comment";
}
- }
- .ui-accordion-content {
- background-color: $white-color;
- border-color: $pedagogy-orange;
- border-right: none;
}
.form-stars {
@@ -235,16 +240,6 @@ $large-devices: 992px;
grid-area: comment;
}
- .ui-accordion-header {
- background-color: $pedagogy-orange;
- color: $pedagogy-white-text;
- clip-path: polygon(0 0%, 0 100%, 30% 100%, 33% 0);
-
- @media screen and (max-width: $large-devices) {
- clip-path: none;
- }
- }
-
.ui-accordion-header-icon {
color: $pedagogy-white-text;
margin-right: 10px;
@@ -282,7 +277,7 @@ $large-devices: 992px;
background-color: $pedagogy-blue;
padding-right: 10px;
- > p {
+ >p {
text-align: right;
font-weight: bold;
}
@@ -356,7 +351,7 @@ $large-devices: 992px;
.grade-type {
grid-area: grade-type;
- > p {
+ >p {
color: $pedagogy-white-text;
font-weight: bold;
text-align: right;
@@ -512,3 +507,20 @@ $large-devices: 992px;
}
}
}
+
+details.accordion summary {
+
+ background: $pedagogy-orange !important;
+ color: $pedagogy-white-text !important;
+ clip-path: polygon(0 0%, 0 100%, 30% 100%, 33% 0);
+
+ @media screen and (max-width: $large-devices) {
+ clip-path: none;
+ }
+}
+
+details.accordion>.accordion-content {
+ background-color: $white-color;
+ border-color: $pedagogy-orange;
+ border-right: none;
+}
\ No newline at end of file
diff --git a/pedagogy/templates/pedagogy/uv_detail.jinja b/pedagogy/templates/pedagogy/uv_detail.jinja
index a5b07f68..9b164583 100644
--- a/pedagogy/templates/pedagogy/uv_detail.jinja
+++ b/pedagogy/templates/pedagogy/uv_detail.jinja
@@ -90,9 +90,9 @@
{% trans %}You already posted a comment on this UV. If you want to comment again, please modify or delete your previous comment.{% endtrans %}
{% elif user.has_perm("pedagogy.add_uvcomment") %}
-