beaucoup trop d'avancées pour les detailler
This commit is contained in:
73
manifeste_velo/package-lock.json
generated
Normal file
73
manifeste_velo/package-lock.json
generated
Normal file
@@ -0,0 +1,73 @@
|
||||
{
|
||||
"name": "manifeste_velo",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"dependencies": {
|
||||
"bootstrap": "5.3.*",
|
||||
"bootstrap-icons": "1.13.*",
|
||||
"htmx.org": "2.0.*",
|
||||
"leaflet": "~1.9.4"
|
||||
}
|
||||
},
|
||||
"node_modules/@popperjs/core": {
|
||||
"version": "2.11.8",
|
||||
"resolved": "https://registry.npmjs.org/@popperjs/core/-/core-2.11.8.tgz",
|
||||
"integrity": "sha512-P1st0aksCrn9sGZhp8GMYwBnQsbvAWsZAX44oXNNvLHGqAOcoVxmjZiohstwQ7SqKnbR47akdNi+uleWD8+g6A==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/popperjs"
|
||||
}
|
||||
},
|
||||
"node_modules/bootstrap": {
|
||||
"version": "5.3.8",
|
||||
"resolved": "https://registry.npmjs.org/bootstrap/-/bootstrap-5.3.8.tgz",
|
||||
"integrity": "sha512-HP1SZDqaLDPwsNiqRqi5NcP0SSXciX2s9E+RyqJIIqGo+vJeN5AJVM98CXmW/Wux0nQ5L7jeWUdplCEf0Ee+tg==",
|
||||
"funding": [
|
||||
{
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/twbs"
|
||||
},
|
||||
{
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/bootstrap"
|
||||
}
|
||||
],
|
||||
"license": "MIT",
|
||||
"peerDependencies": {
|
||||
"@popperjs/core": "^2.11.8"
|
||||
}
|
||||
},
|
||||
"node_modules/bootstrap-icons": {
|
||||
"version": "1.13.1",
|
||||
"resolved": "https://registry.npmjs.org/bootstrap-icons/-/bootstrap-icons-1.13.1.tgz",
|
||||
"integrity": "sha512-ijombt4v6bv5CLeXvRWKy7CuM3TRTuPEuGaGKvTV5cz65rQSY8RQ2JcHt6b90cBBAC7s8fsf2EkQDldzCoXUjw==",
|
||||
"funding": [
|
||||
{
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/twbs"
|
||||
},
|
||||
{
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/bootstrap"
|
||||
}
|
||||
],
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/htmx.org": {
|
||||
"version": "2.0.8",
|
||||
"resolved": "https://registry.npmjs.org/htmx.org/-/htmx.org-2.0.8.tgz",
|
||||
"integrity": "sha512-fm297iru0iWsNJlBrjvtN7V9zjaxd+69Oqjh4F/Vq9Wwi2kFisLcrLCiv5oBX0KLfOX/zG8AUo9ROMU5XUB44Q==",
|
||||
"license": "0BSD"
|
||||
},
|
||||
"node_modules/leaflet": {
|
||||
"version": "1.9.4",
|
||||
"resolved": "https://registry.npmjs.org/leaflet/-/leaflet-1.9.4.tgz",
|
||||
"integrity": "sha512-nxS1ynzJOmOlHp+iL3FyWqK89GtNL8U8rvlMOsQdTTssxZwCXh8N2NB3GDQOL+YR3XnWyZAxwQixURb+FA74PA==",
|
||||
"license": "BSD-2-Clause"
|
||||
}
|
||||
}
|
||||
}
|
||||
8
manifeste_velo/package.json
Normal file
8
manifeste_velo/package.json
Normal file
@@ -0,0 +1,8 @@
|
||||
{
|
||||
"dependencies": {
|
||||
"bootstrap": "5.3.*",
|
||||
"bootstrap-icons": "1.13.*",
|
||||
"htmx.org": "2.0.*",
|
||||
"leaflet": "~1.9.4"
|
||||
}
|
||||
}
|
||||
@@ -1,9 +1,24 @@
|
||||
from django.contrib import admin
|
||||
from django.contrib.auth.admin import UserAdmin as BaseUserAdmin
|
||||
from django.contrib.auth.models import User
|
||||
|
||||
from questionnaire import models
|
||||
|
||||
# Register your models here.
|
||||
|
||||
|
||||
class UtilisateurInline(admin.StackedInline):
|
||||
model = models.Utilisateur
|
||||
can_delete = False
|
||||
|
||||
|
||||
class UserAdmin(BaseUserAdmin):
|
||||
inlines = [UtilisateurInline]
|
||||
|
||||
|
||||
admin.site.unregister(User)
|
||||
admin.site.register(User, UserAdmin)
|
||||
|
||||
admin.site.register(models.Ville)
|
||||
admin.site.register(models.PointManifeste)
|
||||
admin.site.register(models.Liste)
|
||||
|
||||
24
manifeste_velo/questionnaire/migrations/0002_utilisateur.py
Normal file
24
manifeste_velo/questionnaire/migrations/0002_utilisateur.py
Normal file
@@ -0,0 +1,24 @@
|
||||
# Generated by Django 6.0.1 on 2026-01-23 09:35
|
||||
|
||||
import django.db.models.deletion
|
||||
from django.conf import settings
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('questionnaire', '0001_initial'),
|
||||
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='Utilisateur',
|
||||
fields=[
|
||||
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('user', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)),
|
||||
('ville', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='questionnaire.ville')),
|
||||
],
|
||||
),
|
||||
]
|
||||
@@ -0,0 +1,18 @@
|
||||
# Generated by Django 6.0.1 on 2026-01-23 09:37
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('questionnaire', '0002_utilisateur'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='candidat',
|
||||
name='email',
|
||||
field=models.CharField(blank=True, max_length=255, null=True),
|
||||
),
|
||||
]
|
||||
@@ -1,3 +1,4 @@
|
||||
from django.contrib.auth.models import User
|
||||
from django.db import models
|
||||
from django_extensions.db.fields import AutoSlugField
|
||||
from django_extensions.db.models import TimeStampedModel
|
||||
@@ -14,6 +15,14 @@ class Ville(models.Model):
|
||||
return f"{self.nom} ({self.code_insee})"
|
||||
|
||||
|
||||
class Utilisateur(models.Model):
|
||||
user = models.OneToOneField(User, on_delete=models.CASCADE, related_name="profile")
|
||||
ville = models.ForeignKey(Ville, on_delete=models.SET_NULL, blank=True, null=True)
|
||||
|
||||
def __str__(self):
|
||||
return self.user
|
||||
|
||||
|
||||
class PointManifeste(models.Model):
|
||||
titre = models.CharField(max_length=255)
|
||||
texte = models.TextField()
|
||||
@@ -32,9 +41,10 @@ class PointManifeste(models.Model):
|
||||
class Candidat(TimeStampedModel, models.Model):
|
||||
nom = models.CharField(max_length=255)
|
||||
prenom = models.CharField(max_length=255, verbose_name="Prénom")
|
||||
email = models.CharField(max_length=255, blank=True, null=True)
|
||||
|
||||
def __str__(self):
|
||||
return f"{self.prenom} {self.nom} - {self.ville.nom}"
|
||||
return f"{self.prenom} {self.nom}"
|
||||
|
||||
|
||||
class Liste(TimeStampedModel, models.Model):
|
||||
@@ -50,10 +60,11 @@ class Liste(TimeStampedModel, models.Model):
|
||||
responsable_mobilites = models.OneToOneField(
|
||||
Candidat, on_delete=models.PROTECT, verbose_name="Responsable mobilités de la liste"
|
||||
)
|
||||
email_ouvert = models.DateTimeField(blank=True, null=True, verbose_name="L'e-mail au candidat a été ouvert")
|
||||
email_envoye = models.DateTimeField(blank=True, null=True, verbose_name="Date et heure de l'envoi de l'e-mail")
|
||||
email_ouvert = models.DateTimeField(blank=True, null=True, verbose_name="Date et heure d'ouverture de l'e-mail")
|
||||
|
||||
def __str__(self):
|
||||
return f"{self.nom} - {self.ville.nom} - {self.tete_liste.prenom} {self.tete_liste.nom}"
|
||||
return f"{self.nom} - {self.ville.nom} - {self.tete_liste}"
|
||||
|
||||
class Meta:
|
||||
ordering = ["ville", "nom"]
|
||||
@@ -64,6 +75,10 @@ class ReponseListe(TimeStampedModel, models.Model):
|
||||
expression_libre = models.TextField(verbose_name="Expression libre")
|
||||
email_confirmation = models.BooleanField(verbose_name="E-mail de confirmation envoyé")
|
||||
finalise = models.BooleanField(verbose_name="Réponse finalisée")
|
||||
date_heure_finalisation = models.DateTimeField(blank=True, null=True)
|
||||
|
||||
def __str__(self):
|
||||
return f"Réponse pour la liste {self.liste}"
|
||||
|
||||
|
||||
class EngagementChoices(models.TextChoices):
|
||||
|
||||
@@ -0,0 +1,37 @@
|
||||
{% extends "base.html" %}{% load static %}
|
||||
{% block body %}
|
||||
<div class="container-fluid">
|
||||
<nav class="navbar navbar-expand-md bg-dark border-bottom border-body" data-bs-theme="dark">
|
||||
<div class="container-fluid">
|
||||
<a class="navbar-brand" href="{% url "questionnaire:admin_landing" %}">
|
||||
<img src="{% static "questionnaire/logo.png" %}" alt="" width="30" class="d-inline-block align-text-top">
|
||||
Place au Vélo Angers
|
||||
</a>
|
||||
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarSupportedContent" aria-controls="navbarSupportedContent" aria-expanded="false" aria-label="Ouvrir ou Fermer le menu">
|
||||
<span class="navbar-toggler-icon"></i>
|
||||
</button>
|
||||
<div class="collapse navbar-collapse" id="navbarSupportedContent">
|
||||
<ul class="navbar-nav me-auto mb-2 mb-md-0">
|
||||
<li class="nav-item">
|
||||
<a href="{% url "questionnaire:admin_questionnaires" %}" class="nav-link">Questionnaire</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a href="{% url "questionnaire:admin_reponses" %}" class="nav-link">Réponses enregistrées</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a href="{% url "questionnaire:admin_candidats" %}" class="nav-link">Gestion des candidats</a>
|
||||
</li>
|
||||
</ul>
|
||||
<form class="d-flex" method="post" action="{% url "questionnaire:logout" %}">{% csrf_token %}
|
||||
<button type="submit" class="btn btn-outline-success"><span class="bi bi-key"></span>Se déconnecter</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
</div>
|
||||
{% block admin_body %}
|
||||
<div class="container-fluid">
|
||||
<p class="lead">Bienvenue sur la page d'administration, vous pouvez sélectionner une fonction dans le menu</p>
|
||||
</div>
|
||||
{% endblock %}
|
||||
{% endblock %}
|
||||
@@ -0,0 +1,4 @@
|
||||
{% extends "questionnaire/admin.html" %}
|
||||
{% block admin_body %}
|
||||
|
||||
{% endblock %}
|
||||
@@ -0,0 +1,60 @@
|
||||
{% extends "base.html" %}{% load static %}
|
||||
{% block body %}
|
||||
<div class="container">
|
||||
<div class="row mb-2">
|
||||
<div class="col-md-4"></div>
|
||||
<div class="col-md-4">
|
||||
<div class="row">
|
||||
<div class="col d-flex justify-content-center align-items-center">
|
||||
<img src="{% static "questionnaire/logo.png" %}" alt="Place au Vélo Angers">
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
<h2 class="text-center">Connexion à la plateforme de gestion du manifeste</h2>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-4"></div>
|
||||
</div>
|
||||
{% if form.errors %}
|
||||
<div class="row mb-2">
|
||||
<div class="col-md-4"></div>
|
||||
<div class="col-md-4 alert alert-danger" role="alert"><ul class="list-group border-0">
|
||||
{% for error in form.non_field_errors %}
|
||||
<li class="list-group-item list-group-item-danger border-0"><span class="fw-bold">{{ error|escape }}</span></li>
|
||||
{% endfor %}
|
||||
</ul></div>
|
||||
<div class="col-md-4"></div>
|
||||
</div>
|
||||
{% endif %}
|
||||
<form method="post" action="{% url "questionnaire:login" %}">{% csrf_token %}<input type="hidden" id="next" value="{{ next }}">
|
||||
<div class="row mb-2">
|
||||
<div class="col-md-4"></div>
|
||||
<div class="col-md-4">
|
||||
<div class="row mb-1">
|
||||
<label class="col-md-4 col-form-label" for="{{ form.username.id_for_label }}">{{ form.username.label }}</label>
|
||||
<div class="col-md-8">
|
||||
<input type="text" class="form-control" value="{{ form.username.value|default_if_none:"" }}" placeholder="{{ form.username.label }}" name="{{ form.username.html_name }}" id="{{ form.username.id_for_label }}">
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<label class="col-md-4 col-form-label" for="{{ form.password.id_for_label }}">{{ form.password.label }}</label>
|
||||
<div class="col-md-8">
|
||||
<input type="password" class="form-control" name="{{ form.password.html_name }}" id="{{ form.password.id_for_label }}">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-4"></div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-md-4"></div>
|
||||
<div class="col-md-4 text-center">
|
||||
<input type="submit" value="Se connecter" class="btn btn-primary d-block w-100">
|
||||
</div>
|
||||
<div class="col-md-4"></div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
{% endblock %}
|
||||
@@ -0,0 +1,23 @@
|
||||
{% extends "questionnaire/admin.html" %}
|
||||
{% block admin_body %}
|
||||
<div class="container">
|
||||
{% for question in object_list %}
|
||||
<div class="row">
|
||||
<div class="col-md-2"></div>
|
||||
<div class="col-md-8">
|
||||
{% if question.est_commun %}
|
||||
<h5 class="text-success fw-bold">{{ question.ordre }}. {{ question.titre }} (Commun)</h5>
|
||||
<p>{{ question.texte|truncatewords_html:10 }}</p>
|
||||
{% else %}
|
||||
<h5 class="text-success fw-bold">{{ question.ordre }}. {{ question.titre }}{% if request.user.is_staff %} ({{ question.ville.nom }}){% endif %}</h5>
|
||||
<p>{{ question.texte|safe }}</p>
|
||||
{% if question.exemple %}
|
||||
<div class="d-block w-100 p-1 border border-warning">{{ question.exemple }}</div>
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
</div>
|
||||
<div class="col-md-2"></div>
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
{% endblock %}
|
||||
@@ -0,0 +1,4 @@
|
||||
{% extends "questionnaire/admin.html" %}
|
||||
{% block admin_body %}
|
||||
|
||||
{% endblock %}
|
||||
@@ -1,9 +1,16 @@
|
||||
from django.contrib.auth.views import LoginView, LogoutView
|
||||
from django.urls import path
|
||||
|
||||
from questionnaire import views
|
||||
|
||||
app_name = "questionnaire"
|
||||
|
||||
urlpatterns = [
|
||||
path("<uuid:liste>/logo.png", views.OuvertureEmail.as_view(), name="ouverture-email"),
|
||||
path("login", LoginView.as_view(template_name="questionnaire/login.html"), name="login"),
|
||||
path("logout", LogoutView.as_view(template_name="questionnaire/logout.html"), name="logout"),
|
||||
path("admin/candidats", views.CandidatsLanding.as_view(), name="admin_candidats"),
|
||||
path("admin/questions", views.QuestionnairesLanding.as_view(), name="admin_questionnaires"),
|
||||
path("admin/reponses", views.ReponsesLanding.as_view(), name="admin_reponses"),
|
||||
path("admin", views.AdminLanding.as_view(), name="admin_landing"),
|
||||
]
|
||||
|
||||
@@ -1,3 +1,68 @@
|
||||
from django.shortcuts import render
|
||||
from datetime import datetime
|
||||
|
||||
from django.conf import settings
|
||||
from django.contrib.auth.mixins import LoginRequiredMixin
|
||||
from django.db.models import Q
|
||||
from django.urls import reverse_lazy
|
||||
from django.views.generic.base import RedirectView, TemplateView
|
||||
from django.views.generic.list import ListView
|
||||
|
||||
from questionnaire.models import Liste, PointManifeste, ReponseListe
|
||||
|
||||
# Create your views here.
|
||||
|
||||
|
||||
class OuvertureEmail(RedirectView):
|
||||
permanent = True
|
||||
query_string = False
|
||||
url = settings.STATIC_URL + "/questionnaire/logo.png"
|
||||
|
||||
def get_redirect_url(self, *args, **kwargs):
|
||||
liste = Liste.objects.get(uuid=kwargs["liste"])
|
||||
if liste.email_ouvert is None and liste.email_envoye is not None:
|
||||
liste.email_ouvert = datetime.now()
|
||||
liste.save()
|
||||
|
||||
return super().get_redirect_url(*args, **kwargs)
|
||||
|
||||
|
||||
class AdminViewMixin(LoginRequiredMixin):
|
||||
login_url = reverse_lazy("questionnaire:login")
|
||||
redirect_field_name = "next"
|
||||
|
||||
|
||||
class AdminLanding(AdminViewMixin, TemplateView):
|
||||
template_name = "questionnaire/admin.html"
|
||||
|
||||
|
||||
class ReponsesLanding(AdminViewMixin, ListView):
|
||||
model = ReponseListe
|
||||
template_name = "questionnaire/candidats.html"
|
||||
|
||||
def get_queryset(self):
|
||||
if self.request.user.is_staff:
|
||||
return super().get_queryset()
|
||||
return ReponseListe.objects.filter(liste__ville=self.request.user.profile.ville)
|
||||
|
||||
|
||||
class CandidatsLanding(AdminViewMixin, ListView):
|
||||
model = Liste
|
||||
template_name = "questionnaire/candidats.html"
|
||||
|
||||
def get_queryset(self):
|
||||
if self.request.user.is_staff:
|
||||
return super().get_queryset()
|
||||
return Liste.objects.filter(ville=self.request.user.profile.ville)
|
||||
|
||||
|
||||
class QuestionnairesLanding(AdminViewMixin, ListView):
|
||||
model = PointManifeste
|
||||
template_name = "questionnaire/questions.html"
|
||||
|
||||
def get_queryset(self):
|
||||
if self.request.user.is_staff:
|
||||
queryset = super().get_queryset()
|
||||
print(queryset.explain())
|
||||
print("Return full queryset")
|
||||
return super().get_queryset()
|
||||
return PointManifeste.objects.filter(Q(est_commun=True) | Q(ville=self.request.user.profile.ville))
|
||||
|
||||
@@ -1,88 +0,0 @@
|
||||
{% extends 'base.html' %}{% load static %}{% load simple_menu %}
|
||||
{% block body %}{% generate_menu %}
|
||||
<div class="container-fluid">
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
<a class="d-flex align-items-center mt-2 pb-3 mb-3 link-body-emphasis text-decoration-none border-bottom" href="{% url 'admin_benevolat:index' %}">
|
||||
<img src="{% get_media_prefix %}{{ config.LOGO_ASSO }}" height="100px" class="me-3">
|
||||
<span class="fs-4 fw-semibold">{{ config.NOM_ASSO }}</span>
|
||||
</a>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-3 p-3 border-end" role="navigation">
|
||||
<!--<div class="flex-shrink-0 p-3">-->
|
||||
<ul class="list-unstyled ps-0">
|
||||
{% for item in menus.admin %}
|
||||
<li class="mb-1">
|
||||
<button
|
||||
class="btn btn-toggle d-inline-flex align-items-center rounded border-0"
|
||||
data-bs-toggle="collapse" data-bs-target="#{{ item.title|slugify }}-collapse" aria-expanded="false">
|
||||
<i class="bi bi-chevron-right me-1" id="{{ item.title|slugify }}-icon"></i>
|
||||
{% if item.icon != "" %}<i class="bi bi-{{ item.icon }} me-1"></i>{% endif %}{{ item.title }}
|
||||
</button>
|
||||
<div id="{{ item.title|slugify}}-collapse" class="collapse">
|
||||
<ul class="btn-toggle-nav list-unstyled fw-normal ms-3 small">
|
||||
{% for subitem in item.children %}
|
||||
<li>
|
||||
|
||||
<a hx-get="{{ subitem.url }}" hx-target="#admin_body" hx-push-url="true" class="link-body-emphasis d-inline-flex text-decoration-none rounded">
|
||||
{% if subitem.icon %}<i class="bi bi-{{ subitem.icon }} me-1"></i>{% endif %}{{ subitem.title }}
|
||||
</a>
|
||||
{% if subitem.children %}
|
||||
<ul class="nav nav-pills flex-column mb-auto ms-4 smaller">
|
||||
{% for child in subitem.children %}
|
||||
<li class="nav-item">
|
||||
<a class="nav-link{% if child.selected %} active{% endif %}" hx-get="{{ child.url }}" hx-target="#admin_body" hx-push-url="true">
|
||||
{% if child.icon %}<i class="bi bi-{{ child.icon }} me-1"></i>{% endif %}{{ child.title }}
|
||||
</a>
|
||||
</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
{% endif %}
|
||||
</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
</div>
|
||||
</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
<!--</div>-->
|
||||
</div>
|
||||
<div class="col-9" id="admin_body">
|
||||
{% block admin_body %}
|
||||
{% endblock %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
{% block javascripts_footer %}
|
||||
{{ block.super }}
|
||||
<script type="text/javascript">
|
||||
document.querySelectorAll('[id$=-collapse]').forEach(elt => {
|
||||
elt.addEventListener('show.bs.collapse', event => {
|
||||
var title = '#'+event.target.id.split('-collapse')[0] + '-icon';
|
||||
console.log(title);
|
||||
document.querySelector(title).classList.remove('bi-chevron-right');
|
||||
document.querySelector(title).classList.add('bi-chevron-down');
|
||||
});
|
||||
});
|
||||
document.querySelectorAll('[id$=-collapse]').forEach(elt => {
|
||||
elt.addEventListener('hide.bs.collapse', event => {
|
||||
var title = '#'+event.target.id.split('-collapse')[0] + '-icon';
|
||||
console.log(title);
|
||||
document.querySelector(title).classList.remove('bi-chevron-down');
|
||||
document.querySelector(title).classList.add('bi-chevron-right');
|
||||
});
|
||||
});
|
||||
|
||||
document.querySelectorAll("div[role=navigation] a").forEach( elt => {
|
||||
elt.addEventListener('htmx:afterRequest', event => {
|
||||
console.log("so triggered")
|
||||
document.querySelectorAll("div[role=navigation] a").forEach(subelt => { subelt.classList.remove("active"); });
|
||||
event.target.classList.add("active");
|
||||
});
|
||||
});
|
||||
</script>
|
||||
{% endblock %}
|
||||
Reference in New Issue
Block a user