mirror of
https://github.com/ae-utbm/sith.git
synced 2026-05-22 08:50:17 +00:00
Compare commits
35 Commits
basket-timeout
...
taiste
| Author | SHA1 | Date | |
|---|---|---|---|
| 99d85e0361 | |||
| cff0dd2cf6 | |||
| 5238e2e2d6 | |||
| 4375fdf150 | |||
| a10773bb37 | |||
| ce0ddcd184 | |||
| 6cec2e74d0 | |||
| 2228a3f961 | |||
| 4b369b73a7 | |||
| 73f422db23 | |||
| 0aed36c8d9 | |||
| e2c17175f5 | |||
| 59b4c4e73c | |||
| b8623eed11 | |||
| ed791dc544 | |||
|
18e8088cf3
|
|||
| 5ef7fd0294 | |||
| afa3ea4f2c | |||
| c38fe7f9ae | |||
| c6c8781511 | |||
| 30bd4fd3ea | |||
| 019ab95773 | |||
| c83ab7c2c1 | |||
| 23dc7702c4 | |||
| def4f80ec0 | |||
| a5a31b6a4d | |||
| eaf90efd5c | |||
| 4d1019c955 | |||
| 52f112a041 | |||
| e89be22c34 | |||
| fc37cbb454 | |||
| 204e4e97bd | |||
| 57a422de8c | |||
| 75f3094dd5 | |||
| 68aa4515f9 |
@@ -1,7 +1,7 @@
|
|||||||
repos:
|
repos:
|
||||||
- repo: https://github.com/astral-sh/ruff-pre-commit
|
- repo: https://github.com/astral-sh/ruff-pre-commit
|
||||||
# Ruff version.
|
# Ruff version.
|
||||||
rev: v0.15.5
|
rev: v0.15.13
|
||||||
hooks:
|
hooks:
|
||||||
- id: ruff-check # just check the code, and print the errors
|
- id: ruff-check # just check the code, and print the errors
|
||||||
- id: ruff-check # actually fix the fixable errors, but print nothing
|
- id: ruff-check # actually fix the fixable errors, but print nothing
|
||||||
@@ -14,7 +14,7 @@ repos:
|
|||||||
- id: biome-check
|
- id: biome-check
|
||||||
additional_dependencies: ["@biomejs/biome@2.4.6"]
|
additional_dependencies: ["@biomejs/biome@2.4.6"]
|
||||||
- repo: https://github.com/rtts/djhtml
|
- repo: https://github.com/rtts/djhtml
|
||||||
rev: 3.0.10
|
rev: 3.0.11
|
||||||
hooks:
|
hooks:
|
||||||
- id: djhtml
|
- id: djhtml
|
||||||
name: format templates
|
name: format templates
|
||||||
|
|||||||
+1
-1
@@ -38,6 +38,6 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"javascript": {
|
"javascript": {
|
||||||
"globals": ["Alpine", "$", "jQuery", "gettext", "interpolate"]
|
"globals": ["Alpine", "gettext", "interpolate"]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+16
-1
@@ -16,7 +16,7 @@ from django.contrib import admin
|
|||||||
from django.forms.models import ModelForm
|
from django.forms.models import ModelForm
|
||||||
from django.http import HttpRequest
|
from django.http import HttpRequest
|
||||||
|
|
||||||
from club.models import Club, ClubRole, Membership
|
from club.models import Club, ClubLink, ClubRole, LinkType, Membership
|
||||||
|
|
||||||
|
|
||||||
@admin.register(Club)
|
@admin.register(Club)
|
||||||
@@ -67,3 +67,18 @@ class MembershipAdmin(admin.ModelAdmin):
|
|||||||
"club__name",
|
"club__name",
|
||||||
)
|
)
|
||||||
autocomplete_fields = ("user",)
|
autocomplete_fields = ("user",)
|
||||||
|
|
||||||
|
|
||||||
|
@admin.register(LinkType)
|
||||||
|
class LinkTypeAdmin(admin.ModelAdmin):
|
||||||
|
list_display = ("name", "url_base", "icon")
|
||||||
|
search_fields = ("name",)
|
||||||
|
|
||||||
|
|
||||||
|
@admin.register(ClubLink)
|
||||||
|
class ClubLinkAdmin(admin.ModelAdmin):
|
||||||
|
list_display = ("link_type", "club", "url")
|
||||||
|
list_select_related = ("link_type", "club")
|
||||||
|
autocomplete_fields = ("link_type", "club")
|
||||||
|
search_fields = ("link_type__name", "url")
|
||||||
|
list_filter = ("link_type", ("club", admin.RelatedOnlyFieldListFilter))
|
||||||
|
|||||||
+1
-1
@@ -41,7 +41,7 @@ class ClubController(ControllerBase):
|
|||||||
queryset=Membership.objects.ongoing().select_related("user", "role"),
|
queryset=Membership.objects.ongoing().select_related("user", "role"),
|
||||||
)
|
)
|
||||||
return self.get_object_or_exception(
|
return self.get_object_or_exception(
|
||||||
Club.objects.prefetch_related(prefetch), id=club_id
|
Club.objects.prefetch_related(prefetch, "links"), id=club_id
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
+42
-1
@@ -28,7 +28,14 @@ from django.db.models.functions import Lower
|
|||||||
from django.utils.functional import cached_property
|
from django.utils.functional import cached_property
|
||||||
from django.utils.translation import gettext_lazy as _
|
from django.utils.translation import gettext_lazy as _
|
||||||
|
|
||||||
from club.models import Club, ClubRole, Mailing, MailingSubscription, Membership
|
from club.models import (
|
||||||
|
Club,
|
||||||
|
ClubLink,
|
||||||
|
ClubRole,
|
||||||
|
Mailing,
|
||||||
|
MailingSubscription,
|
||||||
|
Membership,
|
||||||
|
)
|
||||||
from core.models import User
|
from core.models import User
|
||||||
from core.views.forms import SelectDateTime
|
from core.views.forms import SelectDateTime
|
||||||
from core.views.widgets.ajax_select import (
|
from core.views.widgets.ajax_select import (
|
||||||
@@ -39,6 +46,26 @@ from counter.models import Counter, Selling
|
|||||||
from counter.schemas import SaleFilterSchema
|
from counter.schemas import SaleFilterSchema
|
||||||
|
|
||||||
|
|
||||||
|
class ClubLinkForm(forms.ModelForm):
|
||||||
|
error_css_class = "error"
|
||||||
|
required_css_class = "required"
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
model = ClubLink
|
||||||
|
fields = ["url", "name", "link_type"]
|
||||||
|
widgets = {
|
||||||
|
"url": forms.URLInput(
|
||||||
|
{"pattern": "https://.*", "placeholder": "https://monlien.com"}
|
||||||
|
),
|
||||||
|
"link_type": forms.HiddenInput(),
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
ClubLinkFormSet = forms.inlineformset_factory(
|
||||||
|
Club, ClubLink, ClubLinkForm, extra=0, can_delete_extra=False
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
class ClubEditForm(forms.ModelForm):
|
class ClubEditForm(forms.ModelForm):
|
||||||
error_css_class = "error"
|
error_css_class = "error"
|
||||||
required_css_class = "required"
|
required_css_class = "required"
|
||||||
@@ -48,6 +75,20 @@ class ClubEditForm(forms.ModelForm):
|
|||||||
fields = ["address", "logo", "short_description"]
|
fields = ["address", "logo", "short_description"]
|
||||||
widgets = {"short_description": forms.Textarea()}
|
widgets = {"short_description": forms.Textarea()}
|
||||||
|
|
||||||
|
def __init__(self, *args, prefix: str | None = None, instance=None, **kwargs):
|
||||||
|
super().__init__(*args, prefix=prefix, instance=instance, **kwargs)
|
||||||
|
self.link_formset = ClubLinkFormSet(
|
||||||
|
*args, instance=self.instance, prefix="link", **kwargs
|
||||||
|
)
|
||||||
|
|
||||||
|
def is_valid(self):
|
||||||
|
return super().is_valid() and self.link_formset.is_valid()
|
||||||
|
|
||||||
|
def save(self, commit=True): # noqa: FBT002
|
||||||
|
res = super().save(commit=commit)
|
||||||
|
self.link_formset.save(commit=commit)
|
||||||
|
return res
|
||||||
|
|
||||||
|
|
||||||
class ClubAdminEditForm(ClubEditForm):
|
class ClubAdminEditForm(ClubEditForm):
|
||||||
admin_fields = ["name", "parent", "is_active"]
|
admin_fields = ["name", "parent", "is_active"]
|
||||||
|
|||||||
@@ -33,7 +33,7 @@ def migrate_roles(apps: StateApps, schema_editor):
|
|||||||
club_id=club_id,
|
club_id=club_id,
|
||||||
order=max(SITH_CLUB_ROLES) - role,
|
order=max(SITH_CLUB_ROLES) - role,
|
||||||
)
|
)
|
||||||
updates.append(When(role=role, then=new_role.id))
|
updates.append(When(club_id=club_id, role=role, then=new_role.id))
|
||||||
# all updates must happen at the same time
|
# all updates must happen at the same time
|
||||||
# otherwise, the 10 first created ClubRole would be
|
# otherwise, the 10 first created ClubRole would be
|
||||||
# re-modified after their initial creation, and it would
|
# re-modified after their initial creation, and it would
|
||||||
|
|||||||
@@ -0,0 +1,105 @@
|
|||||||
|
# Generated by Django 5.2.12 on 2026-04-27 07:39
|
||||||
|
|
||||||
|
import django.db.models.deletion
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
dependencies = [("club", "0016_clubrole_alter_membership_role")]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.CreateModel(
|
||||||
|
name="LinkType",
|
||||||
|
fields=[
|
||||||
|
(
|
||||||
|
"id",
|
||||||
|
models.AutoField(
|
||||||
|
auto_created=True,
|
||||||
|
primary_key=True,
|
||||||
|
serialize=False,
|
||||||
|
verbose_name="ID",
|
||||||
|
),
|
||||||
|
),
|
||||||
|
("name", models.CharField(max_length=40, verbose_name="name")),
|
||||||
|
(
|
||||||
|
"url_base",
|
||||||
|
models.URLField(
|
||||||
|
help_text=(
|
||||||
|
"The base url that links with this type "
|
||||||
|
"must respect (e.g. `https://www.instagram.com`)"
|
||||||
|
),
|
||||||
|
unique=True,
|
||||||
|
verbose_name="url base",
|
||||||
|
),
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"icon",
|
||||||
|
models.CharField(
|
||||||
|
help_text=(
|
||||||
|
"The fontawesome class to use "
|
||||||
|
"(e.g. `fa-brands fa-instagram`)"
|
||||||
|
),
|
||||||
|
max_length=40,
|
||||||
|
verbose_name="icon",
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
options={"verbose_name": "link type", "verbose_name_plural": "link types"},
|
||||||
|
),
|
||||||
|
migrations.CreateModel(
|
||||||
|
name="ClubLink",
|
||||||
|
fields=[
|
||||||
|
(
|
||||||
|
"id",
|
||||||
|
models.AutoField(
|
||||||
|
auto_created=True,
|
||||||
|
primary_key=True,
|
||||||
|
serialize=False,
|
||||||
|
verbose_name="ID",
|
||||||
|
),
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"name",
|
||||||
|
models.CharField(blank=True, max_length=40, verbose_name="name"),
|
||||||
|
),
|
||||||
|
("url", models.URLField(verbose_name="link url")),
|
||||||
|
(
|
||||||
|
"created_at",
|
||||||
|
models.DateTimeField(auto_now_add=True, verbose_name="created at"),
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"updated_at",
|
||||||
|
models.DateTimeField(auto_now=True, verbose_name="updated at"),
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"club",
|
||||||
|
models.ForeignKey(
|
||||||
|
on_delete=django.db.models.deletion.CASCADE,
|
||||||
|
related_name="links",
|
||||||
|
to="club.club",
|
||||||
|
verbose_name="club",
|
||||||
|
),
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"link_type",
|
||||||
|
models.ForeignKey(
|
||||||
|
on_delete=django.db.models.deletion.CASCADE,
|
||||||
|
related_name="links",
|
||||||
|
to="club.linktype",
|
||||||
|
verbose_name="link type",
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
options={
|
||||||
|
"verbose_name": "club link",
|
||||||
|
"verbose_name_plural": "club links",
|
||||||
|
"constraints": [
|
||||||
|
models.UniqueConstraint(
|
||||||
|
fields=["club", "url"],
|
||||||
|
name="club_clublink_unique_club_url",
|
||||||
|
violation_error_message="Duplicated url",
|
||||||
|
)
|
||||||
|
],
|
||||||
|
},
|
||||||
|
),
|
||||||
|
]
|
||||||
@@ -773,3 +773,81 @@ class MailingSubscription(models.Model):
|
|||||||
|
|
||||||
def fetch_format(self):
|
def fetch_format(self):
|
||||||
return self.get_email + " "
|
return self.get_email + " "
|
||||||
|
|
||||||
|
|
||||||
|
class LinkType(models.Model):
|
||||||
|
"""A link type, in order to group links and give them icons.
|
||||||
|
|
||||||
|
Notes:
|
||||||
|
Among all club links, there is a special one, with an empty base url
|
||||||
|
and a default link icon.
|
||||||
|
It is use as a fallback item when no actual link type can be found.
|
||||||
|
|
||||||
|
Danger:
|
||||||
|
LinkType.icon is content that will be raw-rendered in the template.
|
||||||
|
It is NOT safe to allow users to give it.
|
||||||
|
The edition of this field must be reserved to trusted admins.
|
||||||
|
"""
|
||||||
|
|
||||||
|
name = models.CharField(_("name"), max_length=40)
|
||||||
|
url_base = models.URLField(
|
||||||
|
"url base",
|
||||||
|
unique=True,
|
||||||
|
help_text=_(
|
||||||
|
"The base url that links with this type must respect (e.g. `%(url)s`)"
|
||||||
|
)
|
||||||
|
% {"url": "https://www.instagram.com"},
|
||||||
|
)
|
||||||
|
icon = models.CharField(
|
||||||
|
_("icon"),
|
||||||
|
max_length=40,
|
||||||
|
help_text=_("The fontawesome class to use (e.g. `fa-brands fa-instagram`)"),
|
||||||
|
)
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
verbose_name = _("link type")
|
||||||
|
verbose_name_plural = _("link types")
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return self.name
|
||||||
|
|
||||||
|
|
||||||
|
class ClubLink(models.Model):
|
||||||
|
link_type = models.ForeignKey(
|
||||||
|
LinkType,
|
||||||
|
verbose_name=_("link type"),
|
||||||
|
on_delete=models.CASCADE,
|
||||||
|
related_name="links",
|
||||||
|
)
|
||||||
|
name = models.CharField(_("name"), max_length=40, blank=True)
|
||||||
|
url = models.URLField(_("link url"))
|
||||||
|
club = models.ForeignKey(
|
||||||
|
Club, verbose_name=_("club"), on_delete=models.CASCADE, related_name="links"
|
||||||
|
)
|
||||||
|
created_at = models.DateTimeField(_("created at"), auto_now_add=True)
|
||||||
|
updated_at = models.DateTimeField(_("updated at"), auto_now=True)
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
verbose_name = _("club link")
|
||||||
|
verbose_name_plural = _("club links")
|
||||||
|
constraints = [
|
||||||
|
models.UniqueConstraint(
|
||||||
|
fields=["club", "url"],
|
||||||
|
name="club_clublink_unique_club_url",
|
||||||
|
violation_error_message=_("Duplicated url"),
|
||||||
|
)
|
||||||
|
]
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return self.url
|
||||||
|
|
||||||
|
def save(self, **kwargs):
|
||||||
|
if not self.name:
|
||||||
|
self.name = self.link_type.name
|
||||||
|
return super().save(**kwargs)
|
||||||
|
|
||||||
|
def clean(self):
|
||||||
|
if not self.url.startswith(self.link_type.url_base):
|
||||||
|
raise ValidationError(
|
||||||
|
_("This link doesn't match with the url base of its type.")
|
||||||
|
)
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ from typing import Annotated
|
|||||||
|
|
||||||
from django.db.models import Q
|
from django.db.models import Q
|
||||||
from ninja import FilterLookup, FilterSchema, ModelSchema
|
from ninja import FilterLookup, FilterSchema, ModelSchema
|
||||||
|
from pydantic import HttpUrl
|
||||||
|
|
||||||
from club.models import Club, ClubRole, Membership
|
from club.models import Club, ClubRole, Membership
|
||||||
from core.schemas import NonEmptyStr, SimpleUserSchema
|
from core.schemas import NonEmptyStr, SimpleUserSchema
|
||||||
@@ -62,6 +63,11 @@ class ClubSchema(ModelSchema):
|
|||||||
fields = ["id", "name", "logo", "is_active", "short_description", "address"]
|
fields = ["id", "name", "logo", "is_active", "short_description", "address"]
|
||||||
|
|
||||||
members: list[ClubMemberSchema]
|
members: list[ClubMemberSchema]
|
||||||
|
links: list[HttpUrl]
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def resolve_links(obj: Club):
|
||||||
|
return [link.url for link in obj.links.all()]
|
||||||
|
|
||||||
|
|
||||||
class UserMembershipSchema(ModelSchema):
|
class UserMembershipSchema(ModelSchema):
|
||||||
|
|||||||
@@ -0,0 +1,66 @@
|
|||||||
|
#club-detail {
|
||||||
|
img.club-logo {
|
||||||
|
display: block;
|
||||||
|
max-height: 200px;
|
||||||
|
max-width: 200px;
|
||||||
|
}
|
||||||
|
#club-attributes {
|
||||||
|
ul {
|
||||||
|
list-style: none;
|
||||||
|
margin-left: 0;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: .75rem;
|
||||||
|
|
||||||
|
li i {
|
||||||
|
margin-right: .5rem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&:not(.has-links) {
|
||||||
|
#club-attributes {
|
||||||
|
float: right;
|
||||||
|
margin: 1em 0 1em 2em;
|
||||||
|
|
||||||
|
@media screen and (max-width: 650px) {
|
||||||
|
margin-left: 1em;
|
||||||
|
}
|
||||||
|
@media screen and (max-width: 400px) {
|
||||||
|
float: unset;
|
||||||
|
img.club-logo {
|
||||||
|
margin: auto;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&.has-links {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row-reverse;
|
||||||
|
gap: 2em;
|
||||||
|
|
||||||
|
@media screen and (max-width: 650px) {
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 1em;
|
||||||
|
}
|
||||||
|
|
||||||
|
#club-attributes {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 1em;
|
||||||
|
min-width: 200px;
|
||||||
|
@media screen and (max-width: 650px) {
|
||||||
|
margin-top: 1em;
|
||||||
|
flex-direction: row-reverse;
|
||||||
|
justify-content: flex-end;
|
||||||
|
h4 {
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
img.club-logo {
|
||||||
|
margin-left: auto;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -21,15 +21,43 @@
|
|||||||
{% endif %}
|
{% endif %}
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
|
{% block additional_css %}
|
||||||
|
<link rel="stylesheet" href="{{ static("club/detail.scss") }}">
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
{% block content %}
|
{% block content %}
|
||||||
<div id="club_detail">
|
<h3>{{ club.name }}</h3>
|
||||||
{% if club.logo %}
|
<div id="club-detail" {% if links %}class="has-links"{% endif %}>
|
||||||
<div class="club_logo"><img src="{{ club.logo.url }}" alt="{{ club.name }}"></div>
|
<div id="club-attributes">
|
||||||
{% endif %}
|
{% if club.logo %}
|
||||||
<h3>{{ club.name }}</h3>
|
<img
|
||||||
{% if page_revision %}
|
class="club-logo"
|
||||||
{{ page_revision|markdown }}
|
src="{{ club.logo.url }}"
|
||||||
{% endif %}
|
alt="{{ club.name }}"
|
||||||
|
width="{{ club.logo.width }}"
|
||||||
|
height="{{ club.logo.height }}"
|
||||||
|
>
|
||||||
|
{% endif %}
|
||||||
|
{% if links %}
|
||||||
|
<div id="club-links">
|
||||||
|
<h4>{% trans %}Links{% endtrans %}</h4>
|
||||||
|
<ul>
|
||||||
|
{% for link in links %}
|
||||||
|
<li>
|
||||||
|
<a href="{{ link.url }}" rel="noopener external" target="_blank">
|
||||||
|
<i class="{{ link.link_type.icon }} fa-xl"></i>{{ link.name }}
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
{% endfor %}
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
|
<div id="club-page">
|
||||||
|
{% if page_revision %}
|
||||||
|
{{ page_revision|markdown }}
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
|
|||||||
@@ -69,6 +69,18 @@
|
|||||||
{{ club.name }} {% if not club.is_active %}({% trans %}inactive{% endtrans %}){% endif %}
|
{{ club.name }} {% if not club.is_active %}({% trans %}inactive{% endtrans %}){% endif %}
|
||||||
</h4>
|
</h4>
|
||||||
</a>
|
</a>
|
||||||
|
{% set links = club.links.all() %}
|
||||||
|
{% if links %}
|
||||||
|
<br>
|
||||||
|
<div class="row gap-2x">
|
||||||
|
{% for link in club.links.all() %}
|
||||||
|
<a href="{{ link.url }}" rel="noopener external" target="_blank">
|
||||||
|
<i class="{{ link.link_type.icon }} fa-xl"></i>
|
||||||
|
<strong>{{ link.name }}</strong>
|
||||||
|
</a>
|
||||||
|
{% endfor %}
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
{{ club.short_description|markdown }}
|
{{ club.short_description|markdown }}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1,9 +1,63 @@
|
|||||||
{% extends "core/base.jinja" %}
|
{% extends "core/base.jinja" %}
|
||||||
|
|
||||||
|
{% block additional_js %}
|
||||||
|
<script type="module" src="{{ static("bundled/core/dynamic-formset-index.ts") }}"></script>
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
{% block title %}
|
{% block title %}
|
||||||
{% trans name=object %}Edit {{ name }}{% endtrans %}
|
{% trans name=object %}Edit {{ name }}{% endtrans %}
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
|
{% macro link_form(form) %}
|
||||||
|
<fieldset
|
||||||
|
{# set url in x-init rather than in x-data,
|
||||||
|
in order to trigger the $watch on initial load #}
|
||||||
|
x-data="{ url: '', linkType: { icon: '', id: 0 } }"
|
||||||
|
x-init="() => {
|
||||||
|
$watch('url', (u) => linkType = linkTypes.find((t) => u.startsWith(t.url)));
|
||||||
|
url = '{{ form.url.value() or "" }}';
|
||||||
|
}"
|
||||||
|
>
|
||||||
|
{{ form.non_field_errors() }}
|
||||||
|
<div class="form-group row gap-2x">
|
||||||
|
<div>
|
||||||
|
{{ form.url.label_tag() }}
|
||||||
|
{{ form.url.errors }}
|
||||||
|
<span>
|
||||||
|
{# we change the icon when the user change it and leave the input,
|
||||||
|
or when it is pasted from the clipboard #}
|
||||||
|
{{ form.url|add_attr("x-model.change=url,@paste.prevent=url = $event.clipboardData.getData('text')") }}
|
||||||
|
<i
|
||||||
|
:class="linkType.icon || 'fa fa-link'"
|
||||||
|
tooltip="{% trans %}This icon will change according to the given url.{% endtrans %}"
|
||||||
|
></i>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div>{{ form.name.as_field_group() }}</div>
|
||||||
|
</div>
|
||||||
|
{%- if form.DELETE -%}
|
||||||
|
<div class="form-group row gap">
|
||||||
|
{{ form.DELETE.as_field_group() }}
|
||||||
|
</div>
|
||||||
|
{%- else -%}
|
||||||
|
<br>
|
||||||
|
<button
|
||||||
|
class="btn btn-grey"
|
||||||
|
@click.prevent="removeForm($event.target.closest('fieldset'))"
|
||||||
|
>
|
||||||
|
<i class="fa fa-minus"></i> {% trans %}Remove link{% endtrans %}
|
||||||
|
</button>
|
||||||
|
{%- endif -%}
|
||||||
|
{{ form.link_type|add_attr(":value=linkType.id") }}
|
||||||
|
{%- for field in form.hidden_fields() -%}
|
||||||
|
{%- if field != form.link_type -%}
|
||||||
|
{{ field }}
|
||||||
|
{%- endif -%}
|
||||||
|
{%- endfor -%}
|
||||||
|
</fieldset>
|
||||||
|
{% endmacro %}
|
||||||
|
|
||||||
|
|
||||||
{% block content %}
|
{% block content %}
|
||||||
<h2>{% trans name=object %}Edit {{ name }}{% endtrans %}</h2>
|
<h2>{% trans name=object %}Edit {{ name }}{% endtrans %}</h2>
|
||||||
|
|
||||||
@@ -17,7 +71,7 @@
|
|||||||
and explicitly separate them from the non-admin ones,
|
and explicitly separate them from the non-admin ones,
|
||||||
with some help text.
|
with some help text.
|
||||||
Non-admin users will only see the regular form fields,
|
Non-admin users will only see the regular form fields,
|
||||||
so they don't need thoses explanations #}
|
so they don't need those explanations #}
|
||||||
<h3>{% trans %}Club properties{% endtrans %}</h3>
|
<h3>{% trans %}Club properties{% endtrans %}</h3>
|
||||||
<p class="helptext">
|
<p class="helptext">
|
||||||
{% trans trimmed %}
|
{% trans trimmed %}
|
||||||
@@ -25,7 +79,7 @@
|
|||||||
Only admin users can see and edit them.
|
Only admin users can see and edit them.
|
||||||
{% endtrans %}
|
{% endtrans %}
|
||||||
</p>
|
</p>
|
||||||
<fieldset class="required margin-bottom">
|
<fieldset class="margin-bottom">
|
||||||
{% for field_name in form.admin_fields %}
|
{% for field_name in form.admin_fields %}
|
||||||
{% set field = form[field_name] %}
|
{% set field = form[field_name] %}
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
@@ -36,11 +90,13 @@
|
|||||||
{# Remove the the admin fields from the form.
|
{# Remove the the admin fields from the form.
|
||||||
The remaining non-admin fields will be rendered
|
The remaining non-admin fields will be rendered
|
||||||
at once with a simple {{ form.as_p() }} #}
|
at once with a simple {{ form.as_p() }} #}
|
||||||
{% set _ = form.fields.pop(field_name) %}
|
{% do form.fields.pop(field_name) %}
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</fieldset>
|
</fieldset>
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
<h3>{% trans %}Club informations{% endtrans %}</h3>
|
<h3>{% trans %}Club informations{% endtrans %}</h3>
|
||||||
|
{% if form.admin_fields %}
|
||||||
<p class="helptext">
|
<p class="helptext">
|
||||||
{% trans trimmed %}
|
{% trans trimmed %}
|
||||||
The following form fields are linked to the basic description of a club.
|
The following form fields are linked to the basic description of a club.
|
||||||
@@ -48,7 +104,45 @@
|
|||||||
{% endtrans %}
|
{% endtrans %}
|
||||||
</p>
|
</p>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{{ form.as_p() }}
|
<fieldset class="margin-bottom">
|
||||||
<p><input type="submit" value="{% trans %}Save{% endtrans %}" /></p>
|
{{ form.as_p() }}
|
||||||
|
</fieldset>
|
||||||
|
|
||||||
|
<h3>{% trans %}Club links{% endtrans %}</h3>
|
||||||
|
<div x-data="dynamicFormSet({ prefix: '{{ form.link_formset.prefix }}' })" class="margin-bottom">
|
||||||
|
{{ form.link_formset.management_form }}
|
||||||
|
<div x-ref="formContainer">
|
||||||
|
{%- for f in form.link_formset.forms -%}
|
||||||
|
{{ link_form(f) }}
|
||||||
|
{%- endfor -%}
|
||||||
|
</div>
|
||||||
|
<template x-ref="formTemplate">
|
||||||
|
{{ link_form(form.link_formset.empty_form) }}
|
||||||
|
</template>
|
||||||
|
<p>
|
||||||
|
<i>{% trans trimmed %}
|
||||||
|
Note: if the icon of one of your links doesn't exist yet,
|
||||||
|
you can ask the info team to add it.
|
||||||
|
{% endtrans %}</i>
|
||||||
|
</p>
|
||||||
|
<br>
|
||||||
|
<button @click.prevent="addForm()" class="btn btn-grey">
|
||||||
|
<i class="fa fa-plus"></i>{% trans %}Add link{% endtrans %}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<hr>
|
||||||
|
<button type="submit" class="btn btn-blue">
|
||||||
|
<i class="fa fa-check"></i>{% trans %}Save{% endtrans %}
|
||||||
|
</button>
|
||||||
</form>
|
</form>
|
||||||
{% endblock content %}
|
{% endblock content %}
|
||||||
|
|
||||||
|
{% block script %}
|
||||||
|
<script>
|
||||||
|
const linkTypes = [
|
||||||
|
{%- for t in link_types -%}
|
||||||
|
{ id: {{ t.id }}, url: '{{ t.url_base }}', icon: '{{ t.icon }}' },
|
||||||
|
{%- endfor -%}
|
||||||
|
];
|
||||||
|
</script>
|
||||||
|
{% endblock %}
|
||||||
|
|||||||
@@ -88,8 +88,8 @@ class TestFetchClub:
|
|||||||
def test_fetch_club_nb_queries(self, client: Client, club: Club):
|
def test_fetch_club_nb_queries(self, client: Client, club: Club):
|
||||||
user = subscriber_user.make()
|
user = subscriber_user.make()
|
||||||
client.force_login(user)
|
client.force_login(user)
|
||||||
with assertNumQueries(6):
|
with assertNumQueries(7):
|
||||||
# - 4 queries for authentication
|
# - 4 queries for authentication
|
||||||
# - 2 queries for the actual data
|
# - 3 queries for the actual data
|
||||||
res = client.get(reverse("api:fetch_club", kwargs={"club_id": club.id}))
|
res = client.get(reverse("api:fetch_club", kwargs={"club_id": club.id}))
|
||||||
assert res.status_code == 200
|
assert res.status_code == 200
|
||||||
|
|||||||
@@ -21,7 +21,13 @@ def test_club_board_member_cannot_edit_club_properties(client: Client):
|
|||||||
client.force_login(user)
|
client.force_login(user)
|
||||||
res = client.post(
|
res = client.post(
|
||||||
reverse("club:club_edit", kwargs={"club_id": club.id}),
|
reverse("club:club_edit", kwargs={"club_id": club.id}),
|
||||||
{"name": "new name", "is_active": False, "address": "new address"},
|
{
|
||||||
|
"name": "new name",
|
||||||
|
"is_active": False,
|
||||||
|
"address": "new address",
|
||||||
|
"link-TOTAL_FORMS": 0,
|
||||||
|
"link-INITIAL_FORMS": 0,
|
||||||
|
},
|
||||||
)
|
)
|
||||||
# The request should success,
|
# The request should success,
|
||||||
# but admin-only fields shouldn't be taken into account
|
# but admin-only fields shouldn't be taken into account
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ def test_page_display_on_club_main_page(client: Client):
|
|||||||
|
|
||||||
assert res.status_code == 200
|
assert res.status_code == 200
|
||||||
soup = BeautifulSoup(res.text, "lxml")
|
soup = BeautifulSoup(res.text, "lxml")
|
||||||
detail_html = soup.find(id="club_detail").find(class_="markdown")
|
detail_html = soup.find(id="club-page").find(class_="markdown")
|
||||||
assertHTMLEqual(detail_html.decode_contents(), markdown(content))
|
assertHTMLEqual(detail_html.decode_contents(), markdown(content))
|
||||||
|
|
||||||
|
|
||||||
@@ -34,7 +34,7 @@ def test_club_main_page_without_content(client: Client):
|
|||||||
|
|
||||||
assert res.status_code == 200
|
assert res.status_code == 200
|
||||||
soup = BeautifulSoup(res.text, "lxml")
|
soup = BeautifulSoup(res.text, "lxml")
|
||||||
detail_html = soup.find(id="club_detail")
|
detail_html = soup.find(id="club-page")
|
||||||
assert detail_html.find_all("markdown") == []
|
assert detail_html.find_all("markdown") == []
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
+21
-9
@@ -36,7 +36,8 @@ from django.contrib.auth.mixins import (
|
|||||||
from django.contrib.messages.views import SuccessMessageMixin
|
from django.contrib.messages.views import SuccessMessageMixin
|
||||||
from django.core.exceptions import NON_FIELD_ERRORS, PermissionDenied, ValidationError
|
from django.core.exceptions import NON_FIELD_ERRORS, PermissionDenied, ValidationError
|
||||||
from django.core.paginator import InvalidPage, Paginator
|
from django.core.paginator import InvalidPage, Paginator
|
||||||
from django.db.models import F, Q, Sum
|
from django.db.models import F, Prefetch, Q, Sum
|
||||||
|
from django.db.models.functions import Length
|
||||||
from django.http import Http404, StreamingHttpResponse
|
from django.http import Http404, StreamingHttpResponse
|
||||||
from django.shortcuts import get_object_or_404, redirect
|
from django.shortcuts import get_object_or_404, redirect
|
||||||
from django.urls import reverse, reverse_lazy
|
from django.urls import reverse, reverse_lazy
|
||||||
@@ -47,12 +48,7 @@ from django.utils.translation import gettext
|
|||||||
from django.utils.translation import gettext_lazy as _
|
from django.utils.translation import gettext_lazy as _
|
||||||
from django.views.generic import DetailView, ListView, View
|
from django.views.generic import DetailView, ListView, View
|
||||||
from django.views.generic.detail import SingleObjectMixin
|
from django.views.generic.detail import SingleObjectMixin
|
||||||
from django.views.generic.edit import (
|
from django.views.generic.edit import CreateView, DeleteView, FormMixin, UpdateView
|
||||||
CreateView,
|
|
||||||
DeleteView,
|
|
||||||
FormMixin,
|
|
||||||
UpdateView,
|
|
||||||
)
|
|
||||||
|
|
||||||
from club.forms import (
|
from club.forms import (
|
||||||
ClubAddMemberForm,
|
ClubAddMemberForm,
|
||||||
@@ -66,7 +62,15 @@ from club.forms import (
|
|||||||
MailingForm,
|
MailingForm,
|
||||||
SellingsForm,
|
SellingsForm,
|
||||||
)
|
)
|
||||||
from club.models import Club, ClubRole, Mailing, MailingSubscription, Membership
|
from club.models import (
|
||||||
|
Club,
|
||||||
|
ClubLink,
|
||||||
|
ClubRole,
|
||||||
|
LinkType,
|
||||||
|
Mailing,
|
||||||
|
MailingSubscription,
|
||||||
|
Membership,
|
||||||
|
)
|
||||||
from com.models import Poster
|
from com.models import Poster
|
||||||
from com.views import (
|
from com.views import (
|
||||||
PosterCreateBaseView,
|
PosterCreateBaseView,
|
||||||
@@ -210,7 +214,9 @@ class ClubListView(AllowFragment, FormMixin, ListView):
|
|||||||
|
|
||||||
template_name = "club/club_list.jinja"
|
template_name = "club/club_list.jinja"
|
||||||
form_class = ClubSearchForm
|
form_class = ClubSearchForm
|
||||||
queryset = Club.objects.order_by("name")
|
queryset = Club.objects.prefetch_related(
|
||||||
|
Prefetch("links", queryset=ClubLink.objects.select_related("link_type"))
|
||||||
|
).order_by("name")
|
||||||
paginate_by = 20
|
paginate_by = 20
|
||||||
|
|
||||||
def get_form_kwargs(self):
|
def get_form_kwargs(self):
|
||||||
@@ -249,6 +255,7 @@ class ClubView(ClubTabsMixin, DetailView):
|
|||||||
.values_list("content", flat=True)
|
.values_list("content", flat=True)
|
||||||
.first()
|
.first()
|
||||||
)
|
)
|
||||||
|
kwargs["links"] = list(self.object.links.select_related("link_type").all())
|
||||||
return kwargs
|
return kwargs
|
||||||
|
|
||||||
|
|
||||||
@@ -689,6 +696,11 @@ class ClubEditView(ClubTabsMixin, CanEditMixin, UpdateView):
|
|||||||
return ClubAdminEditForm
|
return ClubAdminEditForm
|
||||||
return ClubEditForm
|
return ClubEditForm
|
||||||
|
|
||||||
|
def get_context_data(self, **kwargs):
|
||||||
|
return super().get_context_data(**kwargs) | {
|
||||||
|
"link_types": list(LinkType.objects.order_by(Length("url_base").desc()))
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
class ClubCreateView(PermissionRequiredMixin, CreateView):
|
class ClubCreateView(PermissionRequiredMixin, CreateView):
|
||||||
"""Create a club (for the Sith admin)."""
|
"""Create a club (for the Sith admin)."""
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
import { exportToHtml } from "#core:utils/globals.ts";
|
|
||||||
import { newsDeleteNews, newsFetchNewsDates, newsPublishNews } from "#openapi";
|
import { newsDeleteNews, newsFetchNewsDates, newsPublishNews } from "#openapi";
|
||||||
|
|
||||||
// This will be used in jinja templates,
|
// This will be used in jinja templates,
|
||||||
@@ -13,7 +12,8 @@ const AlertState = {
|
|||||||
// biome-ignore lint/style/useNamingConvention: this feels more like an enum
|
// biome-ignore lint/style/useNamingConvention: this feels more like an enum
|
||||||
DISPLAYED: 4, // When published at page generation
|
DISPLAYED: 4, // When published at page generation
|
||||||
};
|
};
|
||||||
exportToHtml("AlertState", AlertState);
|
// biome-ignore lint/style/useNamingConvention: it's an enum, PascalCase is better
|
||||||
|
Object.assign(window, { AlertState });
|
||||||
|
|
||||||
document.addEventListener("alpine:init", () => {
|
document.addEventListener("alpine:init", () => {
|
||||||
Alpine.data("moderationAlert", (newsId: number) => ({
|
Alpine.data("moderationAlert", (newsId: number) => ({
|
||||||
|
|||||||
@@ -16,9 +16,13 @@
|
|||||||
#right_column {
|
#right_column {
|
||||||
flex: 20%;
|
flex: 20%;
|
||||||
margin: 3.2px;
|
margin: 3.2px;
|
||||||
|
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
vertical-align: top;
|
vertical-align: top;
|
||||||
|
|
||||||
|
@media screen and (min-width: 800px) {
|
||||||
|
max-width: 20%;
|
||||||
|
min-width: 200px;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#left_column {
|
#left_column {
|
||||||
@@ -46,7 +50,7 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@media screen and (max-width: $small-devices) {
|
@media screen and (max-width: 800px) {
|
||||||
#left_column,
|
#left_column,
|
||||||
#right_column {
|
#right_column {
|
||||||
flex: 100%;
|
flex: 100%;
|
||||||
@@ -76,8 +80,8 @@
|
|||||||
display: block;
|
display: block;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
background: white;
|
background: white;
|
||||||
font-size: 70%;
|
|
||||||
margin-bottom: 1em;
|
margin-bottom: 1em;
|
||||||
|
font-size: 85%;
|
||||||
|
|
||||||
#links_content {
|
#links_content {
|
||||||
overflow: auto;
|
overflow: auto;
|
||||||
@@ -96,23 +100,10 @@
|
|||||||
li {
|
li {
|
||||||
margin: 10px;
|
margin: 10px;
|
||||||
|
|
||||||
.fa-facebook {
|
|
||||||
color: $faceblue;
|
|
||||||
}
|
|
||||||
|
|
||||||
.fa-discord {
|
|
||||||
color: $discordblurple;
|
|
||||||
}
|
|
||||||
|
|
||||||
.fa-square-instagram::before {
|
|
||||||
background: $instagradient;
|
|
||||||
background-clip: text;
|
|
||||||
-webkit-text-fill-color: transparent;
|
|
||||||
}
|
|
||||||
|
|
||||||
i {
|
i {
|
||||||
width: 25px;
|
width: 25px;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
|
margin-right: .5rem;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -122,12 +113,13 @@
|
|||||||
#birthdays_content {
|
#birthdays_content {
|
||||||
box-shadow: $shadow-color 1px 1px 1px;
|
box-shadow: $shadow-color 1px 1px 1px;
|
||||||
padding: 1em;
|
padding: 1em;
|
||||||
|
|
||||||
ul.birthdays_year {
|
ul.birthdays_year {
|
||||||
margin: 0;
|
margin: 0;
|
||||||
list-style-type: none;
|
list-style-type: none;
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
|
|
||||||
>li {
|
> li {
|
||||||
padding: 0.5em;
|
padding: 0.5em;
|
||||||
|
|
||||||
&:nth-child(even) {
|
&:nth-child(even) {
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
<title>{% trans %}Slideshow{% endtrans %}</title>
|
<title>{% trans %}Slideshow{% endtrans %}</title>
|
||||||
<link rel="shortcut icon" href="{{ static('core/img/favicon.ico') }}">
|
<link rel="shortcut icon" href="{{ static('core/img/favicon.ico') }}">
|
||||||
<link href="{{ static('css/slideshow.scss') }}" rel="stylesheet" type="text/css" />
|
<link href="{{ static('css/slideshow.scss') }}" rel="stylesheet" type="text/css" />
|
||||||
<script type="module" src="{{ static('bundled/alpine-index.js') }}"></script>
|
<script type="module" src="{{ static('bundled/base-bundle-index.ts') }}"></script>
|
||||||
<script type="module" src="{{ static('bundled/com/slideshow-index.ts') }}"></script>
|
<script type="module" src="{{ static('bundled/com/slideshow-index.ts') }}"></script>
|
||||||
</head>
|
</head>
|
||||||
<body x-data="slideshow([
|
<body x-data="slideshow([
|
||||||
|
|||||||
@@ -36,7 +36,7 @@ from django.utils import timezone
|
|||||||
from django.utils.timezone import localdate
|
from django.utils.timezone import localdate
|
||||||
from PIL import Image
|
from PIL import Image
|
||||||
|
|
||||||
from club.models import Club, ClubRole, Membership
|
from club.models import Club, ClubLink, ClubRole, LinkType, Membership
|
||||||
from com.ics_calendar import IcsCalendar
|
from com.ics_calendar import IcsCalendar
|
||||||
from com.models import News, NewsDate, Sith, Weekmail
|
from com.models import News, NewsDate, Sith, Weekmail
|
||||||
from core.models import BanGroup, Group, Page, PageRev, SithFile, User
|
from core.models import BanGroup, Group, Page, PageRev, SithFile, User
|
||||||
@@ -830,6 +830,54 @@ class Command(BaseCommand):
|
|||||||
):
|
):
|
||||||
roles.append(ClubRole(club=club, order=i, name=role))
|
roles.append(ClubRole(club=club, order=i, name=role))
|
||||||
ClubRole.objects.bulk_create(roles)
|
ClubRole.objects.bulk_create(roles)
|
||||||
|
insta, fb, discord, _ = LinkType.objects.bulk_create(
|
||||||
|
[
|
||||||
|
LinkType(
|
||||||
|
name="instagram",
|
||||||
|
icon="fa-brands fa-square-instagram",
|
||||||
|
url_base="https://www.instagram.com",
|
||||||
|
),
|
||||||
|
LinkType(
|
||||||
|
name="facebook",
|
||||||
|
icon="fa-brands fa-facebook",
|
||||||
|
url_base="https://www.facebook.com",
|
||||||
|
),
|
||||||
|
LinkType(
|
||||||
|
name="discord",
|
||||||
|
icon="fa-brands fa-discord",
|
||||||
|
url_base="https://discord.gg",
|
||||||
|
),
|
||||||
|
LinkType(name="generic", icon="fa fa-link", url_base=""),
|
||||||
|
]
|
||||||
|
)
|
||||||
|
ClubLink.objects.bulk_create(
|
||||||
|
[
|
||||||
|
ClubLink(
|
||||||
|
name="insta AE",
|
||||||
|
url="https://www.instagram.com/ae_utbm/",
|
||||||
|
club=ae,
|
||||||
|
link_type=insta,
|
||||||
|
),
|
||||||
|
ClubLink(
|
||||||
|
name="insta activités AE",
|
||||||
|
url="https://www.instagram.com/activites_ae/",
|
||||||
|
club=ae,
|
||||||
|
link_type=insta,
|
||||||
|
),
|
||||||
|
ClubLink(
|
||||||
|
name="facebook AE",
|
||||||
|
url="https://www.facebook.com/ae_utbm",
|
||||||
|
club=ae,
|
||||||
|
link_type=fb,
|
||||||
|
),
|
||||||
|
ClubLink(
|
||||||
|
name="Discord",
|
||||||
|
url="https://discord.gg/QvTm3XJrHR",
|
||||||
|
club=ae,
|
||||||
|
link_type=discord,
|
||||||
|
),
|
||||||
|
]
|
||||||
|
)
|
||||||
return PopulatedClubs(ae=ae, troll=troll, pdf=pdf, refound=refound)
|
return PopulatedClubs(ae=ae, troll=troll, pdf=pdf, refound=refound)
|
||||||
|
|
||||||
def _create_groups(self) -> PopulatedGroups:
|
def _create_groups(self) -> PopulatedGroups:
|
||||||
|
|||||||
@@ -1,12 +0,0 @@
|
|||||||
import sort from "@alpinejs/sort";
|
|
||||||
import Alpine from "alpinejs";
|
|
||||||
import { limitedChoices } from "#core:alpine/limited-choices.ts";
|
|
||||||
import { alpinePlugin as notificationPlugin } from "#core:utils/notifications.ts";
|
|
||||||
|
|
||||||
Alpine.plugin([sort, limitedChoices]);
|
|
||||||
Alpine.magic("notifications", notificationPlugin);
|
|
||||||
window.Alpine = Alpine;
|
|
||||||
|
|
||||||
window.addEventListener("DOMContentLoaded", () => {
|
|
||||||
Alpine.start();
|
|
||||||
});
|
|
||||||
@@ -0,0 +1,64 @@
|
|||||||
|
/**
|
||||||
|
* File containing main functions and library re-exports
|
||||||
|
* that should be accessible throughout the whole website.
|
||||||
|
*
|
||||||
|
* The idea is to group all that shared code into a single bundle,
|
||||||
|
* for more efficient tree-shaking and gzip compression.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import sort from "@alpinejs/sort";
|
||||||
|
import Alpine from "alpinejs";
|
||||||
|
import { polyfillCountryFlagEmojis } from "country-flag-emoji-polyfill";
|
||||||
|
import htmx from "htmx.org";
|
||||||
|
import { limitedChoices } from "#core:alpine/limited-choices";
|
||||||
|
import { expireOldStorage } from "#core:core/localstorage";
|
||||||
|
import { default as navbar } from "#core:core/navbar";
|
||||||
|
import {
|
||||||
|
type NotificationPlugin,
|
||||||
|
notificationsPlugin as notifications,
|
||||||
|
} from "#core:utils/notifications";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Alpine
|
||||||
|
*/
|
||||||
|
declare module "alpinejs" {
|
||||||
|
interface Magics<T> {
|
||||||
|
$notifications: NotificationPlugin;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Alpine.plugin([sort, limitedChoices, notifications]);
|
||||||
|
// biome-ignore lint/style/useNamingConvention: it's how it's named
|
||||||
|
Object.assign(window, { Alpine });
|
||||||
|
|
||||||
|
window.addEventListener("DOMContentLoaded", () => {
|
||||||
|
Alpine.start();
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Polyfill for country flags (used for language choice)
|
||||||
|
*/
|
||||||
|
polyfillCountryFlagEmojis();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* HTMX
|
||||||
|
*/
|
||||||
|
document.body.addEventListener("htmx:beforeRequest", (event: CustomEvent) => {
|
||||||
|
event.detail.target.ariaBusy = true;
|
||||||
|
});
|
||||||
|
|
||||||
|
document.body.addEventListener("htmx:beforeSwap", (event: CustomEvent) => {
|
||||||
|
event.detail.target.ariaBusy = null;
|
||||||
|
});
|
||||||
|
|
||||||
|
Object.assign(window, { htmx });
|
||||||
|
|
||||||
|
/**
|
||||||
|
* navbar
|
||||||
|
*/
|
||||||
|
navbar();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Script that clears the cache when the cache version changes
|
||||||
|
*/
|
||||||
|
expireOldStorage();
|
||||||
@@ -0,0 +1,72 @@
|
|||||||
|
/**
|
||||||
|
* For more detailed infos on how to use this file,
|
||||||
|
* check /docs/tutorial/front/localstorage.md,
|
||||||
|
* or https://ae-utbm.github.io/sith/tutorial/front/localstorage/
|
||||||
|
*/
|
||||||
|
|
||||||
|
// increment this number when a breaking change is made with localStorage
|
||||||
|
const CURRENT_LOCALSTORAGE_VERSION = 1;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove keys that are no longer used from localStorage
|
||||||
|
*/
|
||||||
|
export function expireOldStorage() {
|
||||||
|
const version = Number.parseInt(localStorage.getItem("version") ?? "0", 10);
|
||||||
|
if (version === CURRENT_LOCALSTORAGE_VERSION) {
|
||||||
|
// The cache schema is up-to-date. Nothing to do.
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
localStorage.removeItem("basket1");
|
||||||
|
// remove all storage items which key is in the form
|
||||||
|
// `userXXXPictures` or `userXXXPicturesNumber`
|
||||||
|
Object.keys(localStorage)
|
||||||
|
.filter(
|
||||||
|
(key) =>
|
||||||
|
key.startsWith("user") &&
|
||||||
|
(key.endsWith("Pictures") || key.endsWith("PicturesNumber")),
|
||||||
|
)
|
||||||
|
.forEach((key) => {
|
||||||
|
localStorage.removeItem(key);
|
||||||
|
});
|
||||||
|
localStorage.setItem("version", CURRENT_LOCALSTORAGE_VERSION.toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
interface VersionedStorageItem<T> {
|
||||||
|
version?: number;
|
||||||
|
val: T | undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const versionedLocalStorage = {
|
||||||
|
...localStorage,
|
||||||
|
/**
|
||||||
|
* set this item in localStorage, alongside its version.
|
||||||
|
*
|
||||||
|
* Note: this expects an object, not a JSON string, because the parsing
|
||||||
|
* into JSON needs to be done inside the function.
|
||||||
|
*/
|
||||||
|
setItem<T>(key: string, value: T, { version }: { version: number }) {
|
||||||
|
localStorage.setItem(key, JSON.stringify({ version: version, val: value }));
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* Get the item linked with the given key and version from localStorage.
|
||||||
|
*
|
||||||
|
* Note: if the given key exists in localStorage but doesn't satisfy
|
||||||
|
* the given version, it will be cleared from cache.
|
||||||
|
*
|
||||||
|
* @return the object if found and with the good version, else null;
|
||||||
|
*/
|
||||||
|
getItem<T>(key: string, { version }: { version: number }): T | null {
|
||||||
|
const stored = localStorage.getItem(key);
|
||||||
|
if (!stored) {
|
||||||
|
// this key doesn't exist, return null;
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
const obj: VersionedStorageItem<T> = JSON.parse(stored);
|
||||||
|
if (obj.version !== version || obj.val === undefined) {
|
||||||
|
// The version is wrong, return null and remove this item from cache
|
||||||
|
localStorage.removeItem(key);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return obj.val;
|
||||||
|
},
|
||||||
|
};
|
||||||
@@ -1,12 +1,10 @@
|
|||||||
import { exportToHtml } from "#core:utils/globals.ts";
|
function showMenu() {
|
||||||
|
|
||||||
exportToHtml("showMenu", () => {
|
|
||||||
const navbar = document.getElementById("navbar-content");
|
const navbar = document.getElementById("navbar-content");
|
||||||
const current = navbar.getAttribute("mobile-display");
|
const current = navbar.getAttribute("mobile-display");
|
||||||
navbar.setAttribute("mobile-display", current === "hidden" ? "revealed" : "hidden");
|
navbar.setAttribute("mobile-display", current === "hidden" ? "revealed" : "hidden");
|
||||||
});
|
}
|
||||||
|
|
||||||
document.addEventListener("alpine:init", () => {
|
function navbarInit() {
|
||||||
const menuItems = document.querySelectorAll(".navbar details[name='navbar'].menu");
|
const menuItems = document.querySelectorAll(".navbar details[name='navbar'].menu");
|
||||||
const isDesktop = () => {
|
const isDesktop = () => {
|
||||||
return window.innerWidth >= 500;
|
return window.innerWidth >= 500;
|
||||||
@@ -33,4 +31,9 @@ document.addEventListener("alpine:init", () => {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
}
|
||||||
|
|
||||||
|
export default () => {
|
||||||
|
Object.assign(document, { showMenu });
|
||||||
|
document.addEventListener("alpine:init", navbarInit);
|
||||||
|
};
|
||||||
@@ -1,3 +0,0 @@
|
|||||||
import { polyfillCountryFlagEmojis } from "country-flag-emoji-polyfill";
|
|
||||||
|
|
||||||
polyfillCountryFlagEmojis();
|
|
||||||
@@ -1,11 +0,0 @@
|
|||||||
import htmx from "htmx.org";
|
|
||||||
|
|
||||||
document.body.addEventListener("htmx:beforeRequest", (event) => {
|
|
||||||
event.detail.target.ariaBusy = true;
|
|
||||||
});
|
|
||||||
|
|
||||||
document.body.addEventListener("htmx:beforeSwap", (event) => {
|
|
||||||
event.detail.target.ariaBusy = null;
|
|
||||||
});
|
|
||||||
|
|
||||||
Object.assign(window, { htmx });
|
|
||||||
@@ -1,6 +1,5 @@
|
|||||||
// biome-ignore lint/performance/noNamespaceImport: this is the recommended way from the documentation
|
// biome-ignore lint/performance/noNamespaceImport: this is the recommended way from the documentation
|
||||||
import * as Sentry from "@sentry/browser";
|
import * as Sentry from "@sentry/browser";
|
||||||
import { exportToHtml } from "#core:utils/globals.ts";
|
|
||||||
|
|
||||||
interface LoggedUser {
|
interface LoggedUser {
|
||||||
name: string;
|
name: string;
|
||||||
@@ -13,7 +12,7 @@ interface SentryOptions {
|
|||||||
user?: LoggedUser;
|
user?: LoggedUser;
|
||||||
}
|
}
|
||||||
|
|
||||||
exportToHtml("loadSentryPopup", (options: SentryOptions) => {
|
const loadSentryPopup = (options: SentryOptions) => {
|
||||||
Sentry.init({
|
Sentry.init({
|
||||||
dsn: options.dsn,
|
dsn: options.dsn,
|
||||||
});
|
});
|
||||||
@@ -21,4 +20,5 @@ exportToHtml("loadSentryPopup", (options: SentryOptions) => {
|
|||||||
eventId: options.eventId,
|
eventId: options.eventId,
|
||||||
...(options.user ?? {}),
|
...(options.user ?? {}),
|
||||||
});
|
});
|
||||||
});
|
};
|
||||||
|
Object.assign(window, { loadSentryPopup });
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import type { NestedKeyOf } from "#core:utils/types.ts";
|
import type { NestedKeyOf } from "#core:types/nested-key";
|
||||||
|
|
||||||
interface StringifyOptions<T extends object> {
|
interface StringifyOptions<T extends object> {
|
||||||
/** The columns to include in the resulting CSV. */
|
/** The columns to include in the resulting CSV. */
|
||||||
|
|||||||
@@ -5,17 +5,3 @@ declare global {
|
|||||||
const gettext: (text: string) => string;
|
const gettext: (text: string) => string;
|
||||||
const interpolate: <T>(fmt: string, args: string[] | T, isNamed?: boolean) => string;
|
const interpolate: <T>(fmt: string, args: string[] | T, isNamed?: boolean) => string;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Helper function to export typescript functions to regular html and jinja files
|
|
||||||
* Without it, you either have to use the any keyword and suppress warnings or do a
|
|
||||||
* very painful type conversion workaround which is only here to please the linter
|
|
||||||
*
|
|
||||||
* This is only useful if you're using typescript, this is equivalent to doing
|
|
||||||
* window.yourFunction = yourFunction
|
|
||||||
**/
|
|
||||||
// biome-ignore lint/suspicious/noExplicitAny: Avoid strange tricks to export functions
|
|
||||||
export function exportToHtml(name: string, func: any) {
|
|
||||||
// biome-ignore lint/suspicious/noExplicitAny: Avoid strange tricks to export functions
|
|
||||||
(window as any)[name] = func;
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -1,36 +1,64 @@
|
|||||||
|
import { Alpine } from "alpinejs";
|
||||||
|
|
||||||
export enum NotificationLevel {
|
export enum NotificationLevel {
|
||||||
Error = "error",
|
Error = "error",
|
||||||
Warning = "warning",
|
Warning = "warning",
|
||||||
Success = "success",
|
Success = "success",
|
||||||
}
|
}
|
||||||
|
|
||||||
export function createNotification(message: string, level: NotificationLevel) {
|
export interface NotificationPlugin {
|
||||||
const element = document.getElementById("quick-notifications");
|
/**
|
||||||
if (element === null) {
|
* Add an error message to the notifications.
|
||||||
return false;
|
*/
|
||||||
}
|
error: (message: string) => void;
|
||||||
return element.dispatchEvent(
|
/**
|
||||||
new CustomEvent("quick-notification-add", {
|
* Add a warning message to the notifications
|
||||||
detail: { text: message, tag: level },
|
*/
|
||||||
}),
|
warning: (message: string) => void;
|
||||||
);
|
/**
|
||||||
|
* Add a success message to the notifications
|
||||||
|
*/
|
||||||
|
success: (message: string) => void;
|
||||||
|
/**
|
||||||
|
* Remove all notifications displayed on the page.
|
||||||
|
*/
|
||||||
|
clear: () => void;
|
||||||
|
/**
|
||||||
|
* Add multiple notifications at once.
|
||||||
|
* The added notifications can have different notification levels.
|
||||||
|
*/
|
||||||
|
addMany: (notifs: Notification[]) => void;
|
||||||
|
/**
|
||||||
|
* Return all notifications displayed on the page.
|
||||||
|
*/
|
||||||
|
getAll: () => Notification[];
|
||||||
}
|
}
|
||||||
|
|
||||||
export function deleteNotifications() {
|
export interface Notification {
|
||||||
const element = document.getElementById("quick-notifications");
|
tag: NotificationLevel;
|
||||||
if (element === null) {
|
text: string;
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return element.dispatchEvent(new CustomEvent("quick-notification-delete"));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export function alpinePlugin() {
|
Alpine.store("notifications", [] as Notification[]);
|
||||||
return {
|
|
||||||
error: (message: string) => createNotification(message, NotificationLevel.Error),
|
function createNotification(message: string, level: NotificationLevel) {
|
||||||
warning: (message: string) =>
|
(Alpine.store("notifications") as Notification[]).push({ text: message, tag: level });
|
||||||
createNotification(message, NotificationLevel.Warning),
|
}
|
||||||
success: (message: string) =>
|
function createManyNotifications(notifs: Notification[]) {
|
||||||
createNotification(message, NotificationLevel.Success),
|
for (const notif of notifs) {
|
||||||
clear: () => deleteNotifications(),
|
createNotification(notif.text, notif.tag);
|
||||||
};
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export const notifications: NotificationPlugin = {
|
||||||
|
error: (message: string) => createNotification(message, NotificationLevel.Error),
|
||||||
|
warning: (message: string) => createNotification(message, NotificationLevel.Warning),
|
||||||
|
success: (message: string) => createNotification(message, NotificationLevel.Success),
|
||||||
|
clear: () => Alpine.store("notifications", []),
|
||||||
|
addMany: (notifs: Notification[]) => createManyNotifications(notifs),
|
||||||
|
getAll: () => Alpine.store("notifications") as Notification[],
|
||||||
|
};
|
||||||
|
|
||||||
|
export function notificationsPlugin(GlobalAlpine: Alpine) {
|
||||||
|
GlobalAlpine.magic("notifications", () => ({ ...notifications }));
|
||||||
}
|
}
|
||||||
|
|||||||
+22
-13
@@ -398,6 +398,28 @@ body {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Fontawesome icons */
|
||||||
|
.fa-brands, .fa-link {
|
||||||
|
color: black;
|
||||||
|
}
|
||||||
|
|
||||||
|
.fa-facebook {
|
||||||
|
color: $faceblue;
|
||||||
|
}
|
||||||
|
|
||||||
|
.fa-discord {
|
||||||
|
color: $discordblurple;
|
||||||
|
}
|
||||||
|
|
||||||
|
.fa-square-instagram::before, .fa-instagram::before {
|
||||||
|
background: $instagradient;
|
||||||
|
background-clip: text;
|
||||||
|
-webkit-text-fill-color: transparent;
|
||||||
|
}
|
||||||
|
|
||||||
|
.fa-bluesky, .fa-square-bluesky {
|
||||||
|
color: #0f73ff;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@media screen and (max-width: $small-devices) {
|
@media screen and (max-width: $small-devices) {
|
||||||
@@ -749,16 +771,3 @@ textarea {
|
|||||||
vertical-align: middle;
|
vertical-align: middle;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/*--------------------------------JQuery-------------------------------*/
|
|
||||||
#club_detail {
|
|
||||||
.club_logo {
|
|
||||||
float: right;
|
|
||||||
|
|
||||||
img {
|
|
||||||
display: block;
|
|
||||||
max-height: 10em;
|
|
||||||
max-width: 10em;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -35,12 +35,9 @@
|
|||||||
<noscript><link rel="stylesheet" href="{{ static('bundled/fontawesome-index.css') }}"></noscript>
|
<noscript><link rel="stylesheet" href="{{ static('bundled/fontawesome-index.css') }}"></noscript>
|
||||||
|
|
||||||
<script src="{{ url('javascript-catalog') }}"></script>
|
<script src="{{ url('javascript-catalog') }}"></script>
|
||||||
<script type="module" src="{{ static("bundled/core/navbar-index.ts") }}"></script>
|
<script type="module" src="{{ static("bundled/base-bundle-index.ts") }}"></script>
|
||||||
<script type="module" src="{{ static("bundled/core/components/include-index.ts") }}"></script>
|
<script type="module" src="{{ static("bundled/core/components/include-index.ts") }}"></script>
|
||||||
<script type="module" src="{{ static('bundled/alpine-index.js') }}"></script>
|
<script type="module" src="{{ static("bundled/core/tooltips-index.ts") }}"></script>
|
||||||
<script type="module" src="{{ static('bundled/htmx-index.js') }}"></script>
|
|
||||||
<script type="module" src="{{ static('bundled/country-flags-index.ts') }}"></script>
|
|
||||||
<script type="module" src="{{ static('bundled/core/tooltips-index.ts') }}"></script>
|
|
||||||
|
|
||||||
{% block additional_css %}{% endblock %}
|
{% block additional_css %}{% endblock %}
|
||||||
{% block additional_js %}{% endblock %}
|
{% block additional_js %}{% endblock %}
|
||||||
|
|||||||
@@ -1,16 +1,13 @@
|
|||||||
<div id="quick-notifications"
|
<div id="quick-notifications"
|
||||||
x-data='{
|
x-init='$notifications.addMany([
|
||||||
messages: [
|
|
||||||
{%- for message in messages -%}
|
{%- for message in messages -%}
|
||||||
{%- if not message.extra_tags -%}
|
{%- if not message.extra_tags -%}
|
||||||
{ tag: {{ message.tags|string|tojson }}, text: {{ message|string|tojson }} },
|
{ tag: "{{ message.tags }}", text: {{ message|string|tojson }} },
|
||||||
{%- endif -%}
|
{%- endif -%}
|
||||||
{%- endfor -%}
|
{%- endfor -%}
|
||||||
]
|
])'
|
||||||
}'
|
>
|
||||||
@quick-notification-add="(e) => messages.push(e?.detail)"
|
<template x-for="(message, index) in $notifications.getAll()">
|
||||||
@quick-notification-delete="messages = []">
|
|
||||||
<template x-for="(message, index) in messages">
|
|
||||||
<div class="alert" :class="`alert-${message.tag}`" x-transition>
|
<div class="alert" :class="`alert-${message.tag}`" x-transition>
|
||||||
<span class="alert-main" x-text="message.text"></span>
|
<span class="alert-main" x-text="message.text"></span>
|
||||||
<span class="clickable" @click="messages = messages.filter((item, i) => i !== index)">
|
<span class="clickable" @click="messages = messages.filter((item, i) => i !== index)">
|
||||||
|
|||||||
@@ -226,7 +226,7 @@
|
|||||||
<button type="button" onclick="checkbox_{{form_id}}(false);">{% trans %}Unselect All{% endtrans %}</button>
|
<button type="button" onclick="checkbox_{{form_id}}(false);">{% trans %}Unselect All{% endtrans %}</button>
|
||||||
{% endmacro %}
|
{% endmacro %}
|
||||||
|
|
||||||
{% macro update_notifications(messages, clear) %}
|
{% macro update_notifications(messages, clear = True) %}
|
||||||
{# Update notification area from new messages sent by django backend
|
{# Update notification area from new messages sent by django backend
|
||||||
This is useful when performing fragment swaps to keep messages up to date
|
This is useful when performing fragment swaps to keep messages up to date
|
||||||
Without this, the fragment would need to take control of the notification area and
|
Without this, the fragment would need to take control of the notification area and
|
||||||
@@ -236,16 +236,19 @@
|
|||||||
messages: messages from django.contrib
|
messages: messages from django.contrib
|
||||||
clear : optional boolean that controls if notifications should be cleared first. True is the default
|
clear : optional boolean that controls if notifications should be cleared first. True is the default
|
||||||
#}
|
#}
|
||||||
{% set clear = clear|default(true) %}
|
|
||||||
{% if messages %}
|
{% if messages %}
|
||||||
<div x-init="() => {
|
<div x-init='() => {
|
||||||
{% if clear %}
|
{%- if clear -%}
|
||||||
$notifications.clear()
|
$notifications.clear();
|
||||||
{% endif %}
|
{%- endif -%}
|
||||||
{% for message in messages %}
|
$notifications.addMany([
|
||||||
$notifications.{{ message.tags }}('{{ message }}')
|
{%- for message in messages -%}
|
||||||
{% endfor %}
|
{%- if not message.extra_tags -%}
|
||||||
}"></div>
|
{ tag: "{{ message.tags }}", text: {{ message|string|tojson }} },
|
||||||
|
{%- endif -%}
|
||||||
|
{%- endfor -%}
|
||||||
|
])
|
||||||
|
}'></div>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% endmacro %}
|
{% endmacro %}
|
||||||
|
|
||||||
|
|||||||
@@ -103,7 +103,7 @@ def add_attr(field: BoundField, attr: str):
|
|||||||
if "=" not in d:
|
if "=" not in d:
|
||||||
attrs["class"] = d
|
attrs["class"] = d
|
||||||
else:
|
else:
|
||||||
key, val = d.split("=")
|
key, val = d.split("=", maxsplit=1)
|
||||||
attrs[key] = val
|
attrs[key] = val
|
||||||
|
|
||||||
return field.as_widget(attrs=attrs)
|
return field.as_widget(attrs=attrs)
|
||||||
|
|||||||
@@ -54,7 +54,7 @@ class FragmentRenderer(Protocol):
|
|||||||
) -> SafeString: ...
|
) -> SafeString: ...
|
||||||
|
|
||||||
|
|
||||||
class FragmentMixin(TemplateResponseMixin, ContextMixin):
|
class FragmentMixin(TemplateResponseMixin, AllowFragment, ContextMixin):
|
||||||
"""Make a view buildable as a fragment that can be embedded in a template.
|
"""Make a view buildable as a fragment that can be embedded in a template.
|
||||||
|
|
||||||
Most fragments are used in two different ways :
|
Most fragments are used in two different ways :
|
||||||
|
|||||||
@@ -32,8 +32,9 @@ class Migration(migrations.Migration):
|
|||||||
(
|
(
|
||||||
"result",
|
"result",
|
||||||
models.OneToOneField(
|
models.OneToOneField(
|
||||||
help_text="The formula product.",
|
help_text="The product got with the formula.",
|
||||||
on_delete=django.db.models.deletion.CASCADE,
|
on_delete=django.db.models.deletion.CASCADE,
|
||||||
|
related_name="formula",
|
||||||
to="counter.product",
|
to="counter.product",
|
||||||
verbose_name="result product",
|
verbose_name="result product",
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
import { showSaveFilePicker } from "native-file-system-adapter";
|
import { showSaveFilePicker } from "native-file-system-adapter";
|
||||||
import type TomSelect from "tom-select";
|
import type TomSelect from "tom-select";
|
||||||
|
import type { NestedKeyOf } from "#core:types/nested-key";
|
||||||
import { paginated } from "#core:utils/api";
|
import { paginated } from "#core:utils/api";
|
||||||
import { csv } from "#core:utils/csv";
|
import { csv } from "#core:utils/csv";
|
||||||
import { getCurrentUrlParams, History, updateQueryString } from "#core:utils/history";
|
import { getCurrentUrlParams, History, updateQueryString } from "#core:utils/history";
|
||||||
import type { NestedKeyOf } from "#core:utils/types";
|
|
||||||
import {
|
import {
|
||||||
type ProductSchema,
|
type ProductSchema,
|
||||||
type ProductSearchProductsDetailedData,
|
type ProductSearchProductsDetailedData,
|
||||||
|
|||||||
@@ -35,8 +35,8 @@ les fichiers sont à mettre dans un dossier `static/bundled` de l'application à
|
|||||||
Pour accéder au fichier, il faut utiliser `static` comme pour le reste mais en ajouter `bundled/` comme prefix.
|
Pour accéder au fichier, il faut utiliser `static` comme pour le reste mais en ajouter `bundled/` comme prefix.
|
||||||
|
|
||||||
```jinja
|
```jinja
|
||||||
{# Example pour ajouter sith/core/bundled/alpine-index.js #}
|
{# Example pour ajouter sith/core/bundled/alpine-index.ts #}
|
||||||
<script type="module" src="{{ static('bundled/alpine-index.js') }}"></script>
|
<script type="module" src="{{ static('bundled/alpine-index.ts') }}"></script>
|
||||||
<script type="module" src="{{ static('bundled/other-index.ts') }}"></script>
|
<script type="module" src="{{ static('bundled/other-index.ts') }}"></script>
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,75 @@
|
|||||||
|
[Documentation du localStorage (mozilla)](https://developer.mozilla.org/fr/docs/Web/API/Window/localStorage)
|
||||||
|
|
||||||
|
## Utilité et limitations
|
||||||
|
|
||||||
|
Le `localStorage` est un cache géré directement par le navigateur.
|
||||||
|
Il permet de stocker des données directement chez le client.
|
||||||
|
Il s'agit donc d'un outil extrêmement puissant, qui permet d'éviter
|
||||||
|
beaucoup de requêtes au serveur, améliorant ainsi les temps de chargement.
|
||||||
|
|
||||||
|
Cependant, il y a deux limitations majeures à prendre en compte :
|
||||||
|
|
||||||
|
- le `localStorage` est entièrement géré par le client,
|
||||||
|
une fois le déploiement effectué, vous ne pouvez plus y toucher ;
|
||||||
|
vous devez donc être sûr de vous avant d'apporter des modifications
|
||||||
|
reposant sur le `localStorage`.
|
||||||
|
- la quantité de données stockable est limitée à 10Mo ;
|
||||||
|
une fois ce quota rempli, le navigateur lèvera une `QuotaExceededError`.
|
||||||
|
|
||||||
|
## Invalidation du `localStorage`
|
||||||
|
|
||||||
|
Pour résoudre le premier de ces deux problèmes, il y a un script permettant
|
||||||
|
d'annuler une partie du cache.
|
||||||
|
Ce dernier se trouve dans le fichier `core/static/bundled/core/cache.ts`.
|
||||||
|
|
||||||
|
Vous devrez modifier ce fichier chaque fois qu'un élément du localStorage
|
||||||
|
cessera d'être utilisé.
|
||||||
|
Les modifications à apporter sont les suivantes :
|
||||||
|
|
||||||
|
- incrémenter la version du cache
|
||||||
|
- ajouter une ligne permettant de retirer votre clef du cache
|
||||||
|
|
||||||
|
```ts hl_lines="2 11"
|
||||||
|
// increment this number when a breaking change is made with localStorage
|
||||||
|
const CURRENT_CACHE_VERSION = 2; // <-- changez cette ligne
|
||||||
|
|
||||||
|
export function cacheBuster() {
|
||||||
|
const version = Number.parseInt(localStorage.getItem("version") ?? "0", 10);
|
||||||
|
if (version === CURRENT_CACHE_VERSION) {
|
||||||
|
// The cache schema is up-to-date. Nothing to do.
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// ...
|
||||||
|
localStorage.removeItem("<clef>"); // <-- et rajoutez cette ligne
|
||||||
|
localStorage.setItem("version", CURRENT_CACHE_VERSION.toString());
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Versionnage d'une clef
|
||||||
|
|
||||||
|
Dans le cas où une paire clef-valeur du localStorage subit un changement
|
||||||
|
dans son schéma de données, utilisez `versionedLocalStorage` :
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
import { versionedLocalStorage } from "#core:core/localstorage";
|
||||||
|
|
||||||
|
const foo = () => {
|
||||||
|
let obj = versionedLocalStorage.getItem("<key>", { version: 1 });
|
||||||
|
if (obj === null) {
|
||||||
|
obj = fetchMyObject();
|
||||||
|
versionedLocalStorage.setItem("<key>", obj, { version: 1 })
|
||||||
|
}
|
||||||
|
// Do something with obj...
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
!!!Warning
|
||||||
|
|
||||||
|
Il existe une différence d'usage entre `localStorage` et `versionedLocalStorage` :
|
||||||
|
les valeurs données à `localStorage` doivent être des strings (généralement
|
||||||
|
obtenus avec `JSON.stringify`), tandis que `versionedLocalStorage` utilise
|
||||||
|
directement des objets JS.
|
||||||
|
|
||||||
|
Cette différence résulte du fait que `versionedLocalStorage` doit légèrement
|
||||||
|
modifier les données pour y inclure la version, et gérer en interne
|
||||||
|
la conversion en JSON.
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
export {};
|
import { versionedLocalStorage } from "#core:core/localstorage";
|
||||||
|
|
||||||
interface BasketItem {
|
interface BasketItem {
|
||||||
priceId: number;
|
priceId: number;
|
||||||
@@ -7,8 +7,8 @@ interface BasketItem {
|
|||||||
unitPrice: number;
|
unitPrice: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
// increment the key number if the data schema of the cached basket changes
|
const BASKET_CACHE_KEY = "basket";
|
||||||
const BASKET_CACHE_KEY = "basket1";
|
const BASKET_CACHE_VERSION = 1;
|
||||||
|
|
||||||
document.addEventListener("alpine:init", () => {
|
document.addEventListener("alpine:init", () => {
|
||||||
Alpine.data("basket", (lastPurchaseTime?: number) => ({
|
Alpine.data("basket", (lastPurchaseTime?: number) => ({
|
||||||
@@ -34,18 +34,16 @@ document.addEventListener("alpine:init", () => {
|
|||||||
},
|
},
|
||||||
|
|
||||||
loadBasket(): BasketItem[] {
|
loadBasket(): BasketItem[] {
|
||||||
if (localStorage.getItem(BASKET_CACHE_KEY) === null) {
|
const cached = versionedLocalStorage.getItem<BasketItem[]>(BASKET_CACHE_KEY, {
|
||||||
return [];
|
version: BASKET_CACHE_VERSION,
|
||||||
}
|
});
|
||||||
try {
|
return cached ?? [];
|
||||||
return JSON.parse(localStorage.getItem(BASKET_CACHE_KEY));
|
|
||||||
} catch (_err) {
|
|
||||||
return [];
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
|
|
||||||
saveBasket() {
|
saveBasket() {
|
||||||
localStorage.setItem(BASKET_CACHE_KEY, JSON.stringify(this.basket));
|
versionedLocalStorage.setItem(BASKET_CACHE_KEY, this.basket, {
|
||||||
|
version: BASKET_CACHE_VERSION,
|
||||||
|
});
|
||||||
localStorage.setItem("basketTimestamp", Date.now().toString());
|
localStorage.setItem("basketTimestamp", Date.now().toString());
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|||||||
@@ -32,5 +32,7 @@
|
|||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
<br>
|
<br>
|
||||||
{{ update_notifications(messages) }}
|
{% if is_fragment %}
|
||||||
|
{{ update_notifications(messages) }}
|
||||||
|
{% endif %}
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -6,7 +6,7 @@
|
|||||||
msgid ""
|
msgid ""
|
||||||
msgstr ""
|
msgstr ""
|
||||||
"Report-Msgid-Bugs-To: \n"
|
"Report-Msgid-Bugs-To: \n"
|
||||||
"POT-Creation-Date: 2026-05-12 09:52+0200\n"
|
"POT-Creation-Date: 2026-05-12 11:12+0200\n"
|
||||||
"PO-Revision-Date: 2016-07-18\n"
|
"PO-Revision-Date: 2016-07-18\n"
|
||||||
"Last-Translator: Maréchal <thomas.girod@utbm.fr\n"
|
"Last-Translator: Maréchal <thomas.girod@utbm.fr\n"
|
||||||
"Language-Team: AE info <ae.info@utbm.fr>\n"
|
"Language-Team: AE info <ae.info@utbm.fr>\n"
|
||||||
@@ -362,6 +362,62 @@ msgstr "Cet email est déjà abonné à cette mailing"
|
|||||||
msgid "Unregistered user"
|
msgid "Unregistered user"
|
||||||
msgstr "Utilisateur non enregistré"
|
msgstr "Utilisateur non enregistré"
|
||||||
|
|
||||||
|
#: club/models.py
|
||||||
|
#, python-format
|
||||||
|
msgid "The base url that links with this type must respect (e.g. `%(url)s`)"
|
||||||
|
msgstr ""
|
||||||
|
"L'url de base que tous les liens de ce type doivent respecter (par exemple "
|
||||||
|
"`%(url)s`)"
|
||||||
|
|
||||||
|
#: club/models.py counter/models.py
|
||||||
|
msgid "icon"
|
||||||
|
msgstr "icône"
|
||||||
|
|
||||||
|
#: club/models.py
|
||||||
|
msgid "The fontawesome class to use (e.g. `fa-brands fa-instagram`)"
|
||||||
|
msgstr ""
|
||||||
|
"La classe fontawesome à utiliser (par exemple `fa-brands fa-instagram`)"
|
||||||
|
|
||||||
|
#: club/models.py
|
||||||
|
msgid "link type"
|
||||||
|
msgstr "type de lien"
|
||||||
|
|
||||||
|
#: club/models.py
|
||||||
|
msgid "link types"
|
||||||
|
msgstr "types de lien"
|
||||||
|
|
||||||
|
#: club/models.py
|
||||||
|
msgid "link url"
|
||||||
|
msgstr "url du lien"
|
||||||
|
|
||||||
|
#: club/models.py core/models.py counter/models.py
|
||||||
|
msgid "created at"
|
||||||
|
msgstr "créé le"
|
||||||
|
|
||||||
|
#: club/models.py core/models.py counter/models.py
|
||||||
|
msgid "updated at"
|
||||||
|
msgstr "mis à jour le"
|
||||||
|
|
||||||
|
#: club/models.py
|
||||||
|
msgid "club link"
|
||||||
|
msgstr "lien de club"
|
||||||
|
|
||||||
|
#: club/models.py
|
||||||
|
msgid "club links"
|
||||||
|
msgstr "liens de club"
|
||||||
|
|
||||||
|
#: club/models.py
|
||||||
|
msgid "Duplicated url"
|
||||||
|
msgstr "Url dupliquée"
|
||||||
|
|
||||||
|
#: club/models.py
|
||||||
|
msgid "This link doesn't match with the url base of its type."
|
||||||
|
msgstr "Ce lien ne correspond pas à l'url de base de son type."
|
||||||
|
|
||||||
|
#: club/templates/club/club_detail.jinja com/templates/com/news_list.jinja
|
||||||
|
msgid "Links"
|
||||||
|
msgstr "Liens"
|
||||||
|
|
||||||
#: club/templates/club/club_list.jinja
|
#: club/templates/club/club_list.jinja
|
||||||
msgid "The list of all clubs existing at UTBM."
|
msgid "The list of all clubs existing at UTBM."
|
||||||
msgstr "La liste de tous les clubs existants à l'UTBM"
|
msgstr "La liste de tous les clubs existants à l'UTBM"
|
||||||
@@ -708,6 +764,14 @@ msgstr "Comptoirs : "
|
|||||||
msgid "Edit %(name)s"
|
msgid "Edit %(name)s"
|
||||||
msgstr "Éditer %(name)s"
|
msgstr "Éditer %(name)s"
|
||||||
|
|
||||||
|
#: club/templates/club/edit_club.jinja
|
||||||
|
msgid "This icon will change according to the given url."
|
||||||
|
msgstr "Cette icône changera en fonction de l'url fournie"
|
||||||
|
|
||||||
|
#: club/templates/club/edit_club.jinja
|
||||||
|
msgid "Remove link"
|
||||||
|
msgstr "Retirer le lien"
|
||||||
|
|
||||||
#: club/templates/club/edit_club.jinja
|
#: club/templates/club/edit_club.jinja
|
||||||
msgid "Club properties"
|
msgid "Club properties"
|
||||||
msgstr "Propriétés du club"
|
msgstr "Propriétés du club"
|
||||||
@@ -732,6 +796,22 @@ msgstr ""
|
|||||||
"Les champs de formulaire suivants sont liées à la description basique d'un "
|
"Les champs de formulaire suivants sont liées à la description basique d'un "
|
||||||
"club. Tous les membres du bureau du club peuvent voir et modifier ceux-ci."
|
"club. Tous les membres du bureau du club peuvent voir et modifier ceux-ci."
|
||||||
|
|
||||||
|
#: club/templates/club/edit_club.jinja
|
||||||
|
msgid "Club links"
|
||||||
|
msgstr "Liens du club"
|
||||||
|
|
||||||
|
#: club/templates/club/edit_club.jinja
|
||||||
|
msgid ""
|
||||||
|
"Note: if the icon of one of your links doesn't exist yet, you can ask the "
|
||||||
|
"info team to add it."
|
||||||
|
msgstr ""
|
||||||
|
"Note : si l'icône d'un de vos liens n'existe pas encore, vous pouvez "
|
||||||
|
"demander au pôle info de l'ajouter."
|
||||||
|
|
||||||
|
#: club/templates/club/edit_club.jinja
|
||||||
|
msgid "Add link"
|
||||||
|
msgstr "Ajouter un lien"
|
||||||
|
|
||||||
#: club/templates/club/fragments/add_member.jinja
|
#: club/templates/club/fragments/add_member.jinja
|
||||||
msgid "Add a new member"
|
msgid "Add a new member"
|
||||||
msgstr "Ajouter un nouveau membre"
|
msgstr "Ajouter un nouveau membre"
|
||||||
@@ -1251,10 +1331,6 @@ msgstr ""
|
|||||||
msgid "All coming events"
|
msgid "All coming events"
|
||||||
msgstr "Tous les événements à venir"
|
msgstr "Tous les événements à venir"
|
||||||
|
|
||||||
#: com/templates/com/news_list.jinja
|
|
||||||
msgid "Links"
|
|
||||||
msgstr "Liens"
|
|
||||||
|
|
||||||
#: com/templates/com/news_list.jinja
|
#: com/templates/com/news_list.jinja
|
||||||
msgid "Our services"
|
msgid "Our services"
|
||||||
msgstr "Nos services"
|
msgstr "Nos services"
|
||||||
@@ -1760,10 +1836,6 @@ msgstr "Visiteur"
|
|||||||
msgid "ban type"
|
msgid "ban type"
|
||||||
msgstr "type de ban"
|
msgstr "type de ban"
|
||||||
|
|
||||||
#: core/models.py counter/models.py
|
|
||||||
msgid "created at"
|
|
||||||
msgstr "créé le"
|
|
||||||
|
|
||||||
#: core/models.py
|
#: core/models.py
|
||||||
msgid "expires at"
|
msgid "expires at"
|
||||||
msgstr "expire le"
|
msgstr "expire le"
|
||||||
@@ -1853,10 +1925,6 @@ msgstr "taille"
|
|||||||
msgid "date"
|
msgid "date"
|
||||||
msgstr "date"
|
msgstr "date"
|
||||||
|
|
||||||
#: core/models.py counter/models.py
|
|
||||||
msgid "updated at"
|
|
||||||
msgstr "mis à jour le"
|
|
||||||
|
|
||||||
#: core/models.py
|
#: core/models.py
|
||||||
msgid "asked for removal"
|
msgid "asked for removal"
|
||||||
msgstr "retrait demandé"
|
msgstr "retrait demandé"
|
||||||
@@ -3327,10 +3395,6 @@ msgstr "prix d'achat"
|
|||||||
msgid "Initial cost of purchasing the product"
|
msgid "Initial cost of purchasing the product"
|
||||||
msgstr "Coût initial d'achat du produit"
|
msgstr "Coût initial d'achat du produit"
|
||||||
|
|
||||||
#: counter/models.py
|
|
||||||
msgid "icon"
|
|
||||||
msgstr "icône"
|
|
||||||
|
|
||||||
#: counter/models.py
|
#: counter/models.py
|
||||||
msgid "limit age"
|
msgid "limit age"
|
||||||
msgstr "âge limite"
|
msgstr "âge limite"
|
||||||
@@ -5497,10 +5561,8 @@ msgid "Amicale/DOCEO member"
|
|||||||
msgstr "Membre de l'Amicale/DOCEO"
|
msgstr "Membre de l'Amicale/DOCEO"
|
||||||
|
|
||||||
#: sith/settings.py
|
#: sith/settings.py
|
||||||
#, fuzzy
|
|
||||||
#| msgid "UT network member"
|
|
||||||
msgid "UT network member (excluding UTC)"
|
msgid "UT network member (excluding UTC)"
|
||||||
msgstr "Cotisant du réseau UT"
|
msgstr "Cotisant du réseau UT (hors UTC)"
|
||||||
|
|
||||||
#: sith/settings.py
|
#: sith/settings.py
|
||||||
msgid "CROUS member"
|
msgid "CROUS member"
|
||||||
|
|||||||
@@ -66,6 +66,8 @@ nav:
|
|||||||
- Gestion des permissions: tutorial/perms.md
|
- Gestion des permissions: tutorial/perms.md
|
||||||
- Gestion des groupes: tutorial/groups.md
|
- Gestion des groupes: tutorial/groups.md
|
||||||
- Les fragments: tutorial/fragments.md
|
- Les fragments: tutorial/fragments.md
|
||||||
|
- Frontend:
|
||||||
|
- localStorage: tutorial/front/localstorage.md
|
||||||
- API:
|
- API:
|
||||||
- Développement: tutorial/api/dev.md
|
- Développement: tutorial/api/dev.md
|
||||||
- Connexion à l'API: tutorial/api/connect.md
|
- Connexion à l'API: tutorial/api/connect.md
|
||||||
|
|||||||
Generated
+227
-198
@@ -9,7 +9,7 @@
|
|||||||
"version": "3",
|
"version": "3",
|
||||||
"license": "GPL-3.0-only",
|
"license": "GPL-3.0-only",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@alpinejs/sort": "^3.15.11",
|
"@alpinejs/sort": "^3.15.12",
|
||||||
"@arendjr/text-clipper": "npm:@jsr/arendjr__text-clipper@^3.0.0",
|
"@arendjr/text-clipper": "npm:@jsr/arendjr__text-clipper@^3.0.0",
|
||||||
"@floating-ui/dom": "^1.7.6",
|
"@floating-ui/dom": "^1.7.6",
|
||||||
"@fortawesome/fontawesome-free": "^7.2.0",
|
"@fortawesome/fontawesome-free": "^7.2.0",
|
||||||
@@ -17,45 +17,46 @@
|
|||||||
"@fullcalendar/daygrid": "^6.1.20",
|
"@fullcalendar/daygrid": "^6.1.20",
|
||||||
"@fullcalendar/icalendar": "^6.1.20",
|
"@fullcalendar/icalendar": "^6.1.20",
|
||||||
"@fullcalendar/list": "^6.1.20",
|
"@fullcalendar/list": "^6.1.20",
|
||||||
"@sentry/browser": "^10.51.0",
|
"@sentry/browser": "^10.53.1",
|
||||||
"@zip.js/zip.js": "^2.8.26",
|
"@zip.js/zip.js": "^2.8.26",
|
||||||
"3d-force-graph": "^1.80.0",
|
"3d-force-graph": "^1.80.0",
|
||||||
"alpinejs": "^3.15.11",
|
"alpinejs": "^3.15.12",
|
||||||
"chart.js": "^4.5.1",
|
"chart.js": "^4.5.1",
|
||||||
"country-flag-emoji-polyfill": "^0.1.8",
|
"country-flag-emoji-polyfill": "^0.1.8",
|
||||||
"cytoscape": "^3.33.2",
|
"cytoscape": "^3.33.4",
|
||||||
"cytoscape-cxtmenu": "^3.5.0",
|
"cytoscape-cxtmenu": "^3.5.0",
|
||||||
"cytoscape-klay": "^3.1.4",
|
"cytoscape-klay": "^3.1.4",
|
||||||
"d3-force-3d": "^3.0.6",
|
"d3-force-3d": "^3.0.6",
|
||||||
"easymde": "^2.20.0",
|
"easymde": "^2.21.0",
|
||||||
"glob": "^13.0.6",
|
"glob": "^13.0.6",
|
||||||
"html2canvas": "^1.4.1",
|
"html2canvas": "^1.4.1",
|
||||||
"htmx.org": "^2.0.10",
|
"htmx.org": "^2.0.10",
|
||||||
"js-cookie": "^3.0.5",
|
"js-cookie": "^3.0.7",
|
||||||
"lit-html": "^3.3.2",
|
"lit-html": "^3.3.3",
|
||||||
"native-file-system-adapter": "^3.0.1",
|
"native-file-system-adapter": "^3.0.1",
|
||||||
"three": "^0.184.0",
|
"three": "^0.184.0",
|
||||||
"three-spritetext": "^1.10.0",
|
"three-spritetext": "^1.10.0",
|
||||||
"tom-select": "^2.6.0"
|
"tom-select": "^2.6.1"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@babel/core": "^7.29.0",
|
"@babel/core": "^7.29.0",
|
||||||
"@babel/preset-env": "^7.29.2",
|
"@babel/preset-env": "^7.29.5",
|
||||||
"@biomejs/biome": "^2.4.13",
|
"@biomejs/biome": "^2.4.15",
|
||||||
"@hey-api/openapi-ts": "^0.94.5",
|
"@hey-api/openapi-ts": "^0.94.5",
|
||||||
"@types/alpinejs": "^3.13.11",
|
"@types/alpinejs": "^3.13.11",
|
||||||
|
"@types/alpinejs__sort": "^3.13.0",
|
||||||
"@types/cytoscape-cxtmenu": "^3.4.5",
|
"@types/cytoscape-cxtmenu": "^3.4.5",
|
||||||
"@types/cytoscape-klay": "^3.1.5",
|
"@types/cytoscape-klay": "^3.1.5",
|
||||||
"@types/js-cookie": "^3.0.6",
|
"@types/js-cookie": "^3.0.6",
|
||||||
"rollup-plugin-visualizer": "^7.0.1",
|
"rollup-plugin-visualizer": "^7.0.1",
|
||||||
"typescript": "^5.9.3",
|
"typescript": "^5.9.3",
|
||||||
"vite": "^8.0.10"
|
"vite": "^8.0.13"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@alpinejs/sort": {
|
"node_modules/@alpinejs/sort": {
|
||||||
"version": "3.15.11",
|
"version": "3.15.12",
|
||||||
"resolved": "https://registry.npmjs.org/@alpinejs/sort/-/sort-3.15.11.tgz",
|
"resolved": "https://registry.npmjs.org/@alpinejs/sort/-/sort-3.15.12.tgz",
|
||||||
"integrity": "sha512-HaDZ0jP7OYjRJ8Pv1aErOd3EaILq6pQWr+g3ZY9ddHTA37o1NoLfe20tu8AI6SOs3dppqxykXG99RTn/tsyShA==",
|
"integrity": "sha512-DNIS7SQFg4H4o5faluRgqYEPi1Q7Hf+HMDgoCcIHXbXWH0g66WPEzz9OMk1zsUYib0KxMms4xXOqk+xh2tHZzg==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"sortablejs": "^1.15.2"
|
"sortablejs": "^1.15.2"
|
||||||
@@ -83,9 +84,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@babel/compat-data": {
|
"node_modules/@babel/compat-data": {
|
||||||
"version": "7.29.0",
|
"version": "7.29.3",
|
||||||
"resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.29.0.tgz",
|
"resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.29.3.tgz",
|
||||||
"integrity": "sha512-T1NCJqT/j9+cn8fvkt7jtwbLBfLC/1y1c7NtCeXFRgzGTsafi68MRv8yzkYSapBnFA6L3U2VSc02ciDzoAJhJg==",
|
"integrity": "sha512-LIVqM46zQWZhj17qA8wb4nW/ixr2y1Nw+r1etiAWgRM6U1IqP+LNhL1yg440jYZR72jCWcWbLWzIosH+uP1fqg==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"engines": {
|
"engines": {
|
||||||
@@ -480,6 +481,23 @@
|
|||||||
"@babel/core": "^7.0.0"
|
"@babel/core": "^7.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@babel/plugin-bugfix-safari-rest-destructuring-rhs-array": {
|
||||||
|
"version": "7.29.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-safari-rest-destructuring-rhs-array/-/plugin-bugfix-safari-rest-destructuring-rhs-array-7.29.3.tgz",
|
||||||
|
"integrity": "sha512-SRS46DFR4HqzUzCVgi90/xMoL+zeBDBvWdKYXSEzh79kXswNFEglUpMKxR04//dPqwYXWUBJ3mpUd933ru9Kmg==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@babel/helper-plugin-utils": "^7.28.6",
|
||||||
|
"@babel/helper-skip-transparent-expression-wrappers": "^7.27.1"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=6.9.0"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"@babel/core": "^7.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": {
|
"node_modules/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": {
|
||||||
"version": "7.27.1",
|
"version": "7.27.1",
|
||||||
"resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining/-/plugin-bugfix-v8-spread-parameters-in-optional-chaining-7.27.1.tgz",
|
"resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining/-/plugin-bugfix-v8-spread-parameters-in-optional-chaining-7.27.1.tgz",
|
||||||
@@ -999,9 +1017,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@babel/plugin-transform-modules-systemjs": {
|
"node_modules/@babel/plugin-transform-modules-systemjs": {
|
||||||
"version": "7.29.0",
|
"version": "7.29.4",
|
||||||
"resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.29.0.tgz",
|
"resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.29.4.tgz",
|
||||||
"integrity": "sha512-PrujnVFbOdUpw4UHiVwKvKRLMMic8+eC0CuNlxjsyZUiBjhFdPsewdXCkveh2KqBA9/waD0W1b4hXSOBQJezpQ==",
|
"integrity": "sha512-N7QmZ0xRZfjHOfZeQLJjwgX2zS9pdGHSVl/cjSGlo4dXMqvurfxXDMKY4RqEKzPozV78VMcd0lxyG13mlbKc4w==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
@@ -1434,19 +1452,20 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@babel/preset-env": {
|
"node_modules/@babel/preset-env": {
|
||||||
"version": "7.29.2",
|
"version": "7.29.5",
|
||||||
"resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.29.2.tgz",
|
"resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.29.5.tgz",
|
||||||
"integrity": "sha512-DYD23veRYGvBFhcTY1iUvJnDNpuqNd/BzBwCvzOTKUnJjKg5kpUBh3/u9585Agdkgj+QuygG7jLfOPWMa2KVNw==",
|
"integrity": "sha512-/69t2aEzGKHD76DyLbHysF/QH2LJOB8iFnYO37unDTKBTubzcMRv0f3H5EiN1Q6ajOd/eB7dAInF0qdFVS06kA==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@babel/compat-data": "^7.29.0",
|
"@babel/compat-data": "^7.29.3",
|
||||||
"@babel/helper-compilation-targets": "^7.28.6",
|
"@babel/helper-compilation-targets": "^7.28.6",
|
||||||
"@babel/helper-plugin-utils": "^7.28.6",
|
"@babel/helper-plugin-utils": "^7.28.6",
|
||||||
"@babel/helper-validator-option": "^7.27.1",
|
"@babel/helper-validator-option": "^7.27.1",
|
||||||
"@babel/plugin-bugfix-firefox-class-in-computed-class-key": "^7.28.5",
|
"@babel/plugin-bugfix-firefox-class-in-computed-class-key": "^7.28.5",
|
||||||
"@babel/plugin-bugfix-safari-class-field-initializer-scope": "^7.27.1",
|
"@babel/plugin-bugfix-safari-class-field-initializer-scope": "^7.27.1",
|
||||||
"@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": "^7.27.1",
|
"@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": "^7.27.1",
|
||||||
|
"@babel/plugin-bugfix-safari-rest-destructuring-rhs-array": "^7.29.3",
|
||||||
"@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": "^7.27.1",
|
"@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": "^7.27.1",
|
||||||
"@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly": "^7.28.6",
|
"@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly": "^7.28.6",
|
||||||
"@babel/plugin-proposal-private-property-in-object": "7.21.0-placeholder-for-preset-env.2",
|
"@babel/plugin-proposal-private-property-in-object": "7.21.0-placeholder-for-preset-env.2",
|
||||||
@@ -1478,7 +1497,7 @@
|
|||||||
"@babel/plugin-transform-member-expression-literals": "^7.27.1",
|
"@babel/plugin-transform-member-expression-literals": "^7.27.1",
|
||||||
"@babel/plugin-transform-modules-amd": "^7.27.1",
|
"@babel/plugin-transform-modules-amd": "^7.27.1",
|
||||||
"@babel/plugin-transform-modules-commonjs": "^7.28.6",
|
"@babel/plugin-transform-modules-commonjs": "^7.28.6",
|
||||||
"@babel/plugin-transform-modules-systemjs": "^7.29.0",
|
"@babel/plugin-transform-modules-systemjs": "^7.29.4",
|
||||||
"@babel/plugin-transform-modules-umd": "^7.27.1",
|
"@babel/plugin-transform-modules-umd": "^7.27.1",
|
||||||
"@babel/plugin-transform-named-capturing-groups-regex": "^7.29.0",
|
"@babel/plugin-transform-named-capturing-groups-regex": "^7.29.0",
|
||||||
"@babel/plugin-transform-new-target": "^7.27.1",
|
"@babel/plugin-transform-new-target": "^7.27.1",
|
||||||
@@ -1591,9 +1610,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@biomejs/biome": {
|
"node_modules/@biomejs/biome": {
|
||||||
"version": "2.4.13",
|
"version": "2.4.15",
|
||||||
"resolved": "https://registry.npmjs.org/@biomejs/biome/-/biome-2.4.13.tgz",
|
"resolved": "https://registry.npmjs.org/@biomejs/biome/-/biome-2.4.15.tgz",
|
||||||
"integrity": "sha512-gLXOwkOBBg0tr7bDsqlkIh4uFeKuMjxvqsrb1Tukww1iDmHcfr4Uu8MoQxp0Rcte+69+osRNWXwHsu/zxT6XqA==",
|
"integrity": "sha512-j5VH3a/h/HXTKBM50MDMxRCzkeLv9S2XJcW2WgnZT1+xyisi+0bISrXR82gCX+8S9lvK0skEvHJRN+3Ktr2hlw==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT OR Apache-2.0",
|
"license": "MIT OR Apache-2.0",
|
||||||
"bin": {
|
"bin": {
|
||||||
@@ -1607,20 +1626,20 @@
|
|||||||
"url": "https://opencollective.com/biome"
|
"url": "https://opencollective.com/biome"
|
||||||
},
|
},
|
||||||
"optionalDependencies": {
|
"optionalDependencies": {
|
||||||
"@biomejs/cli-darwin-arm64": "2.4.13",
|
"@biomejs/cli-darwin-arm64": "2.4.15",
|
||||||
"@biomejs/cli-darwin-x64": "2.4.13",
|
"@biomejs/cli-darwin-x64": "2.4.15",
|
||||||
"@biomejs/cli-linux-arm64": "2.4.13",
|
"@biomejs/cli-linux-arm64": "2.4.15",
|
||||||
"@biomejs/cli-linux-arm64-musl": "2.4.13",
|
"@biomejs/cli-linux-arm64-musl": "2.4.15",
|
||||||
"@biomejs/cli-linux-x64": "2.4.13",
|
"@biomejs/cli-linux-x64": "2.4.15",
|
||||||
"@biomejs/cli-linux-x64-musl": "2.4.13",
|
"@biomejs/cli-linux-x64-musl": "2.4.15",
|
||||||
"@biomejs/cli-win32-arm64": "2.4.13",
|
"@biomejs/cli-win32-arm64": "2.4.15",
|
||||||
"@biomejs/cli-win32-x64": "2.4.13"
|
"@biomejs/cli-win32-x64": "2.4.15"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@biomejs/cli-darwin-arm64": {
|
"node_modules/@biomejs/cli-darwin-arm64": {
|
||||||
"version": "2.4.13",
|
"version": "2.4.15",
|
||||||
"resolved": "https://registry.npmjs.org/@biomejs/cli-darwin-arm64/-/cli-darwin-arm64-2.4.13.tgz",
|
"resolved": "https://registry.npmjs.org/@biomejs/cli-darwin-arm64/-/cli-darwin-arm64-2.4.15.tgz",
|
||||||
"integrity": "sha512-2KImO1jhNFBa2oWConyr0x6flxbQpGKv6902uGXpYM62Xyem8U80j441SyUJ8KyngsmKbQjeIv1q2CQfDkNnYg==",
|
"integrity": "sha512-rF3PPqLq1yoST79zaQbDjVJwsuIeci/O+9bgNmC5QpgOqz6aqYuzA4abyAGx+mgyiDXn4A049xAN8gijbuR1Qg==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"arm64"
|
"arm64"
|
||||||
],
|
],
|
||||||
@@ -1635,9 +1654,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@biomejs/cli-darwin-x64": {
|
"node_modules/@biomejs/cli-darwin-x64": {
|
||||||
"version": "2.4.13",
|
"version": "2.4.15",
|
||||||
"resolved": "https://registry.npmjs.org/@biomejs/cli-darwin-x64/-/cli-darwin-x64-2.4.13.tgz",
|
"resolved": "https://registry.npmjs.org/@biomejs/cli-darwin-x64/-/cli-darwin-x64-2.4.15.tgz",
|
||||||
"integrity": "sha512-BKrJklbaFN4p1Ts4kPBczo+PkbsHQg57kmJ+vON9u2t6uN5okYHaSr7h/MutPCWQgg2lglaWoSmm+zhYW+oOkg==",
|
"integrity": "sha512-/5KHXYMfSJs1fNXiX30xFtI8JcCFV6zaVVLxOa0M2sfqBKHkpQhRTv94yxQWxeTY2lzo2OuTlNvPC+hDQt2wcQ==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"x64"
|
"x64"
|
||||||
],
|
],
|
||||||
@@ -1652,9 +1671,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@biomejs/cli-linux-arm64": {
|
"node_modules/@biomejs/cli-linux-arm64": {
|
||||||
"version": "2.4.13",
|
"version": "2.4.15",
|
||||||
"resolved": "https://registry.npmjs.org/@biomejs/cli-linux-arm64/-/cli-linux-arm64-2.4.13.tgz",
|
"resolved": "https://registry.npmjs.org/@biomejs/cli-linux-arm64/-/cli-linux-arm64-2.4.15.tgz",
|
||||||
"integrity": "sha512-NzkUDSqfvMBrPplKgVr3aXLHZ2NEELvvF4vZxXulEylKWIGqlvNEcwUcj9OLrn75TD3lJ/GIqCVlBwd1MZCuYQ==",
|
"integrity": "sha512-owaAMZD/T4LrD0ELNCk0Km3qrRHuM0X6EAyVE1FSqGY0rbLoiDLrO4Us2tllm6cAeB2Ioa9C2C08NZPdr8+0Ug==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"arm64"
|
"arm64"
|
||||||
],
|
],
|
||||||
@@ -1672,9 +1691,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@biomejs/cli-linux-arm64-musl": {
|
"node_modules/@biomejs/cli-linux-arm64-musl": {
|
||||||
"version": "2.4.13",
|
"version": "2.4.15",
|
||||||
"resolved": "https://registry.npmjs.org/@biomejs/cli-linux-arm64-musl/-/cli-linux-arm64-musl-2.4.13.tgz",
|
"resolved": "https://registry.npmjs.org/@biomejs/cli-linux-arm64-musl/-/cli-linux-arm64-musl-2.4.15.tgz",
|
||||||
"integrity": "sha512-U5MsuBQW25dXaYtqWWSPM3P96H6Y+fHuja3TQpMNnylocHW0tEbtFTDlUj6oM+YJLntvEkQy4grBvQNUD4+RCg==",
|
"integrity": "sha512-ZPcxznxm0pogHBLZhYntyR3sR+MrZjqJIKEr7ZqVen0Rl+P/4upVmfYXjftizi9RoqZntg33fv/1fbdhbYXpEQ==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"arm64"
|
"arm64"
|
||||||
],
|
],
|
||||||
@@ -1692,9 +1711,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@biomejs/cli-linux-x64": {
|
"node_modules/@biomejs/cli-linux-x64": {
|
||||||
"version": "2.4.13",
|
"version": "2.4.15",
|
||||||
"resolved": "https://registry.npmjs.org/@biomejs/cli-linux-x64/-/cli-linux-x64-2.4.13.tgz",
|
"resolved": "https://registry.npmjs.org/@biomejs/cli-linux-x64/-/cli-linux-x64-2.4.15.tgz",
|
||||||
"integrity": "sha512-Az3ZZedYRBo9EQzNnD9SxFcR1G5QsGo6VEc2hIyVPZ1rdKwee/7E9oeBBZFpE8Z44ekxsDQBqbiWGW5ShOhUSQ==",
|
"integrity": "sha512-0jj7THz12GbUOLmMibktK6DZjqz2zV64KFxyBtcFTKPiiOIY0a7vns1elpO1dERvxpsZ5ik0oFfz0oGwFde1+g==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"x64"
|
"x64"
|
||||||
],
|
],
|
||||||
@@ -1712,9 +1731,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@biomejs/cli-linux-x64-musl": {
|
"node_modules/@biomejs/cli-linux-x64-musl": {
|
||||||
"version": "2.4.13",
|
"version": "2.4.15",
|
||||||
"resolved": "https://registry.npmjs.org/@biomejs/cli-linux-x64-musl/-/cli-linux-x64-musl-2.4.13.tgz",
|
"resolved": "https://registry.npmjs.org/@biomejs/cli-linux-x64-musl/-/cli-linux-x64-musl-2.4.15.tgz",
|
||||||
"integrity": "sha512-Z601MienRgTBDza/+u2CH3RSrWoXo9rtr8NK6A4KJzqGgfxx+H3VlyLgTJ4sRo40T3pIsqpTmiOQEvYzQvBRvQ==",
|
"integrity": "sha512-CNq/9W38SYSH023lfcQ4KKU8K0YX8T//FZUhcgtMMRABDojx5XsMV7jlweAvGSl389wJQB29Qo6Zb/a+jdvt+w==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"x64"
|
"x64"
|
||||||
],
|
],
|
||||||
@@ -1732,9 +1751,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@biomejs/cli-win32-arm64": {
|
"node_modules/@biomejs/cli-win32-arm64": {
|
||||||
"version": "2.4.13",
|
"version": "2.4.15",
|
||||||
"resolved": "https://registry.npmjs.org/@biomejs/cli-win32-arm64/-/cli-win32-arm64-2.4.13.tgz",
|
"resolved": "https://registry.npmjs.org/@biomejs/cli-win32-arm64/-/cli-win32-arm64-2.4.15.tgz",
|
||||||
"integrity": "sha512-Px9PS2B5/Q183bUwy/5VHqp3J2lzdOCeVGzMpphYfl8oSa7VDCqenBdqWpy6DCy/en4Rbf/Y1RieZF6dJPcc9A==",
|
"integrity": "sha512-ouhkYdlhp/1GghEJPdWwD/Vi3gQ1nFxuSpMolWsbq3Lsq3QUR4jl6UdhhscdCugKU5vOEuMiJhvKj66O0OCq+w==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"arm64"
|
"arm64"
|
||||||
],
|
],
|
||||||
@@ -1749,9 +1768,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@biomejs/cli-win32-x64": {
|
"node_modules/@biomejs/cli-win32-x64": {
|
||||||
"version": "2.4.13",
|
"version": "2.4.15",
|
||||||
"resolved": "https://registry.npmjs.org/@biomejs/cli-win32-x64/-/cli-win32-x64-2.4.13.tgz",
|
"resolved": "https://registry.npmjs.org/@biomejs/cli-win32-x64/-/cli-win32-x64-2.4.15.tgz",
|
||||||
"integrity": "sha512-tTcMkXyBrmHi9BfrD2VNHs/5rYIUKETqsBlYOvSAABwBkJhSDVb5e7wPukftsQbO3WzQkXe6kaztC6WtUOXSoQ==",
|
"integrity": "sha512-zBrGq5mx5wwpnow4+2BxUvleDM+GNd4sLbPaMapsSLQLD0NGRCquqPBTgN+7XkUteHvj7M+BstuI8tmnV7+HgQ==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"x64"
|
"x64"
|
||||||
],
|
],
|
||||||
@@ -2091,9 +2110,9 @@
|
|||||||
"license": "Apache-2.0"
|
"license": "Apache-2.0"
|
||||||
},
|
},
|
||||||
"node_modules/@oxc-project/types": {
|
"node_modules/@oxc-project/types": {
|
||||||
"version": "0.127.0",
|
"version": "0.130.0",
|
||||||
"resolved": "https://registry.npmjs.org/@oxc-project/types/-/types-0.127.0.tgz",
|
"resolved": "https://registry.npmjs.org/@oxc-project/types/-/types-0.130.0.tgz",
|
||||||
"integrity": "sha512-aIYXQBo4lCbO4z0R3FHeucQHpF46l2LbMdxRvqvuRuW2OxdnSkcng5B8+K12spgLDj93rtN3+J2Vac/TIO+ciQ==",
|
"integrity": "sha512-ibD2usx9JRu7f5pu2tMKMI4cpA4NgXJQoYRP4pQ7Pxmn1l6k/53qWtQWZayhYy3X4QZkt90Ot+mJEaeXouio6Q==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"funding": {
|
"funding": {
|
||||||
@@ -2101,9 +2120,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@rolldown/binding-android-arm64": {
|
"node_modules/@rolldown/binding-android-arm64": {
|
||||||
"version": "1.0.0-rc.17",
|
"version": "1.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/@rolldown/binding-android-arm64/-/binding-android-arm64-1.0.0-rc.17.tgz",
|
"resolved": "https://registry.npmjs.org/@rolldown/binding-android-arm64/-/binding-android-arm64-1.0.1.tgz",
|
||||||
"integrity": "sha512-s70pVGhw4zqGeFnXWvAzJDlvxhlRollagdCCKRgOsgUOH3N1l0LIxf83AtGzmb5SiVM4Hjl5HyarMRfdfj3DaQ==",
|
"integrity": "sha512-fJI3I0r3C3Oj/zdBCpaCmBRZYf07xpaq4yCfDDoSFm+beWNzbIl26puW8RraUdugoJw/95zerNOn6jasAhzSmg==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"arm64"
|
"arm64"
|
||||||
],
|
],
|
||||||
@@ -2118,9 +2137,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@rolldown/binding-darwin-arm64": {
|
"node_modules/@rolldown/binding-darwin-arm64": {
|
||||||
"version": "1.0.0-rc.17",
|
"version": "1.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-arm64/-/binding-darwin-arm64-1.0.0-rc.17.tgz",
|
"resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-arm64/-/binding-darwin-arm64-1.0.1.tgz",
|
||||||
"integrity": "sha512-4ksWc9n0mhlZpZ9PMZgTGjeOPRu8MB1Z3Tz0Mo02eWfWCHMW1zN82Qz/pL/rC+yQa+8ZnutMF0JjJe7PjwasYw==",
|
"integrity": "sha512-cKnAhWEsV7TPcA/5EAteDp6KcJZBQ2G+BqE7zayMMi7kMvwRsbv7WT9aOnn0WNl4SKEIf43vjS31iUPu80nzXg==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"arm64"
|
"arm64"
|
||||||
],
|
],
|
||||||
@@ -2135,9 +2154,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@rolldown/binding-darwin-x64": {
|
"node_modules/@rolldown/binding-darwin-x64": {
|
||||||
"version": "1.0.0-rc.17",
|
"version": "1.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-x64/-/binding-darwin-x64-1.0.0-rc.17.tgz",
|
"resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-x64/-/binding-darwin-x64-1.0.1.tgz",
|
||||||
"integrity": "sha512-SUSDOI6WwUVNcWxd02QEBjLdY1VPHvlEkw6T/8nYG322iYWCTxRb1vzk4E+mWWYehTp7ERibq54LSJGjmouOsw==",
|
"integrity": "sha512-YKrVwQjIRBPo+5G/u03wGjbdy4q7pyzCe93DK9VJ7zkVmeg8LJ7GbgsiHWdR4xSoe4CAXRD7Bcjgbtr64bkXNg==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"x64"
|
"x64"
|
||||||
],
|
],
|
||||||
@@ -2152,9 +2171,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@rolldown/binding-freebsd-x64": {
|
"node_modules/@rolldown/binding-freebsd-x64": {
|
||||||
"version": "1.0.0-rc.17",
|
"version": "1.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/@rolldown/binding-freebsd-x64/-/binding-freebsd-x64-1.0.0-rc.17.tgz",
|
"resolved": "https://registry.npmjs.org/@rolldown/binding-freebsd-x64/-/binding-freebsd-x64-1.0.1.tgz",
|
||||||
"integrity": "sha512-hwnz3nw9dbJ05EDO/PvcjaaewqqDy7Y1rn1UO81l8iIK1GjenME75dl16ajbvSSMfv66WXSRCYKIqfgq2KCfxw==",
|
"integrity": "sha512-z/oBsREo46SsFqBwYtFe0kpJeBijAT48O/WXLI4suiCLBkr03RTtTJMCzSdDd2znlh8VJizL09XVkQgk8IZonw==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"x64"
|
"x64"
|
||||||
],
|
],
|
||||||
@@ -2169,9 +2188,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@rolldown/binding-linux-arm-gnueabihf": {
|
"node_modules/@rolldown/binding-linux-arm-gnueabihf": {
|
||||||
"version": "1.0.0-rc.17",
|
"version": "1.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-1.0.0-rc.17.tgz",
|
"resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-1.0.1.tgz",
|
||||||
"integrity": "sha512-IS+W7epTcwANmFSQFrS1SivEXHtl1JtuQA9wlxrZTcNi6mx+FDOYrakGevvvTwgj2JvWiK8B29/qD9BELZPyXQ==",
|
"integrity": "sha512-ik8q7GM11zxvYxFc2PeDcT6TBvhCQMaUxfph/M5l9sKuTs/Sjg3L+Byw0F7w0ZVLBZmx30P+gG0ECzzN+MFcmQ==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"arm"
|
"arm"
|
||||||
],
|
],
|
||||||
@@ -2186,9 +2205,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@rolldown/binding-linux-arm64-gnu": {
|
"node_modules/@rolldown/binding-linux-arm64-gnu": {
|
||||||
"version": "1.0.0-rc.17",
|
"version": "1.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-1.0.0-rc.17.tgz",
|
"resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-1.0.1.tgz",
|
||||||
"integrity": "sha512-e6usGaHKW5BMNZOymS1UcEYGowQMWcgZ71Z17Sl/h2+ZziNJ1a9n3Zvcz6LdRyIW5572wBCTH/Z+bKuZouGk9Q==",
|
"integrity": "sha512-QoSx2EkyrrdZ6kcyE8stqZ62t0Yra8Fs5ia9lOxJrh6TMQJK7gQKmscdTHf7pOXKREKrVwOtJcQG3qVSfc866A==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"arm64"
|
"arm64"
|
||||||
],
|
],
|
||||||
@@ -2206,9 +2225,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@rolldown/binding-linux-arm64-musl": {
|
"node_modules/@rolldown/binding-linux-arm64-musl": {
|
||||||
"version": "1.0.0-rc.17",
|
"version": "1.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-musl/-/binding-linux-arm64-musl-1.0.0-rc.17.tgz",
|
"resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-musl/-/binding-linux-arm64-musl-1.0.1.tgz",
|
||||||
"integrity": "sha512-b/CgbwAJpmrRLp02RPfhbudf5tZnN9nsPWK82znefso832etkem8H7FSZwxrOI9djcdTP7U6YfNhbRnh7djErg==",
|
"integrity": "sha512-uwNwFpwKeNiZawfAWBgg0VIztPTV3ihhh1vV334h9ivnNLorxnQMU6Fz8wG1Zb4Qh9LC1/MkcyT3YlDXG3Rsgg==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"arm64"
|
"arm64"
|
||||||
],
|
],
|
||||||
@@ -2226,9 +2245,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@rolldown/binding-linux-ppc64-gnu": {
|
"node_modules/@rolldown/binding-linux-ppc64-gnu": {
|
||||||
"version": "1.0.0-rc.17",
|
"version": "1.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/@rolldown/binding-linux-ppc64-gnu/-/binding-linux-ppc64-gnu-1.0.0-rc.17.tgz",
|
"resolved": "https://registry.npmjs.org/@rolldown/binding-linux-ppc64-gnu/-/binding-linux-ppc64-gnu-1.0.1.tgz",
|
||||||
"integrity": "sha512-4EII1iNGRUN5WwGbF/kOh/EIkoDN9HsupgLQoXfY+D1oyJm7/F4t5PYU5n8SWZgG0FEwakyM8pGgwcBYruGTlA==",
|
"integrity": "sha512-zY1bul7OWr7DFBiJ++wofXvnr8B45ce3QsQUhKrIhXsygAh7bTkwyeM1bi1a2g5C/yC/N8TZyGDEoMfm/l9mpg==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"ppc64"
|
"ppc64"
|
||||||
],
|
],
|
||||||
@@ -2246,9 +2265,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@rolldown/binding-linux-s390x-gnu": {
|
"node_modules/@rolldown/binding-linux-s390x-gnu": {
|
||||||
"version": "1.0.0-rc.17",
|
"version": "1.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/@rolldown/binding-linux-s390x-gnu/-/binding-linux-s390x-gnu-1.0.0-rc.17.tgz",
|
"resolved": "https://registry.npmjs.org/@rolldown/binding-linux-s390x-gnu/-/binding-linux-s390x-gnu-1.0.1.tgz",
|
||||||
"integrity": "sha512-AH8oq3XqQo4IibpVXvPeLDI5pzkpYn0WiZAfT05kFzoJ6tQNzwRdDYQ45M8I/gslbodRZwW8uxLhbSBbkv96rA==",
|
"integrity": "sha512-0frlsT/f4Ft6I7SMESTKnF3cZsdicQn1dCMkF/jT9wDLE+gGoiQfv1nmT9e+s7s/fekvvy6tZM2jHvI2tkbJDQ==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"s390x"
|
"s390x"
|
||||||
],
|
],
|
||||||
@@ -2266,9 +2285,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@rolldown/binding-linux-x64-gnu": {
|
"node_modules/@rolldown/binding-linux-x64-gnu": {
|
||||||
"version": "1.0.0-rc.17",
|
"version": "1.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-gnu/-/binding-linux-x64-gnu-1.0.0-rc.17.tgz",
|
"resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-gnu/-/binding-linux-x64-gnu-1.0.1.tgz",
|
||||||
"integrity": "sha512-cLnjV3xfo7KslbU41Z7z8BH/E1y5mzUYzAqih1d1MDaIGZRCMqTijqLv76/P7fyHuvUcfGsIpqCdddbxLLK9rA==",
|
"integrity": "sha512-XABVmGp9Tg0WspTVvwduTc4fpqy6JnAUrSQe6OuyqD/03nI7r0O9OWUkMIwFrjKAIqolvqoA4ZrJppgwE0Gxmw==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"x64"
|
"x64"
|
||||||
],
|
],
|
||||||
@@ -2286,9 +2305,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@rolldown/binding-linux-x64-musl": {
|
"node_modules/@rolldown/binding-linux-x64-musl": {
|
||||||
"version": "1.0.0-rc.17",
|
"version": "1.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-musl/-/binding-linux-x64-musl-1.0.0-rc.17.tgz",
|
"resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-musl/-/binding-linux-x64-musl-1.0.1.tgz",
|
||||||
"integrity": "sha512-0phclDw1spsL7dUB37sIARuis2tAgomCJXAHZlpt8PXZ4Ba0dRP1e+66lsRqrfhISeN9bEGNjQs+T/Fbd7oYGw==",
|
"integrity": "sha512-bV4fzswuzVcKD90o/VM6QqKxnxlDq0g2BISDLNVmxrnhpv1DDbyPhCIjYfvzYLV+MvkKKnQt2Q6AO86SEBULUQ==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"x64"
|
"x64"
|
||||||
],
|
],
|
||||||
@@ -2306,9 +2325,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@rolldown/binding-openharmony-arm64": {
|
"node_modules/@rolldown/binding-openharmony-arm64": {
|
||||||
"version": "1.0.0-rc.17",
|
"version": "1.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/@rolldown/binding-openharmony-arm64/-/binding-openharmony-arm64-1.0.0-rc.17.tgz",
|
"resolved": "https://registry.npmjs.org/@rolldown/binding-openharmony-arm64/-/binding-openharmony-arm64-1.0.1.tgz",
|
||||||
"integrity": "sha512-0ag/hEgXOwgw4t8QyQvUCxvEg+V0KBcA6YuOx9g0r02MprutRF5dyljgm3EmR02O292UX7UeS6HzWHAl6KgyhA==",
|
"integrity": "sha512-/Mh0Zhq3OP7fVs0kcQHZP6lZEthMGTaSf8UBQYSFEZDWGXXlEC+nJ6EqenaK2t4LBXMe3A+K/G2BVXXdtOr4PQ==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"arm64"
|
"arm64"
|
||||||
],
|
],
|
||||||
@@ -2323,9 +2342,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@rolldown/binding-wasm32-wasi": {
|
"node_modules/@rolldown/binding-wasm32-wasi": {
|
||||||
"version": "1.0.0-rc.17",
|
"version": "1.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/@rolldown/binding-wasm32-wasi/-/binding-wasm32-wasi-1.0.0-rc.17.tgz",
|
"resolved": "https://registry.npmjs.org/@rolldown/binding-wasm32-wasi/-/binding-wasm32-wasi-1.0.1.tgz",
|
||||||
"integrity": "sha512-LEXei6vo0E5wTGwpkJ4KoT3OZJRnglwldt5ziLzOlc6qqb55z4tWNq2A+PFqCJuvWWdP53CVhG1Z9NtToDPJrA==",
|
"integrity": "sha512-+1xc9X45l8ufsBAm6Gjvx2qDRIY9lTVt0cgWNcJ+1gdhXvkbxePA60yRTwSTuXL09CMhyJmjpV7E3NoyxbqFQQ==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"wasm32"
|
"wasm32"
|
||||||
],
|
],
|
||||||
@@ -2342,9 +2361,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@rolldown/binding-win32-arm64-msvc": {
|
"node_modules/@rolldown/binding-win32-arm64-msvc": {
|
||||||
"version": "1.0.0-rc.17",
|
"version": "1.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/@rolldown/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-1.0.0-rc.17.tgz",
|
"resolved": "https://registry.npmjs.org/@rolldown/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-1.0.1.tgz",
|
||||||
"integrity": "sha512-gUmyzBl3SPMa6hrqFUth9sVfcLBlYsbMzBx5PlexMroZStgzGqlZ26pYG89rBb45Mnia+oil6YAIFeEWGWhoZA==",
|
"integrity": "sha512-1D+UqZdfnuR+Jy1GgMJwi85bD40H21uNmOPRWQhw4oRSuolZ/B5rixZ45DK2KXOTCvmVCecauWgEhbw8bI7tOw==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"arm64"
|
"arm64"
|
||||||
],
|
],
|
||||||
@@ -2359,9 +2378,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@rolldown/binding-win32-x64-msvc": {
|
"node_modules/@rolldown/binding-win32-x64-msvc": {
|
||||||
"version": "1.0.0-rc.17",
|
"version": "1.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/@rolldown/binding-win32-x64-msvc/-/binding-win32-x64-msvc-1.0.0-rc.17.tgz",
|
"resolved": "https://registry.npmjs.org/@rolldown/binding-win32-x64-msvc/-/binding-win32-x64-msvc-1.0.1.tgz",
|
||||||
"integrity": "sha512-3hkiolcUAvPB9FLb3UZdfjVVNWherN1f/skkGWJP/fgSQhYUZpSIRr0/I8ZK9TkF3F7kxvJAk0+IcKvPHk9qQg==",
|
"integrity": "sha512-INAycaWuhlOK3wk4mRHGsdgwYWmd9cChdPdE9bwWmy6rn9VqVNYNFGhOdXrofXUxwHIncSiPNb8tNm8knDVIeQ==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"x64"
|
"x64"
|
||||||
],
|
],
|
||||||
@@ -2376,82 +2395,82 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@rolldown/pluginutils": {
|
"node_modules/@rolldown/pluginutils": {
|
||||||
"version": "1.0.0-rc.17",
|
"version": "1.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.0-rc.17.tgz",
|
"resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.1.tgz",
|
||||||
"integrity": "sha512-n8iosDOt6Ig1UhJ2AYqoIhHWh/isz0xpicHTzpKBeotdVsTEcxsSA/i3EVM7gQAj0rU27OLAxCjzlj15IWY7bg==",
|
"integrity": "sha512-2j9bGt5Jh8hj+vPtgzPtl72j0yRxHAyumoo6TNfAjsLB04UtpSvPbPcDcBMxz7n+9CYB0c1GxQFxYRg2jimqGw==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
"node_modules/@sentry-internal/browser-utils": {
|
"node_modules/@sentry-internal/browser-utils": {
|
||||||
"version": "10.51.0",
|
"version": "10.53.1",
|
||||||
"resolved": "https://registry.npmjs.org/@sentry-internal/browser-utils/-/browser-utils-10.51.0.tgz",
|
"resolved": "https://registry.npmjs.org/@sentry-internal/browser-utils/-/browser-utils-10.53.1.tgz",
|
||||||
"integrity": "sha512-lNKBS4P7RUvf1niojXQWe9bU3gnBUCbST4Dj0pSiyat1N96cXVyHkeE+uGxowD0RrVWhs+kGHiVX3FcmRWF6sA==",
|
"integrity": "sha512-X4d6y8sBMjmNhcDW4eMBU3ASsNIMz8dqaFkhyIMN/dkYr/yZKnbRZPaVuVUGvHKjnlficPpIH0/HK9KBjrYxPw==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@sentry/core": "10.51.0"
|
"@sentry/core": "10.53.1"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=18"
|
"node": ">=18"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@sentry-internal/feedback": {
|
"node_modules/@sentry-internal/feedback": {
|
||||||
"version": "10.51.0",
|
"version": "10.53.1",
|
||||||
"resolved": "https://registry.npmjs.org/@sentry-internal/feedback/-/feedback-10.51.0.tgz",
|
"resolved": "https://registry.npmjs.org/@sentry-internal/feedback/-/feedback-10.53.1.tgz",
|
||||||
"integrity": "sha512-bCM95bcpphx28e6aU0bwRLxOgwosYsdNzezM1sM0pVOkb0TB3hDFRamramVDK+/Hp1o8qmRxS4c5w/A7YBZGkA==",
|
"integrity": "sha512-vVpTI/aEYN5d9IgZeYJWMqVaN0+iFgidSrYNAsZTh1US5sJUzF/wrl+68KdpmCtFROrN3jiAn1oPSwL5CKvEJA==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@sentry/core": "10.51.0"
|
"@sentry/core": "10.53.1"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=18"
|
"node": ">=18"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@sentry-internal/replay": {
|
"node_modules/@sentry-internal/replay": {
|
||||||
"version": "10.51.0",
|
"version": "10.53.1",
|
||||||
"resolved": "https://registry.npmjs.org/@sentry-internal/replay/-/replay-10.51.0.tgz",
|
"resolved": "https://registry.npmjs.org/@sentry-internal/replay/-/replay-10.53.1.tgz",
|
||||||
"integrity": "sha512-jCpI5HXSwK6ZT2HX70+mDRciAocHzSiDk4DTgvzV69Wvd+Ei5WLgE+d39eaEPsm8lUC0Ydntb5sJIB6uG9D4bw==",
|
"integrity": "sha512-wZNzTBYkgGUPWMuUQv7L64+OJmoCnz7GQNiTrTFK6EVAjJXFBCSsPp/nhif0bLhbk8+0g4xz633uOhpXuQbFdw==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@sentry-internal/browser-utils": "10.51.0",
|
"@sentry-internal/browser-utils": "10.53.1",
|
||||||
"@sentry/core": "10.51.0"
|
"@sentry/core": "10.53.1"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=18"
|
"node": ">=18"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@sentry-internal/replay-canvas": {
|
"node_modules/@sentry-internal/replay-canvas": {
|
||||||
"version": "10.51.0",
|
"version": "10.53.1",
|
||||||
"resolved": "https://registry.npmjs.org/@sentry-internal/replay-canvas/-/replay-canvas-10.51.0.tgz",
|
"resolved": "https://registry.npmjs.org/@sentry-internal/replay-canvas/-/replay-canvas-10.53.1.tgz",
|
||||||
"integrity": "sha512-8PW1Pp+Yl3lPwYqhBCr5SgkuhDanu9ZLzUqD2bPKL/ElqbM2eDVIWxq4z4ZzePrmZa6IcCjTv6sVQJ7Z4dLyLA==",
|
"integrity": "sha512-aueLaf/2prExwA76BGU5/bOXCKWqtt6jQXWA6WJQNrmKpPEtZJB4ypnpsou0McXQCF8tur2Y8U0TEkwQP13yJQ==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@sentry-internal/replay": "10.51.0",
|
"@sentry-internal/replay": "10.53.1",
|
||||||
"@sentry/core": "10.51.0"
|
"@sentry/core": "10.53.1"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=18"
|
"node": ">=18"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@sentry/browser": {
|
"node_modules/@sentry/browser": {
|
||||||
"version": "10.51.0",
|
"version": "10.53.1",
|
||||||
"resolved": "https://registry.npmjs.org/@sentry/browser/-/browser-10.51.0.tgz",
|
"resolved": "https://registry.npmjs.org/@sentry/browser/-/browser-10.53.1.tgz",
|
||||||
"integrity": "sha512-Zdc0sKfenxUtW/OGhtJ7xHFN44bXR7YqxJ1zBDzlZfW0nTbeTTUZBq9z5NUw6qdS0Vs/i3V4qzAKTbRKWfqSEA==",
|
"integrity": "sha512-zXF373hzUOGzUOrqd8xb1U3LQi5uYC3mwv+z5OMKUUinQlu30tTWBs7ypy6YTchtix9QlYaHWlayUF8vBZ5UjA==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@sentry-internal/browser-utils": "10.51.0",
|
"@sentry-internal/browser-utils": "10.53.1",
|
||||||
"@sentry-internal/feedback": "10.51.0",
|
"@sentry-internal/feedback": "10.53.1",
|
||||||
"@sentry-internal/replay": "10.51.0",
|
"@sentry-internal/replay": "10.53.1",
|
||||||
"@sentry-internal/replay-canvas": "10.51.0",
|
"@sentry-internal/replay-canvas": "10.53.1",
|
||||||
"@sentry/core": "10.51.0"
|
"@sentry/core": "10.53.1"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=18"
|
"node": ">=18"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@sentry/core": {
|
"node_modules/@sentry/core": {
|
||||||
"version": "10.51.0",
|
"version": "10.53.1",
|
||||||
"resolved": "https://registry.npmjs.org/@sentry/core/-/core-10.51.0.tgz",
|
"resolved": "https://registry.npmjs.org/@sentry/core/-/core-10.53.1.tgz",
|
||||||
"integrity": "sha512-Y45V/YXvVLEXmOdkbD1oG1gkRWFi9guCEGg3PlIlIpRjAbZUrvLGgjRJIc1E7XpSzmOnWbs5BbUxMv4PDaPj2w==",
|
"integrity": "sha512-XG4ezlkyuAPjBC5+9kXC94rXXuqYTw9NRhfaDHssbTFaGnqBR8vQX2UUgZfY7ucbeelRDGfBu1sywoU+mB04uA==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=18"
|
"node": ">=18"
|
||||||
@@ -2464,9 +2483,9 @@
|
|||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
"node_modules/@tybys/wasm-util": {
|
"node_modules/@tybys/wasm-util": {
|
||||||
"version": "0.10.1",
|
"version": "0.10.2",
|
||||||
"resolved": "https://registry.npmjs.org/@tybys/wasm-util/-/wasm-util-0.10.1.tgz",
|
"resolved": "https://registry.npmjs.org/@tybys/wasm-util/-/wasm-util-0.10.2.tgz",
|
||||||
"integrity": "sha512-9tTaPJLSiejZKx+Bmog4uSubteqTvFrVrURwkmHixBo0G4seD0zUxp98E1DzUBJxLQ3NPwXrGKDiVjwx/DpPsg==",
|
"integrity": "sha512-RoBvJ2X0wuKlWFIjrwffGw1IqZHKQqzIchKaadZZfnNpsAYp2mM0h36JtPCjNDAHGgYez/15uMBpfGwchhiMgg==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"optional": true,
|
"optional": true,
|
||||||
@@ -2481,6 +2500,16 @@
|
|||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
|
"node_modules/@types/alpinejs__sort": {
|
||||||
|
"version": "3.13.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/alpinejs__sort/-/alpinejs__sort-3.13.0.tgz",
|
||||||
|
"integrity": "sha512-iR9vEy6e3yXbYAK45/hpulzlt8SSKTsvYUl/t5nuWjtbJPoGxzxUUqOm3egp83Gqtf//TyJnDCI4OTebAKDRAA==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@types/alpinejs": "*"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@types/codemirror": {
|
"node_modules/@types/codemirror": {
|
||||||
"version": "5.60.17",
|
"version": "5.60.17",
|
||||||
"resolved": "https://registry.npmjs.org/@types/codemirror/-/codemirror-5.60.17.tgz",
|
"resolved": "https://registry.npmjs.org/@types/codemirror/-/codemirror-5.60.17.tgz",
|
||||||
@@ -2603,9 +2632,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/alpinejs": {
|
"node_modules/alpinejs": {
|
||||||
"version": "3.15.11",
|
"version": "3.15.12",
|
||||||
"resolved": "https://registry.npmjs.org/alpinejs/-/alpinejs-3.15.11.tgz",
|
"resolved": "https://registry.npmjs.org/alpinejs/-/alpinejs-3.15.12.tgz",
|
||||||
"integrity": "sha512-m26gkTg/MId8O+F4jHKK3vB3SjbFxxk/JHP+qzmw1H6aQrZuPAg4CUoAefnASzzp/eNroBjrRQe7950bNeaBJw==",
|
"integrity": "sha512-nJvPAQVNPdZZ0NrExJ/kzQco3ijR8LwvCOadQecllESiqT4NyZ/57sN9V2XyvhlBGAbmlKYgeWZvYdKq99ij/Q==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@vue/reactivity": "~3.1.1"
|
"@vue/reactivity": "~3.1.1"
|
||||||
@@ -2728,9 +2757,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/brace-expansion": {
|
"node_modules/brace-expansion": {
|
||||||
"version": "5.0.5",
|
"version": "5.0.6",
|
||||||
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.5.tgz",
|
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.6.tgz",
|
||||||
"integrity": "sha512-VZznLgtwhn+Mact9tfiwx64fA9erHH/MCXEUfB/0bX/6Fz6ny5EGTXYltMocqg4xFAQZtnO3DHWWXi8RiuN7cQ==",
|
"integrity": "sha512-kLpxurY4Z4r9sgMsyG0Z9uzsBlgiU/EFKhj/h91/8yHu0edo7XuixOIH3VcJ8kkxs6/jPzoI6U9Vj3WqbMQ94g==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"balanced-match": "^4.0.2"
|
"balanced-match": "^4.0.2"
|
||||||
@@ -2996,9 +3025,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/cytoscape": {
|
"node_modules/cytoscape": {
|
||||||
"version": "3.33.2",
|
"version": "3.33.4",
|
||||||
"resolved": "https://registry.npmjs.org/cytoscape/-/cytoscape-3.33.2.tgz",
|
"resolved": "https://registry.npmjs.org/cytoscape/-/cytoscape-3.33.4.tgz",
|
||||||
"integrity": "sha512-sj4HXd3DokGhzZAdjDejGvTPLqlt84vNFN8m7bGsOzDY5DyVcxIb2ejIXat2Iy7HxWhdT/N1oKyheJ5YdpsGuw==",
|
"integrity": "sha512-HIN5Pmd9MrX9BkV7tDwnOcEJCSFvCpc8X97h3f508J6I5FsqAY65wKOCvgH2CuP42CaahWaz4tuh32SOOIH7ww==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=0.10"
|
"node": ">=0.10"
|
||||||
@@ -3295,9 +3324,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/easymde": {
|
"node_modules/easymde": {
|
||||||
"version": "2.20.0",
|
"version": "2.21.0",
|
||||||
"resolved": "https://registry.npmjs.org/easymde/-/easymde-2.20.0.tgz",
|
"resolved": "https://registry.npmjs.org/easymde/-/easymde-2.21.0.tgz",
|
||||||
"integrity": "sha512-V1Z5f92TfR42Na852OWnIZMbM7zotWQYTddNaLYZFVKj7APBbyZ3FYJ27gBw2grMW3R6Qdv9J8n5Ij7XRSIgXQ==",
|
"integrity": "sha512-5uE7I/DEN8gvGRwxaqAv7h1PMEK2ykNXVX5zL0dK3nCYROGja3AMbdQz8eCEELnfvCfy7tRkTmLuvyJG8uSWjQ==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@types/codemirror": "^5.60.10",
|
"@types/codemirror": "^5.60.10",
|
||||||
@@ -3656,12 +3685,12 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/js-cookie": {
|
"node_modules/js-cookie": {
|
||||||
"version": "3.0.5",
|
"version": "3.0.7",
|
||||||
"resolved": "https://registry.npmjs.org/js-cookie/-/js-cookie-3.0.5.tgz",
|
"resolved": "https://registry.npmjs.org/js-cookie/-/js-cookie-3.0.7.tgz",
|
||||||
"integrity": "sha512-cEiJEAEoIbWfCZYKWhVwFuvPX1gETRYPw6LlaTKoxD3s2AkXzkCjnp6h0V77ozyqj0jakteJ4YqDJT830+lVGw==",
|
"integrity": "sha512-z/wZZgDrkNV1eA0ULjM/F9/50Ya8fbzgKneSpoPsXSGd0KnpdtHfOZWK+GcwLk+EZbS4F9RBhU+K2RgzuDaItw==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=14"
|
"node": ">=20"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/js-tokens": {
|
"node_modules/js-tokens": {
|
||||||
@@ -3990,9 +4019,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/lit-html": {
|
"node_modules/lit-html": {
|
||||||
"version": "3.3.2",
|
"version": "3.3.3",
|
||||||
"resolved": "https://registry.npmjs.org/lit-html/-/lit-html-3.3.2.tgz",
|
"resolved": "https://registry.npmjs.org/lit-html/-/lit-html-3.3.3.tgz",
|
||||||
"integrity": "sha512-Qy9hU88zcmaxBXcc10ZpdK7cOLXvXpRoBxERdtqV9QOrfpMZZ6pSYP91LhpPtap3sFMUiL7Tw2RImbe0Al2/kw==",
|
"integrity": "sha512-el8M6jK2o3RXBnrSHX3ZKrsN8zEV63pSExTO1wYJz7QndGYZ8353e2a5PPX+qHe2aGayfnchQmkAojaWAREOIA==",
|
||||||
"license": "BSD-3-Clause",
|
"license": "BSD-3-Clause",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@types/trusted-types": "^2.0.2"
|
"@types/trusted-types": "^2.0.2"
|
||||||
@@ -4065,9 +4094,9 @@
|
|||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
"node_modules/nanoid": {
|
"node_modules/nanoid": {
|
||||||
"version": "3.3.11",
|
"version": "3.3.12",
|
||||||
"resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz",
|
"resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.12.tgz",
|
||||||
"integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==",
|
"integrity": "sha512-ZB9RH/39qpq5Vu6Y+NmUaFhQR6pp+M2Xt76XBnEwDaGcVAqhlvxrl3B2bKS5D3NH3QR76v3aSrKaF/Kiy7lEtQ==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"funding": [
|
"funding": [
|
||||||
{
|
{
|
||||||
@@ -4332,9 +4361,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/postcss": {
|
"node_modules/postcss": {
|
||||||
"version": "8.5.12",
|
"version": "8.5.15",
|
||||||
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.12.tgz",
|
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.15.tgz",
|
||||||
"integrity": "sha512-W62t/Se6rA0Az3DfCL0AqJwXuKwBeYg6nOaIgzP+xZ7N5BFCI7DYi1qs6ygUYT6rvfi6t9k65UMLJC+PHZpDAA==",
|
"integrity": "sha512-FfR8sjd4em2T6fb3I2MwAJU7HWVMr9zba+enmQeeWFfCbm+UOC/0X4DS8XtpUTMwWMGbjKYP7xjfNekzyGmB3A==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"funding": [
|
"funding": [
|
||||||
{
|
{
|
||||||
@@ -4352,7 +4381,7 @@
|
|||||||
],
|
],
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"nanoid": "^3.3.11",
|
"nanoid": "^3.3.12",
|
||||||
"picocolors": "^1.1.1",
|
"picocolors": "^1.1.1",
|
||||||
"source-map-js": "^1.2.1"
|
"source-map-js": "^1.2.1"
|
||||||
},
|
},
|
||||||
@@ -4498,14 +4527,14 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/rolldown": {
|
"node_modules/rolldown": {
|
||||||
"version": "1.0.0-rc.17",
|
"version": "1.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/rolldown/-/rolldown-1.0.0-rc.17.tgz",
|
"resolved": "https://registry.npmjs.org/rolldown/-/rolldown-1.0.1.tgz",
|
||||||
"integrity": "sha512-ZrT53oAKrtA4+YtBWPQbtPOxIbVDbxT0orcYERKd63VJTF13zPcgXTvD4843L8pcsI7M6MErt8QtON6lrB9tyA==",
|
"integrity": "sha512-X0KQHljNnEkWNqqiz9zJrGunh1B0HgOxLXvnFpCOcadzcy5qohZ3tqMEUg00vncoRovXuK3ZqCT9KnnKzoInFQ==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@oxc-project/types": "=0.127.0",
|
"@oxc-project/types": "=0.130.0",
|
||||||
"@rolldown/pluginutils": "1.0.0-rc.17"
|
"@rolldown/pluginutils": "^1.0.0"
|
||||||
},
|
},
|
||||||
"bin": {
|
"bin": {
|
||||||
"rolldown": "bin/cli.mjs"
|
"rolldown": "bin/cli.mjs"
|
||||||
@@ -4514,21 +4543,21 @@
|
|||||||
"node": "^20.19.0 || >=22.12.0"
|
"node": "^20.19.0 || >=22.12.0"
|
||||||
},
|
},
|
||||||
"optionalDependencies": {
|
"optionalDependencies": {
|
||||||
"@rolldown/binding-android-arm64": "1.0.0-rc.17",
|
"@rolldown/binding-android-arm64": "1.0.1",
|
||||||
"@rolldown/binding-darwin-arm64": "1.0.0-rc.17",
|
"@rolldown/binding-darwin-arm64": "1.0.1",
|
||||||
"@rolldown/binding-darwin-x64": "1.0.0-rc.17",
|
"@rolldown/binding-darwin-x64": "1.0.1",
|
||||||
"@rolldown/binding-freebsd-x64": "1.0.0-rc.17",
|
"@rolldown/binding-freebsd-x64": "1.0.1",
|
||||||
"@rolldown/binding-linux-arm-gnueabihf": "1.0.0-rc.17",
|
"@rolldown/binding-linux-arm-gnueabihf": "1.0.1",
|
||||||
"@rolldown/binding-linux-arm64-gnu": "1.0.0-rc.17",
|
"@rolldown/binding-linux-arm64-gnu": "1.0.1",
|
||||||
"@rolldown/binding-linux-arm64-musl": "1.0.0-rc.17",
|
"@rolldown/binding-linux-arm64-musl": "1.0.1",
|
||||||
"@rolldown/binding-linux-ppc64-gnu": "1.0.0-rc.17",
|
"@rolldown/binding-linux-ppc64-gnu": "1.0.1",
|
||||||
"@rolldown/binding-linux-s390x-gnu": "1.0.0-rc.17",
|
"@rolldown/binding-linux-s390x-gnu": "1.0.1",
|
||||||
"@rolldown/binding-linux-x64-gnu": "1.0.0-rc.17",
|
"@rolldown/binding-linux-x64-gnu": "1.0.1",
|
||||||
"@rolldown/binding-linux-x64-musl": "1.0.0-rc.17",
|
"@rolldown/binding-linux-x64-musl": "1.0.1",
|
||||||
"@rolldown/binding-openharmony-arm64": "1.0.0-rc.17",
|
"@rolldown/binding-openharmony-arm64": "1.0.1",
|
||||||
"@rolldown/binding-wasm32-wasi": "1.0.0-rc.17",
|
"@rolldown/binding-wasm32-wasi": "1.0.1",
|
||||||
"@rolldown/binding-win32-arm64-msvc": "1.0.0-rc.17",
|
"@rolldown/binding-win32-arm64-msvc": "1.0.1",
|
||||||
"@rolldown/binding-win32-x64-msvc": "1.0.0-rc.17"
|
"@rolldown/binding-win32-x64-msvc": "1.0.1"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/rollup-plugin-visualizer": {
|
"node_modules/rollup-plugin-visualizer": {
|
||||||
@@ -4785,9 +4814,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/tom-select": {
|
"node_modules/tom-select": {
|
||||||
"version": "2.6.0",
|
"version": "2.6.1",
|
||||||
"resolved": "https://registry.npmjs.org/tom-select/-/tom-select-2.6.0.tgz",
|
"resolved": "https://registry.npmjs.org/tom-select/-/tom-select-2.6.1.tgz",
|
||||||
"integrity": "sha512-o2ToBjhUAnrrQvW/hrY9c//TpOpAKYSlfuFnf0DIwNy+ua+mmYnsF4PxN/PpzBfUIfEFkNYAngeGBfOAZWF3tw==",
|
"integrity": "sha512-d/1kngVOQTGcI/2pVDfDLYjtjUgSSd3fSgkYUpi0y+yRtQQu2kzljj3aUdqMfqc45cjPvDEpfDt/hSX4awDFTg==",
|
||||||
"license": "Apache-2.0",
|
"license": "Apache-2.0",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@orchidjs/sifter": "^1.1.0",
|
"@orchidjs/sifter": "^1.1.0",
|
||||||
@@ -4914,16 +4943,16 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/vite": {
|
"node_modules/vite": {
|
||||||
"version": "8.0.10",
|
"version": "8.0.13",
|
||||||
"resolved": "https://registry.npmjs.org/vite/-/vite-8.0.10.tgz",
|
"resolved": "https://registry.npmjs.org/vite/-/vite-8.0.13.tgz",
|
||||||
"integrity": "sha512-rZuUu9j6J5uotLDs+cAA4O5H4K1SfPliUlQwqa6YEwSrWDZzP4rhm00oJR5snMewjxF5V/K3D4kctsUTsIU9Mw==",
|
"integrity": "sha512-MFtjBYgzmSxmgA4RAfjIyXWpGe1oALnjgUTzzV7QLx/TKxCzjtMH6Fd9/eVK+5Fg1qNoz5VAwsmMs/NofrmJvw==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"lightningcss": "^1.32.0",
|
"lightningcss": "^1.32.0",
|
||||||
"picomatch": "^4.0.4",
|
"picomatch": "^4.0.4",
|
||||||
"postcss": "^8.5.10",
|
"postcss": "^8.5.14",
|
||||||
"rolldown": "1.0.0-rc.17",
|
"rolldown": "1.0.1",
|
||||||
"tinyglobby": "^0.2.16"
|
"tinyglobby": "^0.2.16"
|
||||||
},
|
},
|
||||||
"bin": {
|
"bin": {
|
||||||
@@ -4940,7 +4969,7 @@
|
|||||||
},
|
},
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
"@types/node": "^20.19.0 || >=22.12.0",
|
"@types/node": "^20.19.0 || >=22.12.0",
|
||||||
"@vitejs/devtools": "^0.1.0",
|
"@vitejs/devtools": "^0.1.18",
|
||||||
"esbuild": "^0.27.0 || ^0.28.0",
|
"esbuild": "^0.27.0 || ^0.28.0",
|
||||||
"jiti": ">=1.21.0",
|
"jiti": ">=1.21.0",
|
||||||
"less": "^4.0.0",
|
"less": "^4.0.0",
|
||||||
|
|||||||
+12
-11
@@ -25,19 +25,20 @@
|
|||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@babel/core": "^7.29.0",
|
"@babel/core": "^7.29.0",
|
||||||
"@babel/preset-env": "^7.29.2",
|
"@babel/preset-env": "^7.29.5",
|
||||||
"@biomejs/biome": "^2.4.13",
|
"@biomejs/biome": "^2.4.15",
|
||||||
"@hey-api/openapi-ts": "^0.94.5",
|
"@hey-api/openapi-ts": "^0.94.5",
|
||||||
"@types/alpinejs": "^3.13.11",
|
"@types/alpinejs": "^3.13.11",
|
||||||
|
"@types/alpinejs__sort": "^3.13.0",
|
||||||
"@types/cytoscape-cxtmenu": "^3.4.5",
|
"@types/cytoscape-cxtmenu": "^3.4.5",
|
||||||
"@types/cytoscape-klay": "^3.1.5",
|
"@types/cytoscape-klay": "^3.1.5",
|
||||||
"@types/js-cookie": "^3.0.6",
|
"@types/js-cookie": "^3.0.6",
|
||||||
"rollup-plugin-visualizer": "^7.0.1",
|
"rollup-plugin-visualizer": "^7.0.1",
|
||||||
"typescript": "^5.9.3",
|
"typescript": "^5.9.3",
|
||||||
"vite": "^8.0.10"
|
"vite": "^8.0.13"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@alpinejs/sort": "^3.15.11",
|
"@alpinejs/sort": "^3.15.12",
|
||||||
"@arendjr/text-clipper": "npm:@jsr/arendjr__text-clipper@^3.0.0",
|
"@arendjr/text-clipper": "npm:@jsr/arendjr__text-clipper@^3.0.0",
|
||||||
"@floating-ui/dom": "^1.7.6",
|
"@floating-ui/dom": "^1.7.6",
|
||||||
"@fortawesome/fontawesome-free": "^7.2.0",
|
"@fortawesome/fontawesome-free": "^7.2.0",
|
||||||
@@ -45,25 +46,25 @@
|
|||||||
"@fullcalendar/daygrid": "^6.1.20",
|
"@fullcalendar/daygrid": "^6.1.20",
|
||||||
"@fullcalendar/icalendar": "^6.1.20",
|
"@fullcalendar/icalendar": "^6.1.20",
|
||||||
"@fullcalendar/list": "^6.1.20",
|
"@fullcalendar/list": "^6.1.20",
|
||||||
"@sentry/browser": "^10.51.0",
|
"@sentry/browser": "^10.53.1",
|
||||||
"@zip.js/zip.js": "^2.8.26",
|
"@zip.js/zip.js": "^2.8.26",
|
||||||
"3d-force-graph": "^1.80.0",
|
"3d-force-graph": "^1.80.0",
|
||||||
"alpinejs": "^3.15.11",
|
"alpinejs": "^3.15.12",
|
||||||
"chart.js": "^4.5.1",
|
"chart.js": "^4.5.1",
|
||||||
"country-flag-emoji-polyfill": "^0.1.8",
|
"country-flag-emoji-polyfill": "^0.1.8",
|
||||||
"cytoscape": "^3.33.2",
|
"cytoscape": "^3.33.4",
|
||||||
"cytoscape-cxtmenu": "^3.5.0",
|
"cytoscape-cxtmenu": "^3.5.0",
|
||||||
"cytoscape-klay": "^3.1.4",
|
"cytoscape-klay": "^3.1.4",
|
||||||
"d3-force-3d": "^3.0.6",
|
"d3-force-3d": "^3.0.6",
|
||||||
"easymde": "^2.20.0",
|
"easymde": "^2.21.0",
|
||||||
"glob": "^13.0.6",
|
"glob": "^13.0.6",
|
||||||
"html2canvas": "^1.4.1",
|
"html2canvas": "^1.4.1",
|
||||||
"htmx.org": "^2.0.10",
|
"htmx.org": "^2.0.10",
|
||||||
"js-cookie": "^3.0.5",
|
"js-cookie": "^3.0.7",
|
||||||
"lit-html": "^3.3.2",
|
"lit-html": "^3.3.3",
|
||||||
"native-file-system-adapter": "^3.0.1",
|
"native-file-system-adapter": "^3.0.1",
|
||||||
"three": "^0.184.0",
|
"three": "^0.184.0",
|
||||||
"three-spritetext": "^1.10.0",
|
"three-spritetext": "^1.10.0",
|
||||||
"tom-select": "^2.6.0"
|
"tom-select": "^2.6.1"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+30
-30
@@ -19,38 +19,38 @@ authors = [
|
|||||||
license = { text = "GPL-3.0-only" }
|
license = { text = "GPL-3.0-only" }
|
||||||
requires-python = "<4.0,>=3.12"
|
requires-python = "<4.0,>=3.12"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"django>=5.2.13,<6.0.0",
|
"django>=5.2.14,<6.0.0",
|
||||||
"django-ninja>=1.6.2,<6.0.0",
|
"django-ninja>=1.6.2,<2.0.0",
|
||||||
"django-ninja-extra>=0.31.4",
|
"django-ninja-extra>=0.31.4",
|
||||||
"Pillow>=12.2.0,<13.0.0",
|
"Pillow>=12.2.0,<13.0.0",
|
||||||
"mistune>=3.2.0,<4.0.0",
|
"mistune>=3.2.1,<4.0.0",
|
||||||
"django-jinja<3.0.0,>=2.11.0",
|
"django-jinja<3.0.0,>=2.11.0",
|
||||||
"cryptography>=47.0.0,<48.0.0",
|
"cryptography>=48.0.0,<49.0.0",
|
||||||
"django-phonenumber-field>=8.4.0,<9.0.0",
|
"django-phonenumber-field>=8.4.0,<9.0.0",
|
||||||
"phonenumbers>=9.0.29,<10.0.0",
|
"phonenumbers>=9.0.30,<10.0.0",
|
||||||
"reportlab>=4.5.0,<5.0.0",
|
"reportlab>=4.5.1,<5.0.0",
|
||||||
"django-haystack>=3.3.0,<4.0.0",
|
"django-haystack>=3.3.0,<4.0.0",
|
||||||
"xapian-haystack>=4.0.0,<5.0.0",
|
"xapian-haystack>=4.0.0,<5.0.0",
|
||||||
"libsass<1.0.0,>=0.23.0",
|
"libsass>=0.23.0,<1.0.0",
|
||||||
"django-ordered-model<4.0.0,>=3.7.4",
|
"django-ordered-model>=3.7.4,<4.0.0",
|
||||||
"django-simple-captcha<1.0.0,>=0.6.3",
|
"django-simple-captcha>=0.6.3,<1.0.0",
|
||||||
"python-dateutil<3.0.0.0,>=2.9.0.post0",
|
"python-dateutil>=2.9.0.post0,<3.0.0.0",
|
||||||
"sentry-sdk>=2.58.0,<3.0.0",
|
"sentry-sdk>=2.60.0,<3.0.0",
|
||||||
"jinja2<4.0.0,>=3.1.6",
|
"jinja2>=3.1.6,<4.0.0",
|
||||||
"django-countries>=8.2.0,<9.0.0",
|
"django-countries>=8.2.0,<9.0.0",
|
||||||
"dict2xml>=1.7.8,<2.0.0",
|
"dict2xml>=1.7.8,<2.0.0",
|
||||||
"Sphinx<6,>=5",
|
"Sphinx>=9.1.0,<10", # Used by xapian during installation
|
||||||
"tomli>=2.4.1,<3.0.0",
|
"tomli>=2.4.1,<3.0.0",
|
||||||
"django-honeypot>=1.3.0,<2",
|
"django-honeypot>=1.3.0,<2",
|
||||||
"pydantic-extra-types>=2.11.1,<3.0.0",
|
"pydantic-extra-types>=2.11.1,<3.0.0",
|
||||||
"ical>=11.1.0,<14.0.0",
|
"ical>=12.0.0,<14.0.0",
|
||||||
"redis[hiredis]>=6.4.0,<8.0.0",
|
"redis[hiredis]>=3.3.1,<8.0.0",
|
||||||
"environs[django]>=15.0.1,<16.0.0",
|
"environs[django]>=15.0.1,<16",
|
||||||
"requests>=2.32.5,<3.0.0",
|
"requests>=2.34.2,<3.0.0",
|
||||||
"honcho>=2.0.0",
|
"honcho>=2.0.0",
|
||||||
"psutil>=7.2.2,<8.0.0",
|
"psutil>=7.2.2,<8.0.0",
|
||||||
"celery[redis]>=5.6.2,<7",
|
"celery[redis]>=5.6.3,<8",
|
||||||
"django-celery-results>=2.5.1",
|
"django-celery-results>=2.6.0",
|
||||||
"django-celery-beat>=2.9.0",
|
"django-celery-beat>=2.9.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
@@ -60,43 +60,43 @@ documentation = "https://sith-ae.readthedocs.io/"
|
|||||||
|
|
||||||
[dependency-groups]
|
[dependency-groups]
|
||||||
prod = [
|
prod = [
|
||||||
"psycopg[c]>=3.3.3,<4.0.0",
|
"psycopg[c]>=3.3.4,<4.0.0",
|
||||||
]
|
]
|
||||||
dev = [
|
dev = [
|
||||||
"django-debug-toolbar>=6.3.0,<7",
|
"django-debug-toolbar>=6.3.0,<7",
|
||||||
"ipython>=9.13.0,<10.0.0",
|
"ipython>=9.13.0,<10.0.0",
|
||||||
"pre-commit>=4.6.0,<5.0.0",
|
"pre-commit>=4.6.0,<5.0.0",
|
||||||
"ruff>=0.15.12,<1.0.0",
|
"ruff>=0.15.13,<1.0.0",
|
||||||
"djhtml>=3.0.11,<4.0.0",
|
"djhtml>=3.0.11,<4.0.0",
|
||||||
"faker>=40.15.0,<41.0.0",
|
"faker>=40.18.0,<41.0.0",
|
||||||
"rjsmin>=1.2.5,<2.0.0",
|
"rjsmin>=1.2.5,<2.0.0",
|
||||||
]
|
]
|
||||||
tests = [
|
tests = [
|
||||||
"freezegun>=1.5.5,<2.0.0",
|
"freezegun>=1.5.5,<2.0.0",
|
||||||
"pytest>=9.0.3,<10.0.0",
|
"pytest>=9.0.3,<10.0.0",
|
||||||
"pytest-cov>=7.1.0,<8.0.0",
|
"pytest-cov>=7.1.0,<8.0.0",
|
||||||
"pytest-django<5.0.0,>=4.12.0",
|
"pytest-django>=4.12.0,<5.0.0",
|
||||||
"model-bakery<2.0.0,>=1.23.4",
|
"model-bakery>=1.23.4,<2.0.0",
|
||||||
"beautifulsoup4>=4.14.3,<5",
|
"beautifulsoup4>=4.14.3,<5",
|
||||||
"lxml>=6.1.0,<7",
|
"lxml>=6.1.1,<7",
|
||||||
]
|
]
|
||||||
docs = [
|
docs = [
|
||||||
"mkdocs<2.0.0,>=1.6.1",
|
"mkdocs>=1.6.1,<2.0.0",
|
||||||
"mkdocs-material>=9.7.6,<10.0.0",
|
"mkdocs-material>=9.7.6,<10.0.0",
|
||||||
"mkdocstrings>=1.0.4,<2.0.0",
|
"mkdocstrings>=1.0.4,<2.0.0",
|
||||||
"mkdocstrings-python>=2.0.3,<3.0.0",
|
"mkdocstrings-python>=2.0.3,<3.0.0",
|
||||||
"mkdocs-include-markdown-plugin>=7.2.2,<8.0.0",
|
"mkdocs-include-markdown-plugin>=7.3.0,<8.0.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
[tool.uv]
|
[tool.uv]
|
||||||
default-groups = ["dev", "tests", "docs"]
|
default-groups = ["dev", "tests", "docs"]
|
||||||
|
|
||||||
[tool.xapian]
|
[tool.xapian]
|
||||||
version = "1.4.31"
|
version = "2.0.0"
|
||||||
# Those hashes are here to protect against supply chains attacks
|
# Those hashes are here to protect against supply chains attacks
|
||||||
# See `https://ae-utbm.github.io/sith/howto/xapian/` for more information
|
# See `https://ae-utbm.github.io/sith/howto/xapian/` for more information
|
||||||
core-sha256 = "fecf609ea2efdc8a64be369715aac733336a11f7480a6545244964ae6bc80811"
|
core-sha256 = "6cea3f49952a47224439a40bdb3608f928d121ad8721b9921cc42802d548ecf8"
|
||||||
bindings-sha256 = "a38cc7ba4188cc0bd27dc7369f03906772047087a1c54f1b93355d5e9103c304"
|
bindings-sha256 = "9a544b69c31355a92edbcd4102cf0f1ec4407fd0a4645f4870fb52300b736910"
|
||||||
|
|
||||||
[tool.ruff]
|
[tool.ruff]
|
||||||
output-format = "concise" # makes ruff error logs easier to read
|
output-format = "concise" # makes ruff error logs easier to read
|
||||||
|
|||||||
@@ -22,35 +22,35 @@ document.addEventListener("alpine:init", () => {
|
|||||||
albums: [] as Album[],
|
albums: [] as Album[],
|
||||||
|
|
||||||
async fetchPictures(): Promise<PictureSchema[]> {
|
async fetchPictures(): Promise<PictureSchema[]> {
|
||||||
const localStorageKey = `user${config.userId}Pictures`;
|
// Check the cache before hitting the API.
|
||||||
const localStorageInvalidationKey = `user${config.userId}PicturesNumber`;
|
const storageKey = "userPictures";
|
||||||
const lastCachedNumber = localStorage.getItem(localStorageInvalidationKey);
|
const cacheContent: { userId: number; pictures: PictureSchema[] }[] = JSON.parse(
|
||||||
|
sessionStorage.getItem(storageKey) || "[]",
|
||||||
|
);
|
||||||
|
const userPictures = cacheContent.find((obj) => obj.userId === config.userId);
|
||||||
if (
|
if (
|
||||||
lastCachedNumber !== null &&
|
userPictures !== undefined &&
|
||||||
Number.parseInt(lastCachedNumber, 10) === config.nbPictures
|
userPictures.pictures.length === config.nbPictures
|
||||||
) {
|
) {
|
||||||
return JSON.parse(localStorage.getItem(localStorageKey));
|
// The cached value is considered valid
|
||||||
|
// if it contains the right amount of pictures.
|
||||||
|
// This amount is known because it is given in the template.
|
||||||
|
return userPictures.pictures;
|
||||||
}
|
}
|
||||||
const pictures = await paginated(picturesFetchPictures, {
|
const pictures = await paginated(picturesFetchPictures, {
|
||||||
// biome-ignore lint/style/useNamingConvention: from python api
|
// biome-ignore lint/style/useNamingConvention: from python api
|
||||||
query: { users_identified: [config.userId] },
|
query: { users_identified: [config.userId] },
|
||||||
} as PicturesFetchPicturesData);
|
} as PicturesFetchPicturesData);
|
||||||
|
|
||||||
|
cacheContent.push({ userId: config.userId, pictures: pictures });
|
||||||
try {
|
try {
|
||||||
localStorage.setItem(localStorageInvalidationKey, config.nbPictures.toString());
|
// cache only the pictures of the last 4 visited profiles
|
||||||
localStorage.setItem(localStorageKey, JSON.stringify(pictures));
|
sessionStorage.setItem(storageKey, JSON.stringify(cacheContent.slice(-4)));
|
||||||
} catch {
|
} catch {
|
||||||
// an exception is raised if the localstorage is entirely filled
|
// an exception is raised if the storage is entirely filled.
|
||||||
// so just delete all cached user pictures.
|
// To be as safe as possible, delete the cached pictures.
|
||||||
// A cache hit is not worth the page breaking.
|
// A cache hit is not worth the page breaking.
|
||||||
Object.keys(localStorage)
|
sessionStorage.removeItem(storageKey);
|
||||||
.filter(
|
|
||||||
(key) =>
|
|
||||||
key.startsWith("user") &&
|
|
||||||
(key.endsWith("Pictures") || key.endsWith("PicturesNumber")),
|
|
||||||
)
|
|
||||||
.forEach((key) => {
|
|
||||||
localStorage.removeItem(key);
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
return pictures;
|
return pictures;
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -88,6 +88,11 @@ X_FRAME_OPTIONS = "SAMEORIGIN"
|
|||||||
|
|
||||||
ALLOWED_HOSTS = ["*"]
|
ALLOWED_HOSTS = ["*"]
|
||||||
|
|
||||||
|
# RemovedInDjango60Warning: It's a transitional setting helpful in early
|
||||||
|
# adoption of "https" as the new default value of forms.URLField.assume_scheme.
|
||||||
|
# Remove this after upgrading to Django 6.x
|
||||||
|
FORMS_URLFIELD_ASSUME_HTTPS = True
|
||||||
|
|
||||||
# Application definition
|
# Application definition
|
||||||
|
|
||||||
DEFAULT_AUTO_FIELD = "django.db.models.AutoField"
|
DEFAULT_AUTO_FIELD = "django.db.models.AutoField"
|
||||||
|
|||||||
@@ -32,7 +32,7 @@ class JsBundlerManifestEntry:
|
|||||||
# because that's what the user types when importing statics and that's what django gives us
|
# because that's what the user types when importing statics and that's what django gives us
|
||||||
# This is really similar to what we are doing in the bundler, it uses a similar algorithm
|
# This is really similar to what we are doing in the bundler, it uses a similar algorithm
|
||||||
# Example:
|
# Example:
|
||||||
# core/static/bundled/alpine-index.js -> bundled/alpine-index.js
|
# core/static/bundled/alpine-index.ts -> bundled/alpine-index.ts
|
||||||
# core/static/bundled/components/include-index.ts -> core/static/bundled/components/include-index.ts
|
# core/static/bundled/components/include-index.ts -> core/static/bundled/components/include-index.ts
|
||||||
def get_relative_src_name(name: str) -> str:
|
def get_relative_src_name(name: str) -> str:
|
||||||
original_path = Path(name)
|
original_path = Path(name)
|
||||||
|
|||||||
Reference in New Issue
Block a user