feat(cof_clubs): init app
fix(clubs): move to cof-clubs fix(bds): restore clubs app fix(bulmaforms): installed apps fix(bulma): load correct module fix(urls): cof-log{in,out} instead of authens: fix: duplicate import fix: remove club members fix: migrate completely to cof_clubs chore(cof_clubs): remove unused bulma vendoring
This commit is contained in:
parent
342ab6f141
commit
4f24570a0e
52 changed files with 101566 additions and 299 deletions
0
cof_clubs/__init__.py
Normal file
0
cof_clubs/__init__.py
Normal file
6
cof_clubs/admin.py
Normal file
6
cof_clubs/admin.py
Normal file
|
@ -0,0 +1,6 @@
|
|||
from cof_clubs.models import Club, ClubBudgetAccountingPeriod, ClubBudgetLine
|
||||
from django.contrib import admin
|
||||
|
||||
admin.site.register(Club)
|
||||
admin.site.register(ClubBudgetAccountingPeriod)
|
||||
admin.site.register(ClubBudgetLine)
|
5
cof_clubs/apps.py
Normal file
5
cof_clubs/apps.py
Normal file
|
@ -0,0 +1,5 @@
|
|||
from django.apps import AppConfig
|
||||
|
||||
|
||||
class ClubsConfig(AppConfig):
|
||||
name = "cof_clubs"
|
31
cof_clubs/forms.py
Normal file
31
cof_clubs/forms.py
Normal file
|
@ -0,0 +1,31 @@
|
|||
from django import forms
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
|
||||
from .models import Club, ClubBudgetLine
|
||||
|
||||
|
||||
class ClubBudgetLineForm(forms.ModelForm):
|
||||
class Meta:
|
||||
model = ClubBudgetLine
|
||||
fields = ["label", "amount", "date"]
|
||||
widgets = {"date": forms.DateInput(attrs={"type": "date"}, format="%Y-%m-%d")}
|
||||
|
||||
|
||||
class ClubsForm(forms.Form):
|
||||
"""
|
||||
Formulaire d'inscription d'un membre à plusieurs clubs du COF.
|
||||
"""
|
||||
|
||||
respo = forms.ModelMultipleChoiceField(
|
||||
label=_("Respotude de club"),
|
||||
queryset=Club.objects.all(),
|
||||
widget=forms.CheckboxSelectMultiple,
|
||||
required=False,
|
||||
)
|
||||
|
||||
budget_manager = forms.ModelMultipleChoiceField(
|
||||
label=_("Gestionnaire de budget de club"),
|
||||
queryset=Club.objects.all(),
|
||||
widget=forms.CheckboxSelectMultiple,
|
||||
required=False,
|
||||
)
|
150
cof_clubs/migrations/0001_initial.py
Normal file
150
cof_clubs/migrations/0001_initial.py
Normal file
|
@ -0,0 +1,150 @@
|
|||
# Generated by Django 4.2.16 on 2025-03-14 18:25
|
||||
|
||||
import django.db.models.deletion
|
||||
from django.conf import settings
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
initial = True
|
||||
|
||||
dependencies = [
|
||||
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name="Club",
|
||||
fields=[
|
||||
(
|
||||
"id",
|
||||
models.AutoField(
|
||||
auto_created=True,
|
||||
primary_key=True,
|
||||
serialize=False,
|
||||
verbose_name="ID",
|
||||
),
|
||||
),
|
||||
(
|
||||
"name",
|
||||
models.CharField(
|
||||
max_length=1000, unique=True, verbose_name="nom du club"
|
||||
),
|
||||
),
|
||||
(
|
||||
"description",
|
||||
models.TextField(blank=True, verbose_name="description"),
|
||||
),
|
||||
(
|
||||
"budget_managers",
|
||||
models.ManyToManyField(
|
||||
blank=True,
|
||||
related_name="managed_budgets_set",
|
||||
to=settings.AUTH_USER_MODEL,
|
||||
verbose_name="gestionnaires du budget su club",
|
||||
),
|
||||
),
|
||||
(
|
||||
"members",
|
||||
models.ManyToManyField(
|
||||
blank=True,
|
||||
related_name="clubs",
|
||||
to=settings.AUTH_USER_MODEL,
|
||||
verbose_name="membres du club",
|
||||
),
|
||||
),
|
||||
(
|
||||
"respos",
|
||||
models.ManyToManyField(
|
||||
blank=True,
|
||||
related_name="respo_set",
|
||||
to=settings.AUTH_USER_MODEL,
|
||||
verbose_name="responsables du club",
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name="ClubBudgetAccountingPeriod",
|
||||
fields=[
|
||||
(
|
||||
"id",
|
||||
models.AutoField(
|
||||
auto_created=True,
|
||||
primary_key=True,
|
||||
serialize=False,
|
||||
verbose_name="ID",
|
||||
),
|
||||
),
|
||||
(
|
||||
"name",
|
||||
models.CharField(
|
||||
max_length=1000,
|
||||
unique=True,
|
||||
verbose_name="exercice budgétaire des clubs",
|
||||
),
|
||||
),
|
||||
(
|
||||
"is_archived",
|
||||
models.BooleanField(default=False, verbose_name="est archivé"),
|
||||
),
|
||||
],
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name="ClubBudgetLine",
|
||||
fields=[
|
||||
(
|
||||
"id",
|
||||
models.AutoField(
|
||||
auto_created=True,
|
||||
primary_key=True,
|
||||
serialize=False,
|
||||
verbose_name="ID",
|
||||
),
|
||||
),
|
||||
("label", models.CharField(max_length=1000, verbose_name="libellé")),
|
||||
(
|
||||
"amount",
|
||||
models.DecimalField(
|
||||
decimal_places=2,
|
||||
max_digits=12,
|
||||
verbose_name="Montant (dépense en négatif)",
|
||||
),
|
||||
),
|
||||
(
|
||||
"created",
|
||||
models.DateTimeField(
|
||||
auto_now_add=True, verbose_name="date de création"
|
||||
),
|
||||
),
|
||||
(
|
||||
"modified",
|
||||
models.DateTimeField(
|
||||
auto_now=True, verbose_name="date de modification"
|
||||
),
|
||||
),
|
||||
("date", models.DateTimeField(verbose_name="date")),
|
||||
(
|
||||
"posted",
|
||||
models.BooleanField(default=False, verbose_name="est validée"),
|
||||
),
|
||||
(
|
||||
"accounting_period",
|
||||
models.ForeignKey(
|
||||
on_delete=django.db.models.deletion.CASCADE,
|
||||
to="cof_clubs.clubbudgetaccountingperiod",
|
||||
verbose_name="exercice financier",
|
||||
),
|
||||
),
|
||||
(
|
||||
"club",
|
||||
models.ForeignKey(
|
||||
on_delete=django.db.models.deletion.CASCADE,
|
||||
to="cof_clubs.club",
|
||||
verbose_name="club",
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
]
|
17
cof_clubs/migrations/0002_remove_club_members.py
Normal file
17
cof_clubs/migrations/0002_remove_club_members.py
Normal file
|
@ -0,0 +1,17 @@
|
|||
# Generated by Django 4.2.16 on 2025-03-17 08:47
|
||||
|
||||
from django.db import migrations
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
("cof_clubs", "0001_initial"),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.RemoveField(
|
||||
model_name="club",
|
||||
name="members",
|
||||
),
|
||||
]
|
0
cof_clubs/migrations/__init__.py
Normal file
0
cof_clubs/migrations/__init__.py
Normal file
69
cof_clubs/models.py
Normal file
69
cof_clubs/models.py
Normal file
|
@ -0,0 +1,69 @@
|
|||
from django.contrib.auth import get_user_model
|
||||
from django.db import models
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
|
||||
User = get_user_model()
|
||||
|
||||
|
||||
class Club(models.Model):
|
||||
name = models.CharField(_("nom du club"), max_length=1000, unique=True)
|
||||
description = models.TextField(_("description"), blank=True)
|
||||
respos = models.ManyToManyField(
|
||||
User,
|
||||
verbose_name=_("responsables du club"),
|
||||
blank=True,
|
||||
related_name="respo_set",
|
||||
)
|
||||
budget_managers = models.ManyToManyField(
|
||||
User,
|
||||
verbose_name=_("gestionnaires du budget su club"),
|
||||
blank=True,
|
||||
related_name="managed_budgets_set",
|
||||
)
|
||||
|
||||
def __str__(self):
|
||||
return self.name
|
||||
|
||||
|
||||
class ClubBudgetAccountingPeriod(models.Model):
|
||||
name = models.CharField(
|
||||
_("exercice budgétaire des clubs"),
|
||||
max_length=1000,
|
||||
unique=True,
|
||||
)
|
||||
is_archived = models.BooleanField(verbose_name=_("est archivé"), default=False)
|
||||
|
||||
def __str__(self):
|
||||
return self.name
|
||||
|
||||
|
||||
class ClubBudgetLine(models.Model):
|
||||
label = models.CharField(_("libellé"), max_length=1000)
|
||||
|
||||
amount = models.DecimalField(
|
||||
max_digits=12, decimal_places=2, verbose_name="Montant (dépense en négatif)"
|
||||
)
|
||||
|
||||
club = models.ForeignKey(
|
||||
Club,
|
||||
verbose_name=_("club"),
|
||||
on_delete=models.CASCADE,
|
||||
)
|
||||
accounting_period = models.ForeignKey(
|
||||
ClubBudgetAccountingPeriod,
|
||||
verbose_name=_("exercice financier"),
|
||||
on_delete=models.CASCADE,
|
||||
)
|
||||
created = models.DateTimeField(
|
||||
auto_now_add=True,
|
||||
verbose_name=_("date de création"),
|
||||
)
|
||||
modified = models.DateTimeField(
|
||||
auto_now=True,
|
||||
verbose_name=_("date de modification"),
|
||||
)
|
||||
date = models.DateTimeField(verbose_name=_("date"))
|
||||
posted = models.BooleanField(verbose_name=_("est validée"), default=False)
|
||||
|
||||
def __str__(self):
|
||||
return self.label
|
38
cof_clubs/static/cof_clubs/common.js
Normal file
38
cof_clubs/static/cof_clubs/common.js
Normal file
|
@ -0,0 +1,38 @@
|
|||
document.addEventListener('DOMContentLoaded', () => {
|
||||
|
||||
// Get all "navbar-burger" elements
|
||||
const $navbarBurgers = Array.prototype.slice.call(document.querySelectorAll('.navbar-burger'), 0);
|
||||
|
||||
// Add a click event on each of them
|
||||
$navbarBurgers.forEach( el => {
|
||||
el.addEventListener('click', () => {
|
||||
|
||||
// Get the target from the "data-target" attribute
|
||||
const target = el.dataset.target;
|
||||
const $target = document.getElementById(target);
|
||||
|
||||
// Toggle the "is-active" class on both the "navbar-burger" and the "navbar-menu"
|
||||
el.classList.toggle('is-active');
|
||||
$target.classList.toggle('is-active');
|
||||
|
||||
});
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
document.addEventListener('DOMContentLoaded', () => {
|
||||
const $deleteElems = Array.prototype.slice.call(document.querySelectorAll('.delete'), 0);
|
||||
|
||||
// Add a click event on each of them
|
||||
$deleteElems.forEach( el => {
|
||||
el.addEventListener('click', () => {
|
||||
// Get the target from the "data-target" attribute
|
||||
if ('target' in el.dataset) {
|
||||
const target = el.dataset.target;
|
||||
const $target = document.getElementById(target);
|
||||
$target.remove();
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
});
|
5
cof_clubs/templates/cof_clubs/_links.html
Normal file
5
cof_clubs/templates/cof_clubs/_links.html
Normal file
|
@ -0,0 +1,5 @@
|
|||
{% load static %}
|
||||
|
||||
<!-- CSS -->
|
||||
<link href="{% static 'vendor/bulma/css/bulma.min.css' %}" rel="stylesheet" type="text/css" />
|
||||
<link type="text/css" rel="stylesheet" href="{% static 'vendor/font-awesome/css/font-awesome.min.css' %}">
|
55
cof_clubs/templates/cof_clubs/_nav.html
Normal file
55
cof_clubs/templates/cof_clubs/_nav.html
Normal file
|
@ -0,0 +1,55 @@
|
|||
{% load i18n %}
|
||||
{% load static %}
|
||||
<nav class="navbar is-primary" role="navigation" aria-label="main navigation">
|
||||
<div class="navbar-brand">
|
||||
<a class="navbar-item py-0" href="{% url "cof_clubs:club-list" %}" >
|
||||
<i class="fa fa-money"></i>
|
||||
<i class="fa fa-euro"></i>
|
||||
<i class="fa fa-money"></i>
|
||||
</a>
|
||||
<a href="{% url "home" %}" class="navbar-item">
|
||||
{% trans "Retour vers GestioCOF" %}
|
||||
</a>
|
||||
|
||||
<a role="button" class="navbar-burger" aria-label="menu" aria-expanded="false" data-target="mainNavbar">
|
||||
<span aria-hidden="true"></span>
|
||||
<span aria-hidden="true"></span>
|
||||
<span aria-hidden="true"></span>
|
||||
<span aria-hidden="true"></span>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<div id="mainNavbar" class="navbar-menu">
|
||||
<div class="navbar-end">
|
||||
{% if user.is_authenticated %}
|
||||
<div class="navbar-item has-dropdown is-hoverable">
|
||||
<a class="navbar-link">
|
||||
<span class="icon"> <i class="fa fa-user"></i> </span>
|
||||
<span>{{ user.username }}</span>
|
||||
</a>
|
||||
<div class="navbar-dropdown is-right">
|
||||
<div class="dropdown-content">
|
||||
<form class="navbar-item" action="{% url "cof-logout" %}">
|
||||
{% csrf_token %}
|
||||
<button class="button is-link is-size-6" type="submit">
|
||||
<span>{% trans "Se déconnecter" %}</span>
|
||||
</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{%else%}
|
||||
<div class="navbar-item">
|
||||
<div class="buttons">
|
||||
<a href="{% url "cof-login" %}" class="button is-light">
|
||||
<span>{% trans "Se connecter" %}</span>
|
||||
<span class="icon">
|
||||
<i class="fa fa-door-open"></i>
|
||||
</span>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
39
cof_clubs/templates/cof_clubs/base.html
Normal file
39
cof_clubs/templates/cof_clubs/base.html
Normal file
|
@ -0,0 +1,39 @@
|
|||
{% load i18n %}
|
||||
{% load static %}
|
||||
{% load bulma %}
|
||||
<!DOCTYPE html>
|
||||
<html lang="fr">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<meta name="description" content="{% blocktranslate %}Site web de gestion des clubs{% endblocktranslate %}" />
|
||||
<title>{% block title %}{% translate "Budget clubs" %}{% endblock %}</title>
|
||||
|
||||
{% include "cof_clubs/_links.html" %}
|
||||
</head>
|
||||
|
||||
<body>
|
||||
{% include "cof_clubs/_nav.html" %}
|
||||
|
||||
<section id="notifications" class="section py-0">
|
||||
{% for message in messages %}
|
||||
<div class="notification {{ message.level_tag }} my-2" id="message-{{ forloop.counter0 }}">
|
||||
<button class="delete" data-target="message-{{ forloop.counter0 }}"></button>
|
||||
{% if 'safe' in message.tags %}
|
||||
{{ message|safe }}
|
||||
{% else %}
|
||||
{{ message }}
|
||||
{% endif %}
|
||||
</div>
|
||||
{% endfor %}
|
||||
</section>
|
||||
|
||||
{% block content %}
|
||||
{% endblock content %}
|
||||
|
||||
<script src="{% static "cof_clubs/common.js" %}" defer></script>
|
||||
{% block extra_scripts %}
|
||||
{% endblock extra_scripts %}
|
||||
{# TODO: Add analytics #}
|
||||
</body>
|
||||
</html>
|
93
cof_clubs/templates/cof_clubs/club_detail.html
Normal file
93
cof_clubs/templates/cof_clubs/club_detail.html
Normal file
|
@ -0,0 +1,93 @@
|
|||
{% extends "cof_clubs/base.html" %}
|
||||
{% load bulma %}
|
||||
|
||||
{% block content %}
|
||||
|
||||
{% regroup lines by accounting_period__name as lines_list %}
|
||||
<div class="section">
|
||||
<div class="content container is-max-desktop"/>
|
||||
<h1>{{ object.name }}</h1>
|
||||
<h5>Respos:</h5>
|
||||
<div class="grid">
|
||||
{% for r in object.respos.all %}
|
||||
<div class="tag cell">{{r.username}}</div>
|
||||
{% empty %}
|
||||
<div class="tag cell is-warning">Ce club n'a pas de respo déclaré</div>
|
||||
{% endfor%}
|
||||
</div>
|
||||
|
||||
{% if object.budget_managers.all|length > 0 %}
|
||||
<h5>Gestionnaires additionels du budget:</h5>
|
||||
<div class="grid">
|
||||
{% for r in object.budget_managers.all %}
|
||||
<div class="tag cell">{{r.username}}</div>
|
||||
{% endfor%}
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
{% for period in lines_list %}
|
||||
<h2 class="buttons">
|
||||
{{ period.grouper }}
|
||||
<a class="button" href="{% url "cof_clubs:budget-line-create" object.id period.list.0.accounting_period__id %}"><span class="icon"><i class="fa fa-plus"></i></span></a></h2>
|
||||
|
||||
<table class="table is-bordered is-striped is-fullwidth">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Date</th>
|
||||
<th>Libellé</th>
|
||||
<th>Montant</th>
|
||||
<th>Budget restant</th>
|
||||
<th></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for line in period.list %}
|
||||
<tr>
|
||||
<td class="is-vcentered">{{ line.date|date:"SHORT_DATE_FORMAT" }}</td>
|
||||
<td class="is-vcentered">{{ line.label }}</td>
|
||||
<td class="is-vcentered has-text-right">{{ line.amount }} €</td>
|
||||
<td class="is-vcentered">{{ line.remaining }}</td>
|
||||
<td class="is-vcentered">
|
||||
<div class=buttons>
|
||||
{% if line.posted %}
|
||||
<div class="button is-static is-small">
|
||||
<span class="icon has-text-success">
|
||||
<i class="fa fa-check"></i>
|
||||
</span>
|
||||
</div>
|
||||
{% else %}
|
||||
<div class="button is-static is-small">
|
||||
<span class="icon has-text-danger">
|
||||
<i class="fa fa-question"></i>
|
||||
</span>
|
||||
</div>
|
||||
{% endif %}
|
||||
{% if line.can_edit %}
|
||||
<a href="{% if user.profile.is_buro %}{% url "cof_clubs:budget-line-full-update" line.id %}{% else %}{% url "cof_clubs:budget-line-update" line.id %}{% endif %}" class="button is-small">
|
||||
<span class="icon has-text-warning">
|
||||
<i class="fa fa-pencil"></i>
|
||||
</span>
|
||||
</a>
|
||||
<form action="{% url "cof_clubs:budget-line-delete" line.id %}" method="post">
|
||||
{% csrf_token %}
|
||||
<button type="submit" class="button is-small">
|
||||
<span class="icon">
|
||||
<i class="fa fa-trash"></i>
|
||||
</span>
|
||||
</button>
|
||||
</form>
|
||||
{% endif %}
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
{% endfor %}
|
||||
</div>
|
||||
</div>
|
||||
{% endblock content %}
|
||||
|
||||
|
||||
|
44
cof_clubs/templates/cof_clubs/club_list.html
Normal file
44
cof_clubs/templates/cof_clubs/club_list.html
Normal file
|
@ -0,0 +1,44 @@
|
|||
{% extends "cof_clubs/base.html" %}
|
||||
{% load bulma %}
|
||||
|
||||
{% block content %}
|
||||
<div class="section">
|
||||
<div class="content">
|
||||
<h1>Liste des clubs gérés</h1>
|
||||
<table class="table is-bordered is-striped is-fullwidth">
|
||||
<thead>
|
||||
<tr>
|
||||
<th rowspan="2" >Nom du club</th>
|
||||
<th colspan="{{ accounting_periods.count }}" >Budget restant</th>
|
||||
</tr>
|
||||
<tr>
|
||||
{% for period in accounting_periods %}
|
||||
<th>{{ period.name }}</th>
|
||||
{% endfor %}
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for club, data in clubs.items %}
|
||||
<tr>
|
||||
<td><a href="{% url "cof_clubs:club-detail" club %}">{{ data.name }}</a></td>
|
||||
{%for amount in data.amounts %}
|
||||
<td class="has-text-right">
|
||||
{% if amount < 0 %}
|
||||
<span class="icon">
|
||||
<i class="fa fa-exclamation-triangle has-text-danger"></i>
|
||||
|
||||
</span>
|
||||
{% endif %}
|
||||
{{ amount }} €
|
||||
</td>
|
||||
{% endfor %}
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock content %}
|
||||
|
||||
|
||||
|
31
cof_clubs/templates/cof_clubs/clubbudgetline_form.html
Normal file
31
cof_clubs/templates/cof_clubs/clubbudgetline_form.html
Normal file
|
@ -0,0 +1,31 @@
|
|||
{% extends "cof_clubs/base.html" %}
|
||||
|
||||
{% load i18n %}
|
||||
|
||||
{% block content %}
|
||||
<section class="section">
|
||||
<h1 class="title">{% trans "Création/Modification de ligne de budget" %}</h1>
|
||||
<form action="" method="post">
|
||||
{% csrf_token %}
|
||||
|
||||
{% include "bulma/form.html" with errors=True form=form %}
|
||||
|
||||
<div class="field is-grouped">
|
||||
<div class="control">
|
||||
<button class="button is-primary" type="submit">
|
||||
<span>{% trans "Enregister" %}</span>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
{% if not object %}
|
||||
<div class="control">
|
||||
<button class="button is-secondary" type="submit" name="_addanother">
|
||||
<span>{% trans "Enregister et ajouter un nouveau" %}</span>
|
||||
</button>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
</div>
|
||||
</form>
|
||||
</section>
|
||||
{% endblock %}
|
30
cof_clubs/urls.py
Normal file
30
cof_clubs/urls.py
Normal file
|
@ -0,0 +1,30 @@
|
|||
from django.urls import path
|
||||
|
||||
from . import views
|
||||
|
||||
app_name = "cof_clubs"
|
||||
urlpatterns = [
|
||||
path("club/", views.ClubListView.as_view(), name="club-list"),
|
||||
path("club/<int:pk>", views.ClubDetailView.as_view(), name="club-detail"),
|
||||
path("club/add", views.ClubCreateView.as_view(), name="club-create"),
|
||||
path(
|
||||
"line/<int:club>/<int:acct_period>/add",
|
||||
views.BudgetLineCreate.as_view(),
|
||||
name="budget-line-create",
|
||||
),
|
||||
path(
|
||||
"line/<int:pk>/delete",
|
||||
views.BudgetLineDelete.as_view(),
|
||||
name="budget-line-delete",
|
||||
),
|
||||
path(
|
||||
"line/<int:pk>/update",
|
||||
views.BudgetLineUpdate.as_view(),
|
||||
name="budget-line-update",
|
||||
),
|
||||
path(
|
||||
"line/<int:pk>/full-update",
|
||||
views.BudgetLineFullUpdate.as_view(),
|
||||
name="budget-line-full-update",
|
||||
),
|
||||
]
|
204
cof_clubs/views.py
Normal file
204
cof_clubs/views.py
Normal file
|
@ -0,0 +1,204 @@
|
|||
from django.contrib.auth.mixins import AccessMixin, LoginRequiredMixin
|
||||
from django.db.models import Sum
|
||||
from django.shortcuts import get_object_or_404
|
||||
from django.urls import reverse
|
||||
from django.views.generic import DetailView, TemplateView
|
||||
from django.views.generic.edit import CreateView, DeleteView, UpdateView
|
||||
|
||||
from gestioncof.decorators import BuroRequiredMixin
|
||||
|
||||
from .forms import ClubBudgetLineForm
|
||||
from .models import Club, ClubBudgetAccountingPeriod, ClubBudgetLine
|
||||
|
||||
|
||||
class ClubListView(LoginRequiredMixin, TemplateView):
|
||||
template_name = "cof_clubs/club_list.html"
|
||||
|
||||
def get_context_data(self, *args, **kwargs):
|
||||
ctx = super().get_context_data(*args, **kwargs)
|
||||
|
||||
accounting_periods = ClubBudgetAccountingPeriod.objects.filter(
|
||||
is_archived=False
|
||||
).values("name", "id")
|
||||
|
||||
if self.request.user.profile.is_buro:
|
||||
managed_clubs = Club.objects.all().order_by("name") # TODO useless
|
||||
else:
|
||||
managed_clubs = self.request.user.managed_budgets_set.order_by(
|
||||
"name"
|
||||
) # TODO: useless
|
||||
data = (
|
||||
ClubBudgetLine.objects.filter(
|
||||
accounting_period__is_archived=False, club__in=managed_clubs
|
||||
)
|
||||
.values(
|
||||
"club__name",
|
||||
"club",
|
||||
"accounting_period",
|
||||
)
|
||||
.annotate(Sum("amount"))
|
||||
)
|
||||
structured_data = {
|
||||
club.id: {
|
||||
"name": club.name,
|
||||
"amounts": [0 for _ in accounting_periods],
|
||||
}
|
||||
for club in managed_clubs
|
||||
}
|
||||
period_to_index = {a["id"]: i for i, a in enumerate(accounting_periods)}
|
||||
for d in data:
|
||||
structured_data[d["club"]]["amounts"][
|
||||
period_to_index[d["accounting_period"]]
|
||||
] = d["amount__sum"]
|
||||
ctx["accounting_periods"] = accounting_periods
|
||||
ctx["clubs"] = structured_data
|
||||
return ctx
|
||||
|
||||
|
||||
class ClubDetailView(AccessMixin, DetailView):
|
||||
template_name = "cof_clubs/club_detail.html"
|
||||
model = Club
|
||||
|
||||
def get(self, request, *args, **kwargs):
|
||||
self.object = self.get_object()
|
||||
if (
|
||||
not self.request.user.profile.is_buro
|
||||
and self.request.user not in self.object.respos.all()
|
||||
and self.request.user not in self.object.budget_managers.all()
|
||||
):
|
||||
return self.handle_no_permission()
|
||||
|
||||
context = self.get_context_data(object=self.object)
|
||||
return self.render_to_response(context)
|
||||
|
||||
def get_context_data(self, *args, **kwargs):
|
||||
ctx = super().get_context_data(*args, **kwargs)
|
||||
lines_qs = (
|
||||
ctx["object"]
|
||||
.clubbudgetline_set.filter(
|
||||
accounting_period__is_archived=False,
|
||||
)
|
||||
.prefetch_related("club__respos", "club__budget_managers")
|
||||
.order_by(
|
||||
"accounting_period__is_archived", "accounting_period__name", "date"
|
||||
)
|
||||
)
|
||||
lines = lines_qs.values(
|
||||
"accounting_period__name",
|
||||
"accounting_period__id",
|
||||
"date",
|
||||
"amount",
|
||||
"label",
|
||||
"id",
|
||||
"posted",
|
||||
"accounting_period__is_archived",
|
||||
)
|
||||
# Compute rights and partial sums
|
||||
s = 0
|
||||
current_period = None
|
||||
for i, line in enumerate(lines):
|
||||
if line["accounting_period__id"] != current_period:
|
||||
current_period = line["accounting_period__id"]
|
||||
s = 0
|
||||
s += line["amount"]
|
||||
line["can_edit"] = self.request.user.profile.is_buro or (
|
||||
not line["posted"]
|
||||
and not line["accounting_period__is_archived"]
|
||||
and (
|
||||
self.request.user in lines_qs[i].club.respos.all()
|
||||
or self.request.user in lines_qs[i].club.budget_managers.all()
|
||||
)
|
||||
)
|
||||
line["remaining"] = s
|
||||
ctx["lines"] = lines
|
||||
return ctx
|
||||
|
||||
|
||||
class BudgetLineAccessMixin(AccessMixin):
|
||||
def get_club(self):
|
||||
return get_object_or_404(Club, pk=self.kwargs["club"])
|
||||
|
||||
def get_accounting_period(self):
|
||||
return get_object_or_404(
|
||||
ClubBudgetAccountingPeriod, pk=self.kwargs["acct_period"]
|
||||
)
|
||||
|
||||
def dispatch(self, request, *args, **kwargs):
|
||||
self.club = self.get_club()
|
||||
if (
|
||||
not self.request.user.profile.is_buro
|
||||
and self.request.user not in self.club.respos.all()
|
||||
and self.request.user not in self.club.budget_managers.all()
|
||||
):
|
||||
return self.handle_no_permission()
|
||||
self.accounting_period = self.get_accounting_period()
|
||||
if self.accounting_period.is_archived:
|
||||
return self.handle_no_permission()
|
||||
if (
|
||||
not self.request.user.profile.is_buro
|
||||
and hasattr(self, "object")
|
||||
and self.object
|
||||
and self.object.posted
|
||||
):
|
||||
return self.handle_no_permission()
|
||||
|
||||
return super().dispatch(request, *args, **kwargs)
|
||||
|
||||
|
||||
class BudgetLineCreate(BudgetLineAccessMixin, CreateView):
|
||||
|
||||
model = ClubBudgetLine
|
||||
form_class = ClubBudgetLineForm
|
||||
|
||||
def form_valid(self, form):
|
||||
form.instance.accounting_period = self.accounting_period
|
||||
form.instance.club = self.club
|
||||
return super().form_valid(form)
|
||||
|
||||
def get_success_url(self):
|
||||
if "_addanother" in self.request.POST:
|
||||
return reverse("cof_clubs:budget-line-create", kwargs=self.kwargs)
|
||||
return reverse("cof_clubs:club-detail", kwargs={"pk": self.kwargs["club"]})
|
||||
|
||||
|
||||
class BudgetLineUpdate(BudgetLineAccessMixin, UpdateView):
|
||||
model = ClubBudgetLine
|
||||
form_class = ClubBudgetLineForm
|
||||
|
||||
def get_club(self):
|
||||
if not hasattr(self, "object") or not self.object:
|
||||
self.object = self.get_object()
|
||||
return self.object.club
|
||||
|
||||
def get_accounting_period(self):
|
||||
if not hasattr(self, "object") or not self.object:
|
||||
self.object = self.get_object()
|
||||
return self.object.accounting_period
|
||||
|
||||
def get_success_url(self):
|
||||
return reverse("cof_clubs:club-detail", kwargs={"pk": self.club.id})
|
||||
|
||||
|
||||
class BudgetLineFullUpdate(BuroRequiredMixin, UpdateView):
|
||||
fields = ["label", "amount", "date", "posted", "club", "accounting_period"]
|
||||
model = ClubBudgetLine
|
||||
|
||||
def get_success_url(self):
|
||||
return reverse("cof_clubs:club-detail", kwargs={"pk": self.object.club.id})
|
||||
|
||||
|
||||
class BudgetLineDelete(BudgetLineAccessMixin, DeleteView):
|
||||
model = ClubBudgetLine
|
||||
|
||||
def get_club(self):
|
||||
if not hasattr(self, "object") or not self.object:
|
||||
self.object = self.get_object()
|
||||
return self.object.club
|
||||
|
||||
def get_accounting_period(self):
|
||||
if not hasattr(self, "object") or not self.object:
|
||||
self.object = self.get_object()
|
||||
return self.object.accounting_period
|
||||
|
||||
def get_success_url(self):
|
||||
return reverse("cof_clubs:club-detail", kwargs={"pk": self.club.id})
|
|
@ -50,6 +50,7 @@ INSTALLED_APPS = (
|
|||
]
|
||||
+ INSTALLED_APPS
|
||||
+ [
|
||||
"bulma",
|
||||
"bda",
|
||||
"petitscours",
|
||||
"hcaptcha",
|
||||
|
@ -67,6 +68,7 @@ INSTALLED_APPS = (
|
|||
"wagtail.images",
|
||||
"wagtail.search",
|
||||
"wagtail.admin",
|
||||
"cof_clubs",
|
||||
"wagtail",
|
||||
# "wagtail.contrib.modeladmin",
|
||||
"wagtail.contrib.routable_page",
|
||||
|
|
|
@ -57,12 +57,14 @@ INSTALLED_APPS = [
|
|||
"django.contrib.messages",
|
||||
"django.contrib.admin",
|
||||
"django.contrib.admindocs",
|
||||
"bulma",
|
||||
"gestioasso.apps.IgnoreSrcStaticFilesConfig",
|
||||
"django_cas_ng",
|
||||
"bootstrapform",
|
||||
"widget_tweaks",
|
||||
"bda",
|
||||
"petitscours",
|
||||
"cof_clubs",
|
||||
"hcaptcha",
|
||||
"kfet",
|
||||
"kfet.open",
|
||||
|
|
|
@ -34,6 +34,7 @@ app_dict = {
|
|||
"bda": "gestion/bda/",
|
||||
"petitscours": "gestion/petitcours/",
|
||||
"events": "gestion/event_v2/", # the events module is still experimental !
|
||||
"cof_clubs": "gestion/budget/",
|
||||
"authens": "gestion/authens/",
|
||||
}
|
||||
for app_name, url_prefix in app_dict.items():
|
||||
|
|
|
@ -9,7 +9,6 @@ from django.utils.safestring import mark_safe
|
|||
from django.utils.translation import gettext_lazy as _
|
||||
|
||||
from gestioncof.models import (
|
||||
Club,
|
||||
CofProfile,
|
||||
Event,
|
||||
EventCommentField,
|
||||
|
@ -295,25 +294,6 @@ class PetitCoursDemandeAdmin(admin.ModelAdmin):
|
|||
readonly_fields = ("created",)
|
||||
|
||||
|
||||
class ClubAdminForm(forms.ModelForm):
|
||||
def clean(self):
|
||||
cleaned_data = super().clean()
|
||||
respos = cleaned_data.get("respos")
|
||||
members = cleaned_data.get("membres")
|
||||
for respo in respos.all():
|
||||
if respo not in members:
|
||||
raise forms.ValidationError(
|
||||
"Erreur : le respo %s n'est pas membre du club."
|
||||
% respo.get_full_name()
|
||||
)
|
||||
return cleaned_data
|
||||
|
||||
|
||||
class ClubAdmin(admin.ModelAdmin):
|
||||
list_display = ["name"]
|
||||
form = ClubAdminForm
|
||||
|
||||
|
||||
admin.site.register(Survey, SurveyAdmin)
|
||||
admin.site.register(SurveyQuestion, SurveyQuestionAdmin)
|
||||
admin.site.register(Event, EventAdmin)
|
||||
|
@ -321,7 +301,6 @@ admin.site.register(EventOption, EventOptionAdmin)
|
|||
admin.site.unregister(User)
|
||||
admin.site.register(User, UserProfileAdmin)
|
||||
admin.site.register(CofProfile)
|
||||
admin.site.register(Club, ClubAdmin)
|
||||
admin.site.register(PetitCoursSubject)
|
||||
admin.site.register(PetitCoursAbility, PetitCoursAbilityAdmin)
|
||||
admin.site.register(PetitCoursAttribution, PetitCoursAttributionAdmin)
|
||||
|
|
|
@ -7,7 +7,7 @@ from django.utils.translation import gettext_lazy as _
|
|||
from djconfig.forms import ConfigForm
|
||||
|
||||
from bda.models import Spectacle
|
||||
from gestioncof.models import CalendarSubscription, Club, CofProfile, EventCommentValue
|
||||
from gestioncof.models import CalendarSubscription, CofProfile, EventCommentValue
|
||||
from gestioncof.widgets import TriStateCheckbox
|
||||
|
||||
User = get_user_model()
|
||||
|
@ -432,19 +432,6 @@ class CalendarForm(forms.ModelForm):
|
|||
fields = ["subscribe_to_events", "subscribe_to_my_shows", "other_shows"]
|
||||
|
||||
|
||||
class ClubsForm(forms.Form):
|
||||
"""
|
||||
Formulaire d'inscription d'un membre à plusieurs clubs du COF.
|
||||
"""
|
||||
|
||||
clubs = forms.ModelMultipleChoiceField(
|
||||
label="Inscriptions aux clubs du COF",
|
||||
queryset=Club.objects.all(),
|
||||
widget=forms.CheckboxSelectMultiple,
|
||||
required=False,
|
||||
)
|
||||
|
||||
|
||||
# ---
|
||||
# Announcements banner
|
||||
# TODO: move this to the `gestion` app once the supportBDS branch is merged
|
||||
|
|
16
gestioncof/migrations/0021_delete_club.py
Normal file
16
gestioncof/migrations/0021_delete_club.py
Normal file
|
@ -0,0 +1,16 @@
|
|||
# Generated by Django 4.2.16 on 2025-03-14 15:26
|
||||
|
||||
from django.db import migrations
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
("gestioncof", "0020_merge_20241218_2240"),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.DeleteModel(
|
||||
name="Club",
|
||||
),
|
||||
]
|
|
@ -151,17 +151,6 @@ def create_user_profile(sender, instance, created, **kwargs):
|
|||
def post_delete_user(sender, instance, *args, **kwargs):
|
||||
instance.user.delete()
|
||||
|
||||
|
||||
class Club(models.Model):
|
||||
name = models.CharField("Nom", max_length=200, unique=True)
|
||||
description = models.TextField("Description", blank=True)
|
||||
respos = models.ManyToManyField(User, related_name="clubs_geres", blank=True)
|
||||
membres = models.ManyToManyField(User, related_name="clubs", blank=True)
|
||||
|
||||
def __str__(self):
|
||||
return self.name
|
||||
|
||||
|
||||
class Event(models.Model):
|
||||
title = models.CharField("Titre", max_length=200)
|
||||
location = models.CharField("Lieu", max_length=200)
|
||||
|
|
|
@ -67,6 +67,7 @@
|
|||
{% if user.profile.is_cof %}
|
||||
<li><a href="{% url "calendar" %}">Calendrier dynamique</a></li>
|
||||
<li><a href="{% url "petits-cours-inscription" %}">Inscription pour donner des petits cours</a></li>
|
||||
<li><a href="{% url "cof_clubs:club-list" %}">Budget des clubs</a></li>
|
||||
{% endif %}
|
||||
{% if not user.profile.login_clipper %}
|
||||
<li><a href="{% url "password_change" %}">Changer mon mot de passe</a></li>
|
||||
|
@ -97,7 +98,7 @@
|
|||
<li><a href="{% url "admin:index" %}">Administration générale</a></li>
|
||||
<li><a href="{% url "petits-cours-demandes-list" %}">Demandes de petits cours</a></li>
|
||||
<li><a href="{% url "registration" %}">Inscription d'un nouveau membre</a></li>
|
||||
<li><a href="{% url "liste-clubs" %}">Gestion des clubs</a></li>
|
||||
<li><a href="{% url "cof_clubs:club-list" %}">Gestion des clubs</a></li>
|
||||
</ul>
|
||||
<ul>
|
||||
<h4>Évènements & Sondages</h4>
|
||||
|
|
|
@ -1,25 +0,0 @@
|
|||
{% extends "base_title.html" %}
|
||||
|
||||
{% block page_size %}col-sm-8{% endblock %}
|
||||
|
||||
{% block realcontent %}
|
||||
<h2>Clubs enregistrés sur GestioCOF</h2>
|
||||
<ul>
|
||||
{% for club in owned_clubs %}
|
||||
<li>
|
||||
<a href="{% url "membres-club" club.name %}">
|
||||
{{ club }} ({% for respo in club.respos.all %}{{ respo.get_full_name }}, {% empty %}pas de respo{% endfor %})
|
||||
</a>
|
||||
</li>
|
||||
{% endfor %}
|
||||
{% if other_clubs %}
|
||||
{% for club in other_clubs %}
|
||||
<li>
|
||||
<p>
|
||||
{{ club }} ({% for respo in club.respos.all %}{{ respo.get_full_name }}, {% empty %}pas de respo{% endfor %})
|
||||
</p>
|
||||
</li>
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
</ul>
|
||||
{% endblock %}
|
|
@ -1,41 +0,0 @@
|
|||
{% extends "base_title.html" %}
|
||||
|
||||
|
||||
{% block realcontent %}
|
||||
<h2>{{ club }}</h2>
|
||||
|
||||
|
||||
{% if club.respos.exists %}
|
||||
<h3>Respo{{ club.respos.all|pluralize }}</h3>
|
||||
<table class="table table-striped">
|
||||
{% for member in club.respos.all %}
|
||||
<tr>
|
||||
<td>{{ member }}</td>
|
||||
<td>{{ member.email }}</td>
|
||||
<td><a class="glyphicon glyphicon-arrow-down"
|
||||
href="{% url "change-respo" club.name member.id %}"></a></td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</table>
|
||||
{% else %}
|
||||
<h3>Pas de respo</h3>
|
||||
{% endif %}
|
||||
|
||||
|
||||
{% if club.membres.exists %}
|
||||
<h3>Liste des membres</h3>
|
||||
<table class="table table-striped">
|
||||
{% for member in members_no_respo %}
|
||||
<tr>
|
||||
<td>{{ member }}</td>
|
||||
<td>{{ member.email }}</td>
|
||||
<td><a class="glyphicon glyphicon-arrow-up"
|
||||
href="{% url "change-respo" club.name member.id %}"></a></td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</table>
|
||||
{% else %}
|
||||
Ce club ne comporte actuellement aucun membre.
|
||||
{% endif %}
|
||||
|
||||
{% endblock %}
|
|
@ -8,11 +8,11 @@ from django.contrib.messages.storage.base import Message
|
|||
from django.core import mail
|
||||
from django.core.mail import EmailMessage
|
||||
from django.template import loader
|
||||
from django.test import Client, TestCase, override_settings
|
||||
from django.test import TestCase, override_settings
|
||||
from django.urls import reverse
|
||||
|
||||
from bda.models import Salle, Tirage
|
||||
from gestioncof.models import CalendarSubscription, Club, Event, Survey, SurveyAnswer
|
||||
from gestioncof.models import CalendarSubscription, Event, Survey, SurveyAnswer
|
||||
from gestioncof.tests.mixins import MegaHelperMixin, ViewTestCaseMixin
|
||||
from shared.autocomplete import Clipper, LDAPSearch
|
||||
from shared.tests.mixins import CSVResponseMixin, ICalMixin, MockLDAPMixin
|
||||
|
@ -617,121 +617,6 @@ class ExportMegaRemarksViewTests(MegaHelperMixin, ViewTestCaseMixin, TestCase):
|
|||
)
|
||||
|
||||
|
||||
class ClubListViewTests(ViewTestCaseMixin, TestCase):
|
||||
url_name = "liste-clubs"
|
||||
url_expected = "/gestion/clubs/liste"
|
||||
|
||||
auth_user = "member"
|
||||
auth_forbidden = [None, "user"]
|
||||
|
||||
def setUp(self):
|
||||
super().setUp()
|
||||
|
||||
self.c1 = Club.objects.create(name="Club1")
|
||||
self.c2 = Club.objects.create(name="Club2")
|
||||
|
||||
m = self.users["member"]
|
||||
self.c1.membres.add(m)
|
||||
self.c1.respos.add(m)
|
||||
|
||||
def test_as_member(self):
|
||||
r = self.client.get(self.url)
|
||||
|
||||
self.assertEqual(r.status_code, 200)
|
||||
self.assertEqual(r.context["owned_clubs"].get(), self.c1)
|
||||
self.assertEqual(r.context["other_clubs"].get(), self.c2)
|
||||
|
||||
def test_as_staff(self):
|
||||
u = self.users["staff"]
|
||||
c = Client()
|
||||
c.force_login(u, backend="django.contrib.auth.backends.ModelBackend")
|
||||
|
||||
r = c.get(self.url)
|
||||
|
||||
self.assertEqual(r.status_code, 200)
|
||||
self.assertQuerysetEqual(
|
||||
r.context["owned_clubs"],
|
||||
map(repr, [self.c1, self.c2]),
|
||||
transform=repr,
|
||||
ordered=False,
|
||||
)
|
||||
|
||||
|
||||
class ClubMembersViewTests(ViewTestCaseMixin, TestCase):
|
||||
url_name = "membres-club"
|
||||
|
||||
auth_user = "staff"
|
||||
auth_forbidden = [None, "user", "member"]
|
||||
|
||||
@property
|
||||
def url_kwargs(self):
|
||||
return {"name": self.c.name}
|
||||
|
||||
@property
|
||||
def url_expected(self):
|
||||
return "/gestion/clubs/membres/{}".format(self.c.name)
|
||||
|
||||
def setUp(self):
|
||||
super().setUp()
|
||||
|
||||
self.u1 = create_user("u1")
|
||||
self.u2 = create_user("u2")
|
||||
|
||||
self.c = Club.objects.create(name="Club")
|
||||
self.c.membres.add(self.u1, self.u2)
|
||||
self.c.respos.add(self.u1)
|
||||
|
||||
def test_as_staff(self):
|
||||
r = self.client.get(self.url)
|
||||
|
||||
self.assertEqual(r.status_code, 200)
|
||||
self.assertEqual(r.context["members_no_respo"].get(), self.u2)
|
||||
|
||||
def test_as_respo(self):
|
||||
u = self.users["user"]
|
||||
self.c.respos.add(u)
|
||||
|
||||
c = Client()
|
||||
c.force_login(u, backend="django.contrib.auth.backends.ModelBackend")
|
||||
r = c.get(self.url)
|
||||
|
||||
self.assertEqual(r.status_code, 200)
|
||||
|
||||
|
||||
class ClubChangeRespoViewTests(ViewTestCaseMixin, TestCase):
|
||||
url_name = "change-respo"
|
||||
|
||||
auth_user = "staff"
|
||||
auth_forbidden = [None, "user", "member"]
|
||||
|
||||
@property
|
||||
def url_kwargs(self):
|
||||
return {"club_name": self.c.name, "user_id": self.users["user"].pk}
|
||||
|
||||
@property
|
||||
def url_expected(self):
|
||||
return "/gestion/clubs/change_respo/{}/{}".format(
|
||||
self.c.name, self.users["user"].pk
|
||||
)
|
||||
|
||||
def setUp(self):
|
||||
super().setUp()
|
||||
|
||||
self.c = Club.objects.create(name="Club")
|
||||
|
||||
def test(self):
|
||||
u = self.users["user"]
|
||||
expected_redirect = reverse("membres-club", kwargs={"name": self.c.name})
|
||||
self.c.membres.add(u)
|
||||
|
||||
r = self.client.get(self.url)
|
||||
self.assertRedirects(r, expected_redirect)
|
||||
self.assertIn(u, self.c.respos.all())
|
||||
|
||||
self.client.get(self.url)
|
||||
self.assertNotIn(u, self.c.respos.all())
|
||||
|
||||
|
||||
class CalendarViewTests(ViewTestCaseMixin, TestCase):
|
||||
url_name = "calendar"
|
||||
url_expected = "/gestion/calendar/subscription"
|
||||
|
|
|
@ -46,16 +46,6 @@ calendar_patterns = [
|
|||
path("<slug:token>/calendar.ics", views.calendar_ics, name="calendar.ics"),
|
||||
]
|
||||
|
||||
clubs_patterns = [
|
||||
path("membres/<slug:name>", views.membres_club, name="membres-club"),
|
||||
path("liste", views.liste_clubs, name="liste-clubs"),
|
||||
path(
|
||||
"change_respo/<slug:club_name>/<int:user_id>",
|
||||
views.change_respo,
|
||||
name="change-respo",
|
||||
),
|
||||
]
|
||||
|
||||
registration_patterns = [
|
||||
# Inscription d'un nouveau membre
|
||||
path("", views.registration, name="registration"),
|
||||
|
@ -171,10 +161,6 @@ urlpatterns = [
|
|||
# -----
|
||||
path("calendar/", include(calendar_patterns)),
|
||||
# -----
|
||||
# Clubs
|
||||
# -----
|
||||
path("clubs/", include(clubs_patterns)),
|
||||
# -----
|
||||
# Sympa export
|
||||
# -----
|
||||
path("sympa/", include(sympa_patterns)),
|
||||
|
|
|
@ -3,6 +3,7 @@ import uuid
|
|||
from datetime import timedelta
|
||||
from urllib.parse import parse_qs, urlencode, urlparse, urlunparse
|
||||
|
||||
from cof_clubs.forms import ClubsForm
|
||||
from django.contrib import messages
|
||||
from django.contrib.auth.decorators import login_required
|
||||
from django.contrib.auth.mixins import LoginRequiredMixin
|
||||
|
@ -35,7 +36,6 @@ from gestioncof.decorators import (
|
|||
)
|
||||
from gestioncof.forms import (
|
||||
CalendarForm,
|
||||
ClubsForm,
|
||||
EventForm,
|
||||
EventFormset,
|
||||
EventStatusFilterForm,
|
||||
|
@ -54,7 +54,6 @@ from gestioncof.forms import (
|
|||
)
|
||||
from gestioncof.models import (
|
||||
CalendarSubscription,
|
||||
Club,
|
||||
CofProfile,
|
||||
Event,
|
||||
EventCommentField,
|
||||
|
@ -426,7 +425,10 @@ def profile(request):
|
|||
user_form.save()
|
||||
profile_form.save()
|
||||
messages.success(request, _("Votre profil a été mis à jour avec succès !"))
|
||||
context = {"user_form": user_form, "profile_form": profile_form}
|
||||
context = {
|
||||
"user_form": user_form,
|
||||
"profile_form": profile_form,
|
||||
}
|
||||
return render(request, "gestioncof/profile.html", context)
|
||||
|
||||
|
||||
|
@ -501,7 +503,12 @@ def registration_form2(request, login_clipper=None, username=None, fullname=None
|
|||
current_registrations=current_registrations,
|
||||
)
|
||||
# Clubs
|
||||
clubs_form = ClubsForm(initial={"clubs": member.clubs.all()})
|
||||
clubs_form = ClubsForm(
|
||||
initial={
|
||||
"respo": member.respo_set.all(),
|
||||
"budget_manager": member.managed_budgets_set.all(),
|
||||
}
|
||||
)
|
||||
else:
|
||||
profile_form = RegistrationKFProfileForm(instance=profile)
|
||||
registration_set_ro_fields(user_form, profile_form)
|
||||
|
@ -624,13 +631,9 @@ def registration(request):
|
|||
) = EventRegistration.objects.get_or_create(
|
||||
user=member, event=form.event
|
||||
)
|
||||
update_event_form_comments(
|
||||
form.event, form, current_registration
|
||||
)
|
||||
update_event_form_comments(form.event, form, current_registration)
|
||||
current_registration.options.set(all_choices)
|
||||
current_registration.paid = (
|
||||
form.cleaned_data["status"] == "paid"
|
||||
)
|
||||
current_registration.paid = form.cleaned_data["status"] == "paid"
|
||||
current_registration.save()
|
||||
# if form.event.title == "Mega 15" and created_reg:
|
||||
# field = EventCommentField.objects.get(
|
||||
|
@ -647,9 +650,13 @@ def registration(request):
|
|||
# possibilité d'associer un mail aux événements
|
||||
# send_custom_mail(...)
|
||||
# Enregistrement des inscriptions aux clubs
|
||||
member.clubs.clear()
|
||||
for club in clubs_form.cleaned_data["clubs"]:
|
||||
club.membres.add(member)
|
||||
member.respo_set.clear()
|
||||
member.managed_budgets_set.clear()
|
||||
for club in clubs_form.cleaned_data["respo"]:
|
||||
club.respos.add(member)
|
||||
club.save()
|
||||
for club in clubs_form.cleaned_data["budget_manager"]:
|
||||
club.budget_managers.add(member)
|
||||
club.save()
|
||||
|
||||
# ---
|
||||
|
@ -752,48 +759,6 @@ def self_kf_registration(request):
|
|||
# -----
|
||||
|
||||
|
||||
@login_required
|
||||
def membres_club(request, name):
|
||||
# Vérification des permissions : l'utilisateur doit être membre du burô
|
||||
# ou respo du club.
|
||||
user = request.user
|
||||
club = get_object_or_404(Club, name=name)
|
||||
if not request.user.profile.is_buro and club not in user.clubs_geres.all():
|
||||
return HttpResponseForbidden("<h1>Permission denied</h1>")
|
||||
members_no_respo = club.membres.exclude(clubs_geres=club).all()
|
||||
return render(
|
||||
request,
|
||||
"membres_clubs.html",
|
||||
{"club": club, "members_no_respo": members_no_respo},
|
||||
)
|
||||
|
||||
|
||||
@buro_required
|
||||
def change_respo(request, club_name, user_id):
|
||||
club = get_object_or_404(Club, name=club_name)
|
||||
user = get_object_or_404(User, id=user_id)
|
||||
if user in club.respos.all():
|
||||
club.respos.remove(user)
|
||||
elif user in club.membres.all():
|
||||
club.respos.add(user)
|
||||
else:
|
||||
raise Http404
|
||||
return redirect("membres-club", name=club_name)
|
||||
|
||||
|
||||
@cof_required
|
||||
def liste_clubs(request):
|
||||
clubs = Club.objects
|
||||
if request.user.profile.is_buro:
|
||||
data = {"owned_clubs": clubs.all()}
|
||||
else:
|
||||
data = {
|
||||
"owned_clubs": request.user.clubs_geres.all(),
|
||||
"other_clubs": clubs.exclude(respos=request.user),
|
||||
}
|
||||
return render(request, "liste_clubs.html", data)
|
||||
|
||||
|
||||
@buro_required
|
||||
def export_members(request):
|
||||
response = HttpResponse(content_type="text/csv")
|
||||
|
|
9780
shared/static/bulma.css
vendored
Normal file
9780
shared/static/bulma.css
vendored
Normal file
File diff suppressed because it is too large
Load diff
1
shared/static/css.map
Normal file
1
shared/static/css.map
Normal file
File diff suppressed because one or more lines are too long
21
shared/static/vendor/bulma/LICENSE
vendored
Normal file
21
shared/static/vendor/bulma/LICENSE
vendored
Normal file
|
@ -0,0 +1,21 @@
|
|||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2023 Jeremy Thomas
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
145
shared/static/vendor/bulma/README.md
vendored
Normal file
145
shared/static/vendor/bulma/README.md
vendored
Normal file
|
@ -0,0 +1,145 @@
|
|||
# [Bulma](https://bulma.io)
|
||||
|
||||
Bulma is a **modern CSS framework** based on [Flexbox](https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Flexible_Box_Layout/Using_CSS_flexible_boxes).
|
||||
|
||||

|
||||
[][npm-link]
|
||||
[][npm-link]
|
||||
[](https://www.jsdelivr.com/package/npm/bulma)
|
||||
[![Awesome][awesome-badge]][awesome-link]
|
||||
[](https://gitter.im/jgthms/bulma)
|
||||
[](https://travis-ci.org/jgthms/bulma)
|
||||
|
||||
<a href="https://bulma.io"><img src="https://raw.githubusercontent.com/jgthms/bulma/master/docs/images/bulma-banner.png" alt="Bulma: a Flexbox CSS framework" style="max-width:100%;" width="600"></a>
|
||||
|
||||
## Quick install
|
||||
|
||||
Bulma is constantly in development! Try it out now:
|
||||
|
||||
### NPM
|
||||
|
||||
```sh
|
||||
npm install bulma
|
||||
```
|
||||
|
||||
**or**
|
||||
|
||||
### Yarn
|
||||
|
||||
```sh
|
||||
yarn add bulma
|
||||
```
|
||||
|
||||
### Bower
|
||||
|
||||
```sh
|
||||
bower install bulma
|
||||
```
|
||||
|
||||
### Import
|
||||
|
||||
After installation, you can import the CSS file into your project using this snippet:
|
||||
|
||||
```sh
|
||||
@import 'bulma/css/bulma.css'
|
||||
```
|
||||
|
||||
### CDN
|
||||
|
||||
[https://www.jsdelivr.com/package/npm/bulma](https://www.jsdelivr.com/package/npm/bulma)
|
||||
|
||||
Feel free to raise an issue or submit a pull request.
|
||||
|
||||
## CSS only
|
||||
|
||||
Bulma is a **CSS** framework. As such, the sole output is a single CSS file: [bulma.css](https://github.com/jgthms/bulma/blob/main/css/bulma.css)
|
||||
|
||||
You can either use that file, "out of the box", or download the Sass source files to customize the [variables](https://bulma.io/documentation/customize/#docsNav).
|
||||
|
||||
There is **no** JavaScript included. People generally want to use their own JS implementation (and usually already have one). Bulma can be considered "environment agnostic": it's just the style layer on top of the logic.
|
||||
|
||||
## Browser Support
|
||||
|
||||
Bulma uses [autoprefixer](https://github.com/postcss/autoprefixer) to make (most) Flexbox features compatible with earlier browser versions. According to [Can I use](https://caniuse.com/#feat=flexbox), Bulma is compatible with **recent** versions of:
|
||||
|
||||
- Chrome
|
||||
- Edge
|
||||
- Firefox
|
||||
- Opera
|
||||
- Safari
|
||||
|
||||
Internet Explorer (10+) is only partially supported.
|
||||
|
||||
## Documentation
|
||||
|
||||
The documentation resides in the [docs](docs) directory, and is built with the Ruby-based [Jekyll](https://jekyllrb.com/) tool.
|
||||
|
||||
Browse the [online documentation here.](https://bulma.io/documentation/start/overview/)
|
||||
|
||||
## Related projects
|
||||
|
||||
| Project | Description |
|
||||
| ------------------------------------------------------------------------------------ | ---------------------------------------------------------------------------------------------------------------- |
|
||||
| [Bulma with Attribute Modules](https://github.com/j5bot/bulma-attribute-selectors) | Adds support for attribute-based selectors |
|
||||
| [Bulma with Rails](https://github.com/joshuajansen/bulma-rails) | Integrates Bulma with the rails asset pipeline |
|
||||
| [BulmaRazor](https://github.com/loogn/bulmarazor) | A lightweight component library based on Bulma and Blazor. |
|
||||
| [Vue Admin (dead)](https://github.com/vue-bulma/vue-admin) | Vue Admin framework powered by Bulma |
|
||||
| [Bulmaswatch](https://github.com/jenil/bulmaswatch) | Free themes for Bulma |
|
||||
| [Goldfish (read-only)](https://github.com/Caiyeon/goldfish) | Vault UI with Bulma, Golang, and Vue Admin |
|
||||
| [ember-bulma](https://github.com/open-tux/ember-bulma) | Ember addon providing a collection of UI components for Bulma |
|
||||
| [Bloomer](https://bloomer.js.org) | A set of React components for Bulma |
|
||||
| [React-bulma](https://github.com/kulakowka/react-bulma) | React.js components for Bulma |
|
||||
| [Buefy](https://buefy.org/) | Lightweight UI components for Vue.js based on Bulma |
|
||||
| [vue-bulma-components](https://github.com/vouill/vue-bulma-components) | Bulma components for Vue.js with straightforward syntax |
|
||||
| [BulmaJS](https://github.com/VizuaaLOG/BulmaJS) | Javascript integration for Bulma. Written in ES6 with a data-\* API |
|
||||
| [Bulma-modal-fx](https://github.com/postare/bulma-modal-fx) | A set of modal window effects with CSS transitions and animations for Bulma |
|
||||
| [Bulma Stylus](https://github.com/groenroos/bulma-stylus) | Up-to-date 1:1 translation to Stylus |
|
||||
| [Bulma.styl (read-only)](https://github.com/log1x/bulma.styl) | 1:1 Stylus translation of Bulma 0.6.11 |
|
||||
| [elm-bulma](https://github.com/surprisetalk/elm-bulma) | Bulma + Elm |
|
||||
| [elm-bulma-classes](https://github.com/ahstro/elm-bulma-classes) | Bulma classes prepared for usage with Elm |
|
||||
| [Bulma Customizer](https://bulma-customizer.bstash.io/) | Bulma Customizer – Create your own **bespoke** Bulma build |
|
||||
| [Fulma](https://fulma.github.io/Fulma/) | Wrapper around Bulma for [fable-react](https://github.com/fable-compiler/fable-react) |
|
||||
| [Laravel Enso](https://github.com/laravel-enso/enso) | SPA Admin Panel built with Bulma, VueJS and Laravel |
|
||||
| [Django Bulma](https://github.com/timonweb/django-bulma) | Integrates Bulma with Django |
|
||||
| [Bulma Templates](https://github.com/dansup/bulma-templates) | Free Templates for Bulma |
|
||||
| [React Bulma Components](https://github.com/couds/react-bulma-components) | Another React wrap on React for Bulma.io |
|
||||
| [purescript-bulma](https://github.com/sectore/purescript-bulma) | PureScript bindings for Bulma |
|
||||
| [Vue Datatable](https://github.com/laravel-enso/vuedatatable) | Bulma themed datatable based on Vue, Laravel & JSON templates |
|
||||
| [bulma-fluent](https://mubaidr.github.io/bulma-fluent/) | Fluent Design Theme for Bulma inspired by Microsoft’s Fluent Design System |
|
||||
| [csskrt-csskrt](https://github.com/4d11/csskrt-csskrt) | Automatically add Bulma classes to HTML files |
|
||||
| [bulma-pagination-react](https://github.com/hipstersmoothie/bulma-pagination-react) | Bulma pagination as a react component |
|
||||
| [bulma-helpers](https://github.com/jmaczan/bulma-helpers) | Functional / Atomic CSS classes for Bulma |
|
||||
| [bulma-swatch-hook](https://github.com/hipstersmoothie/bulma-swatch-hook) | Bulma swatches as a react hook and a component |
|
||||
| [BulmaWP (read-only)](https://github.com/tomhrtly/BulmaWP) | Starter WordPress theme for Bulma |
|
||||
| [Ralma](https://github.com/aldi/ralma) | Stateless Ractive.js Components for Bulma |
|
||||
| [Django Simple Bulma](https://github.com/python-discord/django-simple-bulma) | Lightweight integration of Bulma and Bulma-Extensions for your Django app |
|
||||
| [rbx](https://dfee.github.io/rbx) | Comprehensive React UI Framework written in TypeScript |
|
||||
| [Awesome Bulma Templates](https://github.com/aldi/awesome-bulma-templates) | Free real-world Templates built with Bulma |
|
||||
| [Trunx](https://github.com/fibo/trunx) | Super Saiyan React components, son of awesome Bulma |
|
||||
| [@aybolit/bulma](https://github.com/web-padawan/aybolit/tree/master/packages/bulma) | Web Components library inspired by Bulma and Bulma-extensions |
|
||||
| [Drulma](https://www.drupal.org/project/drulma) | Drupal theme for Bulma. |
|
||||
| [Bulrush](https://github.com/textbook/bulrush) | A Bulma-based Python Pelican blog theme |
|
||||
| [Bulma Variable Export](https://github.com/service-paradis/bulma-variables-export) | Access Bulma Variables in Javascript/Typescript in project using Webpack |
|
||||
| [Bulmil](https://github.com/gomah/bulmil) | An agnostic UI components library based on Web Components, made with Bulma & Stencil. |
|
||||
| [Svelte Bulma Components](https://github.com/elcobvg/svelte-bulma-components) | Library of UI components to be used in [Svelte.js](https://svelte.technology/) or standalone. |
|
||||
| [Bulma Nunjucks Starterkit](https://github.com/benninkcorien/nunjucks-starter-kit) | Starterkit for Nunjucks with Bulma. |
|
||||
| [Bulma-Social](https://github.com/aldi/bulma-social) | Social Buttons and Colors for Bulma |
|
||||
| [Divjoy](https://divjoy.com/?kit=bulma) | React codebase generator with Bulma templates |
|
||||
| [Blazorise](https://github.com/Megabit/Blazorise) | Blazor component library with the support for Bulma CSS framework |
|
||||
| [Oruga-Bulma](https://github.com/oruga-ui/theme-bulma) | Bulma theme for [Oruga UI](https://oruga.io) |
|
||||
| [@bulvar/bulma](https://github.com/daniil4udo/bulvar/tree/master/packages/bulma) | Bulma with [CSS Variables](https://developer.mozilla.org/en-US/docs/Web/CSS/Using_CSS_custom_properties) support |
|
||||
| [@angular-bulma](https://quinnjr.github.io/angular-bulma) | [Angular](https://angular.io/) directives and components to use in your Bulma projects |
|
||||
| [Bulma CSS Class Completion](https://github.com/eliutdev/bulma-css-class-completion) | CSS class name completion for the HTML class attribute based on Bulma CSS classes. |
|
||||
| [Crispy-Bulma](https://github.com/ckrybus/crispy-bulma) | Bulma template pack for django-crispy-forms |
|
||||
| [Manifest](https://manifest.build) | Manifest is a lightweight Backend-as-a-Service with essential features: DB, Admin panel, API, JS SDK |
|
||||
| [Reactive Bulma](https://github.com/NicolasOmar/reactive-bulma) | A component library based on React, Bulma, Typescript and Rollup |
|
||||
|
||||
<p>Browser testing via<br /><a href="https://www.lambdatest.com/" target="_blank"><img src="https://bulma.io/assets/images/amis/lambdatest-logo.png" width="168" height="40" /></a></p>
|
||||
|
||||
## Copyright and license 
|
||||
|
||||
Code copyright 2023 Jeremy Thomas. Code released under [the MIT license](https://github.com/jgthms/bulma/blob/main/LICENSE).
|
||||
|
||||
[npm-link]: https://www.npmjs.com/package/bulma
|
||||
[awesome-link]: https://github.com/awesome-css-group/awesome-css
|
||||
[awesome-badge]: https://cdn.rawgit.com/sindresorhus/awesome/d7305f38d29fed78fa85652e3a63e154dd8e8829/media/badge.svg
|
4
shared/static/vendor/bulma/bulma.scss
vendored
Normal file
4
shared/static/vendor/bulma/bulma.scss
vendored
Normal file
|
@ -0,0 +1,4 @@
|
|||
@charset "utf-8";
|
||||
|
||||
/*! bulma.io v1.0.3 | MIT License | github.com/jgthms/bulma */
|
||||
@use "sass";
|
21559
shared/static/vendor/bulma/css/bulma.css
vendored
Normal file
21559
shared/static/vendor/bulma/css/bulma.css
vendored
Normal file
File diff suppressed because it is too large
Load diff
1
shared/static/vendor/bulma/css/bulma.css.map
vendored
Normal file
1
shared/static/vendor/bulma/css/bulma.css.map
vendored
Normal file
File diff suppressed because one or more lines are too long
3
shared/static/vendor/bulma/css/bulma.min.css
vendored
Normal file
3
shared/static/vendor/bulma/css/bulma.min.css
vendored
Normal file
File diff suppressed because one or more lines are too long
19656
shared/static/vendor/bulma/css/versions/bulma-no-dark-mode.css
vendored
Normal file
19656
shared/static/vendor/bulma/css/versions/bulma-no-dark-mode.css
vendored
Normal file
File diff suppressed because it is too large
Load diff
1
shared/static/vendor/bulma/css/versions/bulma-no-dark-mode.css.map
vendored
Normal file
1
shared/static/vendor/bulma/css/versions/bulma-no-dark-mode.css.map
vendored
Normal file
File diff suppressed because one or more lines are too long
3
shared/static/vendor/bulma/css/versions/bulma-no-dark-mode.min.css
vendored
Normal file
3
shared/static/vendor/bulma/css/versions/bulma-no-dark-mode.min.css
vendored
Normal file
File diff suppressed because one or more lines are too long
13947
shared/static/vendor/bulma/css/versions/bulma-no-helpers-prefixed.css
vendored
Normal file
13947
shared/static/vendor/bulma/css/versions/bulma-no-helpers-prefixed.css
vendored
Normal file
File diff suppressed because it is too large
Load diff
1
shared/static/vendor/bulma/css/versions/bulma-no-helpers-prefixed.css.map
vendored
Normal file
1
shared/static/vendor/bulma/css/versions/bulma-no-helpers-prefixed.css.map
vendored
Normal file
File diff suppressed because one or more lines are too long
3
shared/static/vendor/bulma/css/versions/bulma-no-helpers-prefixed.min.css
vendored
Normal file
3
shared/static/vendor/bulma/css/versions/bulma-no-helpers-prefixed.min.css
vendored
Normal file
File diff suppressed because one or more lines are too long
13947
shared/static/vendor/bulma/css/versions/bulma-no-helpers.css
vendored
Normal file
13947
shared/static/vendor/bulma/css/versions/bulma-no-helpers.css
vendored
Normal file
File diff suppressed because it is too large
Load diff
1
shared/static/vendor/bulma/css/versions/bulma-no-helpers.css.map
vendored
Normal file
1
shared/static/vendor/bulma/css/versions/bulma-no-helpers.css.map
vendored
Normal file
File diff suppressed because one or more lines are too long
3
shared/static/vendor/bulma/css/versions/bulma-no-helpers.min.css
vendored
Normal file
3
shared/static/vendor/bulma/css/versions/bulma-no-helpers.min.css
vendored
Normal file
File diff suppressed because one or more lines are too long
21559
shared/static/vendor/bulma/css/versions/bulma-prefixed.css
vendored
Normal file
21559
shared/static/vendor/bulma/css/versions/bulma-prefixed.css
vendored
Normal file
File diff suppressed because it is too large
Load diff
1
shared/static/vendor/bulma/css/versions/bulma-prefixed.css.map
vendored
Normal file
1
shared/static/vendor/bulma/css/versions/bulma-prefixed.css.map
vendored
Normal file
File diff suppressed because one or more lines are too long
3
shared/static/vendor/bulma/css/versions/bulma-prefixed.min.css
vendored
Normal file
3
shared/static/vendor/bulma/css/versions/bulma-prefixed.min.css
vendored
Normal file
File diff suppressed because one or more lines are too long
62
shared/static/vendor/bulma/package.json
vendored
Normal file
62
shared/static/vendor/bulma/package.json
vendored
Normal file
|
@ -0,0 +1,62 @@
|
|||
{
|
||||
"name": "bulma",
|
||||
"version": "1.0.3",
|
||||
"homepage": "https://bulma.io",
|
||||
"author": {
|
||||
"name": "Jeremy Thomas",
|
||||
"email": "bbxdesign@gmail.com",
|
||||
"url": "https://jgthms.com"
|
||||
},
|
||||
"description": "Modern CSS framework based on Flexbox",
|
||||
"main": "bulma.scss",
|
||||
"unpkg": "css/bulma.css",
|
||||
"style": "css/bulma.min.css",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git+https://github.com/jgthms/bulma.git"
|
||||
},
|
||||
"license": "MIT",
|
||||
"keywords": [
|
||||
"css",
|
||||
"sass",
|
||||
"scss",
|
||||
"flexbox",
|
||||
"grid",
|
||||
"responsive",
|
||||
"framework"
|
||||
],
|
||||
"bugs": {
|
||||
"url": "https://github.com/jgthms/bulma/issues"
|
||||
},
|
||||
"devDependencies": {
|
||||
"cssnano": "^7.0.6",
|
||||
"postcss-cli": "^11.0.0",
|
||||
"prettier": "^3.4.2",
|
||||
"rimraf": "^6.0.1",
|
||||
"sass": "^1.83.0"
|
||||
},
|
||||
"scripts": {
|
||||
"build-bulma": "sass --style=expanded --source-map bulma.scss css/bulma.css",
|
||||
"minify-bulma": "postcss css/bulma.css --no-map --use cssnano --output css/bulma.min.css",
|
||||
"version-no-dark-mode": "sass --style=expanded --source-map versions/bulma-no-dark-mode.scss css/versions/bulma-no-dark-mode.css",
|
||||
"version-no-helpers": "sass --style=expanded --source-map versions/bulma-no-helpers.scss css/versions/bulma-no-helpers.css",
|
||||
"version-no-helpers-prefixed": "sass --style=expanded --source-map versions/bulma-no-helpers-prefixed.scss css/versions/bulma-no-helpers-prefixed.css",
|
||||
"version-prefixed": "sass --style=expanded --source-map versions/bulma-prefixed.scss css/versions/bulma-prefixed.css",
|
||||
"build-versions": "npm run version-no-dark-mode && npm run version-no-helpers && npm run version-no-helpers-prefixed && npm run version-prefixed",
|
||||
"minify-versions": "postcss css/versions/*.css --dir css/versions --ext min.css --no-map --use cssnano",
|
||||
"build-all": "npm run build-bulma && npm run build-versions",
|
||||
"minify-all": "npm run minify-bulma && npm run minify-versions",
|
||||
"clean": "rimraf css",
|
||||
"deploy": "npm run clean && npm run build-all && npm run minify-all",
|
||||
"test": "sass --style=expanded --source-map --watch test.scss test.css",
|
||||
"start": "npm run build-bulma -- --watch"
|
||||
},
|
||||
"files": [
|
||||
"css",
|
||||
"sass",
|
||||
"versions",
|
||||
"bulma.scss",
|
||||
"LICENSE",
|
||||
"README.md"
|
||||
]
|
||||
}
|
|
@ -12,6 +12,7 @@ let
|
|||
inherit (nix-pkgs)
|
||||
authens
|
||||
django-bootstrap-form
|
||||
django-bulma-forms
|
||||
django-cas-ng
|
||||
loadcredential
|
||||
;
|
||||
|
@ -52,6 +53,7 @@ pkgs.mkShell {
|
|||
django
|
||||
django-autocomplete-light
|
||||
django-bootstrap-form
|
||||
django-bulma-forms
|
||||
django-cas-ng
|
||||
django-cors-headers
|
||||
django-djconfig
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue