diff --git a/.coverage b/.coverage
index 8e91faf61..20a83ead9 100644
Binary files a/.coverage and b/.coverage differ
diff --git a/.github/workflows/django.yml b/.github/workflows/django.yml
index e9d2030f0..d87a66abf 100644
--- a/.github/workflows/django.yml
+++ b/.github/workflows/django.yml
@@ -2,15 +2,12 @@ name: Django CI
on:
push:
-
- branches: [ "develop", "tests", "main" ]
+ branches: ["develop", "tests", "main"]
pull_request:
- branches: [ "develop", "tests", "main" ]
-
+ branches: ["develop", "tests", "main"]
jobs:
build:
-
runs-on: self-hosted
services:
@@ -26,37 +23,36 @@ jobs:
options: --health-cmd pg_isready --health-interval 10s --health-timeout 5s --health-retries 5
-
steps:
- - uses: actions/checkout@v3
- - name: Set up Python 3.10
- uses: actions/setup-python@v3
- with:
- python-version: '3.10'
- - name: Install Dependencies
- run: |
- python3 -m pip install --upgrade pip
- pip install -r requirements.txt
- - name: Linting API
- run: |
- cd api
- flake8 .
- cd ..
- - name: Run Tests
- env:
- CLIENT_ID: ${{ secrets.CLIENT_ID }}
- CLIENT_SECRET: ${{ secrets.CLIENT_SECRET }}
- TENANT_ID: ${{ secrets.TENANT_ID }}
- AD_URL: ${{ secrets.AD_URL }}
- SECRET_KEY: ${{ secrets.SECRET_KEY }}
- DB_NAME: ${{ secrets.DB_NAME }}
- DB_USER: ${{ secrets.DB_USER }}
- DB_PASSWORD: ${{ secrets.DB_PASSWORD }}
- DB_HOST: ${{ secrets.DB_HOST }}
- DB_PORT: ${{ secrets.DB_PORT }}
- DB_ENGINE: ${{secrets.DB_ENGINE}}
-
- run: |
- python manage.py makemigrations api
- python manage.py migrate api
- python manage.py test
+ - uses: actions/checkout@v3
+ - name: Set up Python 3.10
+ uses: actions/setup-python@v3
+ with:
+ python-version: "3.10"
+ - name: Install Dependencies
+ run: |
+ python3 -m pip install --upgrade pip
+ pip install -r requirements.txt
+ - name: Linting API
+ run: |
+ cd api
+ flake8 .
+ cd ..
+ - name: Run Tests
+ env:
+ CLIENT_ID: ${{ secrets.CLIENT_ID }}
+ CLIENT_SECRET: ${{ secrets.CLIENT_SECRET }}
+ TENANT_ID: ${{ secrets.TENANT_ID }}
+ AD_URL: ${{ secrets.AD_URL }}
+ SECRET_KEY: ${{ secrets.SECRET_KEY }}
+ DB_NAME: ${{ secrets.DB_NAME }}
+ DB_USER: ${{ secrets.DB_USER }}
+ DB_PASSWORD: ${{ secrets.DB_PASSWORD }}
+ DB_HOST: ${{ secrets.DB_HOST }}
+ DB_PORT: ${{ secrets.DB_PORT }}
+ DB_ENGINE: ${{secrets.DB_ENGINE}}
+
+ run: |
+ python manage.py makemigrations api
+ python manage.py migrate api
+ python manage.py test
diff --git a/.gitignore b/.gitignore
index b7fd2d29d..259751217 100644
--- a/.gitignore
+++ b/.gitignore
@@ -14,4 +14,8 @@ htmlcov
data
uploads
/data/
+cypress/screenshots
/package-lock.json
+/frontend/package-lock.json
+/frontend/frontend/package-lock.json
+package-lock.json
diff --git a/README.md b/README.md
index d14f9145f..63c0fd4dd 100644
--- a/README.md
+++ b/README.md
@@ -2,7 +2,11 @@
# Wiki
-- Raadpleeg de wiki voor uitgebreidere informatie, waaronder Use-Cases en API-Documentatie en Website-documentatie.
+- Raadpleeg de [Wiki](https://github.com/SELab-2/UGent-4/wiki) voor uitgebreidere informatie, waaronder Use-Cases, API-Documentatie en Website-documentatie.
+
+# Deployment diagram
+![image](https://github.com/SELab-2/UGent-4/assets/49711425/805e37c4-fd67-4e68-8b8a-1310efce7864)
+
# Frontend
@@ -24,7 +28,9 @@ Er kan niet gepushed worden als er nog linting errors aanwezig zijn.
### Testen:
-- WIP
+Je kan visueel in de browser de testen volgen door het commando `npm run cypress-open` te runnen. Je zal dan ook de keuze krijgen om te kiezen tussen E2E testen of om component testen te volgen.
+
+`npm run cypress-e2e` en `cypress-component` zullen de e2e testen en de component testen runnen en in de terminal tonen of ze slagen.
# Backend
@@ -60,4 +66,15 @@ Run het commando `flake8 .`. De output vertelt je waar de codestijl fout is. Om
### Testing:
+## Backend testing:
Run het commando `./manage.py test` in de ***UGENT-4*** directory.
+Om de coverage te raadplegen kan het commando `coverage run manage.py test -v 2 && coverage report && coverage html` gebruikt worden. Dit runt alle testen en toont per bestand de coverage. Ook wordt er voor elk bestand een html gemaakt waar je per lijn kan zien of die door de testen gecovered wordt.
+## Frontend testing:
+Door het commando `npm run cypress-component` uit te voeren, worden de component testen gerund.
+Voor de end-to-end testen verloopt het een beetje anders:
+- Eerst en vooral zorg je ervoor dat je een lokale backend en frontend hebt lopen. In axios.config verander je de baseURL naar `baseURL: 'http://localhost:8000/api/'`, dit zorgt ervoor dat de lokale backend wordt aangesproken. De e2e testen vereisen een lege databank.
+- Met het commando `npm run cypress-component` open je een interactieve cypress browser. Hierin selecteer je e2e (je kan hier ook de component testen interactief runnen).
+- Vervolgens open je een nieuwe terminal en run je het commando `sudo bash switch_test.sh true`. Hiermee zet je de test environment op voor een lesgever. Nu voer je de testen 'course', 'project' en 'groups' (in deze volgorde) uit.
+- Hierna switchen we naar de test environment voor een student door 2 maal `sudo bash switch_test.sh` te runnen.
+- In cypress voer je nu de 'group' en 'submission' testen uit (opnieuw in deze volgorde). Ten slotte zet je weer de test environment op voor een lesgever door 2 maal het commando `sudo bash switch_test.sh true` uit te voeren. Nu kan je in cypress de testen 'score' en 'archive' (in deze volgorde) uitvoeren.
+- Alle e2e testen uitgevoerd en kan je de lokale backend en frontend stopzetten, de baseURL terug veranderen naar `baseURL: 'https://sel2-4.ugent.be/api/'` en de test omgeving verlaten met `sudo bash switch_test.sh`.
diff --git a/api/admin.py b/api/admin.py
index e326c7e85..bb5da771b 100644
--- a/api/admin.py
+++ b/api/admin.py
@@ -3,9 +3,10 @@
from api.models.vak import Vak
from api.models.groep import Groep
from api.models.project import Project
-from api.models.indiening import Indiening, IndieningBestand
+from api.models.indiening import Indiening
from api.models.score import Score
from api.models.restrictie import Restrictie
+from api.models.template import Template
admin.site.register(Gebruiker)
admin.site.register(Vak)
@@ -13,5 +14,5 @@
admin.site.register(Project)
admin.site.register(Indiening)
admin.site.register(Score)
-admin.site.register(IndieningBestand)
admin.site.register(Restrictie)
+admin.site.register(Template)
diff --git a/api/base_templates/file_name.sh b/api/base_templates/file_name.sh
new file mode 100755
index 000000000..4d48a67cd
--- /dev/null
+++ b/api/base_templates/file_name.sh
@@ -0,0 +1,12 @@
+#!/bin/bash
+
+#@param
+# Vul hieronder de naam in dat het ingediende bestand moet hebben
+file_name="verslag.pdf"
+
+
+if [ -e "$file_name" ]; then
+ echo "$file_name present: OK"
+else
+ echo "$file_name not present: FAIL"
+fi
diff --git a/api/base_templates/file_type.sh b/api/base_templates/file_type.sh
new file mode 100755
index 000000000..f303f8620
--- /dev/null
+++ b/api/base_templates/file_type.sh
@@ -0,0 +1,20 @@
+#!/bin/bash
+
+#@param
+# Vul hieronder het type dat het bestand moet hebben
+file_type="pdf"
+
+
+file_exists=false
+for file in *.$file_type; do
+ if [ -e "$file" ]; then
+ file_exists=true
+ break
+ fi
+done
+
+if [ "$file_exists" = true ]; then
+ echo "$file_type: OK"
+else
+ echo "$file_type: FAIL"
+fi
diff --git a/api/base_templates/zip_contains_files.sh b/api/base_templates/zip_contains_files.sh
new file mode 100755
index 000000000..2b8aabf5d
--- /dev/null
+++ b/api/base_templates/zip_contains_files.sh
@@ -0,0 +1,20 @@
+#!/bin/bash
+
+#@param
+# Vul hieronder de namen van de bestanden in die je in de gezipte folder wilt vinden.
+file_names=("testfile1.txt" "testfile2.txt")
+
+#@param
+# Vul hieronder de naam van het zip bestand waarin je de files wil vinden
+zip_file_name="file.zip"
+
+
+for file_name in "${file_names[@]}"; do
+ unzip -l $zip_file_name | grep -q $file_name;
+ if [ "$?" == "0" ]
+ then
+ echo "$file_name present: OK"
+ else
+ echo "$file_name not present: FAIL"
+ fi;
+done
diff --git a/api/docker/python_entrypoint.py b/api/docker/python_entrypoint.py
index b19636bf3..feb748374 100644
--- a/api/docker/python_entrypoint.py
+++ b/api/docker/python_entrypoint.py
@@ -22,4 +22,4 @@ def run_tests_on(indiening_id, project_id):
child.sendline(dot_env_values.get("SUDO_PASSWORD"))
output = child.read().decode("utf-8")
child.close()
- return ": FAIL" in output, output
+ return output
diff --git a/api/docker/rundocker.sh b/api/docker/rundocker.sh
index da254c55b..9320d6e76 100644
--- a/api/docker/rundocker.sh
+++ b/api/docker/rundocker.sh
@@ -1,4 +1,4 @@
yes | docker system prune -a >&2
docker build -t script-demo -f api/docker/Dockerfile .
-docker run --mount type=bind,source="$(pwd)"/data/restricties/project_$2,target=/data/restricties --mount type=bind,source="$(pwd)"/data/indieningen/indiening_$1,target=/data --name demo -d script-demo
+docker run --mount type=bind,source="$(pwd)"/data/restricties/project_$2,target=/restricties --mount type=bind,source="$(pwd)"/data/indieningen/indiening_$1,target=/data --name demo -d script-demo
docker logs demo -f
diff --git a/api/docker/testing_entrypoint.sh b/api/docker/testing_entrypoint.sh
index 62d7ca348..1bd549f19 100644
--- a/api/docker/testing_entrypoint.sh
+++ b/api/docker/testing_entrypoint.sh
@@ -1,7 +1,8 @@
#!/bin/bash
cd data
+mkdir artefacten
-for filename in ./restricties/*; do
+for filename in ../restricties/*; do
echo -n "Testing ${filename}: "
if [[ "$filename" == *.sh ]]
@@ -12,3 +13,6 @@ for filename in ./restricties/*; do
python3 $filename
fi
done
+
+zip -r artefacten.zip artefacten
+rm -rf artefacten
diff --git a/api/middleware.py b/api/middleware/middleware.py
similarity index 72%
rename from api/middleware.py
rename to api/middleware/middleware.py
index ec935f6c2..5c1cd2074 100644
--- a/api/middleware.py
+++ b/api/middleware/middleware.py
@@ -3,7 +3,10 @@
from django.contrib.auth.models import User
from api.models.gebruiker import Gebruiker
from api.serializers.gebruiker import GebruikerSerializer
+from api.serializers.template import TemplateSerializer
+from django.core.files import File
import requests
+import os
URL = "https://graph.microsoft.com/v1.0/me"
@@ -59,6 +62,16 @@ def __call__(self, request):
try:
Gebruiker.objects.get(pk=request.user.id)
except Gebruiker.DoesNotExist:
+ directory_path = "api/base_templates"
+ for filename in os.listdir(directory_path):
+ file_path = os.path.join(directory_path, filename)
+ with open(file_path, "rb") as f:
+ django_file = File(f)
+ template_data = {"user": request.user.id, "bestand": django_file}
+ serializer = TemplateSerializer(data=template_data)
+ if serializer.is_valid():
+ serializer.save()
+
gebruiker_post_data = {
"user": request.user.id,
"subjects": [],
@@ -69,3 +82,13 @@ def __call__(self, request):
serializer.save()
return self.get_response(request)
+
+
+class DisableCSRFMiddleware(object):
+ def __init__(self, get_response):
+ self.get_response = get_response
+
+ def __call__(self, request):
+ setattr(request, "_dont_enforce_csrf_checks", True)
+ response = self.get_response(request)
+ return response
diff --git a/api/middleware/middleware_lesgever_test.py b/api/middleware/middleware_lesgever_test.py
new file mode 100644
index 000000000..c61be97b4
--- /dev/null
+++ b/api/middleware/middleware_lesgever_test.py
@@ -0,0 +1,73 @@
+from django.contrib.auth.models import User
+from api.models.gebruiker import Gebruiker
+from api.serializers.gebruiker import GebruikerSerializer
+from api.serializers.template import TemplateSerializer
+from django.core.files import File
+import os
+
+
+class AuthenticationUserMiddleware:
+ """
+ Middleware voor authenticatie van gebruikers en het aanmaken van gebruikersindeling.
+
+ Args:
+ get_response (callable): De volgende middleware in de keten.
+
+ Returns:
+ HttpResponse: Een HTTP-response-object.
+
+ Raises:
+ Redirect: Redirect naar de inlog-URL als er geen autorisatiegegevens zijn.
+ """
+
+ def __init__(self, get_response):
+ self.get_response = get_response
+
+ def __call__(self, request):
+
+ mail = "lesgever@testing.com"
+ try:
+ user = User.objects.get(username=mail)
+ except User.DoesNotExist:
+ user = User.objects.create_user(
+ username=mail,
+ email=mail,
+ first_name="Lesgever",
+ last_name="Testing",
+ )
+
+ request.user = user
+
+ try:
+ Gebruiker.objects.get(pk=request.user.id)
+ except Gebruiker.DoesNotExist:
+ directory_path = "api/base_templates"
+ for filename in os.listdir(directory_path):
+ file_path = os.path.join(directory_path, filename)
+ with open(file_path, "rb") as f:
+ django_file = File(f)
+ template_data = {"user": request.user.id, "bestand": django_file}
+ serializer = TemplateSerializer(data=template_data)
+ if serializer.is_valid():
+ serializer.save()
+
+ gebruiker_post_data = {
+ "user": request.user.id,
+ "subjects": [],
+ "is_lesgever": True,
+ }
+ serializer = GebruikerSerializer(data=gebruiker_post_data)
+ if serializer.is_valid():
+ serializer.save()
+
+ return self.get_response(request)
+
+
+class DisableCSRFMiddleware(object):
+ def __init__(self, get_response):
+ self.get_response = get_response
+
+ def __call__(self, request):
+ setattr(request, "_dont_enforce_csrf_checks", True)
+ response = self.get_response(request)
+ return response
diff --git a/api/middleware/middleware_original.py b/api/middleware/middleware_original.py
new file mode 100644
index 000000000..5c1cd2074
--- /dev/null
+++ b/api/middleware/middleware_original.py
@@ -0,0 +1,94 @@
+from django.conf import settings
+from django.shortcuts import redirect
+from django.contrib.auth.models import User
+from api.models.gebruiker import Gebruiker
+from api.serializers.gebruiker import GebruikerSerializer
+from api.serializers.template import TemplateSerializer
+from django.core.files import File
+import requests
+import os
+
+URL = "https://graph.microsoft.com/v1.0/me"
+
+
+class AuthenticationUserMiddleware:
+ """
+ Middleware voor authenticatie van gebruikers en het aanmaken van gebruikersindeling.
+
+ Args:
+ get_response (callable): De volgende middleware in de keten.
+
+ Returns:
+ HttpResponse: Een HTTP-response-object.
+
+ Raises:
+ Redirect: Redirect naar de inlog-URL als er geen autorisatiegegevens zijn.
+ """
+
+ def __init__(self, get_response):
+ self.get_response = get_response
+
+ def __call__(self, request):
+ if request.path in ["/oauth2/login", "/oauth2/callback"]:
+ return self.get_response(request)
+
+ if request.user.is_anonymous:
+ authorization = request.headers.get("Authorization")
+ if authorization:
+ headers = {
+ "Authorization": authorization,
+ "Content-Type": "application/json",
+ }
+
+ response = requests.get(url=URL, headers=headers)
+ json_data = response.json()
+ mail = json_data.get("mail")
+ first_name = json_data.get("givenName")
+ last_name = json_data.get("surname")
+ try:
+ user = User.objects.get(username=mail)
+ except User.DoesNotExist:
+ user = User.objects.create_user(
+ username=mail,
+ email=mail,
+ first_name=first_name,
+ last_name=last_name,
+ )
+
+ request.user = user
+ else:
+ return redirect(settings.LOGIN_URL)
+
+ try:
+ Gebruiker.objects.get(pk=request.user.id)
+ except Gebruiker.DoesNotExist:
+ directory_path = "api/base_templates"
+ for filename in os.listdir(directory_path):
+ file_path = os.path.join(directory_path, filename)
+ with open(file_path, "rb") as f:
+ django_file = File(f)
+ template_data = {"user": request.user.id, "bestand": django_file}
+ serializer = TemplateSerializer(data=template_data)
+ if serializer.is_valid():
+ serializer.save()
+
+ gebruiker_post_data = {
+ "user": request.user.id,
+ "subjects": [],
+ "is_lesgever": False,
+ }
+ serializer = GebruikerSerializer(data=gebruiker_post_data)
+ if serializer.is_valid():
+ serializer.save()
+
+ return self.get_response(request)
+
+
+class DisableCSRFMiddleware(object):
+ def __init__(self, get_response):
+ self.get_response = get_response
+
+ def __call__(self, request):
+ setattr(request, "_dont_enforce_csrf_checks", True)
+ response = self.get_response(request)
+ return response
diff --git a/api/middleware/middleware_student_test.py b/api/middleware/middleware_student_test.py
new file mode 100644
index 000000000..b82f9f844
--- /dev/null
+++ b/api/middleware/middleware_student_test.py
@@ -0,0 +1,73 @@
+from django.contrib.auth.models import User
+from api.models.gebruiker import Gebruiker
+from api.serializers.gebruiker import GebruikerSerializer
+from api.serializers.template import TemplateSerializer
+from django.core.files import File
+import os
+
+
+class AuthenticationUserMiddleware:
+ """
+ Middleware voor authenticatie van gebruikers en het aanmaken van gebruikersindeling.
+
+ Args:
+ get_response (callable): De volgende middleware in de keten.
+
+ Returns:
+ HttpResponse: Een HTTP-response-object.
+
+ Raises:
+ Redirect: Redirect naar de inlog-URL als er geen autorisatiegegevens zijn.
+ """
+
+ def __init__(self, get_response):
+ self.get_response = get_response
+
+ def __call__(self, request):
+
+ mail = "student@testing.com"
+ try:
+ user = User.objects.get(username=mail)
+ except User.DoesNotExist:
+ user = User.objects.create_user(
+ username=mail,
+ email=mail,
+ first_name="Student",
+ last_name="Testing",
+ )
+
+ request.user = user
+
+ try:
+ Gebruiker.objects.get(pk=request.user.id)
+ except Gebruiker.DoesNotExist:
+ directory_path = "api/base_templates"
+ for filename in os.listdir(directory_path):
+ file_path = os.path.join(directory_path, filename)
+ with open(file_path, "rb") as f:
+ django_file = File(f)
+ template_data = {"user": request.user.id, "bestand": django_file}
+ serializer = TemplateSerializer(data=template_data)
+ if serializer.is_valid():
+ serializer.save()
+
+ gebruiker_post_data = {
+ "user": request.user.id,
+ "subjects": [],
+ "is_lesgever": False,
+ }
+ serializer = GebruikerSerializer(data=gebruiker_post_data)
+ if serializer.is_valid():
+ serializer.save()
+
+ return self.get_response(request)
+
+
+class DisableCSRFMiddleware(object):
+ def __init__(self, get_response):
+ self.get_response = get_response
+
+ def __call__(self, request):
+ setattr(request, "_dont_enforce_csrf_checks", True)
+ response = self.get_response(request)
+ return response
diff --git a/api/models/gebruiker.py b/api/models/gebruiker.py
index 3f42db522..5566d050e 100644
--- a/api/models/gebruiker.py
+++ b/api/models/gebruiker.py
@@ -12,6 +12,9 @@ class Gebruiker(models.Model):
met een één-op-één-relatie. Dit veld fungeert als het primaire sleutelveld.
is_lesgever (BooleanField): Een boolean veld dat aangeeft of de gebruiker een lesgever is of niet.
Standaard ingesteld op False.
+ gepinde_vakken (ManyToManyField): Een veld dat verwijst naar het Vak model met een
+ veel-op-veel-relatie, om de vakken die de gebruiker heeft gepind op te slaan.
+ Dit veld is optioneel (mag leeg zijn).
Methods:
__str__(): Geeft een representatie van het model als een string terug,
diff --git a/api/models/groep.py b/api/models/groep.py
index 971a305ae..b1074b4eb 100644
--- a/api/models/groep.py
+++ b/api/models/groep.py
@@ -10,6 +10,7 @@ class Groep(models.Model):
de primaire sleutel voor de groep.
studenten (ManyToManyField): Een Many-to-Many relatie met het 'Gebruiker' model,
waarmee meerdere gebruikers aan een groep kunnen worden gekoppeld.
+ Dit veld is optioneel (mag leeg zijn).
project (ForeignKey): Een ForeignKey relatie met het 'Project' model,
waarmee wordt aangegeven tot welk project deze groep behoort.
Als het bijbehorende project wordt verwijderd,
diff --git a/api/models/indiening.py b/api/models/indiening.py
index 43df75e6d..a9741b98b 100644
--- a/api/models/indiening.py
+++ b/api/models/indiening.py
@@ -4,7 +4,11 @@
from api.docker.python_entrypoint import run_tests_on
from threading import Thread
from django.db import transaction
-import re
+from django.core.files.storage import default_storage
+from django.core.files.base import ContentFile
+
+from api.models.restrictie import Restrictie
+from api.utils import send_indiening_confirmation_mail
STATUS_CHOICES = (
@@ -16,7 +20,7 @@
def upload_to(instance, filename):
"""
- Functie om het pad te genereren waar het bestand wordt opgeslagen.
+ Genereert het pad waar het bestand wordt opgeslagen.
Args:
instance: De huidige instantie van het model.
@@ -25,6 +29,9 @@ def upload_to(instance, filename):
Returns:
str: Het pad waar het bestand moet worden opgeslagen.
"""
+ # Use a placeholder for the initial save
+ if instance.indiening_id is None:
+ return f"data/indieningen/temp/{filename}"
return f"data/indieningen/indiening_{instance.indiening_id}/{filename}"
@@ -41,53 +48,47 @@ class Indiening(models.Model):
indieningen verwijderd.
tijdstip (DateTimeField): Een veld dat automatisch het tijdstip
registreert waarop de indiening is aangemaakt.
+ bestand (FileField): Een veld voor het uploaden van het bestand,
+ met een dynamisch gegenereerd pad.
status (IntegerField): Een veld dat de status van de testen zal bijhouden.
result (TextField): Een veld dat het resultaat van de uitgevoerde testen zal bijhouden.
+ artefacten (FileField): Een optioneel veld voor het uploaden van extra artefacten.
Methods:
- __str__(): Geeft een representatie van het model als een string terug, die de ID van de indiening bevat.
+ __str__(): Geeft een representatie van het model als een string terug,
+ die de ID van de indiening bevat.
+ save(*args, **kwargs): Overschrijft de standaard opslaanmethode om het pad van het bestand bij te werken.
"""
indiening_id = models.AutoField(primary_key=True)
groep = models.ForeignKey("Groep", on_delete=models.CASCADE)
tijdstip = models.DateTimeField(auto_now_add=True)
+ bestand = models.FileField(upload_to=upload_to)
status = models.IntegerField(default=0, choices=STATUS_CHOICES)
result = models.TextField(default="", blank=True)
+ artefacten = models.FileField(blank=True)
def __str__(self):
return str(self.indiening_id)
+ def save(self, *args, **kwargs):
+ # TODO: probleem zit hier :sob:
+ if "temp" not in self.bestand.name:
+ super(Indiening, self).save(*args, **kwargs)
-class IndieningBestand(models.Model):
- """
- Model voor een bestand dat aan een indiening is gekoppeld.
-
- Fields:
- indiening_bestand_id (AutoField): Een automatisch gegenereerd veld dat fungeert als
- de primaire sleutel voor het bestand.
- indiening (ForeignKey): Een ForeignKey relatie met het 'Indiening' model,
- waarmee wordt aangegeven tot welke indiening dit bestand behoort.
- Als de bijbehorende indiening wordt verwijderd,
- wordt ook het bijbehorende bestand verwijderd.
- bestand (FileField): Een veld voor het uploaden van het bestand.
-
- Methods:
- __str__(): Geeft een representatie van het model als een string terug, die de bestandsnaam bevat.
- """
-
- indiening_bestand_id = models.AutoField(primary_key=True)
- indiening = models.ForeignKey(
- Indiening, related_name="indiening_bestanden", on_delete=models.CASCADE
- )
- bestand = models.FileField(upload_to=upload_to)
-
- def __str__(self):
- return str(self.bestand.name)
+ if "temp" in self.bestand.name:
+ old_file = self.bestand
+ old_file_name = old_file.name
+ new_path = old_file.name.replace("temp", f"indiening_{self.indiening_id}")
+ default_storage.save(new_path, ContentFile(old_file.read()))
+ self.bestand.name = new_path
+ default_storage.delete(old_file_name)
+ self.save()
def run_tests_async(instance):
"""
- Voert tests uit op een asynchrone manier en werkt de status en resultaat van de indiening bij.
+ Voert tests uit op een asynchrone manier en werkt de status en het resultaat van de indiening bij.
Args:
instance: Het instantie-object van de indiening.
@@ -95,26 +96,21 @@ def run_tests_async(instance):
indiening_id = instance.indiening_id
project_id = instance.groep.project.project_id
result = run_tests_on(indiening_id, project_id)
- matches = re.findall(r"Testing \./.*", result[1])
- try:
- first_match_index = result[1].find(matches[0])
- result = result[1][first_match_index:]
- status = -1 if result[0] else 1
- except Exception:
- result = result[1]
- status = -1
-
with transaction.atomic():
- instance.status = status
+ instance.status = -1 if "FAIL" in result else 1
instance.result = result
+ instance.artefacten = (
+ f"data/indieningen/indiening_{indiening_id}/artefacten.zip"
+ )
instance.save()
+ send_indiening_confirmation_mail(instance)
@receiver(post_save, sender=Indiening)
def indiening_post_init(sender, instance, created, **kwargs):
"""
Een signaalhandler die wordt geactiveerd na het maken van een nieuwe indiening.
- Start een asynchrone thread om de tests uit te voeren.
+ Start een asynchrone thread om de tests uit te voeren of werkt de status bij indien er geen restricties zijn.
Args:
sender: De verzender van het signaal.
@@ -123,5 +119,13 @@ def indiening_post_init(sender, instance, created, **kwargs):
kwargs: Extra argumenten.
"""
if created:
- thread = Thread(target=run_tests_async, args=(instance,))
- thread.start()
+ restricties = Restrictie.objects.filter(project=instance.groep.project)
+ if len(restricties) == 0:
+ with transaction.atomic():
+ instance.status = 1
+ instance.result = "No tests: OK"
+ instance.save()
+ send_indiening_confirmation_mail(instance)
+ else:
+ thread = Thread(target=run_tests_async, args=(instance,))
+ thread.start()
diff --git a/api/models/project.py b/api/models/project.py
index d9981115d..6cf3b282b 100644
--- a/api/models/project.py
+++ b/api/models/project.py
@@ -4,7 +4,7 @@
def upload_to(instance, filename):
"""
- Functie om het pad te genereren waar het opgavebestand wordt opgeslagen.
+ Genereert het pad waar het opgavebestand wordt opgeslagen.
Args:
instance: De huidige instantie van het model.
@@ -26,39 +26,37 @@ class Project(models.Model):
titel (CharField): Titel van het project.
beschrijving (TextField): Beschrijving van het project.
opgave_bestand (FileField): Een veld voor het uploaden van het opgavebestand voor het project.
- (eventueel uit te breiden tot meerdere bestanden mogelijk)
vak (ForeignKey): Een ForeignKey relatie met het 'Vak' model,
- waarmee wordt aangegeven tot welk vak dit project behoort.
+ waarmee wordt aangegeven tot welk vak dit project behoort.
Als het bijbehorende vak wordt verwijderd, worden ook de bijbehorende projecten verwijderd.
deadline (DateTimeField): Een veld voor het instellen van de deadline voor het project.
- Kan optioneel zijn (null=True).
+ Kan optioneel zijn (null=True, blank=True).
extra_deadline (DateTimeField): Een extra veld voor het instellen van een extra deadline voor het project.
- Kan optioneel zijn (null=True).
+ Kan optioneel zijn (null=True, blank=True).
max_score (IntegerField): Een veld voor het instellen van de maximale score voor het project.
- Standaard ingesteld op 20.
- max_groep_grootte (IntegerField): Een veld voor het instellen van de max grootte van de groep voor het project.
- Standaard ingesteld op 1.
+ Standaard ingesteld op 20.
+ max_groep_grootte (IntegerField): Een veld voor het instellen van de maximale grootte van de groep
+ voor het project. Standaard ingesteld op 1.
student_groep (BooleanField): Een veld om aan te geven of het een individueel project is of niet.
- Standaard ingesteld of False.
+ Standaard ingesteld op False.
zichtbaar (BooleanField): Een veld om aan te geven of het project zichtbaar is of niet.
- Standaard ingesteld op True.
+ Standaard ingesteld op True.
gearchiveerd (BooleanField): Een veld om aan te geven of het project gearchiveerd is of niet.
- Standaard ingesteld op False.
+ Standaard ingesteld op False.
Methoden:
- __str__(): Geeft een representatie van het model als een string terug,
- die de titel van het project bevat.
+ __str__(): Geeft een representatie van het model als een string terug, die de titel van het project bevat.
"""
project_id = models.AutoField(primary_key=True)
titel = models.CharField(max_length=100)
beschrijving = models.TextField()
- opgave_bestand = models.FileField(upload_to=upload_to)
+ opgave_bestand = models.FileField(upload_to=upload_to, blank=True)
vak = models.ForeignKey(Vak, on_delete=models.CASCADE)
deadline = models.DateTimeField(null=True, blank=True)
extra_deadline = models.DateTimeField(null=True, blank=True)
- max_score = models.IntegerField(default=20)
- max_groep_grootte = models.IntegerField(default=1)
+ max_score = models.PositiveSmallIntegerField(default=20)
+ max_groep_grootte = models.PositiveSmallIntegerField(default=1)
student_groep = models.BooleanField(default=False, blank=True)
zichtbaar = models.BooleanField(default=True, blank=True)
gearchiveerd = models.BooleanField(default=False, blank=True)
diff --git a/api/models/restrictie.py b/api/models/restrictie.py
index 990e83952..f2341d94d 100644
--- a/api/models/restrictie.py
+++ b/api/models/restrictie.py
@@ -4,7 +4,7 @@
def upload_to(instance, filename):
"""
- Functie om het pad te genereren waar het opgavebestand wordt opgeslagen.
+ Genereert het pad waar het opgavebestand wordt opgeslagen.
Args:
instance: De huidige instantie van het model.
@@ -24,15 +24,15 @@ class Restrictie(models.Model):
Velden:
restrictie_id (AutoField): Automatisch gegenereerd veld dat fungeert als primaire sleutel voor de restrictie.
project (ForeignKey): Een ForeignKey relatie met het 'Project' model,
- waarmee wordt aangegeven welk project deze restrictie betreft.
+ waarmee wordt aangegeven welk project deze restrictie betreft.
Als het bijbehorende project wordt verwijderd, worden ook de bijbehorende restricties verwijderd.
script (FileField): Een veld voor het uploaden van het script van de restrictie/test.
moet_slagen (BooleanField): Een veld om aan te geven of de inzending aan de restrictie/test moet voldoen.
- Standaard ingesteld op False.
+ Standaard ingesteld op False.
Methoden:
__str__(): Geeft een representatie van het model als een string terug,
- die het titel van het bijbehorende project en de beschrijving van de restrictie bevat.
+ die de titel van het bijbehorende project en de beschrijving van de restrictie bevat.
"""
restrictie_id = models.AutoField(primary_key=True)
diff --git a/api/models/score.py b/api/models/score.py
index 166182240..c296e02fc 100644
--- a/api/models/score.py
+++ b/api/models/score.py
@@ -14,11 +14,12 @@ class Score(models.Model):
wordt ook de bijbehorende score verwijderd.
Methods:
- __str__(): Geeft een representatie van het model als een string terug, die de score-ID bevat.
+ __str__(): Geeft een representatie van het model als een string terug,
+ die de score-ID bevat.
"""
score_id = models.AutoField(primary_key=True)
- score = models.SmallIntegerField()
+ score = models.PositiveSmallIntegerField()
indiening = models.ForeignKey("Indiening", on_delete=models.CASCADE)
def __str__(self):
diff --git a/api/models/template.py b/api/models/template.py
new file mode 100644
index 000000000..962b4d4d7
--- /dev/null
+++ b/api/models/template.py
@@ -0,0 +1,43 @@
+from django.db import models
+from django.contrib.auth.models import User
+
+
+def upload_to(instance, filename):
+ """
+ Genereert het pad waar het bestand wordt opgeslagen.
+
+ Args:
+ instance: De huidige instantie van het model.
+ filename (str): De oorspronkelijke bestandsnaam.
+
+ Returns:
+ str: Het pad waar het bestand moet worden opgeslagen.
+ """
+ return f"data/templates/gebruiker_{instance.user.id}/{filename}"
+
+
+class Template(models.Model):
+ """
+ Model voor het bijhouden van templates geüpload door gebruikers.
+
+ Velden:
+ template_id (AutoField): Een automatisch gegenereerd veld dat fungeert als de primaire sleutel
+ voor de template.
+ user (ForeignKey): Een ForeignKey relatie met het 'User' model,
+ waarmee wordt aangegeven welke gebruiker de template heeft geüpload.
+ bestand (FileField): Een veld voor het uploaden van de template,
+ met een dynamisch gegenereerd pad.
+
+ Methoden:
+ __str__(): Geeft een representatie van het model als een string terug,
+ die de bestandsnaam van de template bevat.
+ """
+
+ template_id = models.AutoField(primary_key=True)
+ user = models.ForeignKey(
+ User, related_name="template_user", on_delete=models.CASCADE
+ )
+ bestand = models.FileField(upload_to=upload_to)
+
+ def __str__(self):
+ return str(self.bestand.name)
diff --git a/api/models/vak.py b/api/models/vak.py
index e999a6e50..48f0124bb 100644
--- a/api/models/vak.py
+++ b/api/models/vak.py
@@ -1,4 +1,5 @@
from django.db import models
+from datetime import date
class Vak(models.Model):
@@ -8,10 +9,12 @@ class Vak(models.Model):
Fields:
vak_id (AutoField): Een automatisch gegenereerd veld dat fungeert als de primaire sleutel voor het vak.
naam (CharField): Een veld om de naam van het vak op te slaan.
+ jaartal (IntegerField): Een veld om het jaartal van het vak op te slaan. (voor 2024-2025 zou je 2025 opslaan)
+ gearchiveerd (BooleanField): Een veld om aan te geven of het vak gearchiveerd is. Standaard ingesteld op False.
studenten (ManyToManyField): Een Many-to-Many relatie met het 'Gebruiker' model,
- waarmee meerdere gebruikers aan het vak kunnen worden gekoppeld als studenten.
+ waarmee meerdere gebruikers aan het vak kunnen worden gekoppeld als studenten.
lesgevers (ManyToManyField): Een Many-to-Many relatie met het 'Gebruiker' model,
- waarmee meerdere gebruikers aan het vak kunnen worden gekoppeld als lesgevers.
+ waarmee meerdere gebruikers aan het vak kunnen worden gekoppeld als lesgevers.
Methods:
__str__(): Geeft een representatie van het model als een string terug, die de naam van het vak bevat.
@@ -19,6 +22,8 @@ class Vak(models.Model):
vak_id = models.AutoField(primary_key=True)
naam = models.CharField(max_length=100)
+ jaartal = models.PositiveSmallIntegerField(default=date.today().year)
+ gearchiveerd = models.BooleanField(default=False, blank=True)
studenten = models.ManyToManyField(
"Gebruiker", related_name="vak_gebruikers", blank=True
)
diff --git a/api/serializers/gebruiker.py b/api/serializers/gebruiker.py
index 6bcadcc8b..521fe0a95 100644
--- a/api/serializers/gebruiker.py
+++ b/api/serializers/gebruiker.py
@@ -7,12 +7,12 @@ class GebruikerSerializer(serializers.ModelSerializer):
"""
Serializer voor het serialiseren en deserialiseren van Gebruiker objecten.
- Fields:
+ Velden:
Meta.model (Gebruiker): Het model waarop de serializer is gebaseerd.
- Meta.fields (tuple): De velden die moeten worden opgenomen in de serializer.
+ Meta.fields (list): De velden die moeten worden opgenomen in de serializer.
Hier wordt '__all__' gebruikt om alle velden op te nemen.
- Methods:
+ Methoden:
create(self, validated_data): Maakt een nieuwe gebruiker aan en voegt deze toe aan de database.
update(self, instance, validated_data): Werkt een bestaande gebruiker bij in de database.
"""
@@ -34,6 +34,8 @@ class Meta:
def create(self, validated_data):
"""
+ Maakt een nieuwe gebruiker aan.
+
Args:
validated_data (dict): Gevalideerde gegevens over de gebruiker.
@@ -50,6 +52,8 @@ def create(self, validated_data):
def update(self, instance, validated_data):
"""
+ Werkt een bestaande gebruiker bij.
+
Args:
instance (Gebruiker): De gebruiker die moet worden bijgewerkt.
validated_data (dict): Gevalideerde gegevens over de gebruiker.
@@ -57,12 +61,13 @@ def update(self, instance, validated_data):
Returns:
Gebruiker: De bijgewerkte gebruiker.
"""
- is_lesgever = validated_data.pop("is_lesgever", instance.is_lesgever)
- if instance.is_lesgever != is_lesgever:
- validate_lesgever_change(instance)
- instance.is_lesgever = is_lesgever
- gepinde_vakken = validated_data.pop("gepinde_vakken", instance.gepinde_vakken)
+ validated_data.pop("user", None)
+ validated_data.pop("is_lesgever", None)
+
+ gepinde_vakken = validated_data.pop(
+ "gepinde_vakken", instance.gepinde_vakken.all()
+ )
validate_gepinde_vakken(instance, gepinde_vakken)
instance.gepinde_vakken.set(gepinde_vakken)
@@ -70,28 +75,6 @@ def update(self, instance, validated_data):
return instance
-def validate_lesgever_change(instance):
- """
- Valideert of de lesgever wijziging geldig is.
-
- Args:
- instance (Gebruiker): De gebruiker waarvoor de wijziging wordt gecontroleerd.
-
- Raises:
- serializers.ValidationError: Als de lesgever eerst verwijderd moet worden uit zijn/haar huidige vakken.
- """
- if instance.is_lesgever and Vak.objects.filter(lesgevers=instance):
- raise serializers.ValidationError(
- f"De lesgever {instance} moet eerst verwijderd worden \
- als lesgever in zijn huidige vakken"
- )
- elif not instance.is_lesgever and Vak.objects.filter(studenten=instance):
- raise serializers.ValidationError(
- f"De student {instance} moet eerst verwijderd worden \
- als student in zijn huidige vakken"
- )
-
-
def validate_gepinde_vakken(instance, gepinde_vakken):
"""
Valideert of de gepinde vakken geldig zijn voor de gebruiker.
diff --git a/api/serializers/groep.py b/api/serializers/groep.py
index 48383e16e..b42a2337e 100644
--- a/api/serializers/groep.py
+++ b/api/serializers/groep.py
@@ -49,11 +49,10 @@ def update(self, instance, validated_data):
Returns:
Groep: De bijgewerkte groep.
"""
- students_data = validated_data.pop("studenten", instance.studenten)
- new_project = validated_data.get("project", instance.project)
- validate_students(students_data, new_project, current_group=instance)
- validate_project(instance, new_project)
- validate_groep_grootte(students_data, new_project)
+ students_data = validated_data.pop("studenten", instance.studenten.all())
+ validated_data.pop("project", instance.project)
+ validate_students(students_data, instance.project, current_group=instance)
+ validate_groep_grootte(students_data, instance.project)
super().update(instance=instance, validated_data=validated_data)
instance.studenten.set(students_data)
@@ -62,24 +61,6 @@ def update(self, instance, validated_data):
return instance
-def validate_project(instance, new_project):
- """
- Valideert of het project van een groep niet kan worden aangepast.
-
- Args:
- instance: De huidige instantie van het project.
- new_project: Het nieuwe project waaraan de groep wil worden gekoppeld.
-
- Raises:
- serializers.ValidationError: Wordt opgegooid als het project van een groep wordt aangepast.
- """
-
- if instance.project != new_project:
- raise serializers.ValidationError(
- "Het project van een groep kan niet aangepast worden"
- )
-
-
def validate_students(students_data, project, current_group=None):
"""
Controleert of de opgegeven gebruikers studenten zijn en of ze al in een andere groep voor dit project zitten.
diff --git a/api/serializers/indiening.py b/api/serializers/indiening.py
index ec7d90269..c7ae0d30c 100644
--- a/api/serializers/indiening.py
+++ b/api/serializers/indiening.py
@@ -1,20 +1,5 @@
from rest_framework import serializers
-from api.models.indiening import Indiening, IndieningBestand
-
-
-class IndieningBestandSerializer(serializers.ModelSerializer):
- """
- Serializer voor het serialiseren en deserialiseren van IndieningBestand objecten.
-
- Fields:
- Meta.model (IndieningBestand): Het model waarop de serializer is gebaseerd.
- Meta.fields (tuple): De velden die moeten worden opgenomen in de serializer. Hier worden alle velden opgenomen.
-
- """
-
- class Meta:
- model = IndieningBestand
- fields = "__all__"
+from api.models.indiening import Indiening
class IndieningSerializer(serializers.ModelSerializer):
@@ -27,15 +12,14 @@ class IndieningSerializer(serializers.ModelSerializer):
"""
- indiening_bestanden = IndieningBestandSerializer(many=True, read_only=True)
-
class Meta:
model = Indiening
fields = [
"indiening_id",
"groep",
"tijdstip",
+ "bestand",
"status",
"result",
- "indiening_bestanden",
+ "artefacten",
]
diff --git a/api/serializers/project.py b/api/serializers/project.py
index 5f5a1d07e..e1177f1a9 100644
--- a/api/serializers/project.py
+++ b/api/serializers/project.py
@@ -2,6 +2,7 @@
from api.models.project import Project
from django.utils import timezone
from api.serializers.restrictie import RestrictieSerializer
+from api.serializers.groep import GroepSerializer
class ProjectSerializer(serializers.ModelSerializer):
@@ -51,14 +52,15 @@ def create(self, validated_data):
Returns:
Project: Het aangemaakte project.
"""
- deadline = validated_data.pop("deadline")
- extra_deadline = validated_data.pop("extra_deadline")
+ deadline = validated_data.pop("deadline", None)
+ extra_deadline = validated_data.pop("extra_deadline", None)
validate_deadlines(deadline, extra_deadline)
project = Project.objects.create(**validated_data)
project.deadline = deadline
project.extra_deadline = extra_deadline
project.save()
+ create_groepen(project)
return project
def update(self, instance, validated_data):
@@ -72,6 +74,9 @@ def update(self, instance, validated_data):
Returns:
Project: Het bijgewerkte project.
"""
+ validated_data.pop("max_groep_grootte", None)
+ validated_data.pop("project_groep", None)
+
deadline = validated_data.pop("deadline", instance.deadline)
extra_deadline = validated_data.pop("extra_deadline", instance.extra_deadline)
validate_deadlines(deadline, extra_deadline)
@@ -86,6 +91,31 @@ def update(self, instance, validated_data):
return instance
+def create_groepen(instance):
+ if instance.max_groep_grootte == 1 or instance.student_groep:
+ for student in instance.vak.studenten.all():
+ try:
+ serializer = GroepSerializer(
+ data={"studenten": [student], "project": instance.project_id}
+ )
+ if serializer.is_valid():
+ serializer.save()
+ except Exception:
+ pass
+ else:
+ for _ in range(
+ len(instance.vak.studenten.all()) // instance.max_groep_grootte + 1
+ ):
+ try:
+ serializer = GroepSerializer(
+ data={"studenten": [], "project": instance.project_id}
+ )
+ if serializer.is_valid():
+ serializer.save()
+ except Exception:
+ pass
+
+
def validate_deadlines(deadline, extra_deadline):
"""
Controleert of de opgegeven deadline in de toekomst ligt.
diff --git a/api/serializers/template.py b/api/serializers/template.py
new file mode 100644
index 000000000..7df17a50d
--- /dev/null
+++ b/api/serializers/template.py
@@ -0,0 +1,16 @@
+from rest_framework import serializers
+from api.models.template import Template
+
+
+class TemplateSerializer(serializers.ModelSerializer):
+ """
+ Serializer voor het serialiseren en deserialiseren van Template objecten.
+
+ Fields:
+ Meta.model (Template): Het model waarop de serializer is gebaseerd.
+ Meta.fields (tuple): De velden die moeten worden opgenomen in de serializer. Hier worden alle velden opgenomen.
+ """
+
+ class Meta:
+ model = Template
+ fields = "__all__"
diff --git a/api/serializers/vak.py b/api/serializers/vak.py
index 6e5f4e2b3..feff80121 100644
--- a/api/serializers/vak.py
+++ b/api/serializers/vak.py
@@ -1,5 +1,8 @@
from rest_framework import serializers
from api.models.vak import Vak
+from api.models.project import Project
+from api.models.groep import Groep
+from api.serializers.groep import GroepSerializer
class VakSerializer(serializers.ModelSerializer):
@@ -48,8 +51,8 @@ def update(self, instance, validated_data):
Returns:
Vak: Het bijgewerkte vak.
"""
- students_data = validated_data.pop("studenten", instance.studenten)
- teachers_data = validated_data.pop("lesgevers", instance.lesgevers)
+ students_data = validated_data.pop("studenten", instance.studenten.all())
+ teachers_data = validated_data.pop("lesgevers", instance.lesgevers.all())
validate_students_teachers(students_data, teachers_data)
@@ -58,9 +61,50 @@ def update(self, instance, validated_data):
instance.lesgevers.set(teachers_data)
instance.save()
+
+ add_students_to_group(instance)
+
return instance
+def add_students_to_group(instance):
+ """
+ Voeg studenten automatisch toe aan een projectgroep als het een individueel project is.
+
+ Args:
+ instance (Vak): Een object dat een vak vertegenwoordigt met een verzameling van studenten.
+ """
+ projecten = Project.objects.filter(vak=instance.vak_id)
+ for project in projecten:
+ if project.student_groep or project.max_groep_grootte == 1:
+ for student in instance.studenten.all():
+ try:
+ serializer = GroepSerializer(
+ data={"studenten": [student], "project": project.project_id}
+ )
+ if serializer.is_valid():
+ serializer.save()
+ except Exception:
+ pass
+
+ else:
+ groepen = Groep.objects.filter(project=project.project_id)
+ nieuwe_groepen = (
+ len(instance.studenten.all()) // project.max_groep_grootte
+ + 1
+ - len(groepen)
+ )
+ for _ in range(nieuwe_groepen):
+ try:
+ serializer = GroepSerializer(
+ data={"studenten": [], "project": project.project_id}
+ )
+ if serializer.is_valid():
+ serializer.save()
+ except Exception:
+ pass
+
+
def validate_students_teachers(students_data, teachers_data):
"""
Controleert of alle gebruikers in 'studenten' studenten zijn en alle gebruikers in 'lesgevers' lesgevers zijn.
diff --git a/api/settings.py b/api/settings.py
index 8906ca196..09690536e 100644
--- a/api/settings.py
+++ b/api/settings.py
@@ -52,11 +52,11 @@
"django.contrib.sessions.middleware.SessionMiddleware",
"corsheaders.middleware.CorsMiddleware",
"django.middleware.common.CommonMiddleware",
- "django.middleware.csrf.CsrfViewMiddleware",
+ "api.middleware.middleware.DisableCSRFMiddleware",
"django.contrib.auth.middleware.AuthenticationMiddleware",
"django.contrib.messages.middleware.MessageMiddleware",
"django.middleware.clickjacking.XFrameOptionsMiddleware",
- "api.middleware.AuthenticationUserMiddleware",
+ "api.middleware.middleware.AuthenticationUserMiddleware",
]
ROOT_URLCONF = "api.urls"
diff --git a/api/tests/factories/indiening.py b/api/tests/factories/indiening.py
index 018fb8b0e..879995690 100644
--- a/api/tests/factories/indiening.py
+++ b/api/tests/factories/indiening.py
@@ -1,5 +1,5 @@
import factory
-from api.models.indiening import Indiening, IndieningBestand
+from api.models.indiening import Indiening
from factory.django import DjangoModelFactory
from factory import SubFactory
from .groep import GroepFactory
@@ -23,15 +23,5 @@ class Meta:
)
status = factory.Faker("boolean")
result = factory.Faker("paragraph")
-
- indiening_bestanden = factory.RelatedFactory(
- "api.tests.factories.indiening.IndieningBestandFactory", "indiening"
- )
-
-
-class IndieningBestandFactory(DjangoModelFactory):
- class Meta:
- model = IndieningBestand
-
- indiening = SubFactory(IndieningFactory)
bestand = FileField(filename="test.txt", data=b"file content")
+ artefacten = None
diff --git a/api/tests/factories/project.py b/api/tests/factories/project.py
index ec5e56923..f561c8d8d 100644
--- a/api/tests/factories/project.py
+++ b/api/tests/factories/project.py
@@ -29,5 +29,6 @@ class Meta:
)
max_score = factory.Faker("random_int", min=10, max=100)
max_groep_grootte = factory.Faker("random_int", min=1, max=5)
+ student_groep = factory.Faker("boolean")
zichtbaar = factory.Faker("boolean")
gearchiveerd = factory.Faker("boolean")
diff --git a/api/tests/factories/score.py b/api/tests/factories/score.py
index 844bf18d4..97ff855fb 100644
--- a/api/tests/factories/score.py
+++ b/api/tests/factories/score.py
@@ -14,6 +14,5 @@ class Meta:
@classmethod
def _create(cls, model_class, *args, **kwargs):
indiening = kwargs.pop("indiening")
- max_score = indiening.groep.project.max_score
- kwargs["score"] = random.randint(0, max_score)
+ kwargs["score"] = random.randint(0, indiening.groep.project.max_score)
return super()._create(model_class, indiening=indiening, *args, **kwargs)
diff --git a/api/tests/factories/template.py b/api/tests/factories/template.py
new file mode 100644
index 000000000..1b52c7f8b
--- /dev/null
+++ b/api/tests/factories/template.py
@@ -0,0 +1,12 @@
+import factory
+from factory.django import FileField
+from api.models.template import Template
+from api.tests.factories.gebruiker import UserFactory
+
+
+class TemplateFactory(factory.django.DjangoModelFactory):
+ class Meta:
+ model = Template
+
+ user = factory.SubFactory(UserFactory)
+ bestand = FileField(filename="template.txt", data=b"file content")
diff --git a/api/tests/factories/vak.py b/api/tests/factories/vak.py
index 0cfece05e..172e42286 100644
--- a/api/tests/factories/vak.py
+++ b/api/tests/factories/vak.py
@@ -1,15 +1,16 @@
import factory
from api.models.vak import Vak
from factory.django import DjangoModelFactory
-from factory import Faker
from .gebruiker import GebruikerFactory
+from datetime import date
class VakFactory(DjangoModelFactory):
class Meta:
model = Vak
- naam = Faker("name")
+ jaartal = factory.LazyFunction(lambda: date.today().year)
+ gearchiveerd = False
@factory.post_generation
def studenten(self, create, extracted, **kwargs):
diff --git a/api/tests/models/test_indiening.py b/api/tests/models/test_indiening.py
index e0f065d06..cc579b813 100644
--- a/api/tests/models/test_indiening.py
+++ b/api/tests/models/test_indiening.py
@@ -1,11 +1,16 @@
from django.test import TestCase
-from django.core.files.uploadedfile import SimpleUploadedFile
-from api.tests.factories.indiening import IndieningFactory, IndieningBestandFactory
+from api.tests.factories.indiening import IndieningFactory
from api.models.indiening import upload_to
+from unittest.mock import patch, MagicMock, call
+from api.models.indiening import send_indiening_confirmation_mail
+from email.mime.multipart import MIMEMultipart
+from email.mime.text import MIMEText
+import os
class IndieningModelTest(TestCase):
- def setUp(self):
+ @patch("api.models.indiening.send_indiening_confirmation_mail")
+ def setUp(self, mock_send_mail):
self.indiening = IndieningFactory.create()
def test_str_method(self):
@@ -20,9 +25,6 @@ def test_tijdstip(self):
def test_status(self):
self.assertIsNotNone(self.indiening.status)
- def test_indiening_bestanden(self):
- self.assertEqual(self.indiening.indiening_bestanden.count(), 1)
-
def test_upload_to(self):
filename = "test_indiening.txt"
expected_path = (
@@ -30,20 +32,116 @@ def test_upload_to(self):
)
self.assertEqual(upload_to(self.indiening, filename), expected_path)
+ def test_artefacten(self):
+ self.assertIsNotNone(self.indiening.artefacten)
+
+ def test_result(self):
+ self.assertIsNotNone(self.indiening.result)
+
+ def test_bestand(self):
+ self.assertIsNotNone(self.indiening.bestand)
+
+ @patch("smtplib.SMTP_SSL")
+ @patch("ssl.create_default_context")
+ def test_send_indiening_confirmation_mail(
+ self, mock_create_default_context, mock_smtp
+ ):
+ indiening = self.indiening
+ project = indiening.groep.project
+
+ smtp_server_address = "smtp.gmail.com"
+ smtp_port = 465
+ mail_username = os.environ.get("MAIL_USERNAME")
+ mail_app_password = os.environ.get("MAIL_APP_PASSWORD")
+ indiening_status = {
+ -1: "heeft niet alle testen geslaagd.",
+ 0: "wordt nog getest...",
+ 1: "heeft alle testen geslaagd!",
+ }
+ project_url = f"https://sel2-4.ugent.be/course/{project.vak.vak_id}/assignment/{project.project_id}"
+ indiening_url = f"https://sel2-4.ugent.be/course/{project.vak.vak_id}/assignment/ \
+ {project.project_id}/submission/{indiening.indiening_id}"
+
+ # Setup the mock SMTP_SSL object
+ mock_smtp_instance = MagicMock()
+ mock_smtp.return_value.__enter__.return_value = mock_smtp_instance
+
+ # Call the function
+ send_indiening_confirmation_mail(indiening)
+
+ # Check that create_default_context was called once
+ assert mock_create_default_context.call_count == 1
-class IndieningBestandModelTest(TestCase):
- def setUp(self):
- self.indiening_bestand = IndieningBestandFactory.create(
- bestand=SimpleUploadedFile("file.txt", b"file_content")
+ # Check that SMTP_SSL was called with the correct arguments
+ assert mock_smtp.call_count == indiening.groep.studenten.count()
+ mock_smtp.assert_has_calls(
+ [
+ call(
+ smtp_server_address,
+ smtp_port,
+ context=mock_create_default_context.return_value,
+ )
+ ]
+ * indiening.groep.studenten.count()
)
- def test_str_method(self):
- self.assertEqual(
- str(self.indiening_bestand), str(self.indiening_bestand.bestand.name)
+ # Check that login was called with the correct arguments
+ mock_smtp_instance.login.assert_called_with(mail_username, mail_app_password)
+
+ # Check that sendmail was called once for each student
+ assert (
+ mock_smtp_instance.sendmail.call_count == indiening.groep.studenten.count()
)
- def test_indiening(self):
- self.assertIsNotNone(self.indiening_bestand.indiening)
+ # Check that sendmail was called with the correct arguments
+ for student in indiening.groep.studenten.all():
+ subject = "Indieningsontvangst"
- def test_bestand(self):
- self.assertIsNotNone(self.indiening_bestand.bestand)
+ email = MIMEMultipart("alternative")
+ email["Subject"] = subject
+ email["From"] = mail_username
+ email["To"] = student.user.email
+
+ plain_text = f"""
+ Beste {student.user.first_name} {student.user.last_name},
+
+ Dit is een bevestiging dat uw indiening voor het project {project.titel} is ontvangen.
+ De indiening {indiening_status[indiening.status]}.
+ """
+
+ html_text = f"""
+
+
+ Beste {student.user.first_name} {student.user.last_name}
+ Dit is een bevestiging dat uw indiening voor het project \
+ {project.titel} is ontvangen.
+ De indiening {indiening_status[indiening.status]}
+
+
+ """
+
+ email.attach(MIMEText(plain_text, "plain"))
+ email.attach(MIMEText(html_text, "html"))
+
+ call_args_list = mock_smtp_instance.sendmail.call_args_list
+ for mock_call in call_args_list:
+ (
+ sent_mail_username,
+ sent_student_email,
+ sent_email_content,
+ ) = mock_call.args
+ assert sent_mail_username == mail_username
+ assert sent_student_email == student.user.email
+ assert subject in sent_email_content
+ assert (
+ f"Beste {student.user.first_name} {student.user.last_name}"
+ in sent_email_content
+ )
+ assert (
+ f"Dit is een bevestiging dat uw indiening voor het project {project.titel} is ontvangen."
+ in sent_email_content
+ )
+ assert (
+ f"De indiening {indiening_status[indiening.status]}"
+ in sent_email_content
+ )
diff --git a/api/tests/models/test_score.py b/api/tests/models/test_score.py
index 63b46f676..c6046e72b 100644
--- a/api/tests/models/test_score.py
+++ b/api/tests/models/test_score.py
@@ -1,10 +1,12 @@
from django.test import TestCase
from api.tests.factories.score import ScoreFactory
from api.tests.factories.indiening import IndieningFactory
+from unittest.mock import patch
class ScoreModelTest(TestCase):
- def setUp(self):
+ @patch("api.models.indiening.send_indiening_confirmation_mail")
+ def setUp(self, mock_send_mail):
self.indiening = IndieningFactory.create()
self.score = ScoreFactory.create(indiening=self.indiening)
diff --git a/api/tests/models/test_template.py b/api/tests/models/test_template.py
new file mode 100644
index 000000000..8c95e4816
--- /dev/null
+++ b/api/tests/models/test_template.py
@@ -0,0 +1,21 @@
+from django.test import TestCase
+from api.tests.factories.template import TemplateFactory
+from api.models.template import Template
+from api.models.template import upload_to
+
+
+class TemplateModelTest(TestCase):
+ def setUp(self):
+ self.template = TemplateFactory.create()
+
+ def test_template_creation(self):
+ self.assertIsInstance(self.template, Template)
+ self.assertEqual(Template.objects.count(), 1)
+
+ def test_template_str(self):
+ self.assertEqual(str(self.template), self.template.bestand.name)
+
+ def test_upload_to(self):
+ filename = "test_template.txt"
+ expected_path = f"data/templates/gebruiker_{self.template.user.id}/{filename}"
+ self.assertEqual(upload_to(self.template, filename), expected_path)
diff --git a/api/tests/serializers/test_gebruiker.py b/api/tests/serializers/test_gebruiker.py
index 666ca433b..4f68d28e6 100644
--- a/api/tests/serializers/test_gebruiker.py
+++ b/api/tests/serializers/test_gebruiker.py
@@ -54,43 +54,22 @@ def test_create(self):
self.assertEqual(gebruiker.is_lesgever, data["is_lesgever"])
def test_update(self):
- data = self.serializer.data
- self.assertFalse(data["is_lesgever"])
- data["is_lesgever"] = True
- serializer = GebruikerSerializer(
- instance=self.gebruiker, data=data, partial=True
- )
- self.assertTrue(serializer.is_valid())
- self.gebruiker = serializer.save()
- self.assertTrue(self.gebruiker.is_lesgever)
-
- def test_update_invalid_teacher(self):
vak = VakFactory.create()
vak.studenten.add(self.gebruiker)
data = self.serializer.data
- self.assertFalse(data["is_lesgever"])
- data["is_lesgever"] = True
+ data["gepinde_vakken"] = [vak.vak_id]
serializer = GebruikerSerializer(
instance=self.gebruiker, data=data, partial=True
)
self.assertTrue(serializer.is_valid())
- self.assertRaises(ValidationError, serializer.save, raise_exception=True)
+ self.gebruiker = serializer.save()
+ self.assertEqual(self.gebruiker.gepinde_vakken.first(), vak)
- def test_update_invalid_student(self):
+ def test_update_invalid(self):
self.gebruiker.is_lesgever = True
vak = VakFactory.create()
- vak.lesgevers.add(self.gebruiker)
- data = self.serializer.data
- data["is_lesgever"] = False
- serializer = GebruikerSerializer(
- instance=self.gebruiker, data=data, partial=True
- )
- self.assertTrue(serializer.is_valid())
- self.assertRaises(ValidationError, serializer.save, raise_exception=True)
-
- def test_update_invalid_gepinde_vak(self):
data = self.serializer.data
- data["gepinde_vakken"].append(VakFactory.create().vak_id)
+ data["gepinde_vakken"] = [vak.vak_id]
serializer = GebruikerSerializer(
instance=self.gebruiker, data=data, partial=True
)
diff --git a/api/tests/serializers/test_groep.py b/api/tests/serializers/test_groep.py
index e80920d3f..aa69a887b 100644
--- a/api/tests/serializers/test_groep.py
+++ b/api/tests/serializers/test_groep.py
@@ -121,10 +121,12 @@ def test_update(self):
def test_update_invalid_project(self):
project = ProjectFactory.create(vak=self.groep.project.vak)
data = self.serializer.data
+ current = data["project"]
data["project"] = project.project_id
serializer = GroepSerializer(instance=self.groep, data=data, partial=True)
self.assertTrue(serializer.is_valid())
- self.assertRaises(ValidationError, serializer.save, raise_exception=True)
+ serializer.save()
+ self.assertEqual(self.groep.project.project_id, current)
def test_update_invalid_user_already_in_this_group(self):
data = self.serializer.data
diff --git a/api/tests/serializers/test_indiening.py b/api/tests/serializers/test_indiening.py
index 61c219928..24821279c 100644
--- a/api/tests/serializers/test_indiening.py
+++ b/api/tests/serializers/test_indiening.py
@@ -1,18 +1,16 @@
from django.test import TestCase
from django.core.files.uploadedfile import SimpleUploadedFile
-from api.serializers.indiening import IndieningSerializer, IndieningBestandSerializer
-from api.tests.factories.indiening import IndieningFactory, IndieningBestandFactory
+from api.serializers.indiening import IndieningSerializer
+from api.tests.factories.indiening import IndieningFactory
from api.tests.factories.groep import GroepFactory
-from api.tests.factories.restrictie import RestrictieFactory
+from unittest.mock import patch
class IndieningSerializerTest(TestCase):
- def setUp(self):
- self.indiening = IndieningFactory.create()
- self.serializer = IndieningSerializer(instance=self.indiening)
- self.restrictie = RestrictieFactory(project=self.indiening.groep.project)
-
- def test_indiening_serializer_fields(self):
+ @patch("api.models.indiening.send_indiening_confirmation_mail")
+ def test_indiening_serializer_fields(self, mock_send_mail):
+ indiening = IndieningFactory.create()
+ self.serializer = IndieningSerializer(instance=indiening)
data = self.serializer.data
self.assertEqual(
set(data.keys()),
@@ -23,61 +21,43 @@ def test_indiening_serializer_fields(self):
"tijdstip",
"status",
"result",
- "indiening_bestanden",
+ "artefacten",
+ "bestand",
]
),
)
- def test_indiening_serializer_create(self):
+ @patch("api.models.indiening.send_indiening_confirmation_mail")
+ def test_indiening_serializer_create(self, mock_send_mail):
groep = GroepFactory.create()
- data = {"groep": groep.groep_id}
+ data = {
+ "groep": groep.groep_id,
+ "bestand": SimpleUploadedFile("bestand.txt", b"bestand_content"),
+ }
serializer = IndieningSerializer(data=data)
self.assertTrue(serializer.is_valid())
indiening = serializer.save()
self.assertEqual(indiening.groep, groep)
- def test_indiening_serializer_update(self):
- new_data = {"groep": self.indiening.groep.groep_id}
- serializer = IndieningSerializer(
- instance=self.indiening, data=new_data, partial=True
- )
- self.assertTrue(serializer.is_valid())
- indiening = serializer.save()
- self.assertEqual(indiening.groep, self.indiening.groep)
-
-
-class IndieningBestandSerializerTest(TestCase):
- def setUp(self):
- self.indiening_bestand = IndieningBestandFactory.create(
- bestand=SimpleUploadedFile("file.txt", b"file_content")
- )
- self.serializer = IndieningBestandSerializer(instance=self.indiening_bestand)
+ def test_indiening_serializer_no_file(self):
+ groep = GroepFactory.create()
+ data = {"groep": groep.groep_id}
+ serializer = IndieningSerializer(data=data)
+ self.assertFalse(serializer.is_valid())
- def test_indiening_bestand_serializer_fields(self):
- data = self.serializer.data
- self.assertEqual(
- set(data.keys()), set(["indiening_bestand_id", "indiening", "bestand"])
- )
+ @patch("api.models.indiening.send_indiening_confirmation_mail")
+ def test_indiening_serializer_no_groep(self, mock_send_mail):
+ data = {"bestand": SimpleUploadedFile("bestand.txt", b"bestand_content")}
+ serializer = IndieningSerializer(data=data)
+ self.assertFalse(serializer.is_valid())
- def test_indiening_bestand_serializer_create(self):
+ @patch("api.models.indiening.send_indiening_confirmation_mail")
+ def test_indiening_serializer_update(self, mock_send_mail):
indiening = IndieningFactory.create()
- data = {
- "indiening": indiening.indiening_id,
- "bestand": SimpleUploadedFile("file.txt", b"file_content"),
- }
- serializer = IndieningBestandSerializer(data=data)
- self.assertTrue(serializer.is_valid())
- indiening_bestand = serializer.save()
- self.assertEqual(indiening_bestand.indiening, indiening)
-
- def test_indiening_bestand_serializer_update(self):
- new_data = {
- "indiening": self.indiening_bestand.indiening.indiening_id,
- "bestand": SimpleUploadedFile("file.txt", b"file_content"),
- }
- serializer = IndieningBestandSerializer(
- instance=self.indiening_bestand, data=new_data, partial=True
+ new_data = {"groep": indiening.groep.groep_id}
+ serializer = IndieningSerializer(
+ instance=indiening, data=new_data, partial=True
)
self.assertTrue(serializer.is_valid())
- indiening_bestand = serializer.save()
- self.assertEqual(indiening_bestand.indiening, self.indiening_bestand.indiening)
+ indiening = serializer.save()
+ self.assertEqual(indiening.groep, indiening.groep)
diff --git a/api/tests/serializers/test_project.py b/api/tests/serializers/test_project.py
index c5488b605..d49b84607 100644
--- a/api/tests/serializers/test_project.py
+++ b/api/tests/serializers/test_project.py
@@ -6,6 +6,7 @@
from api.tests.factories.vak import VakFactory
from django.core.files.uploadedfile import SimpleUploadedFile
from datetime import datetime, timedelta
+from django.utils import timezone
class ProjectSerializerTest(APITestCase):
@@ -87,6 +88,7 @@ def test_validation_for_blank_items(self):
"extra_deadline": "",
"max_score": "",
"max_groep_grootte": "",
+ "student_groep": "",
"zichtbaar": "",
"gearchiveerd": "",
}
@@ -100,17 +102,38 @@ def test_create(self):
"beschrijving": "Dit is een test project.",
"opgave_bestand": SimpleUploadedFile("file.txt", b"file_content"),
"vak": vak,
- "deadline": self.serializer.data["deadline"],
- "extra_deadline": self.serializer.data["extra_deadline"],
+ "deadline": timezone.now() + timedelta(days=1),
+ "extra_deadline": timezone.now() + timedelta(days=2),
"max_score": 20,
"max_groep_grootte": 1,
+ "student_groep": False,
"zichtbaar": True,
"gearchiveerd": False,
}
serializer = ProjectSerializer(data=data)
self.assertTrue(serializer.is_valid())
project = serializer.save()
- self.assertEqual(project.deadline, parse(data["deadline"]))
+ self.assertEqual(project.deadline, data["deadline"])
+
+ def test_create_higher_grootte(self):
+ vak = VakFactory.create().vak_id
+ data = {
+ "titel": "test project",
+ "beschrijving": "Dit is een test project.",
+ "opgave_bestand": SimpleUploadedFile("file.txt", b"file_content"),
+ "vak": vak,
+ "deadline": timezone.now() + timedelta(days=1),
+ "extra_deadline": timezone.now() + timedelta(days=2),
+ "max_score": 20,
+ "max_groep_grootte": 5,
+ "student_groep": False,
+ "zichtbaar": True,
+ "gearchiveerd": False,
+ }
+ serializer = ProjectSerializer(data=data)
+ self.assertTrue(serializer.is_valid())
+ project = serializer.save()
+ self.assertEqual(project.deadline, data["deadline"])
def test_create_no_deadline(self):
vak = VakFactory.create().vak_id
@@ -123,6 +146,7 @@ def test_create_no_deadline(self):
"extra_deadline": None,
"max_score": 20,
"max_groep_grootte": 1,
+ "student_groep": False,
"zichtbaar": True,
"gearchiveerd": False,
}
@@ -142,6 +166,7 @@ def test_create_invalid_deadline(self):
"extra_deadline": self.serializer.data["extra_deadline"],
"max_score": 20,
"max_groep_grootte": 1,
+ "student_groep": False,
"zichtbaar": True,
"gearchiveerd": False,
}
@@ -160,6 +185,7 @@ def test_create_invalid_extra_deadline(self):
"extra_deadline": datetime.now() - timedelta(days=1),
"max_score": 20,
"max_groep_grootte": 1,
+ "student_groep": False,
"zichtbaar": True,
"gearchiveerd": False,
}
@@ -177,6 +203,7 @@ def test_update(self):
"extra_deadline": self.serializer.data["extra_deadline"],
"max_score": 20,
"max_groep_grootte": 1,
+ "student_groep": False,
"zichtbaar": True,
"gearchiveerd": False,
}
diff --git a/api/tests/serializers/test_score.py b/api/tests/serializers/test_score.py
index 4e03e7892..decbb4494 100644
--- a/api/tests/serializers/test_score.py
+++ b/api/tests/serializers/test_score.py
@@ -3,10 +3,12 @@
from api.serializers.score import ScoreSerializer
from api.tests.factories.score import ScoreFactory
from api.tests.factories.indiening import IndieningFactory
+from unittest.mock import patch
class ScoreSerializerTest(TestCase):
- def setUp(self):
+ @patch("api.models.indiening.send_indiening_confirmation_mail")
+ def setUp(self, mock_send_mail):
self.score = ScoreFactory.create()
self.score.score = self.score.indiening.groep.project.max_score
self.score.save()
@@ -28,7 +30,8 @@ def test_score_field_content(self):
data = self.serializer.data
self.assertEqual(data["score"], self.score.score)
- def test_score_serializer_create(self):
+ @patch("api.models.indiening.send_indiening_confirmation_mail")
+ def test_score_serializer_create(self, mock_send_mail):
indiening = IndieningFactory.create()
max_score = indiening.groep.project.max_score
data = {"indiening": indiening.indiening_id, "score": max_score}
@@ -38,7 +41,8 @@ def test_score_serializer_create(self):
self.assertEqual(score.indiening, indiening)
self.assertEqual(score.score, data["score"])
- def test_score_serializer_create_invalid(self):
+ @patch("api.models.indiening.send_indiening_confirmation_mail")
+ def test_score_serializer_create_invalid(self, mock_send_mail):
indiening = IndieningFactory.create()
max_score = indiening.groep.project.max_score
data = {"indiening": indiening.indiening_id, "score": max_score}
@@ -50,7 +54,8 @@ def test_score_serializer_create_invalid(self):
self.assertTrue(new_serializer.is_valid())
self.assertRaises(ValidationError, new_serializer.save, raise_exception=True)
- def test_score_serializer_create_invalid_high_score(self):
+ @patch("api.models.indiening.send_indiening_confirmation_mail")
+ def test_score_serializer_create_invalid_high_score(self, mock_send_mail):
indiening = IndieningFactory.create()
max_score = indiening.groep.project.max_score
data = {"indiening": indiening.indiening_id, "score": max_score + 1}
@@ -70,7 +75,8 @@ def test_score_serializer_update(self):
score = serializer.save()
self.assertEqual(score.score, new_data["score"])
- def test_score_serializer_update_invalid(self):
+ @patch("api.models.indiening.send_indiening_confirmation_mail")
+ def test_score_serializer_update_invalid(self, mock_send_mail):
indiening = IndieningFactory.create()
new_data = {
"score": indiening.groep.project.max_score,
diff --git a/api/tests/serializers/test_template.py b/api/tests/serializers/test_template.py
new file mode 100644
index 000000000..24747198d
--- /dev/null
+++ b/api/tests/serializers/test_template.py
@@ -0,0 +1,35 @@
+from django.test import TestCase
+from rest_framework.exceptions import ValidationError
+from api.tests.factories.template import TemplateFactory
+from api.serializers.template import TemplateSerializer
+
+
+class TemplateSerializerTest(TestCase):
+ def setUp(self):
+ self.template = TemplateFactory.create()
+ self.serializer = TemplateSerializer(instance=self.template)
+
+ def test_contains_expected_fields(self):
+ data = self.serializer.data
+ self.assertCountEqual(data.keys(), ["template_id", "user", "bestand"])
+
+ def test_template_id_field_content(self):
+ data = self.serializer.data
+ self.assertEqual(data["template_id"], self.template.template_id)
+
+ def test_user_field_content(self):
+ data = self.serializer.data
+ self.assertEqual(data["user"], self.template.user.id)
+
+ def test_bestand_field_content(self):
+ data = self.serializer.data
+ self.assertEqual(data["bestand"], self.template.bestand.url)
+
+ def test_validation(self):
+ invalid_data = {
+ "user": "",
+ "bestand": "",
+ }
+ serializer = TemplateSerializer(data=invalid_data)
+ with self.assertRaises(ValidationError):
+ serializer.is_valid(raise_exception=True)
diff --git a/api/tests/serializers/test_vak.py b/api/tests/serializers/test_vak.py
index 1c1f3437d..9f7841453 100644
--- a/api/tests/serializers/test_vak.py
+++ b/api/tests/serializers/test_vak.py
@@ -3,6 +3,8 @@
from api.serializers.vak import VakSerializer
from api.tests.factories.vak import VakFactory
from api.tests.factories.gebruiker import GebruikerFactory
+from api.tests.factories.project import ProjectFactory
+from api.tests.factories.groep import GroepFactory
class VakSerializerTest(APITestCase):
@@ -12,7 +14,10 @@ def setUp(self):
def test_contains_expected_fields(self):
data = self.serializer.data
- self.assertCountEqual(data.keys(), ["vak_id", "naam", "studenten", "lesgevers"])
+ self.assertCountEqual(
+ data.keys(),
+ ["vak_id", "naam", "jaartal", "gearchiveerd", "studenten", "lesgevers"],
+ )
def test_vak_id_field_content(self):
data = self.serializer.data
@@ -128,3 +133,74 @@ def test_update(self):
set([teacher.user.id for teacher in vak.lesgevers.all()]),
)
self.assertEqual(vak.naam, "nieuw vak")
+
+ def test_add_students_to_groep(self):
+ students_data = [
+ GebruikerFactory.create(is_lesgever=False).user.id for _ in range(3)
+ ]
+ teachers_data = [
+ GebruikerFactory.create(is_lesgever=True).user.id for _ in range(3)
+ ]
+ data = {
+ "naam": "test vak",
+ "studenten": students_data,
+ "lesgevers": teachers_data,
+ }
+ serializer = VakSerializer(instance=self.vak_data, data=data, partial=True)
+ self.assertTrue(serializer.is_valid())
+ vak = serializer.save()
+ project = ProjectFactory.create(
+ student_groep=True, max_groep_grootte=1, vak=vak
+ )
+ serializer = VakSerializer(instance=self.vak_data, data=data, partial=True)
+ self.assertTrue(serializer.is_valid())
+ vak = serializer.save()
+ self.assertEqual(project.vak, vak)
+
+ def test_add_invalid_students_to_group(self):
+ students_data = [
+ GebruikerFactory.create(is_lesgever=False).user.id for _ in range(3)
+ ]
+ teachers_data = [
+ GebruikerFactory.create(is_lesgever=True).user.id for _ in range(3)
+ ]
+ data = {
+ "naam": "test vak",
+ "studenten": students_data,
+ "lesgevers": teachers_data,
+ }
+ serializer = VakSerializer(instance=self.vak_data, data=data, partial=True)
+ self.assertTrue(serializer.is_valid())
+ vak = serializer.save()
+ project = ProjectFactory.create(
+ student_groep=True, max_groep_grootte=1, vak=vak
+ )
+ # voeg een student toe aan een groep
+ GroepFactory.create(project=project, studenten=[vak.studenten.all()[0]])
+ # nu zal de serializer alle studenten aan een groep toevoegen, maar eentje zit dus al in een groep
+ serializer = VakSerializer(instance=self.vak_data, data=data, partial=True)
+ self.assertTrue(serializer.is_valid())
+ vak = serializer.save()
+
+ def test_add_students_to_group_with_max_groep_grootte(self):
+ students_data = [
+ GebruikerFactory.create(is_lesgever=False).user.id for _ in range(3)
+ ]
+ teachers_data = [
+ GebruikerFactory.create(is_lesgever=True).user.id for _ in range(3)
+ ]
+ data = {
+ "naam": "test vak",
+ "studenten": students_data,
+ "lesgevers": teachers_data,
+ }
+ serializer = VakSerializer(instance=self.vak_data, data=data, partial=True)
+ self.assertTrue(serializer.is_valid())
+ vak = serializer.save()
+ project = ProjectFactory.create(
+ student_groep=False, max_groep_grootte=2, vak=vak
+ )
+ serializer = VakSerializer(instance=self.vak_data, data=data, partial=True)
+ self.assertTrue(serializer.is_valid())
+ vak = serializer.save()
+ self.assertEqual(project.vak, vak)
diff --git a/api/tests/test_middleware.py b/api/tests/test_middleware.py
index e9aacb7a7..ef89452f6 100644
--- a/api/tests/test_middleware.py
+++ b/api/tests/test_middleware.py
@@ -2,7 +2,7 @@
from django.contrib.auth.models import User
from unittest.mock import patch
from api.models.gebruiker import Gebruiker
-from api.middleware import AuthenticationUserMiddleware
+from api.middleware.middleware import AuthenticationUserMiddleware
from django.contrib.auth.models import AnonymousUser
import jwt
diff --git a/api/tests/views/test_gebruiker.py b/api/tests/views/test_gebruiker.py
index 31ad86bc3..57bddea94 100644
--- a/api/tests/views/test_gebruiker.py
+++ b/api/tests/views/test_gebruiker.py
@@ -2,6 +2,8 @@
from api.tests.factories.gebruiker import GebruikerFactory
from django.urls import reverse
from rest_framework import status
+from api.tests.factories.vak import VakFactory
+from api.models.gebruiker import Gebruiker
class GebruikerListViewTest(APITestCase):
@@ -16,7 +18,7 @@ def test_gebruiker_list_get_as_student(self):
self.client.force_login(self.student.user)
response = self.client.get(self.url)
self.assertEqual(response.status_code, status.HTTP_200_OK)
- self.assertEqual(len(response.data), 1)
+ self.assertEqual(len(response.data), Gebruiker.objects.count())
def test_gebruiker_list_get_lesgevers(self):
self.client.force_login(self.teacher.user)
@@ -50,6 +52,10 @@ def setUp(self):
self.gebruiker = GebruikerFactory.create()
self.gebruiker.user.is_superuser = True
self.gebruiker.user.save()
+ self.vak = VakFactory.create()
+ self.vak.studenten.add(self.gebruiker)
+ self.vak.save()
+ self.vak.refresh_from_db()
self.client = APIClient()
self.client.force_login(self.gebruiker.user)
self.url = reverse("gebruiker_detail", kwargs={"id": self.gebruiker.user.id})
@@ -58,27 +64,10 @@ def test_gebruiker_detail_get(self):
response = self.client.get(self.url)
self.assertEqual(response.status_code, status.HTTP_200_OK)
- def test_gebruiker_detail_put(self):
- new_data = {
- "user": self.gebruiker.user.id,
- "is_lesgever": not self.gebruiker.is_lesgever,
- "gepinde_vakken": self.gebruiker.gepinde_vakken.all(),
- }
- response = self.client.put(self.url, new_data, format="json")
- self.assertEqual(response.status_code, status.HTTP_200_OK)
- self.gebruiker.refresh_from_db()
- self.assertEqual(self.gebruiker.is_lesgever, new_data["is_lesgever"])
-
def test_gebruiker_detail_get_non_existing_user(self):
response = self.client.get(reverse("gebruiker_detail", kwargs={"id": 69}))
self.assertEqual(response.status_code, status.HTTP_404_NOT_FOUND)
- def test_gebruiker_detail_get_unauthorized(self):
- student = GebruikerFactory.create(is_lesgever=False)
- self.client.force_login(student.user)
- response = self.client.get(self.url)
- self.assertEqual(response.status_code, status.HTTP_403_FORBIDDEN)
-
def test_gebruiker_detail_put_unauthorized(self):
student = GebruikerFactory.create(is_lesgever=False)
self.client.force_login(student.user)
@@ -99,12 +88,12 @@ def test_gebruiker_detail_put_bad_request(self):
def test_gebruiker_detail_patch(self):
new_data = {
- "is_lesgever": not self.gebruiker.is_lesgever,
+ "gepinde_vakken": [],
}
response = self.client.patch(self.url, new_data, format="json")
self.assertEqual(response.status_code, status.HTTP_200_OK)
self.gebruiker.refresh_from_db()
- self.assertEqual(self.gebruiker.is_lesgever, new_data["is_lesgever"])
+ self.assertEqual(self.gebruiker.gepinde_vakken.all().count(), 0)
class GebruikerDetailMeViewTest(APITestCase):
diff --git a/api/tests/views/test_groep.py b/api/tests/views/test_groep.py
index f558c3186..d12f037f7 100644
--- a/api/tests/views/test_groep.py
+++ b/api/tests/views/test_groep.py
@@ -3,6 +3,7 @@
from rest_framework.test import APIClient, APITestCase
from api.tests.factories.groep import GroepFactory
from api.tests.factories.gebruiker import GebruikerFactory
+from api.models.groep import Groep
class GroepListViewTest(APITestCase):
@@ -25,7 +26,7 @@ def test_groep_list_get_as_student(self):
self.client.force_login(self.student.user)
response = self.client.get(self.url)
self.assertEqual(response.status_code, status.HTTP_200_OK)
- self.assertEqual(len(response.data), 1)
+ self.assertEqual(len(response.data), Groep.objects.count())
self.assertEqual(response.data[0]["groep_id"], self.groep1.groep_id)
def test_groep_list_get_project(self):
@@ -95,12 +96,6 @@ def test_groep_detail_get(self):
response = self.client.get(self.url)
self.assertEqual(response.status_code, status.HTTP_200_OK)
- def test_groep_detail_get_unauthorized(self):
- student = GebruikerFactory.create(is_lesgever=False)
- self.client.force_login(student.user)
- response = self.client.get(self.url)
- self.assertEqual(response.status_code, status.HTTP_403_FORBIDDEN)
-
def test_groep_detail_put(self):
new_data = {
"groep_id": self.groep.groep_id,
diff --git a/api/tests/views/test_indiening.py b/api/tests/views/test_indiening.py
index 9ecc9fac6..7e299b776 100644
--- a/api/tests/views/test_indiening.py
+++ b/api/tests/views/test_indiening.py
@@ -5,10 +5,13 @@
from django.urls import reverse
from rest_framework import status
from django.core.files.uploadedfile import SimpleUploadedFile
+from django.core.files.base import ContentFile
+from unittest.mock import patch
class IndieningListViewTest(TestCase):
- def setUp(self):
+ @patch("api.models.indiening.send_indiening_confirmation_mail")
+ def setUp(self, mock_send_mail):
self.client = APIClient()
self.teacher = GebruikerFactory.create(is_lesgever=True)
self.student = GebruikerFactory.create(is_lesgever=False)
@@ -29,13 +32,13 @@ def test_indiening_list_get_as_student(self):
self.assertEqual(response.status_code, status.HTTP_200_OK)
self.assertEqual(len(response.data), 1)
- def test_indiening_list_post(self):
+ @patch("api.models.indiening.send_indiening_confirmation_mail")
+ def test_indiening_list_post(self, mock_send_mail):
data = {
"groep": self.indiening2.groep_id,
}
- file1 = SimpleUploadedFile("file1.txt", b"file_content")
- file2 = SimpleUploadedFile("file2.txt", b"file_content")
- data["indiening_bestanden"] = [file1, file2]
+ file = SimpleUploadedFile("file1.txt", b"file_content")
+ data["bestand"] = file
response = self.client.post(self.url, data, format="multipart")
self.assertEqual(response.status_code, status.HTTP_201_CREATED)
response = self.client.get(self.url)
@@ -96,7 +99,8 @@ def test_indiening_list_post_invalid(self):
class IndieningDetailViewTest(TestCase):
- def setUp(self):
+ @patch("api.models.indiening.send_indiening_confirmation_mail")
+ def setUp(self, mock_send_mail):
self.client = APIClient()
self.gebruiker = GebruikerFactory.create()
self.gebruiker.user.is_superuser = True
@@ -133,7 +137,8 @@ def test_indiening_detail_delete_unauthorized(self):
class IndieningDetailDownloadBestandenTest(TestCase):
- def setUp(self):
+ @patch("api.models.indiening.send_indiening_confirmation_mail")
+ def setUp(self, mock_send_mail):
self.client = APIClient()
self.teacher = GebruikerFactory.create(is_lesgever=True)
self.student = GebruikerFactory.create(is_lesgever=False)
@@ -164,3 +169,43 @@ def test_indiening_detail_download_bestanden_get_invalid(self):
self.url = reverse("indiening_detail_download_bestanden", kwargs={"id": 69})
response = self.client.get(self.url)
self.assertEqual(response.status_code, status.HTTP_404_NOT_FOUND)
+
+
+class IndieningDetailDownloadArtefactenTest(TestCase):
+ @patch("api.models.indiening.send_indiening_confirmation_mail")
+ def setUp(self, mock_send_mail):
+ self.client = APIClient()
+ self.teacher = GebruikerFactory.create(is_lesgever=True)
+ self.student = GebruikerFactory.create(is_lesgever=False)
+ self.client.force_login(self.teacher.user)
+ self.indiening = IndieningFactory.create()
+ self.indiening.groep.studenten.add(self.student)
+ self.url = reverse(
+ "indiening_detail_download_artefacten",
+ kwargs={"id": self.indiening.indiening_id},
+ )
+
+ def test_indiening_detail_download_artefacten_get_invalid_as_teacher(self):
+ response = self.client.get(self.url)
+ self.assertEqual(response.status_code, status.HTTP_404_NOT_FOUND)
+
+ def test_indiening_detail_download_artefacten_get_as_teacher(self):
+ content = b"Some file content"
+ file_content = ContentFile(
+ content,
+ name="data/indieningen/indiening_{self.indiening.indiening_id}/artefacten.zip",
+ )
+ self.indiening.artefacten.save(file_content.name, file_content)
+ self.indiening.save()
+ response = self.client.get(self.url)
+ self.assertEqual(response.status_code, status.HTTP_200_OK)
+
+ def test_indiening_detail_download_artefacten_get_as_student(self):
+ self.client.force_login(self.student.user)
+ response = self.client.get(self.url)
+ self.assertEqual(response.status_code, status.HTTP_403_FORBIDDEN)
+
+ def test_indiening_detail_download_artefacten_get_invalid(self):
+ self.url = reverse("indiening_detail_download_artefacten", kwargs={"id": 69})
+ response = self.client.get(self.url)
+ self.assertEqual(response.status_code, status.HTTP_404_NOT_FOUND)
diff --git a/api/tests/views/test_project.py b/api/tests/views/test_project.py
index 0ac75969d..0bc4d3a04 100644
--- a/api/tests/views/test_project.py
+++ b/api/tests/views/test_project.py
@@ -120,7 +120,7 @@ def test_project_detail_get_unauthorized(self):
self.assertEqual(response.status_code, status.HTTP_403_FORBIDDEN)
def test_invalid_project_detail_get(self):
- response = self.client.get(reverse("project_detail", kwargs={"id": 69}))
+ response = self.client.get(reverse("project_detail", kwargs={"id": 6969}))
self.assertEqual(response.status_code, status.HTTP_404_NOT_FOUND)
def test_project_detail_put(self):
diff --git a/api/tests/views/test_restrictie.py b/api/tests/views/test_restrictie.py
index 1cad164f5..eb7f24ef2 100644
--- a/api/tests/views/test_restrictie.py
+++ b/api/tests/views/test_restrictie.py
@@ -43,7 +43,7 @@ def test_restrictie_list_get_as_student(self):
student = GebruikerFactory.create(is_lesgever=False)
self.client.force_login(student.user)
response = self.client.get(self.url)
- self.assertEqual(response.status_code, status.HTTP_403_FORBIDDEN)
+ self.assertEqual(response.status_code, status.HTTP_200_OK)
def test_restrictie_list_post(self):
project = ProjectFactory.create()
diff --git a/api/tests/views/test_score.py b/api/tests/views/test_score.py
index 8c3fd9337..961e49f5d 100644
--- a/api/tests/views/test_score.py
+++ b/api/tests/views/test_score.py
@@ -5,10 +5,12 @@
from rest_framework.test import APIClient
from django.urls import reverse
from rest_framework import status
+from unittest.mock import patch
class ScoreListViewTest(TestCase):
- def setUp(self):
+ @patch("api.models.indiening.send_indiening_confirmation_mail")
+ def setUp(self, mock_send_mail):
self.client = APIClient()
self.teacher = GebruikerFactory.create(is_lesgever=True)
self.student = GebruikerFactory.create(is_lesgever=False)
@@ -39,7 +41,8 @@ def test_score_list_get_invalid_indiening(self):
response = self.client.get(self.url, {"indiening": "indiening"})
self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)
- def test_score_list_post(self):
+ @patch("api.models.indiening.send_indiening_confirmation_mail")
+ def test_score_list_post(self, mock_send_mail):
indiening = IndieningFactory.create()
data = {
"score_id": self.score1.score_id,
@@ -49,7 +52,8 @@ def test_score_list_post(self):
response = self.client.post(self.url, data, format="json")
self.assertEqual(response.status_code, status.HTTP_201_CREATED)
- def test_score_list_post_unauthorized(self):
+ @patch("api.models.indiening.send_indiening_confirmation_mail")
+ def test_score_list_post_unauthorized(self, mock_send_mail):
self.client.force_login(self.student.user)
indiening = IndieningFactory.create()
data = {
@@ -71,7 +75,8 @@ def test_score_list_post_invalid(self):
class ScoreDetailViewTest(TestCase):
- def setUp(self):
+ @patch("api.models.indiening.send_indiening_confirmation_mail")
+ def setUp(self, mock_send_mail):
self.client = APIClient()
self.teacher = GebruikerFactory.create(is_lesgever=True)
self.student = GebruikerFactory.create(is_lesgever=False)
diff --git a/api/tests/views/test_template.py b/api/tests/views/test_template.py
new file mode 100644
index 000000000..e011a3379
--- /dev/null
+++ b/api/tests/views/test_template.py
@@ -0,0 +1,186 @@
+from django.urls import reverse
+from rest_framework.test import APIClient, APITestCase
+from api.tests.factories.template import TemplateFactory
+from api.models.template import Template
+from api.tests.factories.gebruiker import GebruikerFactory
+from django.core.files.uploadedfile import SimpleUploadedFile
+from rest_framework import status
+from django.http import FileResponse
+
+
+class TemplateListViewTest(APITestCase):
+ def setUp(self):
+ self.client = APIClient()
+ self.teacher = GebruikerFactory.create(is_lesgever=True)
+ self.student = GebruikerFactory.create(is_lesgever=False)
+ self.template = TemplateFactory.create(user=self.teacher.user)
+ self.client.force_login(self.teacher.user)
+ self.template_list_url = reverse("template_list")
+
+ def test_template_list_get(self):
+ response = self.client.get(self.template_list_url)
+ self.assertEqual(response.status_code, status.HTTP_200_OK)
+ self.assertEqual(len(response.data), Template.objects.count())
+
+ def test_template_list_get_as_student(self):
+ self.client.force_login(self.student.user)
+ response = self.client.get(self.template_list_url)
+ self.assertEqual(response.status_code, status.HTTP_403_FORBIDDEN)
+
+ def test_template_list_get_with_teacher(self):
+ response = self.client.get(
+ self.template_list_url, {"lesgever_id": self.teacher.user.id}
+ )
+ self.assertEqual(response.status_code, status.HTTP_200_OK)
+ self.assertEqual(len(response.data), 1)
+
+ def test_template_list_get_with_invalid_teacher(self):
+ response = self.client.get(self.template_list_url, {"lesgever_id": "invalid"})
+ self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)
+
+ def test_template_list_post(self):
+ data = {
+ "user": self.template.user.id,
+ }
+ file = SimpleUploadedFile("template.txt", b"file_content")
+ data["bestand"] = file
+ response = self.client.post(self.template_list_url, data)
+ self.assertEqual(response.status_code, status.HTTP_201_CREATED)
+ self.assertEqual(Template.objects.count(), 2)
+
+ def test_template_list_post_as_student(self):
+ self.client.force_login(self.student.user)
+ data = {
+ "user": self.template.user.id,
+ }
+ file = SimpleUploadedFile("template.txt", b"file_content")
+ data["bestand"] = file
+ response = self.client.post(self.template_list_url, data)
+ self.assertEqual(response.status_code, status.HTTP_403_FORBIDDEN)
+ self.assertEqual(Template.objects.count(), 1)
+
+ def test_template_list_post_with_invalid_data(self):
+ data = {
+ "user": self.template.user.id,
+ }
+ response = self.client.post(self.template_list_url, data)
+ self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)
+ self.assertEqual(Template.objects.count(), 1)
+
+
+class TemplateDetailViewTest(APITestCase):
+ def setUp(self):
+ self.client = APIClient()
+ self.template = TemplateFactory.create()
+ self.teacher = GebruikerFactory.create(is_lesgever=True)
+ self.student = GebruikerFactory.create(is_lesgever=False)
+ self.client.force_login(self.teacher.user)
+ self.template_detail_url = reverse(
+ "template_detail", kwargs={"id": self.template.template_id}
+ )
+
+ def test_template_detail_get(self):
+ response = self.client.get(self.template_detail_url)
+ self.assertEqual(response.status_code, status.HTTP_200_OK)
+ self.assertEqual(response.data["template_id"], self.template.template_id)
+
+ def test_template_detail_get_as_student(self):
+ self.client.force_login(self.student.user)
+ response = self.client.get(self.template_detail_url)
+ self.assertEqual(response.status_code, status.HTTP_403_FORBIDDEN)
+
+ def test_template_detail_get_invalid(self):
+ response = self.client.get(reverse("template_detail", kwargs={"id": 6969}))
+ self.assertEqual(response.status_code, status.HTTP_404_NOT_FOUND)
+
+ def test_template_detail_put(self):
+ data = {
+ "user": self.template.user.id,
+ }
+ file = SimpleUploadedFile("template.txt", b"file_content")
+ data["bestand"] = file
+ response = self.client.put(self.template_detail_url, data)
+ self.assertEqual(response.status_code, status.HTTP_200_OK)
+ self.assertEqual(response.data["template_id"], self.template.template_id)
+
+ def test_template_detail_patch(self):
+ data = {"bestand": SimpleUploadedFile("template.txt", b"different_content")}
+ response = self.client.patch(self.template_detail_url, data)
+ self.assertEqual(response.status_code, status.HTTP_200_OK)
+ self.assertEqual(response.data["template_id"], self.template.template_id)
+
+ def test_template_detail_put_as_student(self):
+ self.client.force_login(self.student.user)
+ data = {
+ "user": self.template.user.id,
+ }
+ file = SimpleUploadedFile("template.txt", b"file_content")
+ data["bestand"] = file
+ response = self.client.put(self.template_detail_url, data)
+ self.assertEqual(response.status_code, status.HTTP_403_FORBIDDEN)
+
+ def test_template_detail_put_with_invalid_data(self):
+ data = {
+ "user": self.template.user.id,
+ }
+ response = self.client.put(self.template_detail_url, data)
+ self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)
+
+ def test_template_detail_delete(self):
+ response = self.client.delete(self.template_detail_url)
+ self.assertEqual(response.status_code, status.HTTP_204_NO_CONTENT)
+ self.assertEqual(Template.objects.count(), 0)
+
+
+class TemplateDetailBestandViewTest(APITestCase):
+ def setUp(self):
+ self.client = APIClient()
+ self.template = TemplateFactory.create()
+ self.teacher = GebruikerFactory.create(is_lesgever=True)
+ self.student = GebruikerFactory.create(is_lesgever=False)
+ self.client.force_login(self.teacher.user)
+ self.template_detail_bestand_url = reverse(
+ "template_detail_bestand", kwargs={"id": self.template.template_id}
+ )
+
+ def test_template_detail_bestand_get(self):
+ response = self.client.get(self.template_detail_bestand_url)
+ self.assertEqual(response.status_code, status.HTTP_200_OK)
+ self.assertTrue(isinstance(response, FileResponse))
+ response_content = b"".join(response.streaming_content)
+ self.template.bestand.open()
+ template_content = self.template.bestand.read()
+ self.template.bestand.close()
+ self.assertEqual(response_content, template_content)
+
+ def test_template_detail_bestand_get_as_student(self):
+ self.client.force_login(self.student.user)
+ response = self.client.get(self.template_detail_bestand_url)
+ self.assertEqual(response.status_code, status.HTTP_403_FORBIDDEN)
+
+ def test_template_detail_bestand_get_invalid(self):
+ response = self.client.get(
+ reverse("template_detail_bestand", kwargs={"id": 6969})
+ )
+ self.assertEqual(response.status_code, status.HTTP_404_NOT_FOUND)
+
+ def test_template_detail_bestand_get_content_true(self):
+ response = self.client.get(
+ self.template_detail_bestand_url, {"content": "true"}
+ )
+ self.assertEqual(response.status_code, status.HTTP_200_OK)
+ self.template.bestand.open()
+ self.assertEqual(response.data["content"], self.template.bestand.read())
+ self.template.bestand.close()
+
+ def test_template_detail_bestand_get_content_false(self):
+ response = self.client.get(
+ self.template_detail_bestand_url, {"content": "false"}
+ )
+ self.assertEqual(response.status_code, status.HTTP_200_OK)
+ self.assertTrue(isinstance(response, FileResponse))
+ response_content = b"".join(response.streaming_content)
+ self.template.bestand.open()
+ template_content = self.template.bestand.read()
+ self.template.bestand.close()
+ self.assertEqual(response_content, template_content)
diff --git a/api/tests/views/test_vak.py b/api/tests/views/test_vak.py
index 1ea755a93..674ce03c2 100644
--- a/api/tests/views/test_vak.py
+++ b/api/tests/views/test_vak.py
@@ -14,6 +14,7 @@ def setUp(self):
self.lesgevers = GebruikerFactory.create_batch(2, is_lesgever=True)
self.vak1 = VakFactory.create()
self.vak1.studenten.add(self.studenten[0])
+ self.vak1.lesgevers.add(self.lesgevers[0])
self.vak2 = VakFactory.create()
self.client.force_login(self.teacher.user)
self.url = reverse("vak_list")
@@ -27,6 +28,26 @@ def test_vak_list_get_as_student(self):
self.client.force_login(self.studenten[0].user)
response = self.client.get(self.url)
self.assertEqual(response.status_code, status.HTTP_200_OK)
+ self.assertEqual(len(response.data), 2)
+
+ def test_vak_list_get_in_as_teacher(self):
+ self.client.force_login(self.lesgevers[0].user)
+ response = self.client.get(self.url, {"in": True})
+ self.assertEqual(response.status_code, status.HTTP_200_OK)
+ self.assertEqual(len(response.data), 1)
+
+ def test_vak_list_get_in_as_student(self):
+ self.client.force_login(self.studenten[0].user)
+ response = self.client.get(self.url, {"in": True})
+ self.assertEqual(response.status_code, status.HTTP_200_OK)
+ self.assertEqual(len(response.data), 1)
+
+ def test_vak_list_archived(self):
+ self.client.force_login(self.lesgevers[0].user)
+ self.vak1.gearchiveerd = True
+ self.vak1.save()
+ response = self.client.get(self.url, {"gearchiveerd": True})
+ self.assertEqual(response.status_code, status.HTTP_200_OK)
self.assertEqual(len(response.data), 1)
def test_vak_list_post(self):
@@ -79,11 +100,6 @@ def test_vak_detail_get_as_student(self):
self.assertEqual(response.status_code, status.HTTP_200_OK)
self.assertEqual(response.data["vak_id"], self.vak.vak_id)
- def test_vak_detail_get_unauthorized(self):
- self.client.force_login(self.studenten[1].user)
- response = self.client.get(self.url)
- self.assertEqual(response.status_code, status.HTTP_403_FORBIDDEN)
-
def test_vak_detail_get_invalid(self):
response = self.client.get(reverse("vak_detail", args=[69]))
self.assertEqual(response.status_code, status.HTTP_404_NOT_FOUND)
@@ -125,3 +141,31 @@ def test_vak_detail_delete_unauthorized(self):
self.client.force_login(self.studenten[1].user)
response = self.client.delete(self.url)
self.assertEqual(response.status_code, status.HTTP_403_FORBIDDEN)
+
+
+class VakDetailAcceptInviteViewTest(APITestCase):
+ def setUp(self):
+ self.client = APIClient()
+ self.vak = VakFactory.create()
+ self.student = GebruikerFactory.create(is_lesgever=False)
+ self.teacher = GebruikerFactory.create(is_lesgever=True)
+ self.client.force_login(self.student.user)
+ self.url = reverse("vak_detail_accept_invite", args=[self.vak.vak_id])
+ self.vak_url = reverse("vak_detail", args=[self.vak.vak_id])
+
+ def test_vak_detail_accept_invite_as_student(self):
+ response = self.client.get(self.url)
+ self.assertEqual(response.status_code, status.HTTP_302_FOUND)
+ response = self.client.get(self.vak_url)
+ self.assertIn(self.student.user.id, response.data["studenten"])
+
+ def test_vak_detail_accept_invite_as_teacher(self):
+ self.client.force_login(self.teacher.user)
+ response = self.client.get(self.url)
+ self.assertEqual(response.status_code, status.HTTP_302_FOUND)
+ response = self.client.get(self.vak_url)
+ self.assertIn(self.teacher.user.id, response.data["lesgevers"])
+
+ def test_vak_detail_accept_invite_invalid(self):
+ response = self.client.get(reverse("vak_detail_accept_invite", args=[69]))
+ self.assertEqual(response.status_code, status.HTTP_404_NOT_FOUND)
diff --git a/api/urls.py b/api/urls.py
index 0e7d39a82..5bcb7a807 100644
--- a/api/urls.py
+++ b/api/urls.py
@@ -21,15 +21,17 @@
from .views.views import home
from .views.gebruiker import gebruiker_list, gebruiker_detail, gebruiker_detail_me
-from .views.vak import vak_list, vak_detail
+from .views.vak import vak_list, vak_detail, vak_detail_accept_invite
from .views.project import project_list, project_detail, project_detail_download_opgave
from .views.indiening import (
indiening_list,
indiening_detail,
- indiening_detail_download_bestanden,
+ indiening_detail_download_bestand,
+ indiening_detail_download_artefacten,
)
from .views.score import score_list, score_detail
from .views.groep import groep_list, groep_detail
+from .views.template import template_list, template_detail, template_detail_bestand
from .views.restrictie import (
restrictie_list,
restrictie_detail,
@@ -45,6 +47,11 @@
path("api/gebruikers/me/", gebruiker_detail_me, name="gebruiker_detail_me"),
path("api/vakken/", vak_list, name="vak_list"),
path("api/vakken//", vak_detail, name="vak_detail"),
+ path(
+ "api/vakken//accept_invite/",
+ vak_detail_accept_invite,
+ name="vak_detail_accept_invite",
+ ),
path("api/projecten/", project_list, name="project_list"),
path("api/projecten//", project_detail, name="project_detail"),
path(
@@ -55,10 +62,15 @@
path("api/indieningen/", indiening_list, name="indiening_list"),
path("api/indieningen//", indiening_detail, name="indiening_detail"),
path(
- "api/indieningen//indiening_bestanden/",
- indiening_detail_download_bestanden,
+ "api/indieningen//indiening_bestand/",
+ indiening_detail_download_bestand,
name="indiening_detail_download_bestanden",
),
+ path(
+ "api/indieningen//artefacten",
+ indiening_detail_download_artefacten,
+ name="indiening_detail_download_artefacten",
+ ),
path("api/scores/", score_list, name="score_list"),
path("api/scores//", score_detail, name="score_detail"),
path("api/groepen/", groep_list, name="groep_list"),
@@ -70,6 +82,13 @@
restrictie_detail_download_script,
name="restrictie_detail_download_script",
),
+ path("api/templates/", template_list, name="template_list"),
+ path("api/templates//", template_detail, name="template_detail"),
+ path(
+ "api/templates//template/",
+ template_detail_bestand,
+ name="template_detail_bestand",
+ ),
]
urlpatterns = format_suffix_patterns(urlpatterns)
diff --git a/api/utils.py b/api/utils.py
index 0691cc353..67a8da680 100644
--- a/api/utils.py
+++ b/api/utils.py
@@ -1,6 +1,25 @@
from django.conf import settings
from api.models.gebruiker import Gebruiker
import requests
+import smtplib
+from email.mime.text import MIMEText
+from email.mime.multipart import MIMEMultipart
+import ssl
+import os
+from dotenv import load_dotenv
+
+load_dotenv()
+
+smtp_server_address = "smtp.gmail.com"
+smtp_port = 465
+mail_username = os.environ.get("MAIL_USERNAME")
+mail_app_password = os.environ.get("MAIL_APP_PASSWORD")
+
+indiening_status = {
+ -1: "heeft niet alle testen geslaagd.",
+ 0: "wordt nog getest...",
+ 1: "heeft alle testen geslaagd!",
+}
API_URLS = {
@@ -11,6 +30,7 @@
"scores": "api/scores",
"projecten": "api/projecten",
"restricties": "api/restricties",
+ "templates": "api/templates",
}
@@ -39,6 +59,22 @@ def get_graph_token():
return None
+def has_permissions(user):
+ """
+ Controleert of de gebruiker permissies heeft.
+
+ Args:
+ user (User): De gebruiker waarvan moet worden gecontroleerd of deze permissies heeft.
+
+ Returns:
+ bool: True als de gebruiker permissies heeft, anders False.
+ """
+ if user.is_superuser:
+ return True
+ gebruiker = Gebruiker.objects.get(pk=user.id)
+ return gebruiker.is_lesgever
+
+
def is_lesgever(user):
"""
Controleert of de gebruiker een lesgever is.
@@ -49,8 +85,6 @@ def is_lesgever(user):
Returns:
bool: True als de gebruiker een lesgever is, anders False.
"""
- if user.is_superuser:
- return True
gebruiker = Gebruiker.objects.get(pk=user.id)
return gebruiker.is_lesgever
@@ -81,3 +115,52 @@ def get_gebruiker(user):
Gebruiker: De Gebruiker-instantie voor de gegeven gebruiker.
"""
return Gebruiker.objects.get(pk=user.id)
+
+
+def send_indiening_confirmation_mail(indiening):
+ """
+ Verstuurt een bevestigingsmail naar alle studenten in de groep voor een specifieke indiening.
+
+ Args:
+ indiening (Indiening): De indiening waarvoor de bevestigingsmail verstuurd moet worden.
+ """
+ project = indiening.groep.project
+
+ project_url = f"https://sel2-4.ugent.be/course/{project.vak.vak_id}/assignment/{project.project_id}"
+ indiening_url = f"{project_url}/submission/{indiening.indiening_id}"
+
+ for student in indiening.groep.studenten.all():
+ subject = "Indieningsontvangst"
+
+ email = MIMEMultipart("alternative")
+ email["Subject"] = subject
+ email["From"] = mail_username
+ email["To"] = student.user.email
+
+ plain_text = f"""
+ Beste {student.user.first_name} {student.user.last_name},
+
+ Dit is een bevestiging dat uw indiening voor het project {project.titel} is ontvangen.
+ De indiening {indiening_status[indiening.status]}.
+ """
+
+ html_text = f"""
+
+
+ Beste {student.user.first_name} {student.user.last_name}
+ Dit is een bevestiging dat uw indiening voor het project \
+ {project.titel} is ontvangen.
+ De indiening {indiening_status[indiening.status]}
+
+
+ """
+
+ email.attach(MIMEText(plain_text, "plain"))
+ email.attach(MIMEText(html_text, "html"))
+
+ context = ssl.create_default_context()
+ with smtplib.SMTP_SSL(
+ smtp_server_address, smtp_port, context=context
+ ) as smtp_server:
+ smtp_server.login(mail_username, mail_app_password)
+ smtp_server.sendmail(mail_username, student.user.email, email.as_string())
diff --git a/api/views/gebruiker.py b/api/views/gebruiker.py
index 01a21a791..66a316808 100644
--- a/api/views/gebruiker.py
+++ b/api/views/gebruiker.py
@@ -6,7 +6,7 @@
from api.models.gebruiker import Gebruiker
from api.serializers.gebruiker import GebruikerSerializer
-from api.utils import is_lesgever
+from api.utils import has_permissions
@api_view(["GET"])
@@ -25,10 +25,7 @@ def gebruiker_list(request):
Response: Een lijst van gebruikers.
"""
- if is_lesgever(request.user):
- gebruikers = Gebruiker.objects.all()
- else:
- gebruikers = Gebruiker.objects.filter(user=request.user.id)
+ gebruikers = Gebruiker.objects.all()
if "is_lesgever" in request.GET and request.GET.get("is_lesgever").lower() in [
"true",
@@ -64,17 +61,13 @@ def gebruiker_detail(request, id):
return Response(status=status.HTTP_404_NOT_FOUND)
if request.method == "GET":
- if is_lesgever(request.user) or int(id) == request.user.id:
- serializer = GebruikerSerializer(gebruiker)
- return Response(serializer.data)
- return Response(status=status.HTTP_403_FORBIDDEN)
+ serializer = GebruikerSerializer(gebruiker)
+ return Response(serializer.data)
elif request.method in ["PUT", "PATCH"]:
- if request.user.is_superuser:
+ if has_permissions(request.user) or request.user.id == id:
if request.method == "PUT":
serializer = GebruikerSerializer(gebruiker, data=request.data)
else:
- if not request.data.get("gepinde_vakken"):
- request.data["gepinde_vakken"] = gebruiker.gepinde_vakken.all()
serializer = GebruikerSerializer(
gebruiker, data=request.data, partial=True
)
diff --git a/api/views/groep.py b/api/views/groep.py
index dbffc57c6..552b3fb4a 100644
--- a/api/views/groep.py
+++ b/api/views/groep.py
@@ -4,7 +4,7 @@
from api.models.groep import Groep
from api.serializers.groep import GroepSerializer
-from api.utils import is_lesgever, contains
+from api.utils import has_permissions
@api_view(["GET", "POST"])
@@ -28,10 +28,7 @@ def groep_list(request, format=None):
Response: Een lijst van groepen of een nieuw aangemaakte groep.
"""
if request.method == "GET":
- if is_lesgever(request.user):
- groepen = Groep.objects.all()
- else:
- groepen = Groep.objects.filter(studenten=request.user.id)
+ groepen = Groep.objects.all()
if "project" in request.GET:
try:
@@ -51,7 +48,7 @@ def groep_list(request, format=None):
return Response(serializer.data)
elif request.method == "POST":
- if is_lesgever(request.user):
+ if has_permissions(request.user):
serializer = GroepSerializer(data=request.data)
if serializer.is_valid():
serializer.save()
@@ -78,13 +75,13 @@ def groep_detail(request, id, format=None):
except Groep.DoesNotExist:
return Response(status=status.HTTP_404_NOT_FOUND)
if request.method == "GET":
- if is_lesgever(request.user) or contains(groep.studenten, request.user):
- serializer = GroepSerializer(groep)
- return Response(serializer.data)
- return Response(status=status.HTTP_403_FORBIDDEN)
+ serializer = GroepSerializer(groep)
+ return Response(serializer.data)
- if is_lesgever(request.user):
- if request.method in ["PUT", "PATCH"]:
+ if request.method in ["PUT", "PATCH"]:
+ if has_permissions(request.user) or validate_new_students(
+ request.user, groep, request.data
+ ):
if request.method == "PUT":
serializer = GroepSerializer(groep, data=request.data)
else:
@@ -93,8 +90,16 @@ def groep_detail(request, id, format=None):
serializer.save()
return Response(serializer.data)
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
+ return Response(status=status.HTTP_403_FORBIDDEN)
- elif request.method == "DELETE":
+ if has_permissions(request.user):
+ if request.method == "DELETE":
groep.delete()
return Response(status=status.HTTP_204_NO_CONTENT)
return Response(status=status.HTTP_403_FORBIDDEN)
+
+
+def validate_new_students(user, current_groep, data):
+ old = set(map(lambda student: student.user.id, current_groep.studenten.all()))
+ new = set(data.get("studenten"))
+ return (old - new).union(new - old) == {user.id}
diff --git a/api/views/indiening.py b/api/views/indiening.py
index c186b2d4a..3fd65c977 100644
--- a/api/views/indiening.py
+++ b/api/views/indiening.py
@@ -2,17 +2,14 @@
from rest_framework.response import Response
from rest_framework import status
-from api.models.indiening import Indiening, IndieningBestand
+from api.models.indiening import Indiening
from api.models.groep import Groep
from api.models.vak import Vak
from api.models.project import Project
from api.serializers.indiening import IndieningSerializer
-from api.utils import is_lesgever, contains
+from api.utils import has_permissions, contains
-import os
-import tempfile
-import zipfile
-from django.http import HttpResponse
+from django.http import FileResponse
@api_view(["GET", "POST"])
@@ -35,7 +32,7 @@ def indiening_list(request, format=None):
Response: Een lijst van indieningen of een nieuw aangemaakte indiening.
"""
if request.method == "GET":
- if is_lesgever(request.user):
+ if has_permissions(request.user):
indieningen = Indiening.objects.all()
else:
groepen = Groep.objects.filter(studenten=request.user.id)
@@ -69,21 +66,12 @@ def indiening_list(request, format=None):
return Response(serializer.data)
elif request.method == "POST":
- if "indiening_bestanden" not in request.FILES:
- return Response(
- {"indiening_bestanden": ["This field is required."]},
- status=status.HTTP_400_BAD_REQUEST,
- )
-
serializer = IndieningSerializer(data=request.data)
if serializer.is_valid():
serializer.save()
- indiening = serializer.instance
else:
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
- for file in request.FILES.getlist("indiening_bestanden"):
- IndieningBestand.objects.create(indiening=indiening, bestand=file)
return Response(serializer.data, status=status.HTTP_201_CREATED)
@@ -105,7 +93,7 @@ def indiening_detail(request, id, format=None):
return Response(status=status.HTTP_404_NOT_FOUND)
if request.method == "GET":
- if is_lesgever(request.user) or contains(
+ if has_permissions(request.user) or contains(
indiening.groep.studenten, request.user
):
serializer = IndieningSerializer(indiening)
@@ -113,14 +101,14 @@ def indiening_detail(request, id, format=None):
return Response(status=status.HTTP_403_FORBIDDEN)
elif request.method == "DELETE":
- if is_lesgever(request.user):
+ if has_permissions(request.user):
indiening.delete()
return Response(status=status.HTTP_204_NO_CONTENT)
return Response(status=status.HTTP_403_FORBIDDEN)
@api_view(["GET"])
-def indiening_detail_download_bestanden(request, id, format=None):
+def indiening_detail_download_bestand(request, id, format=None):
"""
Een view om de bestanden van een specifieke indiening te downloaden als een zip-archief.
@@ -138,21 +126,37 @@ def indiening_detail_download_bestanden(request, id, format=None):
except Indiening.DoesNotExist:
return Response(status=status.HTTP_404_NOT_FOUND)
- if is_lesgever(request.user) or contains(indiening.groep.studenten, request.user):
- indiening_bestanden = IndieningBestand.objects.filter(indiening=indiening)
+ if has_permissions(request.user) or contains(
+ indiening.groep.studenten, request.user
+ ):
+
+ return FileResponse(indiening.bestand.open(), as_attachment=True)
- temp_dir = tempfile.mkdtemp()
+ return Response(status=status.HTTP_403_FORBIDDEN)
- zip_file_name = f"groep_{indiening.groep.groep_id}_indiening_{indiening.indiening_id}_bestanden.zip"
- zip_file_path = os.path.join(temp_dir, zip_file_name)
- with zipfile.ZipFile(zip_file_path, "w") as zip_file:
- for indiening_bestand in indiening_bestanden:
- path = indiening_bestand.bestand.path
- zip_file.write(path, os.path.basename(path))
- with open(zip_file_path, "rb") as zip_file:
- response = HttpResponse(zip_file.read(), content_type="application/zip")
- response["Content-Disposition"] = f"attachment; filename={zip_file_name}"
- return response
+@api_view(["GET"])
+def indiening_detail_download_artefacten(request, id, format=None):
+ """
+ Een view om de artefacten van een specifieke indiening te downloaden.
+
+ Args:
+ id (int): De primaire sleutel van de indiening.
+ format (str, optional): Het gewenste formaat voor de respons. Standaard is None.
+
+ Returns:
+ Response: Een bestandsrespons met de artefacten van de indiening als bijlage,
+ indien de gebruiker een lesgever is.
+ Anders wordt een foutmelding geretourneerd.
+ """
+ try:
+ indiening = Indiening.objects.get(pk=id)
+ except Indiening.DoesNotExist:
+ return Response(status=status.HTTP_404_NOT_FOUND)
+ if has_permissions(request.user):
+ try:
+ return FileResponse(indiening.artefacten.open(), as_attachment=True)
+ except (ValueError, FileNotFoundError):
+ return Response(status=status.HTTP_404_NOT_FOUND)
return Response(status=status.HTTP_403_FORBIDDEN)
diff --git a/api/views/project.py b/api/views/project.py
index 9e38caa3f..4c5f694a7 100644
--- a/api/views/project.py
+++ b/api/views/project.py
@@ -6,7 +6,7 @@
from api.models.project import Project
from api.models.vak import Vak
from api.serializers.project import ProjectSerializer
-from api.utils import is_lesgever, contains
+from api.utils import has_permissions, contains
@api_view(["GET", "POST"])
@@ -29,7 +29,7 @@ def project_list(request, format=None):
Response: Een lijst van projecten of een nieuw aangemaakt project.
"""
if request.method == "GET":
- if is_lesgever(request.user):
+ if has_permissions(request.user):
projects = Project.objects.all()
else:
vakken = Vak.objects.filter(studenten=request.user.id)
@@ -46,7 +46,7 @@ def project_list(request, format=None):
return Response(serializer.data)
elif request.method == "POST":
- if is_lesgever(request.user):
+ if has_permissions(request.user):
serializer = ProjectSerializer(data=request.data)
if serializer.is_valid():
serializer.save()
@@ -74,12 +74,14 @@ def project_detail(request, id, format=None):
return Response(status=status.HTTP_404_NOT_FOUND)
if request.method == "GET":
- if is_lesgever(request.user) or contains(project.vak.studenten, request.user):
+ if has_permissions(request.user) or contains(
+ project.vak.studenten, request.user
+ ):
serializer = ProjectSerializer(project)
return Response(serializer.data)
return Response(status=status.HTTP_403_FORBIDDEN)
- if is_lesgever(request.user):
+ if has_permissions(request.user):
if request.method in ["PUT", "PATCH"]:
if request.method == "PUT":
serializer = ProjectSerializer(project, data=request.data)
@@ -115,6 +117,6 @@ def project_detail_download_opgave(request, id, format=None):
except Project.DoesNotExist:
return Response(status=status.HTTP_404_NOT_FOUND)
- if is_lesgever(request.user) or contains(project.vak.studenten, request.user):
+ if has_permissions(request.user) or contains(project.vak.studenten, request.user):
return FileResponse(project.opgave_bestand.open(), as_attachment=True)
return Response(status=status.HTTP_403_FORBIDDEN)
diff --git a/api/views/restrictie.py b/api/views/restrictie.py
index 09eadcdc8..eb7484e74 100644
--- a/api/views/restrictie.py
+++ b/api/views/restrictie.py
@@ -5,7 +5,7 @@
from api.models.restrictie import Restrictie
from api.serializers.restrictie import RestrictieSerializer
-from api.utils import is_lesgever
+from api.utils import has_permissions
@api_view(["GET", "POST"])
@@ -22,36 +22,36 @@ def restrictie_list(request, format=None):
Returns:
Response: Een http-respons met de opgevraagde of gemaakte restricties.
"""
- if is_lesgever(request.user):
- if request.method == "GET":
- restricties = Restrictie.objects.all()
-
- if "project" in request.GET:
- try:
- project = eval(request.GET.get("project"))
- restricties = restricties.filter(project=project)
- except NameError:
- return Response(status=status.HTTP_400_BAD_REQUEST)
-
- if "moet_slagen" in request.GET and request.GET.get(
- "moet_slagen"
- ).lower() in [
- "true",
- "false",
- ]:
- restricties = restricties.filter(
- moet_slagen=(request.GET.get("moet_slagen").lower() == "true")
- )
- serializer = RestrictieSerializer(restricties, many=True)
- return Response(serializer.data)
-
- elif request.method == "POST":
+ if request.method == "GET":
+ restricties = Restrictie.objects.all()
+
+ if "project" in request.GET:
+ try:
+ project = eval(request.GET.get("project"))
+ restricties = restricties.filter(project=project)
+ except NameError:
+ return Response(status=status.HTTP_400_BAD_REQUEST)
+
+ if "moet_slagen" in request.GET and request.GET.get("moet_slagen").lower() in [
+ "true",
+ "false",
+ ]:
+ restricties = restricties.filter(
+ moet_slagen=(request.GET.get("moet_slagen").lower() == "true")
+ )
+
+ serializer = RestrictieSerializer(restricties, many=True)
+ return Response(serializer.data)
+
+ if has_permissions(request.user):
+ if request.method == "POST":
serializer = RestrictieSerializer(data=request.data)
if serializer.is_valid():
serializer.save()
return Response(serializer.data, status=status.HTTP_201_CREATED)
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
+
return Response(status=status.HTTP_403_FORBIDDEN)
@@ -81,7 +81,7 @@ def restrictie_detail(request, id, format=None):
except Restrictie.DoesNotExist:
return Response(status=status.HTTP_404_NOT_FOUND)
- if is_lesgever(request.user):
+ if has_permissions(request.user):
if request.method == "GET":
serializer = RestrictieSerializer(restrictie)
return Response(serializer.data)
@@ -122,6 +122,6 @@ def restrictie_detail_download_script(request, id, format=None):
except Restrictie.DoesNotExist:
return Response(status=status.HTTP_404_NOT_FOUND)
- if is_lesgever(request.user):
+ if has_permissions(request.user):
return FileResponse(restrictie.script.open(), as_attachment=True)
return Response(status=status.HTTP_403_FORBIDDEN)
diff --git a/api/views/score.py b/api/views/score.py
index fa381aac5..09dbf7299 100644
--- a/api/views/score.py
+++ b/api/views/score.py
@@ -6,7 +6,7 @@
from api.models.groep import Groep
from api.models.indiening import Indiening
from api.serializers.score import ScoreSerializer
-from api.utils import is_lesgever, contains
+from api.utils import has_permissions, contains
@api_view(["GET", "POST"])
@@ -30,7 +30,7 @@ def score_list(request, format=None):
Response: Een lijst van scores of een nieuwe aangemaakte score.
"""
if request.method == "GET":
- if is_lesgever(request.user):
+ if has_permissions(request.user):
scores = Score.objects.all()
else:
groepen = Groep.objects.filter(studenten=request.user.id)
@@ -48,7 +48,7 @@ def score_list(request, format=None):
return Response(serializer.data)
elif request.method == "POST":
- if is_lesgever(request.user):
+ if has_permissions(request.user):
serializer = ScoreSerializer(data=request.data)
if serializer.is_valid():
serializer.save()
@@ -76,14 +76,14 @@ def score_detail(request, id, format=None):
return Response(status=status.HTTP_404_NOT_FOUND)
if request.method == "GET":
- if is_lesgever(request.user) or contains(
+ if has_permissions(request.user) or contains(
score.indiening.groep.studenten, request.user
):
serializer = ScoreSerializer(score)
return Response(serializer.data)
return Response(status=status.HTTP_403_FORBIDDEN)
- if is_lesgever(request.user):
+ if has_permissions(request.user):
if request.method in ["PUT", "PATCH"]:
if request.method == "PUT":
serializer = ScoreSerializer(score, data=request.data)
diff --git a/api/views/template.py b/api/views/template.py
new file mode 100644
index 000000000..52508e7d0
--- /dev/null
+++ b/api/views/template.py
@@ -0,0 +1,122 @@
+from rest_framework.decorators import api_view
+from rest_framework.response import Response
+from rest_framework import status
+
+from api.models.template import Template
+from api.serializers.template import TemplateSerializer
+from api.utils import has_permissions
+
+from django.http import FileResponse
+
+
+@api_view(["GET", "POST"])
+def template_list(request, format=None):
+ """
+ Een view om een lijst van templates op te halen of een nieuwe template toe te voegen.
+
+ GET:
+ Als de gebruiker een lesgever is, worden alle templates opgehaald.
+
+ Optionele query parameters:
+ lesgever_id (int): Filtert templates op basis van lesgevers
+
+ POST:
+ Voegt een nieuwe template toe.
+
+ Returns:
+ Response: Een lijst van tempaltes of een nieuw aangemaakte template.
+ """
+ if request.method == "GET":
+ if has_permissions(request.user):
+ templates = Template.objects.all()
+
+ if "lesgever_id" in request.GET:
+ try:
+ user = eval(request.GET.get("lesgever_id"))
+ templates = templates.filter(user=user)
+ except NameError:
+ return Response(status=status.HTTP_400_BAD_REQUEST)
+
+ serializer = TemplateSerializer(templates, many=True)
+ return Response(serializer.data)
+
+ elif request.method == "POST":
+ if has_permissions(request.user):
+ serializer = TemplateSerializer(data=request.data)
+ if serializer.is_valid():
+ serializer.save()
+ return Response(serializer.data, status=status.HTTP_201_CREATED)
+ return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
+
+ return Response(status=status.HTTP_403_FORBIDDEN)
+
+
+@api_view(["GET", "PUT", "PATCH", "DELETE"])
+def template_detail(request, id, format=None):
+ """
+ Een view om de gegevens van een specifieke template op te halen (GET),
+ bij te werken (PUT, PATCH) of te verwijderen (DELETE).
+
+ Args:
+ id (int): De primaire sleutel van de template.
+
+ Returns:
+ Response: Gegevens van de template of een foutmelding als de template niet
+ bestaat of als er een ongeautoriseerde toegang is.
+ """
+ try:
+ template = Template.objects.get(pk=id)
+ except Template.DoesNotExist:
+ return Response(status=status.HTTP_404_NOT_FOUND)
+
+ if has_permissions(request.user):
+ if request.method == "GET":
+ serializer = TemplateSerializer(template)
+ return Response(serializer.data)
+ if request.method in ["PUT", "PATCH"]:
+ if request.method == "PUT":
+ serializer = TemplateSerializer(template, data=request.data)
+ else:
+ serializer = TemplateSerializer(
+ template, data=request.data, partial=True
+ )
+ if serializer.is_valid():
+ serializer.save()
+ return Response(serializer.data)
+ return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
+
+ elif request.method == "DELETE":
+ template.delete()
+ return Response(status=status.HTTP_204_NO_CONTENT)
+ return Response(status=status.HTTP_403_FORBIDDEN)
+
+
+@api_view(["GET"])
+def template_detail_bestand(request, id, format=None):
+ """
+ Een view om het bestand van een specifieke template te downloaden of de content in JSON-formaat te krijgen.
+
+ Args:
+ id (int): De primaire sleutel van de template.
+ format (str, optional): Het gewenste formaat voor de respons. Standaard is None.
+
+ Returns:
+ Response: Json met de inhoud van het bestand,
+ indien de content parameter of true stond
+ FileResponse: Attachment met het bestand
+ """
+ try:
+ template = Template.objects.get(pk=id)
+ except Template.DoesNotExist:
+ return Response(status=status.HTTP_404_NOT_FOUND)
+
+ if has_permissions(request.user):
+ if request.method == "GET":
+ bestand = template.bestand.open()
+ if (
+ "content" in request.GET
+ and request.GET.get("content").lower() == "true"
+ ):
+ return Response({"content": bestand.read()})
+ return FileResponse(bestand, as_attachment=True)
+ return Response(status=status.HTTP_403_FORBIDDEN)
diff --git a/api/views/vak.py b/api/views/vak.py
index 4fdf623aa..511156943 100644
--- a/api/views/vak.py
+++ b/api/views/vak.py
@@ -2,9 +2,10 @@
from rest_framework.response import Response
from rest_framework import status
+from django.shortcuts import redirect
from api.models.vak import Vak
-from api.serializers.vak import VakSerializer
-from api.utils import is_lesgever, contains
+from api.serializers.vak import VakSerializer, add_students_to_group
+from api.utils import has_permissions, is_lesgever, get_gebruiker
@api_view(["GET", "POST"])
@@ -13,9 +14,11 @@ def vak_list(request, format=None):
Een view om een lijst van vakken op te halen of een nieuw vak toe te voegen.
GET:
- Als de gebruiker een lesgever is, worden alle vakken opgehaald.
- Als de gebruiker geen lesgever is, worden alleen de vakken opgehaald
- waarin de ingelogde gebruiker zich bevindt.
+ Geeft alle vakken terug.
+
+ Optionele query parameters:
+ in (boolean): Filtert de vakken waarvan de gebruiker deel uitmaakt.
+ gearchiveerd (boolean): Toont alleen de gearchiveerde vakken.
POST:
Voegt een nieuw vak toe.
@@ -24,16 +27,25 @@ def vak_list(request, format=None):
Response: Een lijst van vakken of een nieuw aangemaakt vak.
"""
if request.method == "GET":
- if is_lesgever(request.user):
- vakken = Vak.objects.all()
- else:
- vakken = Vak.objects.filter(studenten=request.user.id)
+ vakken = Vak.objects.all()
+ if "in" in request.GET and request.GET.get("in").lower() == "true":
+ if is_lesgever(request.user):
+ vakken = vakken.filter(lesgevers=request.user.id)
+ else:
+ vakken = vakken.filter(studenten=request.user.id)
+
+ if "gearchiveerd" in request.GET and request.GET.get(
+ "gearchiveerd"
+ ).lower() in ["true", "false"]:
+ vakken = vakken.filter(
+ gearchiveerd=(request.GET.get("gearchiveerd").lower() == "true")
+ )
serializer = VakSerializer(vakken, many=True)
return Response(serializer.data)
elif request.method == "POST":
- if is_lesgever(request.user):
+ if has_permissions(request.user):
serializer = VakSerializer(data=request.data)
if serializer.is_valid():
serializer.save()
@@ -61,21 +73,14 @@ def vak_detail(request, id, format=None):
return Response(status=status.HTTP_404_NOT_FOUND)
if request.method == "GET":
- if is_lesgever(request.user) or contains(vak.studenten, request.user):
- serializer = VakSerializer(vak)
- return Response(serializer.data)
- return Response(status=status.HTTP_403_FORBIDDEN)
- if is_lesgever(request.user):
+ serializer = VakSerializer(vak)
+ return Response(serializer.data)
+ if has_permissions(request.user):
if request.method in ["PUT", "PATCH"]:
if request.method == "PUT":
serializer = VakSerializer(vak, data=request.data)
else:
- data = request.data.copy()
- if not data.get("studenten"):
- data.setlist("studenten", vak.studenten.all())
- if not data.get("lesgevers"):
- data.setlist("lesgevers", vak.lesgevers.all())
- serializer = VakSerializer(vak, data=data, partial=True)
+ serializer = VakSerializer(vak, data=request.data, partial=True)
if serializer.is_valid():
serializer.save()
return Response(serializer.data)
@@ -85,3 +90,32 @@ def vak_detail(request, id, format=None):
vak.delete()
return Response(status=status.HTTP_204_NO_CONTENT)
return Response(status=status.HTTP_403_FORBIDDEN)
+
+
+@api_view(["GET"])
+def vak_detail_accept_invite(request, id, format=None):
+ """
+ Een view om de invite van een vak te accepteren (GET),
+
+ Args:
+ id (int): De primaire sleutel van het vak.
+
+ Returns:
+ Response: Gegevens van het vak.
+ """
+ try:
+ vak = Vak.objects.get(pk=id)
+ except Vak.DoesNotExist:
+ return Response(status=status.HTTP_404_NOT_FOUND)
+
+ gebruiker = get_gebruiker(request.user)
+
+ if gebruiker.is_lesgever:
+ vak.lesgevers.add(gebruiker)
+ else:
+ vak.studenten.add(gebruiker)
+ vak.save()
+
+ add_students_to_group(vak)
+
+ return redirect("vak_detail", id=vak.vak_id)
diff --git a/frontend/frontend/.gitignore b/frontend/frontend/.gitignore
index 72e151430..e5d2f2fab 100644
--- a/frontend/frontend/.gitignore
+++ b/frontend/frontend/.gitignore
@@ -12,6 +12,8 @@ dist
dist-ssr
*.local
+package-lock.json
+
# Editor directories and files
.vscode/*
!.vscode/extensions.json
@@ -23,3 +25,5 @@ dist-ssr
*.sln
*.sw?
./src/authConfig/authSecrets.ts
+./package-lock.json
+package-lock.json
diff --git a/frontend/frontend/cypress.config.ts b/frontend/frontend/cypress.config.ts
new file mode 100644
index 000000000..45e924e1b
--- /dev/null
+++ b/frontend/frontend/cypress.config.ts
@@ -0,0 +1,16 @@
+import { defineConfig } from "cypress";
+
+export default defineConfig({
+ e2e: {
+ setupNodeEvents(on, config) {
+ // implement node event listeners here
+ },
+ },
+
+ component: {
+ devServer: {
+ framework: "react",
+ bundler: "vite",
+ },
+ },
+});
diff --git a/frontend/frontend/cypress/component/AssignmentListItem.cy.tsx b/frontend/frontend/cypress/component/AssignmentListItem.cy.tsx
new file mode 100644
index 000000000..ff6690b9e
--- /dev/null
+++ b/frontend/frontend/cypress/component/AssignmentListItem.cy.tsx
@@ -0,0 +1,108 @@
+import { AssignmentListItem } from '../../src/components/AssignmentListItem'
+import { BrowserRouter } from 'react-router-dom'
+import fixtures from '../fixtures/fixtures.json'
+import { SubmissionStatus } from '../../src/pages/submissionPage/SubmissionPage'
+
+describe('AssignmentListItem', () => {
+ const mockProps = {
+ id: fixtures.id,
+ course_id: fixtures.course_id,
+ projectName: fixtures.project,
+ dueDate: new Date().toLocaleDateString(),
+ status: SubmissionStatus.FAIL,
+ isStudent: true,
+ }
+
+ it('renders with cross', () => {
+ cy.mount(
+
+
+
+ )
+ cy.get('.MuiListItem-root').should('exist')
+ cy.get('#project' + fixtures.id).should('exist')
+ cy.get('[data-cy=projectName]').should('contain.text', fixtures.project)
+ cy.get('#dueDate').should(
+ 'contain.text',
+ new Date().toLocaleDateString()
+ )
+ cy.get('#cross').should('exist')
+ cy.get('#check').should('not.exist')
+ cy.get('#clock').should('not.exist')
+ })
+
+ it('renders with checkmark', () => {
+ mockProps.status = SubmissionStatus.PASSED
+ cy.mount(
+
+
+
+ )
+ cy.get('.MuiListItem-root').should('exist')
+ cy.get('#project' + fixtures.id).should('exist')
+ cy.get('[data-cy=projectName]').should('contain.text', fixtures.project)
+ cy.get('#dueDate').should('contain.text', new Date().toLocaleDateString())
+ cy.get('#cross').should('not.exist')
+ cy.get('#check').should('exist')
+ cy.get('#clock').should('not.exist')
+ })
+
+ it('renders with alarm', () => {
+ mockProps.status = SubmissionStatus.PENDING
+ cy.mount(
+
+
+
+ )
+ cy.get('.MuiListItem-root').should('exist')
+ cy.get('#project' + fixtures.id).should('exist')
+ cy.get('[data-cy=projectName]').should('contain.text', fixtures.project)
+ cy.get('#dueDate').should(
+ 'contain.text',
+ new Date().toLocaleDateString()
+ )
+ cy.get('#cross').should('not.exist')
+ cy.get('#check').should('not.exist')
+ cy.get('#clock').should('exist')
+ })
+
+ it('renders with no due date', () => {
+ mockProps.dueDate = undefined
+ mockProps.status = SubmissionStatus.PASSED
+ cy.mount(
+
+
+
+ )
+ cy.get('.MuiListItem-root').should('exist')
+ cy.get('#project' + fixtures.id).should('exist')
+ cy.get('[data-cy=projectName]').should('contain.text', fixtures.project)
+ // normaal zou hier 'no deadline' moeten staan in de correcte taal, maar blijkbaar gaat dat niet in de testen
+ // omdat er hier niet echt een taal is ingesteld?
+ cy.get('#dueDate').should(
+ 'not.contain.text',
+ new Date().toLocaleDateString()
+ )
+ cy.get('#cross').should('not.exist')
+ cy.get('#check').should('exist')
+ })
+
+ it('renders as teacher', () => {
+ mockProps.isStudent = false
+ mockProps.dueDate = new Date().toLocaleDateString()
+ cy.mount(
+
+
+
+ )
+ cy.get('.MuiListItem-root').should('exist')
+ cy.get('#project' + fixtures.id).should('exist')
+ cy.get('[data-cy=projectName]').should('contain.text', fixtures.project)
+ cy.get('#dueDate').should(
+ 'contain.text',
+ new Date().toLocaleDateString()
+ )
+ cy.get('#cross').should('not.exist')
+ cy.get('#check').should('not.exist')
+ })
+})
diff --git a/frontend/frontend/cypress/component/CourseCard.cy.tsx b/frontend/frontend/cypress/component/CourseCard.cy.tsx
new file mode 100644
index 000000000..1191d4a4e
--- /dev/null
+++ b/frontend/frontend/cypress/component/CourseCard.cy.tsx
@@ -0,0 +1,82 @@
+import { CourseCard } from '../../src/components/CourseCard'
+import { BrowserRouter } from 'react-router-dom'
+import fixtures from '../fixtures/fixtures.json'
+
+describe('CourseCard', () => {
+ const mockProps = {
+ courseId: fixtures.id,
+ archived: false,
+ isStudent: true,
+ }
+
+ it('renders as student', () => {
+ cy.mount(
+
+
+
+ )
+ // data is fetched from the backend, so we can't check that
+ cy.get('.MuiCard-root').should('exist')
+ cy.get('.MuiCardContent-root').should('exist')
+ cy.get('#courseInfo')
+ .should('have.text', 'undefined: undefined: 0')
+ .click()
+ cy.get('#student').should('exist')
+ cy.get('#teacherArchived').should('not.exist')
+ cy.get('#teacherNonArchived').should('not.exist')
+ cy.get('#project').contains('Project')
+ cy.get('#deadline').contains('Deadline')
+ cy.get('#status').contains('Status')
+ cy.get('#archiveButton').should('not.exist')
+ })
+
+ it('renders archived as teacher', () => {
+ const mockProps = {
+ courseId: fixtures.id,
+ archived: true,
+ isStudent: false,
+ }
+ cy.mount(
+
+
+
+ )
+ cy.get('.MuiCard-root').should('exist')
+ cy.get('.MuiCardContent-root').should('exist')
+ cy.get('#courseInfo')
+ .should('have.text', 'undefined: undefined: 0')
+ .click()
+ cy.get('#student').should('not.exist')
+ cy.get('#teacherArchived').should('exist')
+ cy.get('#teacherNonArchived').should('not.exist')
+ cy.get('#project').contains('Project')
+ cy.get('#deadline').contains('Deadline')
+ cy.get('#status').should('not.exist')
+ cy.get('#archiveButton').should('not.exist')
+ })
+
+ it('renders non-archived as teacher', () => {
+ const mockProps = {
+ courseId: fixtures.id,
+ archived: false,
+ isStudent: false,
+ }
+ cy.mount(
+
+
+
+ )
+ cy.get('.MuiCard-root').should('exist')
+ cy.get('.MuiCardContent-root').should('exist')
+ cy.get('#courseInfo')
+ .should('have.text', 'undefined: undefined: 0')
+ .click()
+ cy.get('#student').should('not.exist')
+ cy.get('#teacherArchived').should('not.exist')
+ cy.get('#teacherNonArchived').should('exist')
+ cy.get('#project').contains('Project')
+ cy.get('#deadline').contains('Deadline')
+ cy.get('#status').should('not.exist')
+ cy.get('#archiveButton').should('exist')
+ })
+})
diff --git a/frontend/frontend/cypress/component/FileUploadButton.cy.tsx b/frontend/frontend/cypress/component/FileUploadButton.cy.tsx
new file mode 100644
index 000000000..6aed5cc3d
--- /dev/null
+++ b/frontend/frontend/cypress/component/FileUploadButton.cy.tsx
@@ -0,0 +1,32 @@
+import InputFileUpload from '../../src/components/FileUploadButton'
+import fixtures from '../fixtures/fixtures.json'
+
+describe('FileUploadButton', () => {
+ const mockProps = {
+ name: fixtures.name,
+ tooltip: fixtures.tooltip,
+ onFileChange: () => {},
+ fileTypes: ['pdf'],
+ path: '../fixtures/test.pdf',
+ }
+
+ it('renders', () => {
+ cy.mount( )
+ cy.get('#uploadButton')
+ .should('exist')
+ .should('have.text', fixtures.name)
+ cy.get('input[type=file]').should('exist')
+ cy.contains(fixtures.tooltip).should('not.exist')
+ cy.get('#uploadButton').trigger('mouseover')
+ cy.get('#uploadButton').trigger('mouseout')
+ cy.contains(fixtures.tooltip).should('not.exist')
+ cy.get('#clearButton').should('exist')
+ })
+
+ it('no path', () => {
+ mockProps.path = ''
+ cy.mount( )
+ cy.get('#uploadButton').should('exist').should('have.text', fixtures.name)
+ cy.get('#clearButton').should('not.exist')
+ })
+})
diff --git a/frontend/frontend/cypress/component/GroupAccessComponent.cy.tsx b/frontend/frontend/cypress/component/GroupAccessComponent.cy.tsx
new file mode 100644
index 000000000..ac3c7b3bb
--- /dev/null
+++ b/frontend/frontend/cypress/component/GroupAccessComponent.cy.tsx
@@ -0,0 +1,18 @@
+import { GroupAccessComponent } from '../../src/components/GroupAccessComponent'
+import { BrowserRouter } from 'react-router-dom'
+
+describe('GroupAccessComponent', () => {
+ const mockProps = {
+ assignmentid: 1,
+ courseid: 1,
+ }
+
+ it('toggles group access', () => {
+ cy.mount(
+
+
+
+ )
+ cy.get('[data-cy=groupButton]').should('exist')
+ })
+})
diff --git a/frontend/frontend/cypress/component/Header.cy.tsx b/frontend/frontend/cypress/component/Header.cy.tsx
new file mode 100644
index 000000000..37b7e1829
--- /dev/null
+++ b/frontend/frontend/cypress/component/Header.cy.tsx
@@ -0,0 +1,62 @@
+import { Header } from '../../src/components/Header'
+import { BrowserRouter } from 'react-router-dom'
+import fixtures from '../fixtures/fixtures.json'
+
+describe('Header', () => {
+ const mockProps = {
+ // variant: "not_main" | "editable" | "default";
+ variant: 'not_main',
+ title: fixtures.title,
+ }
+
+ it('renders not main', () => {
+ cy.mount(
+
+
+
+ )
+ cy.get('.MuiToolbar-root').should('exist')
+ cy.get('[data-cy=title]')
+ .should('exist')
+ .should('have.text', fixtures.title)
+ cy.get('#logo').click()
+ cy.get('#editButton').should('not.exist')
+ cy.get('[data-cy=backButton').trigger('mouseover')
+ cy.contains('back').should('be.visible')
+ cy.contains('Logout').should('be.visible')
+ })
+
+ it ('renders editable', () => {
+ mockProps.variant = 'editable'
+ cy.mount(
+
+
+
+ )
+ cy.get('.MuiToolbar-root').should('exist')
+ cy.get('[data-cy=title]')
+ .should('exist')
+ .should('have.text', fixtures.title)
+ cy.get('#logo').click()
+ cy.get('#editButton').should('exist')
+ cy.get('[data-cy=backButton').trigger('mouseover')
+ cy.contains('back').should('be.visible')
+ cy.contains('Logout').should('be.visible')
+ })
+
+ it ('renders default', () => {
+ mockProps.variant = 'default'
+ cy.mount(
+
+
+
+ )
+ cy.get('.MuiToolbar-root').should('exist')
+ cy.get('[data-cy=title]')
+ .should('exist')
+ .should('have.text', fixtures.title)
+ cy.get('#logo').click()
+ cy.get('#editButton').should('not.exist')
+ cy.contains('Logout').should('be.visible')
+ })
+})
diff --git a/frontend/frontend/cypress/component/ItemList.cy.tsx b/frontend/frontend/cypress/component/ItemList.cy.tsx
new file mode 100644
index 000000000..ef0272106
--- /dev/null
+++ b/frontend/frontend/cypress/component/ItemList.cy.tsx
@@ -0,0 +1,50 @@
+import { ItemList } from '../../src/components/ItemList'
+import fixtures from '../fixtures/fixtures.json'
+
+describe('ItemList', () => {
+ const mockProps = {
+ itemList: fixtures.itemList,
+ }
+
+ it('renders', () => {
+ cy.mount( )
+ cy.get('.MuiPaper-root').should('exist')
+ cy.get('.MuiTable-root').should('exist')
+ cy.get('#head').should('exist')
+ cy.get('#head')
+ .find('#opdracht')
+ .should('exist')
+ .should('have.text', 'Opdracht')
+ cy.get('#head')
+ .find('#deadline')
+ .should('exist')
+ .should('have.text', 'Deadline')
+ cy.get('#head')
+ .find('#status')
+ .should('exist')
+ .should('have.text', 'Status')
+ cy.get('#body').should('exist')
+ cy.get('.MuiTableCell-root').should('exist')
+ cy.get('.MuiTableRow-root').should('exist')
+
+ for (let i = 0; i < fixtures.itemList.length; i++) {
+ cy.get(`#${i}`).should('exist')
+ cy.get(`#${i}`)
+ .find('#opdracht')
+ .should('exist')
+ .should('have.text', fixtures.itemList[i].opdracht)
+ cy.get(`#${i}`)
+ .find('#deadline')
+ .should('exist')
+ .should('have.text', fixtures.itemList[i].deadline)
+ cy.get(`#${i}`)
+ .find('#status')
+ .should('exist')
+ .should('have.text', fixtures.itemList[i].status)
+ cy.get(`#${i}`)
+ .find('#score')
+ .should('exist')
+ .should('have.text', fixtures.itemList[i].score)
+ }
+ })
+})
diff --git a/frontend/frontend/cypress/component/LanguageSwitcher.cy.tsx b/frontend/frontend/cypress/component/LanguageSwitcher.cy.tsx
new file mode 100644
index 000000000..73daa1074
--- /dev/null
+++ b/frontend/frontend/cypress/component/LanguageSwitcher.cy.tsx
@@ -0,0 +1,9 @@
+import { LanguageSwitcher } from '../../src/components/LanguageSwitcher'
+
+describe('LanguageSwitcher', () => {
+ it('renders', () => {
+ cy.mount( )
+ cy.get('[data-cy=en]').should('exist').should('have.text', 'En')
+ cy.get('[data-cy=nl]').should('exist').should('have.text', 'Nl')
+ })
+})
diff --git a/frontend/frontend/cypress/component/RestrictionCard.cy.tsx b/frontend/frontend/cypress/component/RestrictionCard.cy.tsx
new file mode 100644
index 000000000..3798ebb73
--- /dev/null
+++ b/frontend/frontend/cypress/component/RestrictionCard.cy.tsx
@@ -0,0 +1,19 @@
+import { RestrictionCard } from '../../src/components/RestrictionCard'
+
+describe('RestrictionCard', () => {
+ const mockProps = {
+ restriction: {
+ script: 'This is a test script',
+ },
+ restrictions: [],
+ setRestrictions: () => {},
+ }
+
+ it('renders', () => {
+ cy.mount( )
+ cy.get('#script')
+ .should('exist')
+ .should('have.text', 'This is a test script')
+ cy.get('#closeButton').should('exist').click()
+ })
+})
diff --git a/frontend/frontend/cypress/component/SubmissionListItemStudentPage.cy.tsx b/frontend/frontend/cypress/component/SubmissionListItemStudentPage.cy.tsx
new file mode 100644
index 000000000..0b4bce209
--- /dev/null
+++ b/frontend/frontend/cypress/component/SubmissionListItemStudentPage.cy.tsx
@@ -0,0 +1,48 @@
+import { SubmissionListItemStudentPage } from '../../src/components/SubmissionListItemStudentPage'
+import { BrowserRouter } from 'react-router-dom'
+
+describe('SubmissionListItemStudentPage', () => {
+ const mockProps = {
+ realId: '1',
+ visualId: 'Logisch programmeren - Opdracht 1',
+ timestamp: new Date().toLocaleString(),
+ status: true,
+ assignment_id: 1,
+ course_id: 1,
+ }
+
+ it('renders correct submission', () => {
+ cy.mount(
+
+
+
+ )
+ cy.get('.MuiListItemButton-root').should('exist')
+ cy.get('[data-cy=visualId]')
+ .should('exist')
+ .should('have.text', mockProps.visualId)
+ cy.get('[data-cy=submissionTimestamp]')
+ .should('exist')
+ .should('have.text', mockProps.timestamp)
+ cy.get('#check').should('exist')
+ cy.get('#cross').should('not.exist')
+ })
+
+ it('renders incorrect submission', () => {
+ mockProps.status = false
+ cy.mount(
+
+
+
+ )
+ cy.get('.MuiListItemButton-root').should('exist')
+ cy.get('[data-cy=visualId]')
+ .should('exist')
+ .should('have.text', mockProps.visualId)
+ cy.get('[data-cy=submissionTimestamp]')
+ .should('exist')
+ .should('have.text', mockProps.timestamp)
+ cy.get('#check').should('not.exist')
+ cy.get('#cross').should('exist')
+ })
+})
diff --git a/frontend/frontend/cypress/component/SubmissionListItemTeacherPage.cy.tsx b/frontend/frontend/cypress/component/SubmissionListItemTeacherPage.cy.tsx
new file mode 100644
index 000000000..b7e4a701c
--- /dev/null
+++ b/frontend/frontend/cypress/component/SubmissionListItemTeacherPage.cy.tsx
@@ -0,0 +1,31 @@
+import { SubmissionListItemTeacherPage } from '../../src/components/SubmissionListItemTeacherPage'
+import { BrowserRouter } from 'react-router-dom'
+
+describe('SubmissionListItemTeacherPage', () => {
+ const mockProps = {
+ group_name: 'group1',
+ group_id: 1,
+ assignment_id: 1,
+ course_id: 1,
+ }
+
+ it('renders correct submission', () => {
+ // The data needs to be fetched from the backend, so we can't check that
+ cy.mount(
+
+
+
+ )
+ cy.get('.MuiListItemButton-root').should('exist')
+ cy.get('[data-cy=submissionTimestamp]').should('exist')
+ cy.get('[data-cy=groupName')
+ .should('exist')
+ .should('have.text', mockProps.group_name)
+ cy.get('[data-cy=submissionScore]').should('exist')
+ cy.get('[data-cy=statusIcon]').should('exist')
+ cy.get('[data-cy=check]').should('not.exist')
+ cy.get('[data-cy=cross]').should('exist')
+ cy.get('[data-cy=downloadIconGray]').should('exist')
+ cy.get('[data-cy=downloadIconColor]').should('not.exist')
+ })
+})
diff --git a/frontend/frontend/cypress/component/TabSwitcher.cy.tsx b/frontend/frontend/cypress/component/TabSwitcher.cy.tsx
new file mode 100644
index 000000000..f19122951
--- /dev/null
+++ b/frontend/frontend/cypress/component/TabSwitcher.cy.tsx
@@ -0,0 +1,42 @@
+import TabSwitcher from '../../src/components/TabSwitcher'
+import { ThemeProvider } from '@mui/system'
+import theme from '../../src/Theme.ts'
+
+describe('TabSwitcher', () => {
+ const mockProps = {
+ titles: ['assignment', 'students', 'groups'],
+ nodes: [node1
, node2
, node3
],
+ }
+
+ it('renders', () => {
+ cy.mount(
+
+
+
+ )
+ // the titles do not render because of internationalization?
+ cy.get('#tab0').should('exist')
+ cy.get('#node0').should('exist').should('be.visible')
+ cy.get('#node1').should('exist').should('not.be.visible')
+ cy.get('#node2').should('exist').should('not.be.visible')
+ cy.contains('node1').should('exist')
+ cy.contains('node2').should('not.exist')
+ cy.contains('node3').should('not.exist')
+
+ cy.get('#tab1').click()
+ cy.get('#node0').should('exist').should('not.be.visible')
+ cy.get('#node1').should('exist').should('be.visible')
+ cy.get('#node2').should('exist').should('not.be.visible')
+ cy.contains('node1').should('not.exist')
+ cy.contains('node2').should('exist')
+ cy.contains('node3').should('not.exist')
+
+ cy.get('#tab2').click()
+ cy.get('#node0').should('exist').should('not.be.visible')
+ cy.get('#node1').should('exist').should('not.be.visible')
+ cy.get('#node2').should('exist').should('be.visible')
+ cy.contains('node1').should('not.exist')
+ cy.contains('node2').should('not.exist')
+ cy.contains('node3').should('exist')
+ })
+})
diff --git a/frontend/frontend/cypress/component/WarningPopup.cy.tsx b/frontend/frontend/cypress/component/WarningPopup.cy.tsx
new file mode 100644
index 000000000..1075eeb88
--- /dev/null
+++ b/frontend/frontend/cypress/component/WarningPopup.cy.tsx
@@ -0,0 +1,35 @@
+import WarningPopup from '../../src/components/WarningPopup'
+import fixtures from '../fixtures/fixtures.json'
+
+describe('WarningPopup', () => {
+ const mockProps = {
+ title: fixtures.title,
+ content: fixtures.warning,
+ buttonName: fixtures.button,
+ open: true,
+ handleClose: () => {},
+ doAction: () => {},
+ }
+
+ it('renders', () => {
+ cy.mount( )
+ // cancel not visible because of internationlization
+ cy.get('#popUpTitle')
+ .should('exist')
+ .should('have.text', fixtures.title)
+ cy.get('#popUpText')
+ .should('exist')
+ .should('have.text', fixtures.warning)
+ cy.get('#cancelButton').should('exist')
+ cy.get('#confirm').should('exist').should('have.text', fixtures.button)
+ })
+
+ it('renders closed', () => {
+ mockProps.open = false
+ cy.mount( )
+ cy.get('#popUpTitle').should('not.exist')
+ cy.get('#popUpText').should('not.exist')
+ cy.get('#cancelButton').should('not.exist')
+ cy.get('#actionButton').should('not.exist')
+ })
+})
diff --git a/frontend/frontend/cypress/e2e/login.cy.ts b/frontend/frontend/cypress/e2e/login.cy.ts
new file mode 100644
index 000000000..1a763d4ba
--- /dev/null
+++ b/frontend/frontend/cypress/e2e/login.cy.ts
@@ -0,0 +1,6 @@
+describe('test', () => {
+ it('passes', () => {
+ cy.visit('http://localhost:5173');
+ cy.contains('Logout').should('be.visible');
+ })
+})
\ No newline at end of file
diff --git a/frontend/frontend/cypress/e2e/student/group.cy.ts b/frontend/frontend/cypress/e2e/student/group.cy.ts
new file mode 100644
index 000000000..87a3088b4
--- /dev/null
+++ b/frontend/frontend/cypress/e2e/student/group.cy.ts
@@ -0,0 +1,24 @@
+describe('student chooses a group and leaves', () => {
+ beforeEach(() => {
+ cy.visit('http://localhost:5173')
+ })
+
+ it('leave', () => {
+ // student should be in a group because of the teacher groups test
+ cy.contains('test course').click()
+ cy.contains('test project').click()
+ cy.get('#group').click()
+ cy.contains('Student Testing').should('exist')
+ cy.get('#leaveGroup').click()
+ })
+
+ it('join', () => {
+ // student should able to choose a group because of the teacher groups test
+ cy.contains('test course').click()
+ cy.contains('test project').click()
+ cy.get('#group').click()
+ cy.contains('No members yet').should('exist')
+ cy.get('#joinGroup').click()
+ })
+
+})
\ No newline at end of file
diff --git a/frontend/frontend/cypress/e2e/student/submission.cy.ts b/frontend/frontend/cypress/e2e/student/submission.cy.ts
new file mode 100644
index 000000000..61c498824
--- /dev/null
+++ b/frontend/frontend/cypress/e2e/student/submission.cy.ts
@@ -0,0 +1,13 @@
+describe('submit files for an assignment', () => {
+ beforeEach(() => {
+ cy.visit('http://localhost:5173')
+ })
+
+ it('submit', () => {
+ cy.contains('test course').click()
+ cy.contains('test project').click()
+ cy.get('#uploadButton').selectFile('cypress/fixtures/test.pdf')
+ cy.contains('test.pdf')
+ cy.get('#submit').click()
+ })
+})
\ No newline at end of file
diff --git a/frontend/frontend/cypress/e2e/teacher/archive.cy.ts b/frontend/frontend/cypress/e2e/teacher/archive.cy.ts
new file mode 100644
index 000000000..ed1c2152d
--- /dev/null
+++ b/frontend/frontend/cypress/e2e/teacher/archive.cy.ts
@@ -0,0 +1,26 @@
+describe('archive projects and courses', () => {
+ beforeEach(() => {
+ cy.visit('http://localhost:5173')
+ })
+
+ it('archive project', () => {
+
+ cy.contains('test course').click()
+ cy.get('#archive').click()
+ cy.get('#confirm').click()
+ // check if the project is archived
+ cy.contains('Archived').click()
+ cy.contains('test project').should('exist')
+ })
+
+ it('archive course', () => {
+
+ cy.get('#archiveButton').first().click()
+ cy.get('#confirm').click()
+ // check if the course is archived
+ cy.contains('Archived').click()
+ cy.contains('test course').should('exist')
+ })
+
+
+})
\ No newline at end of file
diff --git a/frontend/frontend/cypress/e2e/teacher/course.cy.ts b/frontend/frontend/cypress/e2e/teacher/course.cy.ts
new file mode 100644
index 000000000..f5ffa6895
--- /dev/null
+++ b/frontend/frontend/cypress/e2e/teacher/course.cy.ts
@@ -0,0 +1,33 @@
+describe('create and change a course', () => {
+ beforeEach(() => {
+ cy.visit('http://localhost:5173')
+ })
+
+ it('create new course', () => {
+
+ cy.get('#addCourse').click()
+ // fill in the fields
+ cy.get('#courseName').type('test course')
+ cy.get('#save').click()
+ cy.get('#confirm').click()
+ // check if course was added
+ cy.get('#logo').click();
+ cy.contains('test course').should('exist')
+ cy.contains('Students: 0').should('exist')
+ })
+
+ it('alter course', () => {
+
+ cy.contains('test course').click()
+ cy.get('#editButton').click()
+ // add a student
+ cy.get('#uploadStudent').get('#email').type('student@testing.com')
+ cy.get('#uploadStudent').get('#add').click()
+ // save course
+ cy.get('#save').click()
+ cy.get('#confirm').click()
+ // check if the student was added
+ cy.get('#logo').click();
+ cy.contains('Students: 1').should('exist')
+ })
+})
\ No newline at end of file
diff --git a/frontend/frontend/cypress/e2e/teacher/groups.cy.ts b/frontend/frontend/cypress/e2e/teacher/groups.cy.ts
new file mode 100644
index 000000000..26a73d5ae
--- /dev/null
+++ b/frontend/frontend/cypress/e2e/teacher/groups.cy.ts
@@ -0,0 +1,26 @@
+describe('add students to groups or let them choose', () => {
+ beforeEach(() => {
+ cy.visit('http://localhost:5173')
+ })
+
+ it('add', () => {
+ cy.contains('test course').click()
+ cy.contains('test project').click()
+ cy.get('#groupButton').click()
+ cy.get('#randomGroups').click()
+ cy.get('#confirm').click()
+ cy.get('#saveGroups').click()
+ cy.get('#confirm').click()
+ })
+
+ it('choose', () => {
+ cy.contains('test course').click()
+ cy.contains('test project').click()
+ cy.get('#groupButton').click()
+ cy.get('#studentsChoose').click()
+ cy.get('#saveGroups').click()
+ cy.get('#confirm').click()
+ })
+
+
+})
\ No newline at end of file
diff --git a/frontend/frontend/cypress/e2e/teacher/project.cy.ts b/frontend/frontend/cypress/e2e/teacher/project.cy.ts
new file mode 100644
index 000000000..4b0af6ad1
--- /dev/null
+++ b/frontend/frontend/cypress/e2e/teacher/project.cy.ts
@@ -0,0 +1,65 @@
+import { cyan } from "@mui/material/colors"
+
+describe('add and change projects', () => {
+ beforeEach(() => {
+ cy.visit('http://localhost:5173')
+ })
+
+ it('create new project with restrictions', () => {
+
+ cy.contains('test course').click()
+ cy.get('#addProject').click()
+ // fill in the fields
+ cy.get('#projectName').type('test project')
+ cy.get('.MuiInputBase-input').eq(1).type('220820242359')
+ cy.get('.MuiInputBase-input').eq(2).type('290820242359')
+ cy.get('#description').type('This is a test project set up by the e2e tests.')
+ cy.get('#setInVisible').click()
+ cy.get('#setVisible').click()
+ cy.get('#groupSize').clear().type('2')
+ cy.get('#maxScore').clear().type('30')
+ // add an assignment
+ cy.get('#uploadButton').selectFile('cypress/fixtures/test.pdf')
+ cy.contains('test.pdf').should('exist')
+ // add a restriction script
+ cy.get('#addRestrictionButton').click()
+ // create a new script
+ cy.get('#newScript').click()
+ cy.get('#scriptName').type('test script')
+ cy.get('#extension').click()
+ cy.contains('.py').click()
+ cy.contains('.sh').click()
+ cy.get('#scriptContent').type('#!/bin/bash\nvar="Hello World"\necho "$var"')
+ cy.get('#saveScript').click()
+ // confirm in popup
+ cy.get('#confirm').click()
+ // save assignment
+ cy.get('#save').click()
+ // confirm in popup
+ cy.get('#confirm').click()
+ // check if project was added
+ cy.contains('test project').should('exist')
+ cy.contains('22/08/2024 23:59').should('exist').click()
+ cy.contains('This is a test project set up by the e2e tests.').should('exist')
+ })
+
+ it('change existing project', () => {
+
+ cy.contains('test course').click()
+ cy.contains('test project').click()
+ cy.get('#editButton').click()
+ // change some fields
+ cy.get('.MuiInputBase-input').eq(2).type('270820242359')
+ cy.get('#description').type(' This project has been altered.')
+ // save assignment
+ cy.get('#save').click()
+ // confirm in popup
+ cy.get('#confirm').click()
+ // check if project was altered
+ cy.contains('test project').should('exist')
+ cy.contains('This is a test project set up by the e2e tests. This project has been altered.').should('exist')
+ cy.contains('22/08/2024 23:59')
+ cy.contains('27/08/2024 23:59')
+ })
+
+})
\ No newline at end of file
diff --git a/frontend/frontend/cypress/e2e/teacher/score.cy.ts b/frontend/frontend/cypress/e2e/teacher/score.cy.ts
new file mode 100644
index 000000000..36b80b8c4
--- /dev/null
+++ b/frontend/frontend/cypress/e2e/teacher/score.cy.ts
@@ -0,0 +1,16 @@
+describe('assign scores to submissions', () => {
+ beforeEach(() => {
+ cy.visit('http://localhost:5173')
+ })
+
+ it('assign scores', () => {
+ cy.contains('test course').click()
+ cy.contains('test project').click()
+ cy.get('#adjustScores').click()
+ cy.get('#score').type('10')
+ cy.get('#saveScores').click()
+ cy.get('#confirm').click()
+ // check if the score is assigned
+ cy.contains('10/30').should('exist')
+ })
+})
\ No newline at end of file
diff --git a/frontend/frontend/cypress/fixtures/fixtures.json b/frontend/frontend/cypress/fixtures/fixtures.json
new file mode 100644
index 000000000..146052dea
--- /dev/null
+++ b/frontend/frontend/cypress/fixtures/fixtures.json
@@ -0,0 +1,89 @@
+{
+ "id": "1",
+ "course_id": "1",
+ "name": "Test Name",
+ "project": "Test Project",
+ "score": "10",
+ "tooltip": "Test Tooltip",
+ "studenten": ["1", "2", "3"],
+ "groupMemberNames": ["Test Student 1", "Test Student 2", "Test Student 3"],
+ "title": "Test Title",
+ "itemList": [{"opdracht": "test 1", "deadline": "12-12-2025","status": "fail", "score": "8"},
+ {"opdracht": "test 2", "deadline": "12-12-2026","status": "pass", "score": "10"},
+ {"opdracht": "test 3", "deadline": "12-12-2027","status": "excellent", "score": "19"}
+ ],
+ "maxScore": "100",
+ "warning": "This Is A Test Warning",
+ "button": "Test Button",
+ "fullProject": {
+ "project_id": "1",
+ "titel": "Test Project",
+ "beschrijving": "Test Description",
+ "opgave_bestand": "path/to/file",
+ "vak": "1",
+ "deadline": "2025-12-12T00:00:00Z",
+ "extra_deadline": "2025-12-13T00:00:00Z",
+ "max_score": "20",
+ "max_groep_grootte": "1",
+ "student_groep": "false",
+ "zichtbaar": "true",
+ "gearchiveerd": "false"
+ },
+ "scoreGroups": [
+ {
+ "group": {
+ "groep_id": 1,
+ "studenten": ["1", "2"],
+ "project": "1"
+ },
+ "group_number": 1,
+ "lastSubmission": "1",
+ "score": "10"
+ },
+ {
+ "group": {
+ "groep_id": 2,
+ "studenten": ["3"],
+ "project": "1"
+ },
+ "group_number": 2,
+ "lastSubmission": "2",
+ "score": "15"
+ }
+ ],
+ "projects": [
+ {
+ "project_id": 1,
+ "titel": "Project 1",
+ "beschrijving": "Description for Project 1",
+ "opgave_bestand": "path/to/file1",
+ "vak": 1,
+ "max_score": 20,
+ "max_groep_grootte": 1,
+ "deadline": "2022-12-31T00:00:00Z",
+ "extra_deadline": null,
+ "zichtbaar": true,
+ "gearchiveerd": false
+ },
+ {
+ "project_id": 2,
+ "titel": "Project 2",
+ "beschrijving": "Description for Project 2",
+ "opgave_bestand": "path/to/file2",
+ "vak": 2,
+ "max_score": 30,
+ "max_groep_grootte": 2,
+ "deadline": "2023-01-31T00:00:00Z",
+ "extra_deadline": "2023-02-28T00:00:00Z",
+ "zichtbaar": true,
+ "gearchiveerd": false
+ }],
+ "gebruiker": {
+ "user": 1,
+ "is_lesgever": false,
+ "first_name": "Test",
+ "last_name": "User",
+ "email": "test.user@example.com"
+ }
+
+}
diff --git a/frontend/frontend/cypress/fixtures/test.pdf b/frontend/frontend/cypress/fixtures/test.pdf
new file mode 100644
index 000000000..9b30cd439
Binary files /dev/null and b/frontend/frontend/cypress/fixtures/test.pdf differ
diff --git a/frontend/frontend/cypress/page/addChangeAssignmentPage/AddChangeAssignmentPage.cy.tsx b/frontend/frontend/cypress/page/addChangeAssignmentPage/AddChangeAssignmentPage.cy.tsx
new file mode 100644
index 000000000..be946e494
--- /dev/null
+++ b/frontend/frontend/cypress/page/addChangeAssignmentPage/AddChangeAssignmentPage.cy.tsx
@@ -0,0 +1,18 @@
+import { AddChangeAssignmentPage } from '../../../src/pages/addChangeAssignmentPage/AddChangeAssignmentPage' // Check if the file path is correct and if the required module exists in the specified location.
+import { BrowserRouter } from 'react-router-dom'
+
+// This page fetches data from the backend.
+// So as far as the component test is concerned,
+// we can only show what shows up before the fetch.
+// This is why only the loading animation is checked.
+// The rest of the tests are in the integration tests.
+describe('AddChangeAssignmentPage', () => {
+ it('renders assignment name', () => {
+ cy.mount(
+
+
+
+ )
+ cy.get('[data-cy=loadingAnimation]').should('exist')
+ })
+})
diff --git a/frontend/frontend/cypress/page/addChangeAssignmentPage/AddRestrictionButton.cy.tsx b/frontend/frontend/cypress/page/addChangeAssignmentPage/AddRestrictionButton.cy.tsx
new file mode 100644
index 000000000..b2a079d26
--- /dev/null
+++ b/frontend/frontend/cypress/page/addChangeAssignmentPage/AddRestrictionButton.cy.tsx
@@ -0,0 +1,21 @@
+import AddRestrictionButton from '../../../src/pages/addChangeAssignmentPage/AddRestrictionButton'
+import { BrowserRouter } from 'react-router-dom'
+
+describe('AddRestrictionsButton', () => {
+ const mockProps = {
+ restrictions: [],
+ setRestrictions: () => {},
+ }
+
+ it('renders the restrictions add button', () => {
+ cy.mount(
+
+
+
+ )
+ cy.get('#addRestrictionButton').should('exist').click()
+ cy.get('#upload').should('exist')
+ cy.get('#newScript').should('exist')
+ cy.get('#cancelButton').should('exist').click()
+ })
+})
diff --git a/frontend/frontend/cypress/page/addChangeAssignmentPage/RestrictionTemplateUI.cy.tsx b/frontend/frontend/cypress/page/addChangeAssignmentPage/RestrictionTemplateUI.cy.tsx
new file mode 100644
index 000000000..ae29e336c
--- /dev/null
+++ b/frontend/frontend/cypress/page/addChangeAssignmentPage/RestrictionTemplateUI.cy.tsx
@@ -0,0 +1,49 @@
+import RestrictionTemplateUI from '../../../src/pages/addChangeAssignmentPage/RestrictionTemplateUI'
+import { BrowserRouter } from 'react-router-dom'
+
+const code = `
+#!/bin/bash
+
+#@param
+# Vul hieronder de namen van de bestanden in die je in de gezipte folder wilt vinden.
+file_names=("testfile1.txt" "testfile2.txt")
+
+#@param
+# Vul hieronder de naam van het zip bestand waarin je de files wil vinden
+zip_file_name="file.zip"
+
+
+for file_name in "\${file_names[@]}"; do
+ unzip -l $zip_file_name | grep -q $file_name;
+ if [ "$?" == "0" ]
+ then
+ echo "$file_name present: OK"
+ else
+ echo "$file_name not present: FAIL"
+ fi;
+done
+
+`
+describe('RestrictionsDialog', () => {
+ const mockProps = {
+ restrictionCode: code,
+ handleCloseTemplateInterface: () => {},
+ templateFileName: 'files_in_zip.sh',
+ restrictions: [],
+ setRestrictions: () => {},
+ }
+
+ it('renders the dialog', () => {
+ cy.mount(
+
+
+
+ )
+ cy.get('[data-cy=closeIcon]').should('exist')
+ cy.get('[data-cy=templateName]').should(
+ 'have.text',
+ mockProps.templateFileName.split('.')[0]
+ )
+ cy.get('[data-cy=saveButton]').should('have.text', 'save')
+ })
+})
diff --git a/frontend/frontend/cypress/page/addChangeAssignmentPage/RestrictionsDialog.cy.tsx b/frontend/frontend/cypress/page/addChangeAssignmentPage/RestrictionsDialog.cy.tsx
new file mode 100644
index 000000000..4556bbc1c
--- /dev/null
+++ b/frontend/frontend/cypress/page/addChangeAssignmentPage/RestrictionsDialog.cy.tsx
@@ -0,0 +1,31 @@
+import RestrictionsDialog from '../../../src/pages/addChangeAssignmentPage/RestrictionsDialog'
+import { BrowserRouter } from 'react-router-dom'
+
+describe('RestrictionsDialog', () => {
+ const mockProps = {
+ userid: 1,
+ restrictions: [],
+ setRestrictions: () => {},
+ closeParentDialog: () => {},
+ }
+
+ it('renders the dialog', () => {
+ cy.mount(
+
+
+
+ )
+ cy.get('[data-cy=new_scripts_section]').should('exist')
+ cy.get('#upload').should('exist')
+ cy.get('[data-cy=uploadInput]').should('exist')
+ cy.get('#newScript').should('exist')
+ cy.get('[data-cy=existing_scripts_section]').should('exist')
+ cy.get('#newScript').click()
+ cy.get('[data-cy=closeIcon]').should('exist')
+ cy.get('#scriptName').should('exist')
+ cy.get('#extension').should('exist')
+ cy.get('#saveTemplate').should('exist')
+ cy.get('#saveScript').should('exist')
+ cy.get('#scriptContent').should('exist')
+ })
+})
diff --git a/frontend/frontend/cypress/page/assignmentPage/AssignmentPage.cy.tsx b/frontend/frontend/cypress/page/assignmentPage/AssignmentPage.cy.tsx
new file mode 100644
index 000000000..a89222466
--- /dev/null
+++ b/frontend/frontend/cypress/page/assignmentPage/AssignmentPage.cy.tsx
@@ -0,0 +1,18 @@
+import { AssignmentPage } from '../../../src/pages/assignmentPage/AssignmentPage'
+import { BrowserRouter } from 'react-router-dom'
+
+// This page fetches data from the backend.
+// So as far as the component test is concerned,
+// we can only show what shows up before the fetch.
+// This is why only the loading animation is checked.
+// The rest of the tests are in the integration tests.
+describe('AssignmentPage', () => {
+ it('renders', () => {
+ cy.mount(
+
+
+
+ )
+ cy.get('[data-cy=loadingAnimation]').should('exist')
+ })
+})
diff --git a/frontend/frontend/cypress/page/groupsPage/ChooseGroup.cy.tsx b/frontend/frontend/cypress/page/groupsPage/ChooseGroup.cy.tsx
new file mode 100644
index 000000000..41a598d66
--- /dev/null
+++ b/frontend/frontend/cypress/page/groupsPage/ChooseGroup.cy.tsx
@@ -0,0 +1,18 @@
+import { ChooseGroup } from '../../../src/pages/groupsPage/ChooseGroup'
+import { BrowserRouter } from 'react-router-dom'
+
+// This page fetches data from the backend.
+// So as far as the component test is concerned,
+// we can only show what shows up before the fetch.
+// This is why only the loading animation is checked.
+// The rest of the tests are in the integration tests.
+describe('ChooseGroup', () => {
+ it('renders', () => {
+ cy.mount(
+
+
+
+ )
+ cy.get('[data-cy=loadingAnimation]').should('exist')
+ })
+})
diff --git a/frontend/frontend/cypress/page/groupsPage/GroupsPage.cy.tsx b/frontend/frontend/cypress/page/groupsPage/GroupsPage.cy.tsx
new file mode 100644
index 000000000..94469dbab
--- /dev/null
+++ b/frontend/frontend/cypress/page/groupsPage/GroupsPage.cy.tsx
@@ -0,0 +1,18 @@
+import { GroupsPage } from '../../../src/pages/groupsPage/GroupsPage'
+import { BrowserRouter } from 'react-router-dom'
+
+// This page fetches data from the backend.
+// So as far as the component test is concerned,
+// we can only show what shows up before the fetch.
+// This is why only the loading animation is checked.
+// The rest of the tests are in the integration tests.
+describe('GroupsPage', () => {
+ it('renders', () => {
+ cy.mount(
+
+
+
+ )
+ cy.get('[data-cy=loadingAnimation]').should('exist')
+ })
+})
diff --git a/frontend/frontend/cypress/page/loginPage/LoginPage.cy.tsx b/frontend/frontend/cypress/page/loginPage/LoginPage.cy.tsx
new file mode 100644
index 000000000..caf61ccab
--- /dev/null
+++ b/frontend/frontend/cypress/page/loginPage/LoginPage.cy.tsx
@@ -0,0 +1,15 @@
+import { LoginPage } from '../../../src/pages/loginPage/LoginPage.tsx'
+import { BrowserRouter } from 'react-router-dom'
+
+describe('LoginPage', () => {
+ it('renders', () => {
+ cy.mount(
+
+
+
+ )
+ cy.get('#logo').should('exist')
+ cy.get('[data-cy=loginButton]').should('exist')
+ cy.get('[data-cy=logoDuif]').should('exist')
+ })
+})
diff --git a/frontend/frontend/cypress/page/mainPage/ArchivedView.cy.tsx b/frontend/frontend/cypress/page/mainPage/ArchivedView.cy.tsx
new file mode 100644
index 000000000..8364bf685
--- /dev/null
+++ b/frontend/frontend/cypress/page/mainPage/ArchivedView.cy.tsx
@@ -0,0 +1,29 @@
+import { ArchivedView } from '../../../src/pages/mainPage/ArchivedView';
+import {BrowserRouter} from 'react-router-dom';
+
+describe('ArchivedView', () => {
+
+ const mockProps = {
+ isStudent: false,
+ archivedCourses: [],
+ pinnedCourses: [1],
+ pinCourse: () => {},
+ };
+
+ it('renders no archived courses', () => {
+ cy.mount( );
+ cy.get('div').should('exist');
+ cy.contains('Project').should('not.exist');
+ cy.contains('Deadline').should('not.exist');
+ });
+
+ it('renders archived courses', () => {
+ mockProps.archivedCourses = [{vak_id: 1},{vak_id: 2},];
+ cy.mount( );
+ cy.get('div').should('exist');
+ cy.contains('Project').should('exist');
+ cy.contains('Deadline').should('exist');
+ cy.get('.MuiPaper-root').should('have.length', mockProps.archivedCourses.length);
+ });
+
+});
\ No newline at end of file
diff --git a/frontend/frontend/cypress/page/mainPage/CoursesView.cy.tsx b/frontend/frontend/cypress/page/mainPage/CoursesView.cy.tsx
new file mode 100644
index 000000000..48ffa80f8
--- /dev/null
+++ b/frontend/frontend/cypress/page/mainPage/CoursesView.cy.tsx
@@ -0,0 +1,17 @@
+import { CoursesView } from '../../../src/pages/mainPage/CoursesView';
+import {BrowserRouter} from 'react-router-dom';
+
+describe('CoursesView', () => {
+
+ const mockProps = {
+ isStudent: false,
+ activecourses: []
+ };
+
+ it('renders the CoursesView', () => {
+ cy.mount( );
+ // Het enige dat in deze pagina gegarandeerd is is een plus button voor de vakken.
+ cy.get('button').should('exist');
+ });
+
+});
\ No newline at end of file
diff --git a/frontend/frontend/cypress/page/scoresPage/ProjectScoresPage.cy.tsx b/frontend/frontend/cypress/page/scoresPage/ProjectScoresPage.cy.tsx
new file mode 100644
index 000000000..9251b0aaa
--- /dev/null
+++ b/frontend/frontend/cypress/page/scoresPage/ProjectScoresPage.cy.tsx
@@ -0,0 +1,18 @@
+import { ProjectScoresPage } from '../../../src/pages/scoresPage/ProjectScoresPage'
+import { BrowserRouter } from 'react-router-dom'
+
+// This page fetches data from the backend.
+// So as far as the component test is concerned,
+// we can only show what shows up before the fetch.
+// This is why only the loading animation is checked.
+// The rest of the tests are in the integration tests.
+describe('ProjectScoresPage', () => {
+ it('renders', () => {
+ cy.mount(
+
+
+
+ )
+ cy.get('[data-cy=loadingAnimation]').should('exist')
+ })
+})
diff --git a/frontend/frontend/cypress/page/scoresPage/StudentScoreListItem.cy.tsx b/frontend/frontend/cypress/page/scoresPage/StudentScoreListItem.cy.tsx
new file mode 100644
index 000000000..1c440185a
--- /dev/null
+++ b/frontend/frontend/cypress/page/scoresPage/StudentScoreListItem.cy.tsx
@@ -0,0 +1,24 @@
+import { StudentScoreListItem } from '../../../src/pages/scoresPage/StudentScoreListItem'
+import fixtures from '../../fixtures/fixtures.json'
+
+// This page fetches data from the backend.
+// So as far as the component test is concerned,
+// we can only show what shows up before the fetch.
+// This is why only the loading animation is checked.
+// The rest of the tests are in the integration tests.
+describe('StudentScoreListItem', () => {
+ const mockProps = {
+ key: fixtures.id,
+ groupNumber: fixtures.id,
+ studenten: fixtures.studenten,
+ lastSubmission: undefined,
+ score: fixtures.score,
+ maxScore: fixtures.maxScore,
+ changeScore: () => {},
+ }
+
+ it('renders', () => {
+ cy.mount( )
+ cy.get('[data-cy=loadingAnimation]').should('exist')
+ })
+})
\ No newline at end of file
diff --git a/frontend/frontend/cypress/page/scoresPage/studentsView.cy.tsx b/frontend/frontend/cypress/page/scoresPage/studentsView.cy.tsx
new file mode 100644
index 000000000..e9d39ac26
--- /dev/null
+++ b/frontend/frontend/cypress/page/scoresPage/studentsView.cy.tsx
@@ -0,0 +1,25 @@
+import { StudentsView } from '../../../src/pages/scoresPage/StudentsView'
+import { BrowserRouter } from 'react-router-dom'
+import fixtures from '../../fixtures/fixtures.json'
+import { t } from 'i18next'
+
+describe('StudentsView', () => {
+ const mockProps = {
+ project: fixtures.fullProject,
+ groepen: fixtures.scoreGroups,
+ setGroepen: () => {},
+ changeScore: () => {},
+ }
+
+ it('renders', () => {
+ cy.mount(
+
+
+
+ )
+ //cy.get('[data-cy=groupHeader]').should('have.text', t('group'))
+ //cy.get('[data-cy=timeHeader]').should('have.text', t('time'))
+ cy.get('[data-cy=scoreHeader]').should('have.text', 'Score')
+ cy.get('[data-cy=downloadHeader]').should('have.text', 'Download')
+ })
+})
diff --git a/frontend/frontend/cypress/page/subjectsPage/AddChangeSubjectsPage.cy.tsx b/frontend/frontend/cypress/page/subjectsPage/AddChangeSubjectsPage.cy.tsx
new file mode 100644
index 000000000..823033ad2
--- /dev/null
+++ b/frontend/frontend/cypress/page/subjectsPage/AddChangeSubjectsPage.cy.tsx
@@ -0,0 +1,18 @@
+import { AddChangeSubjectPage } from '../../../src/pages/subjectsPage/AddChangeSubjectPage'
+import { BrowserRouter } from 'react-router-dom'
+
+// This page fetches data from the backend.
+// So as far as the component test is concerned,
+// we can only show what shows up before the fetch.
+// This is why only the loading animation is checked.
+// The rest of the tests are in the integration tests.
+describe('AddChangeSubjectsPage', () => {
+ it('renders', () => {
+ cy.mount(
+
+
+
+ )
+ cy.get('[data-cy=loadingAnimation]').should('exist')
+ })
+})
diff --git a/frontend/frontend/cypress/page/subjectsPage/AssignmentListItemSubjectsPage.cy.tsx b/frontend/frontend/cypress/page/subjectsPage/AssignmentListItemSubjectsPage.cy.tsx
new file mode 100644
index 000000000..704f2d0b0
--- /dev/null
+++ b/frontend/frontend/cypress/page/subjectsPage/AssignmentListItemSubjectsPage.cy.tsx
@@ -0,0 +1,59 @@
+import { AssignmentListItemSubjectsPage } from '../../../src/pages/subjectsPage/AssignmentListItemSubjectsPage'
+import { BrowserRouter } from 'react-router-dom'
+import fixtures from '../../fixtures/fixtures.json'
+import dayjs from 'dayjs'
+
+describe('AssignmentListItemSubjectsPage', () => {
+ const mockProps = {
+ projectName: fixtures.project,
+ dueDate: fixtures.fullProject.deadline,
+ submissions: 3,
+ score: { score: fixtures.fullProject.max_score },
+ maxScore: fixtures.fullProject.max_score,
+ isStudent: true,
+ archived: false,
+ visible: true,
+ deleteEvent: () => {},
+ archiveEvent: () => {},
+ visibilityEvent: () => {},
+ courseId: fixtures.id,
+ assignmentId: fixtures.id,
+ }
+
+ it('renders as student', () => {
+ cy.mount(
+
+
+
+ )
+ cy.get(`#${mockProps.projectName.replace(/\s/g, '')}`)
+ .should('exist')
+ .should('contain', mockProps.projectName)
+ cy.get('[data-cy=deadline]')
+ .should('exist')
+ .should(
+ 'contain',
+ dayjs(mockProps.dueDate).format('DD/MM/YYYY HH:mm')
+ )
+ cy.get('[data-cy=score]')
+ .should('exist')
+ .should('contain', mockProps.score.score + '/' + mockProps.maxScore)
+ cy.get('[data-cy=submissions]')
+ .should('exist')
+ .should('contain', mockProps.submissions)
+ });
+
+ it('renders as teacher', () => {
+ mockProps.isStudent = false;
+ cy.mount( );
+ cy.get(`[data-cy=teacherProjectName]`)
+ .should('exist')
+ .should('contain', mockProps.projectName)
+ cy.get('[data-cy=teacherDeadline]')
+ .should('exist')
+ .should(
+ 'contain',
+ dayjs(mockProps.dueDate).format('DD/MM/YYYY HH:mm')
+ )
+ })
+})
diff --git a/frontend/frontend/cypress/page/subjectsPage/ProjectsView.cy.tsx b/frontend/frontend/cypress/page/subjectsPage/ProjectsView.cy.tsx
new file mode 100644
index 000000000..785a1c1b8
--- /dev/null
+++ b/frontend/frontend/cypress/page/subjectsPage/ProjectsView.cy.tsx
@@ -0,0 +1,29 @@
+import { ProjectsView } from '../../../src/pages/subjectsPage/ProjectsView'
+import { BrowserRouter } from 'react-router-dom'
+import fixtures from '../../fixtures/fixtures.json'
+
+describe('ProjectsView', () => {
+ const mockProps = {
+ gebruiker: fixtures.gebruiker,
+ archived: false,
+ assignments: fixtures.projects,
+ deleteAssignment: () => {},
+ archiveAssignment: () => {},
+ changeVisibilityAssignment: () => {},
+ courseId: '1',
+ }
+
+ it('renders', () => {
+ cy.mount(
+
+
+
+ )
+ for (const assignment of mockProps.assignments) {
+ cy.get('#' + assignment.titel.replace(/\s/g, '')).contains(
+ assignment.titel
+ )
+ // dit is een assignmentListItem en wordt in een andere test al getest
+ }
+ })
+})
diff --git a/frontend/frontend/cypress/page/subjectsPage/StudentPopUp.cy.tsx b/frontend/frontend/cypress/page/subjectsPage/StudentPopUp.cy.tsx
new file mode 100644
index 000000000..09df8770f
--- /dev/null
+++ b/frontend/frontend/cypress/page/subjectsPage/StudentPopUp.cy.tsx
@@ -0,0 +1,30 @@
+import StudentPopUp from '../../../src/pages/subjectsPage/StudentPopUp'
+import { BrowserRouter } from 'react-router-dom'
+import fixtures from '../../fixtures/fixtures.json'
+import { t } from 'i18next'
+
+describe('StudentPopUp', () => {
+ const mockProps = {
+ students: [],
+ text: 'test popup text',
+ noGroup: false,
+ }
+
+ it('renders', () => {
+ cy.mount(
+
+
+
+ )
+ cy.get('[data-cy=secondaryButton]').should('exist')
+ cy.get('#scroll-dialog-title').should('not.exist')
+ //cy.get('#scroll-dialog-title').should('have.text', mockProps.text)
+ //cy.get('[data-cy=noGroup]').should('exist')
+ //cy.get('[data-cy=noGroup]').should('have.text', t('noGroup'))
+ //cy.get('[data-cy=contactTeacher]').should('exist')
+ //cy.get('[data-cy=contactTeacher]').should(
+ // 'have.text',
+ // t('contactTeacher')
+ //)
+ })
+})
diff --git a/frontend/frontend/cypress/page/submissionPage/SubmissionPage.cy.tsx b/frontend/frontend/cypress/page/submissionPage/SubmissionPage.cy.tsx
new file mode 100644
index 000000000..fb4164254
--- /dev/null
+++ b/frontend/frontend/cypress/page/submissionPage/SubmissionPage.cy.tsx
@@ -0,0 +1,21 @@
+import { SubmissionPage } from '../../../src/pages/submissionPage/SubmissionPage'
+import { BrowserRouter } from 'react-router-dom'
+import { t } from 'i18next'
+
+describe('SubmissionPage', () => {
+ it('renders', () => {
+ cy.mount(
+
+
+
+ )
+ cy.get('[data-cy=header]').should('exist')
+ cy.get('[data-cy=assignmentTitle]').should('exist')
+ cy.get('[data-cy=projectBeschrijving]').should('exist')
+ cy.get('[data-cy=filename]').should('exist')
+ cy.get('[data-cy=downloadSubmissionButton]').should('exist')
+ cy.get('[data-cy=restrictionsTitle]').should('exist')
+ cy.get('[data-cy=statusTitle]').should('exist')
+ cy.get('[data-cy=resultTitle]').should('exist')
+ })
+})
diff --git a/frontend/frontend/cypress/support/commands.ts b/frontend/frontend/cypress/support/commands.ts
new file mode 100644
index 000000000..698b01a42
--- /dev/null
+++ b/frontend/frontend/cypress/support/commands.ts
@@ -0,0 +1,37 @@
+///
+// ***********************************************
+// This example commands.ts shows you how to
+// create various custom commands and overwrite
+// existing commands.
+//
+// For more comprehensive examples of custom
+// commands please read more here:
+// https://on.cypress.io/custom-commands
+// ***********************************************
+//
+//
+// -- This is a parent command --
+// Cypress.Commands.add('login', (email, password) => { ... })
+//
+//
+// -- This is a child command --
+// Cypress.Commands.add('drag', { prevSubject: 'element'}, (subject, options) => { ... })
+//
+//
+// -- This is a dual command --
+// Cypress.Commands.add('dismiss', { prevSubject: 'optional'}, (subject, options) => { ... })
+//
+//
+// -- This will overwrite an existing command --
+// Cypress.Commands.overwrite('visit', (originalFn, url, options) => { ... })
+//
+// declare global {
+// namespace Cypress {
+// interface Chainable {
+// login(email: string, password: string): Chainable
+// drag(subject: string, options?: Partial): Chainable
+// dismiss(subject: string, options?: Partial): Chainable
+// visit(originalFn: CommandOriginalFn, url: string, options: Partial): Chainable
+// }
+// }
+// }
\ No newline at end of file
diff --git a/frontend/frontend/cypress/support/component-index.html b/frontend/frontend/cypress/support/component-index.html
new file mode 100644
index 000000000..ac6e79fd8
--- /dev/null
+++ b/frontend/frontend/cypress/support/component-index.html
@@ -0,0 +1,12 @@
+
+
+
+
+
+
+ Components App
+
+
+
+
+
\ No newline at end of file
diff --git a/frontend/frontend/cypress/support/component.ts b/frontend/frontend/cypress/support/component.ts
new file mode 100644
index 000000000..37f59edbe
--- /dev/null
+++ b/frontend/frontend/cypress/support/component.ts
@@ -0,0 +1,39 @@
+// ***********************************************************
+// This example support/component.ts is processed and
+// loaded automatically before your test files.
+//
+// This is a great place to put global configuration and
+// behavior that modifies Cypress.
+//
+// You can change the location of this file or turn off
+// automatically serving support files with the
+// 'supportFile' configuration option.
+//
+// You can read more here:
+// https://on.cypress.io/configuration
+// ***********************************************************
+
+// Import commands.js using ES2015 syntax:
+import './commands'
+
+// Alternatively you can use CommonJS syntax:
+// require('./commands')
+
+import { mount } from 'cypress/react18'
+
+// Augment the Cypress namespace to include type definitions for
+// your custom command.
+// Alternatively, can be defined in cypress/support/component.d.ts
+// with a at the top of your spec.
+declare global {
+ namespace Cypress {
+ interface Chainable {
+ mount: typeof mount
+ }
+ }
+}
+
+Cypress.Commands.add('mount', mount)
+
+// Example use:
+// cy.mount( )
\ No newline at end of file
diff --git a/frontend/frontend/cypress/support/e2e.ts b/frontend/frontend/cypress/support/e2e.ts
new file mode 100644
index 000000000..ed5730de1
--- /dev/null
+++ b/frontend/frontend/cypress/support/e2e.ts
@@ -0,0 +1,20 @@
+// ***********************************************************
+// This example support/e2e.ts is processed and
+// loaded automatically before your test files.
+//
+// This is a great place to put global configuration and
+// behavior that modifies Cypress.
+//
+// You can change the location of this file or turn off
+// automatically serving support files with the
+// 'supportFile' configuration option.
+//
+// You can read more here:
+// https://on.cypress.io/configuration
+// ***********************************************************
+
+// Import commands.js using ES2015 syntax:
+import './commands'
+
+// Alternatively you can use CommonJS syntax:
+// require('./commands')
diff --git a/frontend/frontend/package-lock.json b/frontend/frontend/package-lock.json
index 4a41e9956..ddde9ac7d 100644
--- a/frontend/frontend/package-lock.json
+++ b/frontend/frontend/package-lock.json
@@ -1,6 +1,10936 @@
{
- "name": "frontend",
- "lockfileVersion": 2,
- "requires": true,
- "packages": {}
-}
\ No newline at end of file
+ "name": "frontend",
+ "version": "0.0.0",
+ "lockfileVersion": 2,
+ "requires": true,
+ "packages": {
+ "": {
+ "name": "frontend",
+ "version": "0.0.0",
+ "dependencies": {
+ "@azure/msal-browser": "^3.11.0",
+ "@azure/msal-react": "^2.0.13",
+ "@emotion/react": "^11.11.4",
+ "@emotion/styled": "^11.11.0",
+ "@fontsource/roboto": "^5.0.8",
+ "@mui/icons-material": "^5.15.11",
+ "@mui/material": "^5.15.12",
+ "@mui/x-date-pickers": "^6.19.6",
+ "axios": "^1.6.7",
+ "dayjs": "^1.11.10",
+ "husky": "^9.0.11",
+ "i18next": "^23.10.0",
+ "i18next-browser-languagedetector": "^7.2.0",
+ "i18next-http-backend": "^2.5.0",
+ "jszip": "^3.10.1",
+ "localforage": "^1.10.0",
+ "match-sorter": "^6.3.4",
+ "papaparse": "^5.4.1",
+ "react": "^18.2.0",
+ "react-dom": "^18.2.0",
+ "react-helmet": "^6.1.0",
+ "react-helmet-async": "^2.0.4",
+ "react-i18next": "^14.0.5",
+ "react-router-dom": "^6.22.2"
+ },
+ "devDependencies": {
+ "@types/papaparse": "^5.3.14",
+ "@types/react": "^18.2.56",
+ "@types/react-dom": "^18.2.19",
+ "@types/react-helmet": "^6.1.11",
+ "@typescript-eslint/eslint-plugin": "^7.0.2",
+ "@typescript-eslint/parser": "^7.0.2",
+ "@vitejs/plugin-react": "^4.2.1",
+ "cypress": "^13.9.0",
+ "eslint": "^8.56.0",
+ "eslint-config-prettier": "^9.1.0",
+ "eslint-plugin-prettier": "^5.1.3",
+ "eslint-plugin-react-hooks": "^4.6.0",
+ "eslint-plugin-react-refresh": "^0.4.5",
+ "husky": "^7.0.0",
+ "prettier": "^3.2.5",
+ "typescript": "^5.2.2",
+ "vite": "^5.2.11"
+ }
+ },
+ "node_modules/@ampproject/remapping": {
+ "version": "2.3.0",
+ "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.3.0.tgz",
+ "integrity": "sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==",
+ "dev": true,
+ "dependencies": {
+ "@jridgewell/gen-mapping": "^0.3.5",
+ "@jridgewell/trace-mapping": "^0.3.24"
+ },
+ "engines": {
+ "node": ">=6.0.0"
+ }
+ },
+ "node_modules/@azure/msal-browser": {
+ "version": "3.13.0",
+ "resolved": "https://registry.npmjs.org/@azure/msal-browser/-/msal-browser-3.13.0.tgz",
+ "integrity": "sha512-fD906nmJei3yE7la6DZTdUtXKvpwzJURkfsiz9747Icv4pit77cegSm6prJTKLQ1fw4iiZzrrWwxnhMLrTf5gQ==",
+ "dependencies": {
+ "@azure/msal-common": "14.9.0"
+ },
+ "engines": {
+ "node": ">=0.8.0"
+ }
+ },
+ "node_modules/@azure/msal-common": {
+ "version": "14.9.0",
+ "resolved": "https://registry.npmjs.org/@azure/msal-common/-/msal-common-14.9.0.tgz",
+ "integrity": "sha512-yzBPRlWPnTBeixxLNI3BBIgF5/bHpbhoRVuuDBnYjCyWRavaPUsKAHUDYLqpGkBLDciA6TCc6GOxN4/S3WiSxg==",
+ "engines": {
+ "node": ">=0.8.0"
+ }
+ },
+ "node_modules/@azure/msal-react": {
+ "version": "2.0.15",
+ "resolved": "https://registry.npmjs.org/@azure/msal-react/-/msal-react-2.0.15.tgz",
+ "integrity": "sha512-WtZGPxA6rWqGhovmWPUXjUyKTp/TUa4Oekk6FSa9ldCayd1QzYT9XcoAEqA0EfT0Fx12KiNvJyDDKckBU6J/+Q==",
+ "engines": {
+ "node": ">=10"
+ },
+ "peerDependencies": {
+ "@azure/msal-browser": "^3.13.0",
+ "react": "^16.8.0 || ^17 || ^18"
+ }
+ },
+ "node_modules/@babel/code-frame": {
+ "version": "7.24.2",
+ "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.24.2.tgz",
+ "integrity": "sha512-y5+tLQyV8pg3fsiln67BVLD1P13Eg4lh5RW9mF0zUuvLrv9uIQ4MCL+CRT+FTsBlBjcIan6PGsLcBN0m3ClUyQ==",
+ "dependencies": {
+ "@babel/highlight": "^7.24.2",
+ "picocolors": "^1.0.0"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/compat-data": {
+ "version": "7.24.4",
+ "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.24.4.tgz",
+ "integrity": "sha512-vg8Gih2MLK+kOkHJp4gBEIkyaIi00jgWot2D9QOmmfLC8jINSOzmCLta6Bvz/JSBCqnegV0L80jhxkol5GWNfQ==",
+ "dev": true,
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/core": {
+ "version": "7.24.5",
+ "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.24.5.tgz",
+ "integrity": "sha512-tVQRucExLQ02Boi4vdPp49svNGcfL2GhdTCT9aldhXgCJVAI21EtRfBettiuLUwce/7r6bFdgs6JFkcdTiFttA==",
+ "dev": true,
+ "dependencies": {
+ "@ampproject/remapping": "^2.2.0",
+ "@babel/code-frame": "^7.24.2",
+ "@babel/generator": "^7.24.5",
+ "@babel/helper-compilation-targets": "^7.23.6",
+ "@babel/helper-module-transforms": "^7.24.5",
+ "@babel/helpers": "^7.24.5",
+ "@babel/parser": "^7.24.5",
+ "@babel/template": "^7.24.0",
+ "@babel/traverse": "^7.24.5",
+ "@babel/types": "^7.24.5",
+ "convert-source-map": "^2.0.0",
+ "debug": "^4.1.0",
+ "gensync": "^1.0.0-beta.2",
+ "json5": "^2.2.3",
+ "semver": "^6.3.1"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/babel"
+ }
+ },
+ "node_modules/@babel/core/node_modules/convert-source-map": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz",
+ "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==",
+ "dev": true
+ },
+ "node_modules/@babel/core/node_modules/semver": {
+ "version": "6.3.1",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz",
+ "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==",
+ "dev": true,
+ "bin": {
+ "semver": "bin/semver.js"
+ }
+ },
+ "node_modules/@babel/generator": {
+ "version": "7.24.5",
+ "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.24.5.tgz",
+ "integrity": "sha512-x32i4hEXvr+iI0NEoEfDKzlemF8AmtOP8CcrRaEcpzysWuoEb1KknpcvMsHKPONoKZiDuItklgWhB18xEhr9PA==",
+ "dev": true,
+ "dependencies": {
+ "@babel/types": "^7.24.5",
+ "@jridgewell/gen-mapping": "^0.3.5",
+ "@jridgewell/trace-mapping": "^0.3.25",
+ "jsesc": "^2.5.1"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/helper-compilation-targets": {
+ "version": "7.23.6",
+ "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.23.6.tgz",
+ "integrity": "sha512-9JB548GZoQVmzrFgp8o7KxdgkTGm6xs9DW0o/Pim72UDjzr5ObUQ6ZzYPqA+g9OTS2bBQoctLJrky0RDCAWRgQ==",
+ "dev": true,
+ "dependencies": {
+ "@babel/compat-data": "^7.23.5",
+ "@babel/helper-validator-option": "^7.23.5",
+ "browserslist": "^4.22.2",
+ "lru-cache": "^5.1.1",
+ "semver": "^6.3.1"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/helper-compilation-targets/node_modules/semver": {
+ "version": "6.3.1",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz",
+ "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==",
+ "dev": true,
+ "bin": {
+ "semver": "bin/semver.js"
+ }
+ },
+ "node_modules/@babel/helper-environment-visitor": {
+ "version": "7.22.20",
+ "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.22.20.tgz",
+ "integrity": "sha512-zfedSIzFhat/gFhWfHtgWvlec0nqB9YEIVrpuwjruLlXfUSnA8cJB0miHKwqDnQ7d32aKo2xt88/xZptwxbfhA==",
+ "dev": true,
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/helper-function-name": {
+ "version": "7.23.0",
+ "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.23.0.tgz",
+ "integrity": "sha512-OErEqsrxjZTJciZ4Oo+eoZqeW9UIiOcuYKRJA4ZAgV9myA+pOXhhmpfNCKjEH/auVfEYVFJ6y1Tc4r0eIApqiw==",
+ "dev": true,
+ "dependencies": {
+ "@babel/template": "^7.22.15",
+ "@babel/types": "^7.23.0"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/helper-hoist-variables": {
+ "version": "7.22.5",
+ "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.22.5.tgz",
+ "integrity": "sha512-wGjk9QZVzvknA6yKIUURb8zY3grXCcOZt+/7Wcy8O2uctxhplmUPkOdlgoNhmdVee2c92JXbf1xpMtVNbfoxRw==",
+ "dev": true,
+ "dependencies": {
+ "@babel/types": "^7.22.5"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/helper-module-imports": {
+ "version": "7.24.3",
+ "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.24.3.tgz",
+ "integrity": "sha512-viKb0F9f2s0BCS22QSF308z/+1YWKV/76mwt61NBzS5izMzDPwdq1pTrzf+Li3npBWX9KdQbkeCt1jSAM7lZqg==",
+ "dependencies": {
+ "@babel/types": "^7.24.0"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/helper-module-transforms": {
+ "version": "7.24.5",
+ "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.24.5.tgz",
+ "integrity": "sha512-9GxeY8c2d2mdQUP1Dye0ks3VDyIMS98kt/llQ2nUId8IsWqTF0l1LkSX0/uP7l7MCDrzXS009Hyhe2gzTiGW8A==",
+ "dev": true,
+ "dependencies": {
+ "@babel/helper-environment-visitor": "^7.22.20",
+ "@babel/helper-module-imports": "^7.24.3",
+ "@babel/helper-simple-access": "^7.24.5",
+ "@babel/helper-split-export-declaration": "^7.24.5",
+ "@babel/helper-validator-identifier": "^7.24.5"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0"
+ }
+ },
+ "node_modules/@babel/helper-plugin-utils": {
+ "version": "7.24.5",
+ "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.24.5.tgz",
+ "integrity": "sha512-xjNLDopRzW2o6ba0gKbkZq5YWEBaK3PCyTOY1K2P/O07LGMhMqlMXPxwN4S5/RhWuCobT8z0jrlKGlYmeR1OhQ==",
+ "dev": true,
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/helper-simple-access": {
+ "version": "7.24.5",
+ "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.24.5.tgz",
+ "integrity": "sha512-uH3Hmf5q5n7n8mz7arjUlDOCbttY/DW4DYhE6FUsjKJ/oYC1kQQUvwEQWxRwUpX9qQKRXeqLwWxrqilMrf32sQ==",
+ "dev": true,
+ "dependencies": {
+ "@babel/types": "^7.24.5"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/helper-split-export-declaration": {
+ "version": "7.24.5",
+ "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.24.5.tgz",
+ "integrity": "sha512-5CHncttXohrHk8GWOFCcCl4oRD9fKosWlIRgWm4ql9VYioKm52Mk2xsmoohvm7f3JoiLSM5ZgJuRaf5QZZYd3Q==",
+ "dev": true,
+ "dependencies": {
+ "@babel/types": "^7.24.5"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/helper-string-parser": {
+ "version": "7.24.1",
+ "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.24.1.tgz",
+ "integrity": "sha512-2ofRCjnnA9y+wk8b9IAREroeUP02KHp431N2mhKniy2yKIDKpbrHv9eXwm8cBeWQYcJmzv5qKCu65P47eCF7CQ==",
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/helper-validator-identifier": {
+ "version": "7.24.5",
+ "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.24.5.tgz",
+ "integrity": "sha512-3q93SSKX2TWCG30M2G2kwaKeTYgEUp5Snjuj8qm729SObL6nbtUldAi37qbxkD5gg3xnBio+f9nqpSepGZMvxA==",
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/helper-validator-option": {
+ "version": "7.23.5",
+ "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.23.5.tgz",
+ "integrity": "sha512-85ttAOMLsr53VgXkTbkx8oA6YTfT4q7/HzXSLEYmjcSTJPMPQtvq1BD79Byep5xMUYbGRzEpDsjUf3dyp54IKw==",
+ "dev": true,
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/helpers": {
+ "version": "7.24.5",
+ "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.24.5.tgz",
+ "integrity": "sha512-CiQmBMMpMQHwM5m01YnrM6imUG1ebgYJ+fAIW4FZe6m4qHTPaRHti+R8cggAwkdz4oXhtO4/K9JWlh+8hIfR2Q==",
+ "dev": true,
+ "dependencies": {
+ "@babel/template": "^7.24.0",
+ "@babel/traverse": "^7.24.5",
+ "@babel/types": "^7.24.5"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/highlight": {
+ "version": "7.24.5",
+ "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.24.5.tgz",
+ "integrity": "sha512-8lLmua6AVh/8SLJRRVD6V8p73Hir9w5mJrhE+IPpILG31KKlI9iz5zmBYKcWPS59qSfgP9RaSBQSHHE81WKuEw==",
+ "dependencies": {
+ "@babel/helper-validator-identifier": "^7.24.5",
+ "chalk": "^2.4.2",
+ "js-tokens": "^4.0.0",
+ "picocolors": "^1.0.0"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/parser": {
+ "version": "7.24.5",
+ "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.24.5.tgz",
+ "integrity": "sha512-EOv5IK8arwh3LI47dz1b0tKUb/1uhHAnHJOrjgtQMIpu1uXd9mlFrJg9IUgGUgZ41Ch0K8REPTYpO7B76b4vJg==",
+ "dev": true,
+ "bin": {
+ "parser": "bin/babel-parser.js"
+ },
+ "engines": {
+ "node": ">=6.0.0"
+ }
+ },
+ "node_modules/@babel/plugin-transform-react-jsx-self": {
+ "version": "7.24.5",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-self/-/plugin-transform-react-jsx-self-7.24.5.tgz",
+ "integrity": "sha512-RtCJoUO2oYrYwFPtR1/jkoBEcFuI1ae9a9IMxeyAVa3a1Ap4AnxmyIKG2b2FaJKqkidw/0cxRbWN+HOs6ZWd1w==",
+ "dev": true,
+ "dependencies": {
+ "@babel/helper-plugin-utils": "^7.24.5"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/plugin-transform-react-jsx-source": {
+ "version": "7.24.1",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-source/-/plugin-transform-react-jsx-source-7.24.1.tgz",
+ "integrity": "sha512-1v202n7aUq4uXAieRTKcwPzNyphlCuqHHDcdSNc+vdhoTEZcFMh+L5yZuCmGaIO7bs1nJUNfHB89TZyoL48xNA==",
+ "dev": true,
+ "dependencies": {
+ "@babel/helper-plugin-utils": "^7.24.0"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/runtime": {
+ "version": "7.24.5",
+ "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.24.5.tgz",
+ "integrity": "sha512-Nms86NXrsaeU9vbBJKni6gXiEXZ4CVpYVzEjDH9Sb8vmZ3UljyA1GSOJl/6LGPO8EHLuSF9H+IxNXHPX8QHJ4g==",
+ "dependencies": {
+ "regenerator-runtime": "^0.14.0"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/template": {
+ "version": "7.24.0",
+ "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.24.0.tgz",
+ "integrity": "sha512-Bkf2q8lMB0AFpX0NFEqSbx1OkTHf0f+0j82mkw+ZpzBnkk7e9Ql0891vlfgi+kHwOk8tQjiQHpqh4LaSa0fKEA==",
+ "dev": true,
+ "dependencies": {
+ "@babel/code-frame": "^7.23.5",
+ "@babel/parser": "^7.24.0",
+ "@babel/types": "^7.24.0"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/traverse": {
+ "version": "7.24.5",
+ "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.24.5.tgz",
+ "integrity": "sha512-7aaBLeDQ4zYcUFDUD41lJc1fG8+5IU9DaNSJAgal866FGvmD5EbWQgnEC6kO1gGLsX0esNkfnJSndbTXA3r7UA==",
+ "dev": true,
+ "dependencies": {
+ "@babel/code-frame": "^7.24.2",
+ "@babel/generator": "^7.24.5",
+ "@babel/helper-environment-visitor": "^7.22.20",
+ "@babel/helper-function-name": "^7.23.0",
+ "@babel/helper-hoist-variables": "^7.22.5",
+ "@babel/helper-split-export-declaration": "^7.24.5",
+ "@babel/parser": "^7.24.5",
+ "@babel/types": "^7.24.5",
+ "debug": "^4.3.1",
+ "globals": "^11.1.0"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/types": {
+ "version": "7.24.5",
+ "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.24.5.tgz",
+ "integrity": "sha512-6mQNsaLeXTw0nxYUYu+NSa4Hx4BlF1x1x8/PMFbiR+GBSr+2DkECc69b8hgy2frEodNcvPffeH8YfWd3LI6jhQ==",
+ "dependencies": {
+ "@babel/helper-string-parser": "^7.24.1",
+ "@babel/helper-validator-identifier": "^7.24.5",
+ "to-fast-properties": "^2.0.0"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@colors/colors": {
+ "version": "1.5.0",
+ "resolved": "https://registry.npmjs.org/@colors/colors/-/colors-1.5.0.tgz",
+ "integrity": "sha512-ooWCrlZP11i8GImSjTHYHLkvFDP48nS4+204nGb1RiX/WXYHmJA2III9/e2DWVabCESdW7hBAEzHRqUn9OUVvQ==",
+ "dev": true,
+ "optional": true,
+ "engines": {
+ "node": ">=0.1.90"
+ }
+ },
+ "node_modules/@cypress/request": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/@cypress/request/-/request-3.0.1.tgz",
+ "integrity": "sha512-TWivJlJi8ZDx2wGOw1dbLuHJKUYX7bWySw377nlnGOW3hP9/MUKIsEdXT/YngWxVdgNCHRBmFlBipE+5/2ZZlQ==",
+ "dev": true,
+ "dependencies": {
+ "aws-sign2": "~0.7.0",
+ "aws4": "^1.8.0",
+ "caseless": "~0.12.0",
+ "combined-stream": "~1.0.6",
+ "extend": "~3.0.2",
+ "forever-agent": "~0.6.1",
+ "form-data": "~2.3.2",
+ "http-signature": "~1.3.6",
+ "is-typedarray": "~1.0.0",
+ "isstream": "~0.1.2",
+ "json-stringify-safe": "~5.0.1",
+ "mime-types": "~2.1.19",
+ "performance-now": "^2.1.0",
+ "qs": "6.10.4",
+ "safe-buffer": "^5.1.2",
+ "tough-cookie": "^4.1.3",
+ "tunnel-agent": "^0.6.0",
+ "uuid": "^8.3.2"
+ },
+ "engines": {
+ "node": ">= 6"
+ }
+ },
+ "node_modules/@cypress/request/node_modules/form-data": {
+ "version": "2.3.3",
+ "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.3.tgz",
+ "integrity": "sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ==",
+ "dev": true,
+ "dependencies": {
+ "asynckit": "^0.4.0",
+ "combined-stream": "^1.0.6",
+ "mime-types": "^2.1.12"
+ },
+ "engines": {
+ "node": ">= 0.12"
+ }
+ },
+ "node_modules/@cypress/xvfb": {
+ "version": "1.2.4",
+ "resolved": "https://registry.npmjs.org/@cypress/xvfb/-/xvfb-1.2.4.tgz",
+ "integrity": "sha512-skbBzPggOVYCbnGgV+0dmBdW/s77ZkAOXIC1knS8NagwDjBrNC1LuXtQJeiN6l+m7lzmHtaoUw/ctJKdqkG57Q==",
+ "dev": true,
+ "dependencies": {
+ "debug": "^3.1.0",
+ "lodash.once": "^4.1.1"
+ }
+ },
+ "node_modules/@cypress/xvfb/node_modules/debug": {
+ "version": "3.2.7",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz",
+ "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==",
+ "dev": true,
+ "dependencies": {
+ "ms": "^2.1.1"
+ }
+ },
+ "node_modules/@emotion/babel-plugin": {
+ "version": "11.11.0",
+ "resolved": "https://registry.npmjs.org/@emotion/babel-plugin/-/babel-plugin-11.11.0.tgz",
+ "integrity": "sha512-m4HEDZleaaCH+XgDDsPF15Ht6wTLsgDTeR3WYj9Q/k76JtWhrJjcP4+/XlG8LGT/Rol9qUfOIztXeA84ATpqPQ==",
+ "dependencies": {
+ "@babel/helper-module-imports": "^7.16.7",
+ "@babel/runtime": "^7.18.3",
+ "@emotion/hash": "^0.9.1",
+ "@emotion/memoize": "^0.8.1",
+ "@emotion/serialize": "^1.1.2",
+ "babel-plugin-macros": "^3.1.0",
+ "convert-source-map": "^1.5.0",
+ "escape-string-regexp": "^4.0.0",
+ "find-root": "^1.1.0",
+ "source-map": "^0.5.7",
+ "stylis": "4.2.0"
+ }
+ },
+ "node_modules/@emotion/cache": {
+ "version": "11.11.0",
+ "resolved": "https://registry.npmjs.org/@emotion/cache/-/cache-11.11.0.tgz",
+ "integrity": "sha512-P34z9ssTCBi3e9EI1ZsWpNHcfY1r09ZO0rZbRO2ob3ZQMnFI35jB536qoXbkdesr5EUhYi22anuEJuyxifaqAQ==",
+ "dependencies": {
+ "@emotion/memoize": "^0.8.1",
+ "@emotion/sheet": "^1.2.2",
+ "@emotion/utils": "^1.2.1",
+ "@emotion/weak-memoize": "^0.3.1",
+ "stylis": "4.2.0"
+ }
+ },
+ "node_modules/@emotion/hash": {
+ "version": "0.9.1",
+ "resolved": "https://registry.npmjs.org/@emotion/hash/-/hash-0.9.1.tgz",
+ "integrity": "sha512-gJB6HLm5rYwSLI6PQa+X1t5CFGrv1J1TWG+sOyMCeKz2ojaj6Fnl/rZEspogG+cvqbt4AE/2eIyD2QfLKTBNlQ=="
+ },
+ "node_modules/@emotion/is-prop-valid": {
+ "version": "1.2.2",
+ "resolved": "https://registry.npmjs.org/@emotion/is-prop-valid/-/is-prop-valid-1.2.2.tgz",
+ "integrity": "sha512-uNsoYd37AFmaCdXlg6EYD1KaPOaRWRByMCYzbKUX4+hhMfrxdVSelShywL4JVaAeM/eHUOSprYBQls+/neX3pw==",
+ "dependencies": {
+ "@emotion/memoize": "^0.8.1"
+ }
+ },
+ "node_modules/@emotion/memoize": {
+ "version": "0.8.1",
+ "resolved": "https://registry.npmjs.org/@emotion/memoize/-/memoize-0.8.1.tgz",
+ "integrity": "sha512-W2P2c/VRW1/1tLox0mVUalvnWXxavmv/Oum2aPsRcoDJuob75FC3Y8FbpfLwUegRcxINtGUMPq0tFCvYNTBXNA=="
+ },
+ "node_modules/@emotion/react": {
+ "version": "11.11.4",
+ "resolved": "https://registry.npmjs.org/@emotion/react/-/react-11.11.4.tgz",
+ "integrity": "sha512-t8AjMlF0gHpvvxk5mAtCqR4vmxiGHCeJBaQO6gncUSdklELOgtwjerNY2yuJNfwnc6vi16U/+uMF+afIawJ9iw==",
+ "dependencies": {
+ "@babel/runtime": "^7.18.3",
+ "@emotion/babel-plugin": "^11.11.0",
+ "@emotion/cache": "^11.11.0",
+ "@emotion/serialize": "^1.1.3",
+ "@emotion/use-insertion-effect-with-fallbacks": "^1.0.1",
+ "@emotion/utils": "^1.2.1",
+ "@emotion/weak-memoize": "^0.3.1",
+ "hoist-non-react-statics": "^3.3.1"
+ },
+ "peerDependencies": {
+ "react": ">=16.8.0"
+ },
+ "peerDependenciesMeta": {
+ "@types/react": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@emotion/serialize": {
+ "version": "1.1.4",
+ "resolved": "https://registry.npmjs.org/@emotion/serialize/-/serialize-1.1.4.tgz",
+ "integrity": "sha512-RIN04MBT8g+FnDwgvIUi8czvr1LU1alUMI05LekWB5DGyTm8cCBMCRpq3GqaiyEDRptEXOyXnvZ58GZYu4kBxQ==",
+ "dependencies": {
+ "@emotion/hash": "^0.9.1",
+ "@emotion/memoize": "^0.8.1",
+ "@emotion/unitless": "^0.8.1",
+ "@emotion/utils": "^1.2.1",
+ "csstype": "^3.0.2"
+ }
+ },
+ "node_modules/@emotion/sheet": {
+ "version": "1.2.2",
+ "resolved": "https://registry.npmjs.org/@emotion/sheet/-/sheet-1.2.2.tgz",
+ "integrity": "sha512-0QBtGvaqtWi+nx6doRwDdBIzhNdZrXUppvTM4dtZZWEGTXL/XE/yJxLMGlDT1Gt+UHH5IX1n+jkXyytE/av7OA=="
+ },
+ "node_modules/@emotion/styled": {
+ "version": "11.11.5",
+ "resolved": "https://registry.npmjs.org/@emotion/styled/-/styled-11.11.5.tgz",
+ "integrity": "sha512-/ZjjnaNKvuMPxcIiUkf/9SHoG4Q196DRl1w82hQ3WCsjo1IUR8uaGWrC6a87CrYAW0Kb/pK7hk8BnLgLRi9KoQ==",
+ "dependencies": {
+ "@babel/runtime": "^7.18.3",
+ "@emotion/babel-plugin": "^11.11.0",
+ "@emotion/is-prop-valid": "^1.2.2",
+ "@emotion/serialize": "^1.1.4",
+ "@emotion/use-insertion-effect-with-fallbacks": "^1.0.1",
+ "@emotion/utils": "^1.2.1"
+ },
+ "peerDependencies": {
+ "@emotion/react": "^11.0.0-rc.0",
+ "react": ">=16.8.0"
+ },
+ "peerDependenciesMeta": {
+ "@types/react": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@emotion/unitless": {
+ "version": "0.8.1",
+ "resolved": "https://registry.npmjs.org/@emotion/unitless/-/unitless-0.8.1.tgz",
+ "integrity": "sha512-KOEGMu6dmJZtpadb476IsZBclKvILjopjUii3V+7MnXIQCYh8W3NgNcgwo21n9LXZX6EDIKvqfjYxXebDwxKmQ=="
+ },
+ "node_modules/@emotion/use-insertion-effect-with-fallbacks": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/@emotion/use-insertion-effect-with-fallbacks/-/use-insertion-effect-with-fallbacks-1.0.1.tgz",
+ "integrity": "sha512-jT/qyKZ9rzLErtrjGgdkMBn2OP8wl0G3sQlBb3YPryvKHsjvINUhVaPFfP+fpBcOkmrVOVEEHQFJ7nbj2TH2gw==",
+ "peerDependencies": {
+ "react": ">=16.8.0"
+ }
+ },
+ "node_modules/@emotion/utils": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/@emotion/utils/-/utils-1.2.1.tgz",
+ "integrity": "sha512-Y2tGf3I+XVnajdItskUCn6LX+VUDmP6lTL4fcqsXAv43dnlbZiuW4MWQW38rW/BVWSE7Q/7+XQocmpnRYILUmg=="
+ },
+ "node_modules/@emotion/weak-memoize": {
+ "version": "0.3.1",
+ "resolved": "https://registry.npmjs.org/@emotion/weak-memoize/-/weak-memoize-0.3.1.tgz",
+ "integrity": "sha512-EsBwpc7hBUJWAsNPBmJy4hxWx12v6bshQsldrVmjxJoc3isbxhOrF2IcCpaXxfvq03NwkI7sbsOLXbYuqF/8Ww=="
+ },
+ "node_modules/@esbuild/aix-ppc64": {
+ "version": "0.20.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.20.2.tgz",
+ "integrity": "sha512-D+EBOJHXdNZcLJRBkhENNG8Wji2kgc9AZ9KiPr1JuZjsNtyHzrsfLRrY0tk2H2aoFu6RANO1y1iPPUCDYWkb5g==",
+ "cpu": [
+ "ppc64"
+ ],
+ "dev": true,
+ "optional": true,
+ "os": [
+ "aix"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@esbuild/android-arm": {
+ "version": "0.20.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.20.2.tgz",
+ "integrity": "sha512-t98Ra6pw2VaDhqNWO2Oph2LXbz/EJcnLmKLGBJwEwXX/JAN83Fym1rU8l0JUWK6HkIbWONCSSatf4sf2NBRx/w==",
+ "cpu": [
+ "arm"
+ ],
+ "dev": true,
+ "optional": true,
+ "os": [
+ "android"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@esbuild/android-arm64": {
+ "version": "0.20.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.20.2.tgz",
+ "integrity": "sha512-mRzjLacRtl/tWU0SvD8lUEwb61yP9cqQo6noDZP/O8VkwafSYwZ4yWy24kan8jE/IMERpYncRt2dw438LP3Xmg==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "optional": true,
+ "os": [
+ "android"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@esbuild/android-x64": {
+ "version": "0.20.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.20.2.tgz",
+ "integrity": "sha512-btzExgV+/lMGDDa194CcUQm53ncxzeBrWJcncOBxuC6ndBkKxnHdFJn86mCIgTELsooUmwUm9FkhSp5HYu00Rg==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "optional": true,
+ "os": [
+ "android"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@esbuild/darwin-arm64": {
+ "version": "0.20.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.20.2.tgz",
+ "integrity": "sha512-4J6IRT+10J3aJH3l1yzEg9y3wkTDgDk7TSDFX+wKFiWjqWp/iCfLIYzGyasx9l0SAFPT1HwSCR+0w/h1ES/MjA==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@esbuild/darwin-x64": {
+ "version": "0.20.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.20.2.tgz",
+ "integrity": "sha512-tBcXp9KNphnNH0dfhv8KYkZhjc+H3XBkF5DKtswJblV7KlT9EI2+jeA8DgBjp908WEuYll6pF+UStUCfEpdysA==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@esbuild/freebsd-arm64": {
+ "version": "0.20.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.20.2.tgz",
+ "integrity": "sha512-d3qI41G4SuLiCGCFGUrKsSeTXyWG6yem1KcGZVS+3FYlYhtNoNgYrWcvkOoaqMhwXSMrZRl69ArHsGJ9mYdbbw==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "optional": true,
+ "os": [
+ "freebsd"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@esbuild/freebsd-x64": {
+ "version": "0.20.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.20.2.tgz",
+ "integrity": "sha512-d+DipyvHRuqEeM5zDivKV1KuXn9WeRX6vqSqIDgwIfPQtwMP4jaDsQsDncjTDDsExT4lR/91OLjRo8bmC1e+Cw==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "optional": true,
+ "os": [
+ "freebsd"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@esbuild/linux-arm": {
+ "version": "0.20.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.20.2.tgz",
+ "integrity": "sha512-VhLPeR8HTMPccbuWWcEUD1Az68TqaTYyj6nfE4QByZIQEQVWBB8vup8PpR7y1QHL3CpcF6xd5WVBU/+SBEvGTg==",
+ "cpu": [
+ "arm"
+ ],
+ "dev": true,
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@esbuild/linux-arm64": {
+ "version": "0.20.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.20.2.tgz",
+ "integrity": "sha512-9pb6rBjGvTFNira2FLIWqDk/uaf42sSyLE8j1rnUpuzsODBq7FvpwHYZxQ/It/8b+QOS1RYfqgGFNLRI+qlq2A==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@esbuild/linux-ia32": {
+ "version": "0.20.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.20.2.tgz",
+ "integrity": "sha512-o10utieEkNPFDZFQm9CoP7Tvb33UutoJqg3qKf1PWVeeJhJw0Q347PxMvBgVVFgouYLGIhFYG0UGdBumROyiig==",
+ "cpu": [
+ "ia32"
+ ],
+ "dev": true,
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@esbuild/linux-loong64": {
+ "version": "0.20.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.20.2.tgz",
+ "integrity": "sha512-PR7sp6R/UC4CFVomVINKJ80pMFlfDfMQMYynX7t1tNTeivQ6XdX5r2XovMmha/VjR1YN/HgHWsVcTRIMkymrgQ==",
+ "cpu": [
+ "loong64"
+ ],
+ "dev": true,
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@esbuild/linux-mips64el": {
+ "version": "0.20.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.20.2.tgz",
+ "integrity": "sha512-4BlTqeutE/KnOiTG5Y6Sb/Hw6hsBOZapOVF6njAESHInhlQAghVVZL1ZpIctBOoTFbQyGW+LsVYZ8lSSB3wkjA==",
+ "cpu": [
+ "mips64el"
+ ],
+ "dev": true,
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@esbuild/linux-ppc64": {
+ "version": "0.20.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.20.2.tgz",
+ "integrity": "sha512-rD3KsaDprDcfajSKdn25ooz5J5/fWBylaaXkuotBDGnMnDP1Uv5DLAN/45qfnf3JDYyJv/ytGHQaziHUdyzaAg==",
+ "cpu": [
+ "ppc64"
+ ],
+ "dev": true,
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@esbuild/linux-riscv64": {
+ "version": "0.20.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.20.2.tgz",
+ "integrity": "sha512-snwmBKacKmwTMmhLlz/3aH1Q9T8v45bKYGE3j26TsaOVtjIag4wLfWSiZykXzXuE1kbCE+zJRmwp+ZbIHinnVg==",
+ "cpu": [
+ "riscv64"
+ ],
+ "dev": true,
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@esbuild/linux-s390x": {
+ "version": "0.20.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.20.2.tgz",
+ "integrity": "sha512-wcWISOobRWNm3cezm5HOZcYz1sKoHLd8VL1dl309DiixxVFoFe/o8HnwuIwn6sXre88Nwj+VwZUvJf4AFxkyrQ==",
+ "cpu": [
+ "s390x"
+ ],
+ "dev": true,
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@esbuild/linux-x64": {
+ "version": "0.20.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.20.2.tgz",
+ "integrity": "sha512-1MdwI6OOTsfQfek8sLwgyjOXAu+wKhLEoaOLTjbijk6E2WONYpH9ZU2mNtR+lZ2B4uwr+usqGuVfFT9tMtGvGw==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@esbuild/netbsd-x64": {
+ "version": "0.20.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.20.2.tgz",
+ "integrity": "sha512-K8/DhBxcVQkzYc43yJXDSyjlFeHQJBiowJ0uVL6Tor3jGQfSGHNNJcWxNbOI8v5k82prYqzPuwkzHt3J1T1iZQ==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "optional": true,
+ "os": [
+ "netbsd"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@esbuild/openbsd-x64": {
+ "version": "0.20.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.20.2.tgz",
+ "integrity": "sha512-eMpKlV0SThJmmJgiVyN9jTPJ2VBPquf6Kt/nAoo6DgHAoN57K15ZghiHaMvqjCye/uU4X5u3YSMgVBI1h3vKrQ==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "optional": true,
+ "os": [
+ "openbsd"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@esbuild/sunos-x64": {
+ "version": "0.20.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.20.2.tgz",
+ "integrity": "sha512-2UyFtRC6cXLyejf/YEld4Hajo7UHILetzE1vsRcGL3earZEW77JxrFjH4Ez2qaTiEfMgAXxfAZCm1fvM/G/o8w==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "optional": true,
+ "os": [
+ "sunos"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@esbuild/win32-arm64": {
+ "version": "0.20.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.20.2.tgz",
+ "integrity": "sha512-GRibxoawM9ZCnDxnP3usoUDO9vUkpAxIIZ6GQI+IlVmr5kP3zUq+l17xELTHMWTWzjxa2guPNyrpq1GWmPvcGQ==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@esbuild/win32-ia32": {
+ "version": "0.20.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.20.2.tgz",
+ "integrity": "sha512-HfLOfn9YWmkSKRQqovpnITazdtquEW8/SoHW7pWpuEeguaZI4QnCRW6b+oZTztdBnZOS2hqJ6im/D5cPzBTTlQ==",
+ "cpu": [
+ "ia32"
+ ],
+ "dev": true,
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@esbuild/win32-x64": {
+ "version": "0.20.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.20.2.tgz",
+ "integrity": "sha512-N49X4lJX27+l9jbLKSqZ6bKNjzQvHaT8IIFUy+YIqmXQdjYCToGWwOItDrfby14c78aDd5NHQl29xingXfCdLQ==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@eslint-community/eslint-utils": {
+ "version": "4.4.0",
+ "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz",
+ "integrity": "sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==",
+ "dev": true,
+ "dependencies": {
+ "eslint-visitor-keys": "^3.3.0"
+ },
+ "engines": {
+ "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+ },
+ "peerDependencies": {
+ "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0"
+ }
+ },
+ "node_modules/@eslint-community/regexpp": {
+ "version": "4.10.0",
+ "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.10.0.tgz",
+ "integrity": "sha512-Cu96Sd2By9mCNTx2iyKOmq10v22jUVQv0lQnlGNy16oE9589yE+QADPbrMGCkA51cKZSg3Pu/aTJVTGfL/qjUA==",
+ "dev": true,
+ "engines": {
+ "node": "^12.0.0 || ^14.0.0 || >=16.0.0"
+ }
+ },
+ "node_modules/@eslint/eslintrc": {
+ "version": "2.1.4",
+ "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.1.4.tgz",
+ "integrity": "sha512-269Z39MS6wVJtsoUl10L60WdkhJVdPG24Q4eZTH3nnF6lpvSShEK3wQjDX9JRWAUPvPh7COouPpU9IrqaZFvtQ==",
+ "dev": true,
+ "dependencies": {
+ "ajv": "^6.12.4",
+ "debug": "^4.3.2",
+ "espree": "^9.6.0",
+ "globals": "^13.19.0",
+ "ignore": "^5.2.0",
+ "import-fresh": "^3.2.1",
+ "js-yaml": "^4.1.0",
+ "minimatch": "^3.1.2",
+ "strip-json-comments": "^3.1.1"
+ },
+ "engines": {
+ "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/eslint"
+ }
+ },
+ "node_modules/@eslint/eslintrc/node_modules/brace-expansion": {
+ "version": "1.1.11",
+ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
+ "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
+ "dev": true,
+ "dependencies": {
+ "balanced-match": "^1.0.0",
+ "concat-map": "0.0.1"
+ }
+ },
+ "node_modules/@eslint/eslintrc/node_modules/globals": {
+ "version": "13.24.0",
+ "resolved": "https://registry.npmjs.org/globals/-/globals-13.24.0.tgz",
+ "integrity": "sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==",
+ "dev": true,
+ "dependencies": {
+ "type-fest": "^0.20.2"
+ },
+ "engines": {
+ "node": ">=8"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/@eslint/eslintrc/node_modules/minimatch": {
+ "version": "3.1.2",
+ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
+ "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==",
+ "dev": true,
+ "dependencies": {
+ "brace-expansion": "^1.1.7"
+ },
+ "engines": {
+ "node": "*"
+ }
+ },
+ "node_modules/@eslint/js": {
+ "version": "8.57.0",
+ "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.57.0.tgz",
+ "integrity": "sha512-Ys+3g2TaW7gADOJzPt83SJtCDhMjndcDMFVQ/Tj9iA1BfJzFKD9mAUXT3OenpuPHbI6P/myECxRJrofUsDx/5g==",
+ "dev": true,
+ "engines": {
+ "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+ }
+ },
+ "node_modules/@floating-ui/core": {
+ "version": "1.6.1",
+ "resolved": "https://registry.npmjs.org/@floating-ui/core/-/core-1.6.1.tgz",
+ "integrity": "sha512-42UH54oPZHPdRHdw6BgoBD6cg/eVTmVrFcgeRDM3jbO7uxSoipVcmcIGFcA5jmOHO5apcyvBhkSKES3fQJnu7A==",
+ "dependencies": {
+ "@floating-ui/utils": "^0.2.0"
+ }
+ },
+ "node_modules/@floating-ui/dom": {
+ "version": "1.6.5",
+ "resolved": "https://registry.npmjs.org/@floating-ui/dom/-/dom-1.6.5.tgz",
+ "integrity": "sha512-Nsdud2X65Dz+1RHjAIP0t8z5e2ff/IRbei6BqFrl1urT8sDVzM1HMQ+R0XcU5ceRfyO3I6ayeqIfh+6Wb8LGTw==",
+ "dependencies": {
+ "@floating-ui/core": "^1.0.0",
+ "@floating-ui/utils": "^0.2.0"
+ }
+ },
+ "node_modules/@floating-ui/react-dom": {
+ "version": "2.0.9",
+ "resolved": "https://registry.npmjs.org/@floating-ui/react-dom/-/react-dom-2.0.9.tgz",
+ "integrity": "sha512-q0umO0+LQK4+p6aGyvzASqKbKOJcAHJ7ycE9CuUvfx3s9zTHWmGJTPOIlM/hmSBfUfg/XfY5YhLBLR/LHwShQQ==",
+ "dependencies": {
+ "@floating-ui/dom": "^1.0.0"
+ },
+ "peerDependencies": {
+ "react": ">=16.8.0",
+ "react-dom": ">=16.8.0"
+ }
+ },
+ "node_modules/@floating-ui/utils": {
+ "version": "0.2.2",
+ "resolved": "https://registry.npmjs.org/@floating-ui/utils/-/utils-0.2.2.tgz",
+ "integrity": "sha512-J4yDIIthosAsRZ5CPYP/jQvUAQtlZTTD/4suA08/FEnlxqW3sKS9iAhgsa9VYLZ6vDHn/ixJgIqRQPotoBjxIw=="
+ },
+ "node_modules/@fontsource/roboto": {
+ "version": "5.0.13",
+ "resolved": "https://registry.npmjs.org/@fontsource/roboto/-/roboto-5.0.13.tgz",
+ "integrity": "sha512-j61DHjsdUCKMXSdNLTOxcG701FWnF0jcqNNQi2iPCDxU8seN/sMxeh62dC++UiagCWq9ghTypX+Pcy7kX+QOeQ=="
+ },
+ "node_modules/@humanwhocodes/config-array": {
+ "version": "0.11.14",
+ "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.14.tgz",
+ "integrity": "sha512-3T8LkOmg45BV5FICb15QQMsyUSWrQ8AygVfC7ZG32zOalnqrilm018ZVCw0eapXux8FtA33q8PSRSstjee3jSg==",
+ "dev": true,
+ "dependencies": {
+ "@humanwhocodes/object-schema": "^2.0.2",
+ "debug": "^4.3.1",
+ "minimatch": "^3.0.5"
+ },
+ "engines": {
+ "node": ">=10.10.0"
+ }
+ },
+ "node_modules/@humanwhocodes/config-array/node_modules/brace-expansion": {
+ "version": "1.1.11",
+ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
+ "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
+ "dev": true,
+ "dependencies": {
+ "balanced-match": "^1.0.0",
+ "concat-map": "0.0.1"
+ }
+ },
+ "node_modules/@humanwhocodes/config-array/node_modules/minimatch": {
+ "version": "3.1.2",
+ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
+ "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==",
+ "dev": true,
+ "dependencies": {
+ "brace-expansion": "^1.1.7"
+ },
+ "engines": {
+ "node": "*"
+ }
+ },
+ "node_modules/@humanwhocodes/module-importer": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz",
+ "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==",
+ "dev": true,
+ "engines": {
+ "node": ">=12.22"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/nzakas"
+ }
+ },
+ "node_modules/@humanwhocodes/object-schema": {
+ "version": "2.0.3",
+ "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-2.0.3.tgz",
+ "integrity": "sha512-93zYdMES/c1D69yZiKDBj0V24vqNzB/koF26KPaagAfd3P/4gUlh3Dys5ogAK+Exi9QyzlD8x/08Zt7wIKcDcA==",
+ "dev": true
+ },
+ "node_modules/@jridgewell/gen-mapping": {
+ "version": "0.3.5",
+ "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.5.tgz",
+ "integrity": "sha512-IzL8ZoEDIBRWEzlCcRhOaCupYyN5gdIK+Q6fbFdPDg6HqX6jpkItn7DFIpW9LQzXG6Df9sA7+OKnq0qlz/GaQg==",
+ "dev": true,
+ "dependencies": {
+ "@jridgewell/set-array": "^1.2.1",
+ "@jridgewell/sourcemap-codec": "^1.4.10",
+ "@jridgewell/trace-mapping": "^0.3.24"
+ },
+ "engines": {
+ "node": ">=6.0.0"
+ }
+ },
+ "node_modules/@jridgewell/resolve-uri": {
+ "version": "3.1.2",
+ "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz",
+ "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==",
+ "dev": true,
+ "engines": {
+ "node": ">=6.0.0"
+ }
+ },
+ "node_modules/@jridgewell/set-array": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.2.1.tgz",
+ "integrity": "sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==",
+ "dev": true,
+ "engines": {
+ "node": ">=6.0.0"
+ }
+ },
+ "node_modules/@jridgewell/sourcemap-codec": {
+ "version": "1.4.15",
+ "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz",
+ "integrity": "sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==",
+ "dev": true
+ },
+ "node_modules/@jridgewell/trace-mapping": {
+ "version": "0.3.25",
+ "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz",
+ "integrity": "sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==",
+ "dev": true,
+ "dependencies": {
+ "@jridgewell/resolve-uri": "^3.1.0",
+ "@jridgewell/sourcemap-codec": "^1.4.14"
+ }
+ },
+ "node_modules/@mui/base": {
+ "version": "5.0.0-beta.40",
+ "resolved": "https://registry.npmjs.org/@mui/base/-/base-5.0.0-beta.40.tgz",
+ "integrity": "sha512-I/lGHztkCzvwlXpjD2+SNmvNQvB4227xBXhISPjEaJUXGImOQ9f3D2Yj/T3KasSI/h0MLWy74X0J6clhPmsRbQ==",
+ "dependencies": {
+ "@babel/runtime": "^7.23.9",
+ "@floating-ui/react-dom": "^2.0.8",
+ "@mui/types": "^7.2.14",
+ "@mui/utils": "^5.15.14",
+ "@popperjs/core": "^2.11.8",
+ "clsx": "^2.1.0",
+ "prop-types": "^15.8.1"
+ },
+ "engines": {
+ "node": ">=12.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/mui-org"
+ },
+ "peerDependencies": {
+ "@types/react": "^17.0.0 || ^18.0.0",
+ "react": "^17.0.0 || ^18.0.0",
+ "react-dom": "^17.0.0 || ^18.0.0"
+ },
+ "peerDependenciesMeta": {
+ "@types/react": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@mui/core-downloads-tracker": {
+ "version": "5.15.16",
+ "resolved": "https://registry.npmjs.org/@mui/core-downloads-tracker/-/core-downloads-tracker-5.15.16.tgz",
+ "integrity": "sha512-PTIbMJs5C/vYMfyJNW8ArOezh4eyHkg2pTeA7bBxh2kLP1Uzs0Nm+krXWbWGJPwTWjM8EhnDrr4aCF26+2oleg==",
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/mui-org"
+ }
+ },
+ "node_modules/@mui/icons-material": {
+ "version": "5.15.16",
+ "resolved": "https://registry.npmjs.org/@mui/icons-material/-/icons-material-5.15.16.tgz",
+ "integrity": "sha512-s8vYbyACzTNZRKv+20fCfVXJwJqNcVotns2EKnu1wmAga6wv2LAo5kB1d5yqQqZlMFtp34EJvRXf7cy8X0tJVA==",
+ "dependencies": {
+ "@babel/runtime": "^7.23.9"
+ },
+ "engines": {
+ "node": ">=12.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/mui-org"
+ },
+ "peerDependencies": {
+ "@mui/material": "^5.0.0",
+ "@types/react": "^17.0.0 || ^18.0.0",
+ "react": "^17.0.0 || ^18.0.0"
+ },
+ "peerDependenciesMeta": {
+ "@types/react": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@mui/material": {
+ "version": "5.15.16",
+ "resolved": "https://registry.npmjs.org/@mui/material/-/material-5.15.16.tgz",
+ "integrity": "sha512-ery2hFReewko9gpDBqOr2VmXwQG9ifXofPhGzIx09/b9JqCQC/06kZXZDGGrOTpIddK9HlIf4yrS+G70jPAzUQ==",
+ "dependencies": {
+ "@babel/runtime": "^7.23.9",
+ "@mui/base": "5.0.0-beta.40",
+ "@mui/core-downloads-tracker": "^5.15.16",
+ "@mui/system": "^5.15.15",
+ "@mui/types": "^7.2.14",
+ "@mui/utils": "^5.15.14",
+ "@types/react-transition-group": "^4.4.10",
+ "clsx": "^2.1.0",
+ "csstype": "^3.1.3",
+ "prop-types": "^15.8.1",
+ "react-is": "^18.2.0",
+ "react-transition-group": "^4.4.5"
+ },
+ "engines": {
+ "node": ">=12.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/mui-org"
+ },
+ "peerDependencies": {
+ "@emotion/react": "^11.5.0",
+ "@emotion/styled": "^11.3.0",
+ "@types/react": "^17.0.0 || ^18.0.0",
+ "react": "^17.0.0 || ^18.0.0",
+ "react-dom": "^17.0.0 || ^18.0.0"
+ },
+ "peerDependenciesMeta": {
+ "@emotion/react": {
+ "optional": true
+ },
+ "@emotion/styled": {
+ "optional": true
+ },
+ "@types/react": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@mui/private-theming": {
+ "version": "5.15.14",
+ "resolved": "https://registry.npmjs.org/@mui/private-theming/-/private-theming-5.15.14.tgz",
+ "integrity": "sha512-UH0EiZckOWcxiXLX3Jbb0K7rC8mxTr9L9l6QhOZxYc4r8FHUkefltV9VDGLrzCaWh30SQiJvAEd7djX3XXY6Xw==",
+ "dependencies": {
+ "@babel/runtime": "^7.23.9",
+ "@mui/utils": "^5.15.14",
+ "prop-types": "^15.8.1"
+ },
+ "engines": {
+ "node": ">=12.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/mui-org"
+ },
+ "peerDependencies": {
+ "@types/react": "^17.0.0 || ^18.0.0",
+ "react": "^17.0.0 || ^18.0.0"
+ },
+ "peerDependenciesMeta": {
+ "@types/react": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@mui/styled-engine": {
+ "version": "5.15.14",
+ "resolved": "https://registry.npmjs.org/@mui/styled-engine/-/styled-engine-5.15.14.tgz",
+ "integrity": "sha512-RILkuVD8gY6PvjZjqnWhz8fu68dVkqhM5+jYWfB5yhlSQKg+2rHkmEwm75XIeAqI3qwOndK6zELK5H6Zxn4NHw==",
+ "dependencies": {
+ "@babel/runtime": "^7.23.9",
+ "@emotion/cache": "^11.11.0",
+ "csstype": "^3.1.3",
+ "prop-types": "^15.8.1"
+ },
+ "engines": {
+ "node": ">=12.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/mui-org"
+ },
+ "peerDependencies": {
+ "@emotion/react": "^11.4.1",
+ "@emotion/styled": "^11.3.0",
+ "react": "^17.0.0 || ^18.0.0"
+ },
+ "peerDependenciesMeta": {
+ "@emotion/react": {
+ "optional": true
+ },
+ "@emotion/styled": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@mui/system": {
+ "version": "5.15.15",
+ "resolved": "https://registry.npmjs.org/@mui/system/-/system-5.15.15.tgz",
+ "integrity": "sha512-aulox6N1dnu5PABsfxVGOZffDVmlxPOVgj56HrUnJE8MCSh8lOvvkd47cebIVQQYAjpwieXQXiDPj5pwM40jTQ==",
+ "dependencies": {
+ "@babel/runtime": "^7.23.9",
+ "@mui/private-theming": "^5.15.14",
+ "@mui/styled-engine": "^5.15.14",
+ "@mui/types": "^7.2.14",
+ "@mui/utils": "^5.15.14",
+ "clsx": "^2.1.0",
+ "csstype": "^3.1.3",
+ "prop-types": "^15.8.1"
+ },
+ "engines": {
+ "node": ">=12.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/mui-org"
+ },
+ "peerDependencies": {
+ "@emotion/react": "^11.5.0",
+ "@emotion/styled": "^11.3.0",
+ "@types/react": "^17.0.0 || ^18.0.0",
+ "react": "^17.0.0 || ^18.0.0"
+ },
+ "peerDependenciesMeta": {
+ "@emotion/react": {
+ "optional": true
+ },
+ "@emotion/styled": {
+ "optional": true
+ },
+ "@types/react": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@mui/types": {
+ "version": "7.2.14",
+ "resolved": "https://registry.npmjs.org/@mui/types/-/types-7.2.14.tgz",
+ "integrity": "sha512-MZsBZ4q4HfzBsywtXgM1Ksj6HDThtiwmOKUXH1pKYISI9gAVXCNHNpo7TlGoGrBaYWZTdNoirIN7JsQcQUjmQQ==",
+ "peerDependencies": {
+ "@types/react": "^17.0.0 || ^18.0.0"
+ },
+ "peerDependenciesMeta": {
+ "@types/react": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@mui/utils": {
+ "version": "5.15.14",
+ "resolved": "https://registry.npmjs.org/@mui/utils/-/utils-5.15.14.tgz",
+ "integrity": "sha512-0lF/7Hh/ezDv5X7Pry6enMsbYyGKjADzvHyo3Qrc/SSlTsQ1VkbDMbH0m2t3OR5iIVLwMoxwM7yGd+6FCMtTFA==",
+ "dependencies": {
+ "@babel/runtime": "^7.23.9",
+ "@types/prop-types": "^15.7.11",
+ "prop-types": "^15.8.1",
+ "react-is": "^18.2.0"
+ },
+ "engines": {
+ "node": ">=12.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/mui-org"
+ },
+ "peerDependencies": {
+ "@types/react": "^17.0.0 || ^18.0.0",
+ "react": "^17.0.0 || ^18.0.0"
+ },
+ "peerDependenciesMeta": {
+ "@types/react": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@mui/x-date-pickers": {
+ "version": "6.19.9",
+ "resolved": "https://registry.npmjs.org/@mui/x-date-pickers/-/x-date-pickers-6.19.9.tgz",
+ "integrity": "sha512-B2m4Fv/fOme5qmV6zuE3QnWQSvj3zKtI2OvikPz5prpiCcIxqpeytkQ7VfrWH3/Aqd5yhG1Yr4IgbqG0ymIXGg==",
+ "dependencies": {
+ "@babel/runtime": "^7.23.2",
+ "@mui/base": "^5.0.0-beta.22",
+ "@mui/utils": "^5.14.16",
+ "@types/react-transition-group": "^4.4.8",
+ "clsx": "^2.0.0",
+ "prop-types": "^15.8.1",
+ "react-transition-group": "^4.4.5"
+ },
+ "engines": {
+ "node": ">=14.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/mui"
+ },
+ "peerDependencies": {
+ "@emotion/react": "^11.9.0",
+ "@emotion/styled": "^11.8.1",
+ "@mui/material": "^5.8.6",
+ "@mui/system": "^5.8.0",
+ "date-fns": "^2.25.0 || ^3.2.0",
+ "date-fns-jalali": "^2.13.0-0",
+ "dayjs": "^1.10.7",
+ "luxon": "^3.0.2",
+ "moment": "^2.29.4",
+ "moment-hijri": "^2.1.2",
+ "moment-jalaali": "^0.7.4 || ^0.8.0 || ^0.9.0 || ^0.10.0",
+ "react": "^17.0.0 || ^18.0.0",
+ "react-dom": "^17.0.0 || ^18.0.0"
+ },
+ "peerDependenciesMeta": {
+ "@emotion/react": {
+ "optional": true
+ },
+ "@emotion/styled": {
+ "optional": true
+ },
+ "date-fns": {
+ "optional": true
+ },
+ "date-fns-jalali": {
+ "optional": true
+ },
+ "dayjs": {
+ "optional": true
+ },
+ "luxon": {
+ "optional": true
+ },
+ "moment": {
+ "optional": true
+ },
+ "moment-hijri": {
+ "optional": true
+ },
+ "moment-jalaali": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@nodelib/fs.scandir": {
+ "version": "2.1.5",
+ "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz",
+ "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==",
+ "dev": true,
+ "dependencies": {
+ "@nodelib/fs.stat": "2.0.5",
+ "run-parallel": "^1.1.9"
+ },
+ "engines": {
+ "node": ">= 8"
+ }
+ },
+ "node_modules/@nodelib/fs.stat": {
+ "version": "2.0.5",
+ "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz",
+ "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==",
+ "dev": true,
+ "engines": {
+ "node": ">= 8"
+ }
+ },
+ "node_modules/@nodelib/fs.walk": {
+ "version": "1.2.8",
+ "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz",
+ "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==",
+ "dev": true,
+ "dependencies": {
+ "@nodelib/fs.scandir": "2.1.5",
+ "fastq": "^1.6.0"
+ },
+ "engines": {
+ "node": ">= 8"
+ }
+ },
+ "node_modules/@pkgr/core": {
+ "version": "0.1.1",
+ "resolved": "https://registry.npmjs.org/@pkgr/core/-/core-0.1.1.tgz",
+ "integrity": "sha512-cq8o4cWH0ibXh9VGi5P20Tu9XF/0fFXl9EUinr9QfTM7a7p0oTA4iJRCQWppXR1Pg8dSM0UCItCkPwsk9qWWYA==",
+ "dev": true,
+ "engines": {
+ "node": "^12.20.0 || ^14.18.0 || >=16.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/unts"
+ }
+ },
+ "node_modules/@popperjs/core": {
+ "version": "2.11.8",
+ "resolved": "https://registry.npmjs.org/@popperjs/core/-/core-2.11.8.tgz",
+ "integrity": "sha512-P1st0aksCrn9sGZhp8GMYwBnQsbvAWsZAX44oXNNvLHGqAOcoVxmjZiohstwQ7SqKnbR47akdNi+uleWD8+g6A==",
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/popperjs"
+ }
+ },
+ "node_modules/@remix-run/router": {
+ "version": "1.16.0",
+ "resolved": "https://registry.npmjs.org/@remix-run/router/-/router-1.16.0.tgz",
+ "integrity": "sha512-Quz1KOffeEf/zwkCBM3kBtH4ZoZ+pT3xIXBG4PPW/XFtDP7EGhtTiC2+gpL9GnR7+Qdet5Oa6cYSvwKYg6kN9Q==",
+ "engines": {
+ "node": ">=14.0.0"
+ }
+ },
+ "node_modules/@rollup/rollup-android-arm-eabi": {
+ "version": "4.17.2",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.17.2.tgz",
+ "integrity": "sha512-NM0jFxY8bB8QLkoKxIQeObCaDlJKewVlIEkuyYKm5An1tdVZ966w2+MPQ2l8LBZLjR+SgyV+nRkTIunzOYBMLQ==",
+ "cpu": [
+ "arm"
+ ],
+ "dev": true,
+ "optional": true,
+ "os": [
+ "android"
+ ]
+ },
+ "node_modules/@rollup/rollup-android-arm64": {
+ "version": "4.17.2",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.17.2.tgz",
+ "integrity": "sha512-yeX/Usk7daNIVwkq2uGoq2BYJKZY1JfyLTaHO/jaiSwi/lsf8fTFoQW/n6IdAsx5tx+iotu2zCJwz8MxI6D/Bw==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "optional": true,
+ "os": [
+ "android"
+ ]
+ },
+ "node_modules/@rollup/rollup-darwin-arm64": {
+ "version": "4.17.2",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.17.2.tgz",
+ "integrity": "sha512-kcMLpE6uCwls023+kknm71ug7MZOrtXo+y5p/tsg6jltpDtgQY1Eq5sGfHcQfb+lfuKwhBmEURDga9N0ol4YPw==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "optional": true,
+ "os": [
+ "darwin"
+ ]
+ },
+ "node_modules/@rollup/rollup-darwin-x64": {
+ "version": "4.17.2",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.17.2.tgz",
+ "integrity": "sha512-AtKwD0VEx0zWkL0ZjixEkp5tbNLzX+FCqGG1SvOu993HnSz4qDI6S4kGzubrEJAljpVkhRSlg5bzpV//E6ysTQ==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "optional": true,
+ "os": [
+ "darwin"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-arm-gnueabihf": {
+ "version": "4.17.2",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.17.2.tgz",
+ "integrity": "sha512-3reX2fUHqN7sffBNqmEyMQVj/CKhIHZd4y631duy0hZqI8Qoqf6lTtmAKvJFYa6bhU95B1D0WgzHkmTg33In0A==",
+ "cpu": [
+ "arm"
+ ],
+ "dev": true,
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-arm-musleabihf": {
+ "version": "4.17.2",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.17.2.tgz",
+ "integrity": "sha512-uSqpsp91mheRgw96xtyAGP9FW5ChctTFEoXP0r5FAzj/3ZRv3Uxjtc7taRQSaQM/q85KEKjKsZuiZM3GyUivRg==",
+ "cpu": [
+ "arm"
+ ],
+ "dev": true,
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-arm64-gnu": {
+ "version": "4.17.2",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.17.2.tgz",
+ "integrity": "sha512-EMMPHkiCRtE8Wdk3Qhtciq6BndLtstqZIroHiiGzB3C5LDJmIZcSzVtLRbwuXuUft1Cnv+9fxuDtDxz3k3EW2A==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-arm64-musl": {
+ "version": "4.17.2",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.17.2.tgz",
+ "integrity": "sha512-NMPylUUZ1i0z/xJUIx6VUhISZDRT+uTWpBcjdv0/zkp7b/bQDF+NfnfdzuTiB1G6HTodgoFa93hp0O1xl+/UbA==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-powerpc64le-gnu": {
+ "version": "4.17.2",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.17.2.tgz",
+ "integrity": "sha512-T19My13y8uYXPw/L/k0JYaX1fJKFT/PWdXiHr8mTbXWxjVF1t+8Xl31DgBBvEKclw+1b00Chg0hxE2O7bTG7GQ==",
+ "cpu": [
+ "ppc64"
+ ],
+ "dev": true,
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-riscv64-gnu": {
+ "version": "4.17.2",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.17.2.tgz",
+ "integrity": "sha512-BOaNfthf3X3fOWAB+IJ9kxTgPmMqPPH5f5k2DcCsRrBIbWnaJCgX2ll77dV1TdSy9SaXTR5iDXRL8n7AnoP5cg==",
+ "cpu": [
+ "riscv64"
+ ],
+ "dev": true,
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-s390x-gnu": {
+ "version": "4.17.2",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.17.2.tgz",
+ "integrity": "sha512-W0UP/x7bnn3xN2eYMql2T/+wpASLE5SjObXILTMPUBDB/Fg/FxC+gX4nvCfPBCbNhz51C+HcqQp2qQ4u25ok6g==",
+ "cpu": [
+ "s390x"
+ ],
+ "dev": true,
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-x64-gnu": {
+ "version": "4.17.2",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.17.2.tgz",
+ "integrity": "sha512-Hy7pLwByUOuyaFC6mAr7m+oMC+V7qyifzs/nW2OJfC8H4hbCzOX07Ov0VFk/zP3kBsELWNFi7rJtgbKYsav9QQ==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-x64-musl": {
+ "version": "4.17.2",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.17.2.tgz",
+ "integrity": "sha512-h1+yTWeYbRdAyJ/jMiVw0l6fOOm/0D1vNLui9iPuqgRGnXA0u21gAqOyB5iHjlM9MMfNOm9RHCQ7zLIzT0x11Q==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-win32-arm64-msvc": {
+ "version": "4.17.2",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.17.2.tgz",
+ "integrity": "sha512-tmdtXMfKAjy5+IQsVtDiCfqbynAQE/TQRpWdVataHmhMb9DCoJxp9vLcCBjEQWMiUYxO1QprH/HbY9ragCEFLA==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "optional": true,
+ "os": [
+ "win32"
+ ]
+ },
+ "node_modules/@rollup/rollup-win32-ia32-msvc": {
+ "version": "4.17.2",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.17.2.tgz",
+ "integrity": "sha512-7II/QCSTAHuE5vdZaQEwJq2ZACkBpQDOmQsE6D6XUbnBHW8IAhm4eTufL6msLJorzrHDFv3CF8oCA/hSIRuZeQ==",
+ "cpu": [
+ "ia32"
+ ],
+ "dev": true,
+ "optional": true,
+ "os": [
+ "win32"
+ ]
+ },
+ "node_modules/@rollup/rollup-win32-x64-msvc": {
+ "version": "4.17.2",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.17.2.tgz",
+ "integrity": "sha512-TGGO7v7qOq4CYmSBVEYpI1Y5xDuCEnbVC5Vth8mOsW0gDSzxNrVERPc790IGHsrT2dQSimgMr9Ub3Y1Jci5/8w==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "optional": true,
+ "os": [
+ "win32"
+ ]
+ },
+ "node_modules/@types/babel__core": {
+ "version": "7.20.5",
+ "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.5.tgz",
+ "integrity": "sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==",
+ "dev": true,
+ "dependencies": {
+ "@babel/parser": "^7.20.7",
+ "@babel/types": "^7.20.7",
+ "@types/babel__generator": "*",
+ "@types/babel__template": "*",
+ "@types/babel__traverse": "*"
+ }
+ },
+ "node_modules/@types/babel__generator": {
+ "version": "7.6.8",
+ "resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.6.8.tgz",
+ "integrity": "sha512-ASsj+tpEDsEiFr1arWrlN6V3mdfjRMZt6LtK/Vp/kreFLnr5QH5+DhvD5nINYZXzwJvXeGq+05iUXcAzVrqWtw==",
+ "dev": true,
+ "dependencies": {
+ "@babel/types": "^7.0.0"
+ }
+ },
+ "node_modules/@types/babel__template": {
+ "version": "7.4.4",
+ "resolved": "https://registry.npmjs.org/@types/babel__template/-/babel__template-7.4.4.tgz",
+ "integrity": "sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A==",
+ "dev": true,
+ "dependencies": {
+ "@babel/parser": "^7.1.0",
+ "@babel/types": "^7.0.0"
+ }
+ },
+ "node_modules/@types/babel__traverse": {
+ "version": "7.20.5",
+ "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.20.5.tgz",
+ "integrity": "sha512-WXCyOcRtH37HAUkpXhUduaxdm82b4GSlyTqajXviN4EfiuPgNYR109xMCKvpl6zPIpua0DGlMEDCq+g8EdoheQ==",
+ "dev": true,
+ "dependencies": {
+ "@babel/types": "^7.20.7"
+ }
+ },
+ "node_modules/@types/estree": {
+ "version": "1.0.5",
+ "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.5.tgz",
+ "integrity": "sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==",
+ "dev": true
+ },
+ "node_modules/@types/json-schema": {
+ "version": "7.0.15",
+ "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz",
+ "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==",
+ "dev": true
+ },
+ "node_modules/@types/node": {
+ "version": "20.12.8",
+ "resolved": "https://registry.npmjs.org/@types/node/-/node-20.12.8.tgz",
+ "integrity": "sha512-NU0rJLJnshZWdE/097cdCBbyW1h4hEg0xpovcoAQYHl8dnEyp/NAOiE45pvc+Bd1Dt+2r94v2eGFpQJ4R7g+2w==",
+ "dev": true,
+ "dependencies": {
+ "undici-types": "~5.26.4"
+ }
+ },
+ "node_modules/@types/papaparse": {
+ "version": "5.3.14",
+ "resolved": "https://registry.npmjs.org/@types/papaparse/-/papaparse-5.3.14.tgz",
+ "integrity": "sha512-LxJ4iEFcpqc6METwp9f6BV6VVc43m6MfH0VqFosHvrUgfXiFe6ww7R3itkOQ+TCK6Y+Iv/+RnnvtRZnkc5Kc9g==",
+ "dev": true,
+ "dependencies": {
+ "@types/node": "*"
+ }
+ },
+ "node_modules/@types/parse-json": {
+ "version": "4.0.2",
+ "resolved": "https://registry.npmjs.org/@types/parse-json/-/parse-json-4.0.2.tgz",
+ "integrity": "sha512-dISoDXWWQwUquiKsyZ4Ng+HX2KsPL7LyHKHQwgGFEA3IaKac4Obd+h2a/a6waisAoepJlBcx9paWqjA8/HVjCw=="
+ },
+ "node_modules/@types/prop-types": {
+ "version": "15.7.12",
+ "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.12.tgz",
+ "integrity": "sha512-5zvhXYtRNRluoE/jAp4GVsSduVUzNWKkOZrCDBWYtE7biZywwdC2AcEzg+cSMLFRfVgeAFqpfNabiPjxFddV1Q=="
+ },
+ "node_modules/@types/react": {
+ "version": "18.3.1",
+ "resolved": "https://registry.npmjs.org/@types/react/-/react-18.3.1.tgz",
+ "integrity": "sha512-V0kuGBX3+prX+DQ/7r2qsv1NsdfnCLnTgnRJ1pYnxykBhGMz+qj+box5lq7XsO5mtZsBqpjwwTu/7wszPfMBcw==",
+ "dependencies": {
+ "@types/prop-types": "*",
+ "csstype": "^3.0.2"
+ }
+ },
+ "node_modules/@types/react-dom": {
+ "version": "18.3.0",
+ "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-18.3.0.tgz",
+ "integrity": "sha512-EhwApuTmMBmXuFOikhQLIBUn6uFg81SwLMOAUgodJF14SOBOCMdU04gDoYi0WOJJHD144TL32z4yDqCW3dnkQg==",
+ "dev": true,
+ "dependencies": {
+ "@types/react": "*"
+ }
+ },
+ "node_modules/@types/react-helmet": {
+ "version": "6.1.11",
+ "resolved": "https://registry.npmjs.org/@types/react-helmet/-/react-helmet-6.1.11.tgz",
+ "integrity": "sha512-0QcdGLddTERotCXo3VFlUSWO3ztraw8nZ6e3zJSgG7apwV5xt+pJUS8ewPBqT4NYB1optGLprNQzFleIY84u/g==",
+ "dev": true,
+ "dependencies": {
+ "@types/react": "*"
+ }
+ },
+ "node_modules/@types/react-transition-group": {
+ "version": "4.4.10",
+ "resolved": "https://registry.npmjs.org/@types/react-transition-group/-/react-transition-group-4.4.10.tgz",
+ "integrity": "sha512-hT/+s0VQs2ojCX823m60m5f0sL5idt9SO6Tj6Dg+rdphGPIeJbJ6CxvBYkgkGKrYeDjvIpKTR38UzmtHJOGW3Q==",
+ "dependencies": {
+ "@types/react": "*"
+ }
+ },
+ "node_modules/@types/semver": {
+ "version": "7.5.8",
+ "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.5.8.tgz",
+ "integrity": "sha512-I8EUhyrgfLrcTkzV3TSsGyl1tSuPrEDzr0yd5m90UgNxQkyDXULk3b6MlQqTCpZpNtWe1K0hzclnZkTcLBe2UQ==",
+ "dev": true
+ },
+ "node_modules/@types/sinonjs__fake-timers": {
+ "version": "8.1.1",
+ "resolved": "https://registry.npmjs.org/@types/sinonjs__fake-timers/-/sinonjs__fake-timers-8.1.1.tgz",
+ "integrity": "sha512-0kSuKjAS0TrGLJ0M/+8MaFkGsQhZpB6pxOmvS3K8FYI72K//YmdfoW9X2qPsAKh1mkwxGD5zib9s1FIFed6E8g==",
+ "dev": true
+ },
+ "node_modules/@types/sizzle": {
+ "version": "2.3.8",
+ "resolved": "https://registry.npmjs.org/@types/sizzle/-/sizzle-2.3.8.tgz",
+ "integrity": "sha512-0vWLNK2D5MT9dg0iOo8GlKguPAU02QjmZitPEsXRuJXU/OGIOt9vT9Fc26wtYuavLxtO45v9PGleoL9Z0k1LHg==",
+ "dev": true
+ },
+ "node_modules/@types/yauzl": {
+ "version": "2.10.3",
+ "resolved": "https://registry.npmjs.org/@types/yauzl/-/yauzl-2.10.3.tgz",
+ "integrity": "sha512-oJoftv0LSuaDZE3Le4DbKX+KS9G36NzOeSap90UIK0yMA/NhKJhqlSGtNDORNRaIbQfzjXDrQa0ytJ6mNRGz/Q==",
+ "dev": true,
+ "optional": true,
+ "dependencies": {
+ "@types/node": "*"
+ }
+ },
+ "node_modules/@typescript-eslint/eslint-plugin": {
+ "version": "7.8.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-7.8.0.tgz",
+ "integrity": "sha512-gFTT+ezJmkwutUPmB0skOj3GZJtlEGnlssems4AjkVweUPGj7jRwwqg0Hhg7++kPGJqKtTYx+R05Ftww372aIg==",
+ "dev": true,
+ "dependencies": {
+ "@eslint-community/regexpp": "^4.10.0",
+ "@typescript-eslint/scope-manager": "7.8.0",
+ "@typescript-eslint/type-utils": "7.8.0",
+ "@typescript-eslint/utils": "7.8.0",
+ "@typescript-eslint/visitor-keys": "7.8.0",
+ "debug": "^4.3.4",
+ "graphemer": "^1.4.0",
+ "ignore": "^5.3.1",
+ "natural-compare": "^1.4.0",
+ "semver": "^7.6.0",
+ "ts-api-utils": "^1.3.0"
+ },
+ "engines": {
+ "node": "^18.18.0 || >=20.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ },
+ "peerDependencies": {
+ "@typescript-eslint/parser": "^7.0.0",
+ "eslint": "^8.56.0"
+ },
+ "peerDependenciesMeta": {
+ "typescript": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@typescript-eslint/parser": {
+ "version": "7.8.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-7.8.0.tgz",
+ "integrity": "sha512-KgKQly1pv0l4ltcftP59uQZCi4HUYswCLbTqVZEJu7uLX8CTLyswqMLqLN+2QFz4jCptqWVV4SB7vdxcH2+0kQ==",
+ "dev": true,
+ "dependencies": {
+ "@typescript-eslint/scope-manager": "7.8.0",
+ "@typescript-eslint/types": "7.8.0",
+ "@typescript-eslint/typescript-estree": "7.8.0",
+ "@typescript-eslint/visitor-keys": "7.8.0",
+ "debug": "^4.3.4"
+ },
+ "engines": {
+ "node": "^18.18.0 || >=20.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ },
+ "peerDependencies": {
+ "eslint": "^8.56.0"
+ },
+ "peerDependenciesMeta": {
+ "typescript": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@typescript-eslint/scope-manager": {
+ "version": "7.8.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-7.8.0.tgz",
+ "integrity": "sha512-viEmZ1LmwsGcnr85gIq+FCYI7nO90DVbE37/ll51hjv9aG+YZMb4WDE2fyWpUR4O/UrhGRpYXK/XajcGTk2B8g==",
+ "dev": true,
+ "dependencies": {
+ "@typescript-eslint/types": "7.8.0",
+ "@typescript-eslint/visitor-keys": "7.8.0"
+ },
+ "engines": {
+ "node": "^18.18.0 || >=20.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ }
+ },
+ "node_modules/@typescript-eslint/type-utils": {
+ "version": "7.8.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-7.8.0.tgz",
+ "integrity": "sha512-H70R3AefQDQpz9mGv13Uhi121FNMh+WEaRqcXTX09YEDky21km4dV1ZXJIp8QjXc4ZaVkXVdohvWDzbnbHDS+A==",
+ "dev": true,
+ "dependencies": {
+ "@typescript-eslint/typescript-estree": "7.8.0",
+ "@typescript-eslint/utils": "7.8.0",
+ "debug": "^4.3.4",
+ "ts-api-utils": "^1.3.0"
+ },
+ "engines": {
+ "node": "^18.18.0 || >=20.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ },
+ "peerDependencies": {
+ "eslint": "^8.56.0"
+ },
+ "peerDependenciesMeta": {
+ "typescript": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@typescript-eslint/types": {
+ "version": "7.8.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-7.8.0.tgz",
+ "integrity": "sha512-wf0peJ+ZGlcH+2ZS23aJbOv+ztjeeP8uQ9GgwMJGVLx/Nj9CJt17GWgWWoSmoRVKAX2X+7fzEnAjxdvK2gqCLw==",
+ "dev": true,
+ "engines": {
+ "node": "^18.18.0 || >=20.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ }
+ },
+ "node_modules/@typescript-eslint/typescript-estree": {
+ "version": "7.8.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-7.8.0.tgz",
+ "integrity": "sha512-5pfUCOwK5yjPaJQNy44prjCwtr981dO8Qo9J9PwYXZ0MosgAbfEMB008dJ5sNo3+/BN6ytBPuSvXUg9SAqB0dg==",
+ "dev": true,
+ "dependencies": {
+ "@typescript-eslint/types": "7.8.0",
+ "@typescript-eslint/visitor-keys": "7.8.0",
+ "debug": "^4.3.4",
+ "globby": "^11.1.0",
+ "is-glob": "^4.0.3",
+ "minimatch": "^9.0.4",
+ "semver": "^7.6.0",
+ "ts-api-utils": "^1.3.0"
+ },
+ "engines": {
+ "node": "^18.18.0 || >=20.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ },
+ "peerDependenciesMeta": {
+ "typescript": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@typescript-eslint/utils": {
+ "version": "7.8.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-7.8.0.tgz",
+ "integrity": "sha512-L0yFqOCflVqXxiZyXrDr80lnahQfSOfc9ELAAZ75sqicqp2i36kEZZGuUymHNFoYOqxRT05up760b4iGsl02nQ==",
+ "dev": true,
+ "dependencies": {
+ "@eslint-community/eslint-utils": "^4.4.0",
+ "@types/json-schema": "^7.0.15",
+ "@types/semver": "^7.5.8",
+ "@typescript-eslint/scope-manager": "7.8.0",
+ "@typescript-eslint/types": "7.8.0",
+ "@typescript-eslint/typescript-estree": "7.8.0",
+ "semver": "^7.6.0"
+ },
+ "engines": {
+ "node": "^18.18.0 || >=20.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ },
+ "peerDependencies": {
+ "eslint": "^8.56.0"
+ }
+ },
+ "node_modules/@typescript-eslint/visitor-keys": {
+ "version": "7.8.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-7.8.0.tgz",
+ "integrity": "sha512-q4/gibTNBQNA0lGyYQCmWRS5D15n8rXh4QjK3KV+MBPlTYHpfBUT3D3PaPR/HeNiI9W6R7FvlkcGhNyAoP+caA==",
+ "dev": true,
+ "dependencies": {
+ "@typescript-eslint/types": "7.8.0",
+ "eslint-visitor-keys": "^3.4.3"
+ },
+ "engines": {
+ "node": "^18.18.0 || >=20.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ }
+ },
+ "node_modules/@ungap/structured-clone": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.2.0.tgz",
+ "integrity": "sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ==",
+ "dev": true
+ },
+ "node_modules/@vitejs/plugin-react": {
+ "version": "4.2.1",
+ "resolved": "https://registry.npmjs.org/@vitejs/plugin-react/-/plugin-react-4.2.1.tgz",
+ "integrity": "sha512-oojO9IDc4nCUUi8qIR11KoQm0XFFLIwsRBwHRR4d/88IWghn1y6ckz/bJ8GHDCsYEJee8mDzqtJxh15/cisJNQ==",
+ "dev": true,
+ "dependencies": {
+ "@babel/core": "^7.23.5",
+ "@babel/plugin-transform-react-jsx-self": "^7.23.3",
+ "@babel/plugin-transform-react-jsx-source": "^7.23.3",
+ "@types/babel__core": "^7.20.5",
+ "react-refresh": "^0.14.0"
+ },
+ "engines": {
+ "node": "^14.18.0 || >=16.0.0"
+ },
+ "peerDependencies": {
+ "vite": "^4.2.0 || ^5.0.0"
+ }
+ },
+ "node_modules/acorn": {
+ "version": "8.11.3",
+ "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.11.3.tgz",
+ "integrity": "sha512-Y9rRfJG5jcKOE0CLisYbojUjIrIEE7AGMzA/Sm4BslANhbS+cDMpgBdcPT91oJ7OuJ9hYJBx59RjbhxVnrF8Xg==",
+ "dev": true,
+ "bin": {
+ "acorn": "bin/acorn"
+ },
+ "engines": {
+ "node": ">=0.4.0"
+ }
+ },
+ "node_modules/acorn-jsx": {
+ "version": "5.3.2",
+ "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz",
+ "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==",
+ "dev": true,
+ "peerDependencies": {
+ "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0"
+ }
+ },
+ "node_modules/aggregate-error": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/aggregate-error/-/aggregate-error-3.1.0.tgz",
+ "integrity": "sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA==",
+ "dev": true,
+ "dependencies": {
+ "clean-stack": "^2.0.0",
+ "indent-string": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/ajv": {
+ "version": "6.12.6",
+ "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz",
+ "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==",
+ "dev": true,
+ "dependencies": {
+ "fast-deep-equal": "^3.1.1",
+ "fast-json-stable-stringify": "^2.0.0",
+ "json-schema-traverse": "^0.4.1",
+ "uri-js": "^4.2.2"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/epoberezkin"
+ }
+ },
+ "node_modules/ansi-colors": {
+ "version": "4.1.3",
+ "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.3.tgz",
+ "integrity": "sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw==",
+ "dev": true,
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/ansi-escapes": {
+ "version": "4.3.2",
+ "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz",
+ "integrity": "sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==",
+ "dev": true,
+ "dependencies": {
+ "type-fest": "^0.21.3"
+ },
+ "engines": {
+ "node": ">=8"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/ansi-escapes/node_modules/type-fest": {
+ "version": "0.21.3",
+ "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz",
+ "integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==",
+ "dev": true,
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/ansi-regex": {
+ "version": "5.0.1",
+ "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
+ "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==",
+ "dev": true,
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/ansi-styles": {
+ "version": "3.2.1",
+ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz",
+ "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==",
+ "dependencies": {
+ "color-convert": "^1.9.0"
+ },
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/arch": {
+ "version": "2.2.0",
+ "resolved": "https://registry.npmjs.org/arch/-/arch-2.2.0.tgz",
+ "integrity": "sha512-Of/R0wqp83cgHozfIYLbBMnej79U/SVGOOyuB3VVFv1NRM/PSFMK12x9KVtiYzJqmnU5WR2qp0Z5rHb7sWGnFQ==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/feross"
+ },
+ {
+ "type": "patreon",
+ "url": "https://www.patreon.com/feross"
+ },
+ {
+ "type": "consulting",
+ "url": "https://feross.org/support"
+ }
+ ]
+ },
+ "node_modules/argparse": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz",
+ "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==",
+ "dev": true
+ },
+ "node_modules/array-union": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz",
+ "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==",
+ "dev": true,
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/asn1": {
+ "version": "0.2.6",
+ "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.6.tgz",
+ "integrity": "sha512-ix/FxPn0MDjeyJ7i/yoHGFt/EX6LyNbxSEhPPXODPL+KB0VPk86UYfL0lMdy+KCnv+fmvIzySwaK5COwqVbWTQ==",
+ "dev": true,
+ "dependencies": {
+ "safer-buffer": "~2.1.0"
+ }
+ },
+ "node_modules/assert-plus": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz",
+ "integrity": "sha512-NfJ4UzBCcQGLDlQq7nHxH+tv3kyZ0hHQqF5BO6J7tNJeP5do1llPr8dZ8zHonfhAu0PHAdMkSo+8o0wxg9lZWw==",
+ "dev": true,
+ "engines": {
+ "node": ">=0.8"
+ }
+ },
+ "node_modules/astral-regex": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-2.0.0.tgz",
+ "integrity": "sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ==",
+ "dev": true,
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/async": {
+ "version": "3.2.5",
+ "resolved": "https://registry.npmjs.org/async/-/async-3.2.5.tgz",
+ "integrity": "sha512-baNZyqaaLhyLVKm/DlvdW051MSgO6b8eVfIezl9E5PqWxFgzLm/wQntEW4zOytVburDEr0JlALEpdOFwvErLsg==",
+ "dev": true
+ },
+ "node_modules/asynckit": {
+ "version": "0.4.0",
+ "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz",
+ "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q=="
+ },
+ "node_modules/at-least-node": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/at-least-node/-/at-least-node-1.0.0.tgz",
+ "integrity": "sha512-+q/t7Ekv1EDY2l6Gda6LLiX14rU9TV20Wa3ofeQmwPFZbOMo9DXrLbOjFaaclkXKWidIaopwAObQDqwWtGUjqg==",
+ "dev": true,
+ "engines": {
+ "node": ">= 4.0.0"
+ }
+ },
+ "node_modules/aws-sign2": {
+ "version": "0.7.0",
+ "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz",
+ "integrity": "sha512-08kcGqnYf/YmjoRhfxyu+CLxBjUtHLXLXX/vUfx9l2LYzG3c1m61nrpyFUZI6zeS+Li/wWMMidD9KgrqtGq3mA==",
+ "dev": true,
+ "engines": {
+ "node": "*"
+ }
+ },
+ "node_modules/aws4": {
+ "version": "1.12.0",
+ "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.12.0.tgz",
+ "integrity": "sha512-NmWvPnx0F1SfrQbYwOi7OeaNGokp9XhzNioJ/CSBs8Qa4vxug81mhJEAVZwxXuBmYB5KDRfMq/F3RR0BIU7sWg==",
+ "dev": true
+ },
+ "node_modules/axios": {
+ "version": "1.6.8",
+ "resolved": "https://registry.npmjs.org/axios/-/axios-1.6.8.tgz",
+ "integrity": "sha512-v/ZHtJDU39mDpyBoFVkETcd/uNdxrWRrg3bKpOKzXFA6Bvqopts6ALSMU3y6ijYxbw2B+wPrIv46egTzJXCLGQ==",
+ "dependencies": {
+ "follow-redirects": "^1.15.6",
+ "form-data": "^4.0.0",
+ "proxy-from-env": "^1.1.0"
+ }
+ },
+ "node_modules/babel-plugin-macros": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/babel-plugin-macros/-/babel-plugin-macros-3.1.0.tgz",
+ "integrity": "sha512-Cg7TFGpIr01vOQNODXOOaGz2NpCU5gl8x1qJFbb6hbZxR7XrcE2vtbAsTAbJ7/xwJtUuJEw8K8Zr/AE0LHlesg==",
+ "dependencies": {
+ "@babel/runtime": "^7.12.5",
+ "cosmiconfig": "^7.0.0",
+ "resolve": "^1.19.0"
+ },
+ "engines": {
+ "node": ">=10",
+ "npm": ">=6"
+ }
+ },
+ "node_modules/balanced-match": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
+ "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==",
+ "dev": true
+ },
+ "node_modules/base64-js": {
+ "version": "1.5.1",
+ "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz",
+ "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/feross"
+ },
+ {
+ "type": "patreon",
+ "url": "https://www.patreon.com/feross"
+ },
+ {
+ "type": "consulting",
+ "url": "https://feross.org/support"
+ }
+ ]
+ },
+ "node_modules/bcrypt-pbkdf": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz",
+ "integrity": "sha512-qeFIXtP4MSoi6NLqO12WfqARWWuCKi2Rn/9hJLEmtB5yTNr9DqFWkJRCf2qShWzPeAMRnOgCrq0sg/KLv5ES9w==",
+ "dev": true,
+ "dependencies": {
+ "tweetnacl": "^0.14.3"
+ }
+ },
+ "node_modules/blob-util": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/blob-util/-/blob-util-2.0.2.tgz",
+ "integrity": "sha512-T7JQa+zsXXEa6/8ZhHcQEW1UFfVM49Ts65uBkFL6fz2QmrElqmbajIDJvuA0tEhRe5eIjpV9ZF+0RfZR9voJFQ==",
+ "dev": true
+ },
+ "node_modules/bluebird": {
+ "version": "3.7.2",
+ "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.7.2.tgz",
+ "integrity": "sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg==",
+ "dev": true
+ },
+ "node_modules/brace-expansion": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz",
+ "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==",
+ "dev": true,
+ "dependencies": {
+ "balanced-match": "^1.0.0"
+ }
+ },
+ "node_modules/braces": {
+ "version": "3.0.2",
+ "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz",
+ "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==",
+ "dev": true,
+ "dependencies": {
+ "fill-range": "^7.0.1"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/browserslist": {
+ "version": "4.23.0",
+ "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.23.0.tgz",
+ "integrity": "sha512-QW8HiM1shhT2GuzkvklfjcKDiWFXHOeFCIA/huJPwHsslwcydgk7X+z2zXpEijP98UCY7HbubZt5J2Zgvf0CaQ==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/browserslist"
+ },
+ {
+ "type": "tidelift",
+ "url": "https://tidelift.com/funding/github/npm/browserslist"
+ },
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/ai"
+ }
+ ],
+ "dependencies": {
+ "caniuse-lite": "^1.0.30001587",
+ "electron-to-chromium": "^1.4.668",
+ "node-releases": "^2.0.14",
+ "update-browserslist-db": "^1.0.13"
+ },
+ "bin": {
+ "browserslist": "cli.js"
+ },
+ "engines": {
+ "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7"
+ }
+ },
+ "node_modules/buffer": {
+ "version": "5.7.1",
+ "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz",
+ "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/feross"
+ },
+ {
+ "type": "patreon",
+ "url": "https://www.patreon.com/feross"
+ },
+ {
+ "type": "consulting",
+ "url": "https://feross.org/support"
+ }
+ ],
+ "dependencies": {
+ "base64-js": "^1.3.1",
+ "ieee754": "^1.1.13"
+ }
+ },
+ "node_modules/buffer-crc32": {
+ "version": "0.2.13",
+ "resolved": "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-0.2.13.tgz",
+ "integrity": "sha512-VO9Ht/+p3SN7SKWqcrgEzjGbRSJYTx+Q1pTQC0wrWqHx0vpJraQ6GtHx8tvcg1rlK1byhU5gccxgOgj7B0TDkQ==",
+ "dev": true,
+ "engines": {
+ "node": "*"
+ }
+ },
+ "node_modules/cachedir": {
+ "version": "2.4.0",
+ "resolved": "https://registry.npmjs.org/cachedir/-/cachedir-2.4.0.tgz",
+ "integrity": "sha512-9EtFOZR8g22CL7BWjJ9BUx1+A/djkofnyW3aOXZORNW2kxoUpx2h+uN2cOqwPmFhnpVmxg+KW2OjOSgChTEvsQ==",
+ "dev": true,
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/call-bind": {
+ "version": "1.0.7",
+ "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.7.tgz",
+ "integrity": "sha512-GHTSNSYICQ7scH7sZ+M2rFopRoLh8t2bLSW6BbgrtLsahOIB5iyAVJf9GjWK3cYTDaMj4XdBpM1cA6pIS0Kv2w==",
+ "dev": true,
+ "dependencies": {
+ "es-define-property": "^1.0.0",
+ "es-errors": "^1.3.0",
+ "function-bind": "^1.1.2",
+ "get-intrinsic": "^1.2.4",
+ "set-function-length": "^1.2.1"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/callsites": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz",
+ "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==",
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/caniuse-lite": {
+ "version": "1.0.30001616",
+ "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001616.tgz",
+ "integrity": "sha512-RHVYKov7IcdNjVHJFNY/78RdG4oGVjbayxv8u5IO74Wv7Hlq4PnJE6mo/OjFijjVFNy5ijnCt6H3IIo4t+wfEw==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/browserslist"
+ },
+ {
+ "type": "tidelift",
+ "url": "https://tidelift.com/funding/github/npm/caniuse-lite"
+ },
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/ai"
+ }
+ ]
+ },
+ "node_modules/caseless": {
+ "version": "0.12.0",
+ "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz",
+ "integrity": "sha512-4tYFyifaFfGacoiObjJegolkwSU4xQNGbVgUiNYVUxbQ2x2lUsFvY4hVgVzGiIe6WLOPqycWXA40l+PWsxthUw==",
+ "dev": true
+ },
+ "node_modules/chalk": {
+ "version": "2.4.2",
+ "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz",
+ "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==",
+ "dependencies": {
+ "ansi-styles": "^3.2.1",
+ "escape-string-regexp": "^1.0.5",
+ "supports-color": "^5.3.0"
+ },
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/chalk/node_modules/escape-string-regexp": {
+ "version": "1.0.5",
+ "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz",
+ "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==",
+ "engines": {
+ "node": ">=0.8.0"
+ }
+ },
+ "node_modules/check-more-types": {
+ "version": "2.24.0",
+ "resolved": "https://registry.npmjs.org/check-more-types/-/check-more-types-2.24.0.tgz",
+ "integrity": "sha512-Pj779qHxV2tuapviy1bSZNEL1maXr13bPYpsvSDB68HlYcYuhlDrmGd63i0JHMCLKzc7rUSNIrpdJlhVlNwrxA==",
+ "dev": true,
+ "engines": {
+ "node": ">= 0.8.0"
+ }
+ },
+ "node_modules/ci-info": {
+ "version": "3.9.0",
+ "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.9.0.tgz",
+ "integrity": "sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/sibiraj-s"
+ }
+ ],
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/clean-stack": {
+ "version": "2.2.0",
+ "resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-2.2.0.tgz",
+ "integrity": "sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A==",
+ "dev": true,
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/cli-cursor": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-3.1.0.tgz",
+ "integrity": "sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==",
+ "dev": true,
+ "dependencies": {
+ "restore-cursor": "^3.1.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/cli-table3": {
+ "version": "0.6.5",
+ "resolved": "https://registry.npmjs.org/cli-table3/-/cli-table3-0.6.5.tgz",
+ "integrity": "sha512-+W/5efTR7y5HRD7gACw9yQjqMVvEMLBHmboM/kPWam+H+Hmyrgjh6YncVKK122YZkXrLudzTuAukUw9FnMf7IQ==",
+ "dev": true,
+ "dependencies": {
+ "string-width": "^4.2.0"
+ },
+ "engines": {
+ "node": "10.* || >= 12.*"
+ },
+ "optionalDependencies": {
+ "@colors/colors": "1.5.0"
+ }
+ },
+ "node_modules/cli-truncate": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/cli-truncate/-/cli-truncate-2.1.0.tgz",
+ "integrity": "sha512-n8fOixwDD6b/ObinzTrp1ZKFzbgvKZvuz/TvejnLn1aQfC6r52XEx85FmuC+3HI+JM7coBRXUvNqEU2PHVrHpg==",
+ "dev": true,
+ "dependencies": {
+ "slice-ansi": "^3.0.0",
+ "string-width": "^4.2.0"
+ },
+ "engines": {
+ "node": ">=8"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/clsx": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz",
+ "integrity": "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==",
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/color-convert": {
+ "version": "1.9.3",
+ "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz",
+ "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==",
+ "dependencies": {
+ "color-name": "1.1.3"
+ }
+ },
+ "node_modules/color-name": {
+ "version": "1.1.3",
+ "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz",
+ "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw=="
+ },
+ "node_modules/colorette": {
+ "version": "2.0.20",
+ "resolved": "https://registry.npmjs.org/colorette/-/colorette-2.0.20.tgz",
+ "integrity": "sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w==",
+ "dev": true
+ },
+ "node_modules/combined-stream": {
+ "version": "1.0.8",
+ "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz",
+ "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==",
+ "dependencies": {
+ "delayed-stream": "~1.0.0"
+ },
+ "engines": {
+ "node": ">= 0.8"
+ }
+ },
+ "node_modules/commander": {
+ "version": "6.2.1",
+ "resolved": "https://registry.npmjs.org/commander/-/commander-6.2.1.tgz",
+ "integrity": "sha512-U7VdrJFnJgo4xjrHpTzu0yrHPGImdsmD95ZlgYSEajAn2JKzDhDTPG9kBTefmObL2w/ngeZnilk+OV9CG3d7UA==",
+ "dev": true,
+ "engines": {
+ "node": ">= 6"
+ }
+ },
+ "node_modules/common-tags": {
+ "version": "1.8.2",
+ "resolved": "https://registry.npmjs.org/common-tags/-/common-tags-1.8.2.tgz",
+ "integrity": "sha512-gk/Z852D2Wtb//0I+kRFNKKE9dIIVirjoqPoA1wJU+XePVXZfGeBpk45+A1rKO4Q43prqWBNY/MiIeRLbPWUaA==",
+ "dev": true,
+ "engines": {
+ "node": ">=4.0.0"
+ }
+ },
+ "node_modules/concat-map": {
+ "version": "0.0.1",
+ "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
+ "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==",
+ "dev": true
+ },
+ "node_modules/convert-source-map": {
+ "version": "1.9.0",
+ "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.9.0.tgz",
+ "integrity": "sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A=="
+ },
+ "node_modules/core-util-is": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz",
+ "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ=="
+ },
+ "node_modules/cosmiconfig": {
+ "version": "7.1.0",
+ "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-7.1.0.tgz",
+ "integrity": "sha512-AdmX6xUzdNASswsFtmwSt7Vj8po9IuqXm0UXz7QKPuEUmPB4XyjGfaAr2PSuELMwkRMVH1EpIkX5bTZGRB3eCA==",
+ "dependencies": {
+ "@types/parse-json": "^4.0.0",
+ "import-fresh": "^3.2.1",
+ "parse-json": "^5.0.0",
+ "path-type": "^4.0.0",
+ "yaml": "^1.10.0"
+ },
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/cross-fetch": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/cross-fetch/-/cross-fetch-4.0.0.tgz",
+ "integrity": "sha512-e4a5N8lVvuLgAWgnCrLr2PP0YyDOTHa9H/Rj54dirp61qXnNq46m82bRhNqIA5VccJtWBvPTFRV3TtvHUKPB1g==",
+ "dependencies": {
+ "node-fetch": "^2.6.12"
+ }
+ },
+ "node_modules/cross-spawn": {
+ "version": "7.0.3",
+ "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz",
+ "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==",
+ "dev": true,
+ "dependencies": {
+ "path-key": "^3.1.0",
+ "shebang-command": "^2.0.0",
+ "which": "^2.0.1"
+ },
+ "engines": {
+ "node": ">= 8"
+ }
+ },
+ "node_modules/csstype": {
+ "version": "3.1.3",
+ "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz",
+ "integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw=="
+ },
+ "node_modules/cypress": {
+ "version": "13.9.0",
+ "resolved": "https://registry.npmjs.org/cypress/-/cypress-13.9.0.tgz",
+ "integrity": "sha512-atNjmYfHsvTuCaxTxLZr9xGoHz53LLui3266WWxXJHY7+N6OdwJdg/feEa3T+buez9dmUXHT1izCOklqG82uCQ==",
+ "dev": true,
+ "hasInstallScript": true,
+ "dependencies": {
+ "@cypress/request": "^3.0.0",
+ "@cypress/xvfb": "^1.2.4",
+ "@types/sinonjs__fake-timers": "8.1.1",
+ "@types/sizzle": "^2.3.2",
+ "arch": "^2.2.0",
+ "blob-util": "^2.0.2",
+ "bluebird": "^3.7.2",
+ "buffer": "^5.7.1",
+ "cachedir": "^2.3.0",
+ "chalk": "^4.1.0",
+ "check-more-types": "^2.24.0",
+ "cli-cursor": "^3.1.0",
+ "cli-table3": "~0.6.1",
+ "commander": "^6.2.1",
+ "common-tags": "^1.8.0",
+ "dayjs": "^1.10.4",
+ "debug": "^4.3.4",
+ "enquirer": "^2.3.6",
+ "eventemitter2": "6.4.7",
+ "execa": "4.1.0",
+ "executable": "^4.1.1",
+ "extract-zip": "2.0.1",
+ "figures": "^3.2.0",
+ "fs-extra": "^9.1.0",
+ "getos": "^3.2.1",
+ "is-ci": "^3.0.1",
+ "is-installed-globally": "~0.4.0",
+ "lazy-ass": "^1.6.0",
+ "listr2": "^3.8.3",
+ "lodash": "^4.17.21",
+ "log-symbols": "^4.0.0",
+ "minimist": "^1.2.8",
+ "ospath": "^1.2.2",
+ "pretty-bytes": "^5.6.0",
+ "process": "^0.11.10",
+ "proxy-from-env": "1.0.0",
+ "request-progress": "^3.0.0",
+ "semver": "^7.5.3",
+ "supports-color": "^8.1.1",
+ "tmp": "~0.2.1",
+ "untildify": "^4.0.0",
+ "yauzl": "^2.10.0"
+ },
+ "bin": {
+ "cypress": "bin/cypress"
+ },
+ "engines": {
+ "node": "^16.0.0 || ^18.0.0 || >=20.0.0"
+ }
+ },
+ "node_modules/cypress/node_modules/ansi-styles": {
+ "version": "4.3.0",
+ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
+ "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
+ "dev": true,
+ "dependencies": {
+ "color-convert": "^2.0.1"
+ },
+ "engines": {
+ "node": ">=8"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/ansi-styles?sponsor=1"
+ }
+ },
+ "node_modules/cypress/node_modules/chalk": {
+ "version": "4.1.2",
+ "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
+ "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
+ "dev": true,
+ "dependencies": {
+ "ansi-styles": "^4.1.0",
+ "supports-color": "^7.1.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/chalk?sponsor=1"
+ }
+ },
+ "node_modules/cypress/node_modules/chalk/node_modules/supports-color": {
+ "version": "7.2.0",
+ "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
+ "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
+ "dev": true,
+ "dependencies": {
+ "has-flag": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/cypress/node_modules/color-convert": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
+ "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
+ "dev": true,
+ "dependencies": {
+ "color-name": "~1.1.4"
+ },
+ "engines": {
+ "node": ">=7.0.0"
+ }
+ },
+ "node_modules/cypress/node_modules/color-name": {
+ "version": "1.1.4",
+ "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
+ "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
+ "dev": true
+ },
+ "node_modules/cypress/node_modules/has-flag": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
+ "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
+ "dev": true,
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/cypress/node_modules/proxy-from-env": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.0.0.tgz",
+ "integrity": "sha512-F2JHgJQ1iqwnHDcQjVBsq3n/uoaFL+iPW/eAeL7kVxy/2RrWaN4WroKjjvbsoRtv0ftelNyC01bjRhn/bhcf4A==",
+ "dev": true
+ },
+ "node_modules/cypress/node_modules/supports-color": {
+ "version": "8.1.1",
+ "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz",
+ "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==",
+ "dev": true,
+ "dependencies": {
+ "has-flag": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/supports-color?sponsor=1"
+ }
+ },
+ "node_modules/dashdash": {
+ "version": "1.14.1",
+ "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz",
+ "integrity": "sha512-jRFi8UDGo6j+odZiEpjazZaWqEal3w/basFjQHQEwVtZJGDpxbH1MeYluwCS8Xq5wmLJooDlMgvVarmWfGM44g==",
+ "dev": true,
+ "dependencies": {
+ "assert-plus": "^1.0.0"
+ },
+ "engines": {
+ "node": ">=0.10"
+ }
+ },
+ "node_modules/dayjs": {
+ "version": "1.11.11",
+ "resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.11.11.tgz",
+ "integrity": "sha512-okzr3f11N6WuqYtZSvm+F776mB41wRZMhKP+hc34YdW+KmtYYK9iqvHSwo2k9FEH3fhGXvOPV6yz2IcSrfRUDg=="
+ },
+ "node_modules/debug": {
+ "version": "4.3.4",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz",
+ "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==",
+ "dev": true,
+ "dependencies": {
+ "ms": "2.1.2"
+ },
+ "engines": {
+ "node": ">=6.0"
+ },
+ "peerDependenciesMeta": {
+ "supports-color": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/deep-is": {
+ "version": "0.1.4",
+ "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz",
+ "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==",
+ "dev": true
+ },
+ "node_modules/define-data-property": {
+ "version": "1.1.4",
+ "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz",
+ "integrity": "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==",
+ "dev": true,
+ "dependencies": {
+ "es-define-property": "^1.0.0",
+ "es-errors": "^1.3.0",
+ "gopd": "^1.0.1"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/delayed-stream": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz",
+ "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==",
+ "engines": {
+ "node": ">=0.4.0"
+ }
+ },
+ "node_modules/dir-glob": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz",
+ "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==",
+ "dev": true,
+ "dependencies": {
+ "path-type": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/doctrine": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz",
+ "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==",
+ "dev": true,
+ "dependencies": {
+ "esutils": "^2.0.2"
+ },
+ "engines": {
+ "node": ">=6.0.0"
+ }
+ },
+ "node_modules/dom-helpers": {
+ "version": "5.2.1",
+ "resolved": "https://registry.npmjs.org/dom-helpers/-/dom-helpers-5.2.1.tgz",
+ "integrity": "sha512-nRCa7CK3VTrM2NmGkIy4cbK7IZlgBE/PYMn55rrXefr5xXDP0LdtfPnblFDoVdcAfslJ7or6iqAUnx0CCGIWQA==",
+ "dependencies": {
+ "@babel/runtime": "^7.8.7",
+ "csstype": "^3.0.2"
+ }
+ },
+ "node_modules/ecc-jsbn": {
+ "version": "0.1.2",
+ "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz",
+ "integrity": "sha512-eh9O+hwRHNbG4BLTjEl3nw044CkGm5X6LoaCf7LPp7UU8Qrt47JYNi6nPX8xjW97TKGKm1ouctg0QSpZe9qrnw==",
+ "dev": true,
+ "dependencies": {
+ "jsbn": "~0.1.0",
+ "safer-buffer": "^2.1.0"
+ }
+ },
+ "node_modules/electron-to-chromium": {
+ "version": "1.4.756",
+ "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.756.tgz",
+ "integrity": "sha512-RJKZ9+vEBMeiPAvKNWyZjuYyUqMndcP1f335oHqn3BEQbs2NFtVrnK5+6Xg5wSM9TknNNpWghGDUCKGYF+xWXw==",
+ "dev": true
+ },
+ "node_modules/emoji-regex": {
+ "version": "8.0.0",
+ "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
+ "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==",
+ "dev": true
+ },
+ "node_modules/end-of-stream": {
+ "version": "1.4.4",
+ "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz",
+ "integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==",
+ "dev": true,
+ "dependencies": {
+ "once": "^1.4.0"
+ }
+ },
+ "node_modules/enquirer": {
+ "version": "2.4.1",
+ "resolved": "https://registry.npmjs.org/enquirer/-/enquirer-2.4.1.tgz",
+ "integrity": "sha512-rRqJg/6gd538VHvR3PSrdRBb/1Vy2YfzHqzvbhGIQpDRKIa4FgV/54b5Q1xYSxOOwKvjXweS26E0Q+nAMwp2pQ==",
+ "dev": true,
+ "dependencies": {
+ "ansi-colors": "^4.1.1",
+ "strip-ansi": "^6.0.1"
+ },
+ "engines": {
+ "node": ">=8.6"
+ }
+ },
+ "node_modules/error-ex": {
+ "version": "1.3.2",
+ "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz",
+ "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==",
+ "dependencies": {
+ "is-arrayish": "^0.2.1"
+ }
+ },
+ "node_modules/es-define-property": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.0.tgz",
+ "integrity": "sha512-jxayLKShrEqqzJ0eumQbVhTYQM27CfT1T35+gCgDFoL82JLsXqTJ76zv6A0YLOgEnLUMvLzsDsGIrl8NFpT2gQ==",
+ "dev": true,
+ "dependencies": {
+ "get-intrinsic": "^1.2.4"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/es-errors": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz",
+ "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==",
+ "dev": true,
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/esbuild": {
+ "version": "0.20.2",
+ "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.20.2.tgz",
+ "integrity": "sha512-WdOOppmUNU+IbZ0PaDiTst80zjnrOkyJNHoKupIcVyU8Lvla3Ugx94VzkQ32Ijqd7UhHJy75gNWDMUekcrSJ6g==",
+ "dev": true,
+ "hasInstallScript": true,
+ "bin": {
+ "esbuild": "bin/esbuild"
+ },
+ "engines": {
+ "node": ">=12"
+ },
+ "optionalDependencies": {
+ "@esbuild/aix-ppc64": "0.20.2",
+ "@esbuild/android-arm": "0.20.2",
+ "@esbuild/android-arm64": "0.20.2",
+ "@esbuild/android-x64": "0.20.2",
+ "@esbuild/darwin-arm64": "0.20.2",
+ "@esbuild/darwin-x64": "0.20.2",
+ "@esbuild/freebsd-arm64": "0.20.2",
+ "@esbuild/freebsd-x64": "0.20.2",
+ "@esbuild/linux-arm": "0.20.2",
+ "@esbuild/linux-arm64": "0.20.2",
+ "@esbuild/linux-ia32": "0.20.2",
+ "@esbuild/linux-loong64": "0.20.2",
+ "@esbuild/linux-mips64el": "0.20.2",
+ "@esbuild/linux-ppc64": "0.20.2",
+ "@esbuild/linux-riscv64": "0.20.2",
+ "@esbuild/linux-s390x": "0.20.2",
+ "@esbuild/linux-x64": "0.20.2",
+ "@esbuild/netbsd-x64": "0.20.2",
+ "@esbuild/openbsd-x64": "0.20.2",
+ "@esbuild/sunos-x64": "0.20.2",
+ "@esbuild/win32-arm64": "0.20.2",
+ "@esbuild/win32-ia32": "0.20.2",
+ "@esbuild/win32-x64": "0.20.2"
+ }
+ },
+ "node_modules/escalade": {
+ "version": "3.1.2",
+ "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.2.tgz",
+ "integrity": "sha512-ErCHMCae19vR8vQGe50xIsVomy19rg6gFu3+r3jkEO46suLMWBksvVyoGgQV+jOfl84ZSOSlmv6Gxa89PmTGmA==",
+ "dev": true,
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/escape-string-regexp": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz",
+ "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==",
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/eslint": {
+ "version": "8.57.0",
+ "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.57.0.tgz",
+ "integrity": "sha512-dZ6+mexnaTIbSBZWgou51U6OmzIhYM2VcNdtiTtI7qPNZm35Akpr0f6vtw3w1Kmn5PYo+tZVfh13WrhpS6oLqQ==",
+ "dev": true,
+ "dependencies": {
+ "@eslint-community/eslint-utils": "^4.2.0",
+ "@eslint-community/regexpp": "^4.6.1",
+ "@eslint/eslintrc": "^2.1.4",
+ "@eslint/js": "8.57.0",
+ "@humanwhocodes/config-array": "^0.11.14",
+ "@humanwhocodes/module-importer": "^1.0.1",
+ "@nodelib/fs.walk": "^1.2.8",
+ "@ungap/structured-clone": "^1.2.0",
+ "ajv": "^6.12.4",
+ "chalk": "^4.0.0",
+ "cross-spawn": "^7.0.2",
+ "debug": "^4.3.2",
+ "doctrine": "^3.0.0",
+ "escape-string-regexp": "^4.0.0",
+ "eslint-scope": "^7.2.2",
+ "eslint-visitor-keys": "^3.4.3",
+ "espree": "^9.6.1",
+ "esquery": "^1.4.2",
+ "esutils": "^2.0.2",
+ "fast-deep-equal": "^3.1.3",
+ "file-entry-cache": "^6.0.1",
+ "find-up": "^5.0.0",
+ "glob-parent": "^6.0.2",
+ "globals": "^13.19.0",
+ "graphemer": "^1.4.0",
+ "ignore": "^5.2.0",
+ "imurmurhash": "^0.1.4",
+ "is-glob": "^4.0.0",
+ "is-path-inside": "^3.0.3",
+ "js-yaml": "^4.1.0",
+ "json-stable-stringify-without-jsonify": "^1.0.1",
+ "levn": "^0.4.1",
+ "lodash.merge": "^4.6.2",
+ "minimatch": "^3.1.2",
+ "natural-compare": "^1.4.0",
+ "optionator": "^0.9.3",
+ "strip-ansi": "^6.0.1",
+ "text-table": "^0.2.0"
+ },
+ "bin": {
+ "eslint": "bin/eslint.js"
+ },
+ "engines": {
+ "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/eslint"
+ }
+ },
+ "node_modules/eslint-config-prettier": {
+ "version": "9.1.0",
+ "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-9.1.0.tgz",
+ "integrity": "sha512-NSWl5BFQWEPi1j4TjVNItzYV7dZXZ+wP6I6ZhrBGpChQhZRUaElihE9uRRkcbRnNb76UMKDF3r+WTmNcGPKsqw==",
+ "dev": true,
+ "bin": {
+ "eslint-config-prettier": "bin/cli.js"
+ },
+ "peerDependencies": {
+ "eslint": ">=7.0.0"
+ }
+ },
+ "node_modules/eslint-plugin-prettier": {
+ "version": "5.1.3",
+ "resolved": "https://registry.npmjs.org/eslint-plugin-prettier/-/eslint-plugin-prettier-5.1.3.tgz",
+ "integrity": "sha512-C9GCVAs4Eq7ZC/XFQHITLiHJxQngdtraXaM+LoUFoFp/lHNl2Zn8f3WQbe9HvTBBQ9YnKFB0/2Ajdqwo5D1EAw==",
+ "dev": true,
+ "dependencies": {
+ "prettier-linter-helpers": "^1.0.0",
+ "synckit": "^0.8.6"
+ },
+ "engines": {
+ "node": "^14.18.0 || >=16.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/eslint-plugin-prettier"
+ },
+ "peerDependencies": {
+ "@types/eslint": ">=8.0.0",
+ "eslint": ">=8.0.0",
+ "eslint-config-prettier": "*",
+ "prettier": ">=3.0.0"
+ },
+ "peerDependenciesMeta": {
+ "@types/eslint": {
+ "optional": true
+ },
+ "eslint-config-prettier": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/eslint-plugin-react-hooks": {
+ "version": "4.6.2",
+ "resolved": "https://registry.npmjs.org/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-4.6.2.tgz",
+ "integrity": "sha512-QzliNJq4GinDBcD8gPB5v0wh6g8q3SUi6EFF0x8N/BL9PoVs0atuGc47ozMRyOWAKdwaZ5OnbOEa3WR+dSGKuQ==",
+ "dev": true,
+ "engines": {
+ "node": ">=10"
+ },
+ "peerDependencies": {
+ "eslint": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0"
+ }
+ },
+ "node_modules/eslint-plugin-react-refresh": {
+ "version": "0.4.6",
+ "resolved": "https://registry.npmjs.org/eslint-plugin-react-refresh/-/eslint-plugin-react-refresh-0.4.6.tgz",
+ "integrity": "sha512-NjGXdm7zgcKRkKMua34qVO9doI7VOxZ6ancSvBELJSSoX97jyndXcSoa8XBh69JoB31dNz3EEzlMcizZl7LaMA==",
+ "dev": true,
+ "peerDependencies": {
+ "eslint": ">=7"
+ }
+ },
+ "node_modules/eslint-scope": {
+ "version": "7.2.2",
+ "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.2.2.tgz",
+ "integrity": "sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==",
+ "dev": true,
+ "dependencies": {
+ "esrecurse": "^4.3.0",
+ "estraverse": "^5.2.0"
+ },
+ "engines": {
+ "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/eslint"
+ }
+ },
+ "node_modules/eslint-visitor-keys": {
+ "version": "3.4.3",
+ "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz",
+ "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==",
+ "dev": true,
+ "engines": {
+ "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/eslint"
+ }
+ },
+ "node_modules/eslint/node_modules/ansi-styles": {
+ "version": "4.3.0",
+ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
+ "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
+ "dev": true,
+ "dependencies": {
+ "color-convert": "^2.0.1"
+ },
+ "engines": {
+ "node": ">=8"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/ansi-styles?sponsor=1"
+ }
+ },
+ "node_modules/eslint/node_modules/brace-expansion": {
+ "version": "1.1.11",
+ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
+ "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
+ "dev": true,
+ "dependencies": {
+ "balanced-match": "^1.0.0",
+ "concat-map": "0.0.1"
+ }
+ },
+ "node_modules/eslint/node_modules/chalk": {
+ "version": "4.1.2",
+ "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
+ "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
+ "dev": true,
+ "dependencies": {
+ "ansi-styles": "^4.1.0",
+ "supports-color": "^7.1.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/chalk?sponsor=1"
+ }
+ },
+ "node_modules/eslint/node_modules/color-convert": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
+ "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
+ "dev": true,
+ "dependencies": {
+ "color-name": "~1.1.4"
+ },
+ "engines": {
+ "node": ">=7.0.0"
+ }
+ },
+ "node_modules/eslint/node_modules/color-name": {
+ "version": "1.1.4",
+ "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
+ "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
+ "dev": true
+ },
+ "node_modules/eslint/node_modules/globals": {
+ "version": "13.24.0",
+ "resolved": "https://registry.npmjs.org/globals/-/globals-13.24.0.tgz",
+ "integrity": "sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==",
+ "dev": true,
+ "dependencies": {
+ "type-fest": "^0.20.2"
+ },
+ "engines": {
+ "node": ">=8"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/eslint/node_modules/has-flag": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
+ "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
+ "dev": true,
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/eslint/node_modules/minimatch": {
+ "version": "3.1.2",
+ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
+ "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==",
+ "dev": true,
+ "dependencies": {
+ "brace-expansion": "^1.1.7"
+ },
+ "engines": {
+ "node": "*"
+ }
+ },
+ "node_modules/eslint/node_modules/supports-color": {
+ "version": "7.2.0",
+ "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
+ "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
+ "dev": true,
+ "dependencies": {
+ "has-flag": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/espree": {
+ "version": "9.6.1",
+ "resolved": "https://registry.npmjs.org/espree/-/espree-9.6.1.tgz",
+ "integrity": "sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==",
+ "dev": true,
+ "dependencies": {
+ "acorn": "^8.9.0",
+ "acorn-jsx": "^5.3.2",
+ "eslint-visitor-keys": "^3.4.1"
+ },
+ "engines": {
+ "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/eslint"
+ }
+ },
+ "node_modules/esquery": {
+ "version": "1.5.0",
+ "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.5.0.tgz",
+ "integrity": "sha512-YQLXUplAwJgCydQ78IMJywZCceoqk1oH01OERdSAJc/7U2AylwjhSCLDEtqwg811idIS/9fIU5GjG73IgjKMVg==",
+ "dev": true,
+ "dependencies": {
+ "estraverse": "^5.1.0"
+ },
+ "engines": {
+ "node": ">=0.10"
+ }
+ },
+ "node_modules/esrecurse": {
+ "version": "4.3.0",
+ "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz",
+ "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==",
+ "dev": true,
+ "dependencies": {
+ "estraverse": "^5.2.0"
+ },
+ "engines": {
+ "node": ">=4.0"
+ }
+ },
+ "node_modules/estraverse": {
+ "version": "5.3.0",
+ "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz",
+ "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==",
+ "dev": true,
+ "engines": {
+ "node": ">=4.0"
+ }
+ },
+ "node_modules/esutils": {
+ "version": "2.0.3",
+ "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz",
+ "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==",
+ "dev": true,
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/eventemitter2": {
+ "version": "6.4.7",
+ "resolved": "https://registry.npmjs.org/eventemitter2/-/eventemitter2-6.4.7.tgz",
+ "integrity": "sha512-tYUSVOGeQPKt/eC1ABfhHy5Xd96N3oIijJvN3O9+TsC28T5V9yX9oEfEK5faP0EFSNVOG97qtAS68GBrQB2hDg==",
+ "dev": true
+ },
+ "node_modules/execa": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/execa/-/execa-4.1.0.tgz",
+ "integrity": "sha512-j5W0//W7f8UxAn8hXVnwG8tLwdiUy4FJLcSupCg6maBYZDpyBvTApK7KyuI4bKj8KOh1r2YH+6ucuYtJv1bTZA==",
+ "dev": true,
+ "dependencies": {
+ "cross-spawn": "^7.0.0",
+ "get-stream": "^5.0.0",
+ "human-signals": "^1.1.1",
+ "is-stream": "^2.0.0",
+ "merge-stream": "^2.0.0",
+ "npm-run-path": "^4.0.0",
+ "onetime": "^5.1.0",
+ "signal-exit": "^3.0.2",
+ "strip-final-newline": "^2.0.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sindresorhus/execa?sponsor=1"
+ }
+ },
+ "node_modules/executable": {
+ "version": "4.1.1",
+ "resolved": "https://registry.npmjs.org/executable/-/executable-4.1.1.tgz",
+ "integrity": "sha512-8iA79xD3uAch729dUG8xaaBBFGaEa0wdD2VkYLFHwlqosEj/jT66AzcreRDSgV7ehnNLBW2WR5jIXwGKjVdTLg==",
+ "dev": true,
+ "dependencies": {
+ "pify": "^2.2.0"
+ },
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/extend": {
+ "version": "3.0.2",
+ "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz",
+ "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==",
+ "dev": true
+ },
+ "node_modules/extract-zip": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/extract-zip/-/extract-zip-2.0.1.tgz",
+ "integrity": "sha512-GDhU9ntwuKyGXdZBUgTIe+vXnWj0fppUEtMDL0+idd5Sta8TGpHssn/eusA9mrPr9qNDym6SxAYZjNvCn/9RBg==",
+ "dev": true,
+ "dependencies": {
+ "debug": "^4.1.1",
+ "get-stream": "^5.1.0",
+ "yauzl": "^2.10.0"
+ },
+ "bin": {
+ "extract-zip": "cli.js"
+ },
+ "engines": {
+ "node": ">= 10.17.0"
+ },
+ "optionalDependencies": {
+ "@types/yauzl": "^2.9.1"
+ }
+ },
+ "node_modules/extsprintf": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz",
+ "integrity": "sha512-11Ndz7Nv+mvAC1j0ktTa7fAb0vLyGGX+rMHNBYQviQDGU0Hw7lhctJANqbPhu9nV9/izT/IntTgZ7Im/9LJs9g==",
+ "dev": true,
+ "engines": [
+ "node >=0.6.0"
+ ]
+ },
+ "node_modules/fast-deep-equal": {
+ "version": "3.1.3",
+ "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz",
+ "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==",
+ "dev": true
+ },
+ "node_modules/fast-diff": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/fast-diff/-/fast-diff-1.3.0.tgz",
+ "integrity": "sha512-VxPP4NqbUjj6MaAOafWeUn2cXWLcCtljklUtZf0Ind4XQ+QPtmA0b18zZy0jIQx+ExRVCR/ZQpBmik5lXshNsw==",
+ "dev": true
+ },
+ "node_modules/fast-glob": {
+ "version": "3.3.2",
+ "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.2.tgz",
+ "integrity": "sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow==",
+ "dev": true,
+ "dependencies": {
+ "@nodelib/fs.stat": "^2.0.2",
+ "@nodelib/fs.walk": "^1.2.3",
+ "glob-parent": "^5.1.2",
+ "merge2": "^1.3.0",
+ "micromatch": "^4.0.4"
+ },
+ "engines": {
+ "node": ">=8.6.0"
+ }
+ },
+ "node_modules/fast-glob/node_modules/glob-parent": {
+ "version": "5.1.2",
+ "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz",
+ "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==",
+ "dev": true,
+ "dependencies": {
+ "is-glob": "^4.0.1"
+ },
+ "engines": {
+ "node": ">= 6"
+ }
+ },
+ "node_modules/fast-json-stable-stringify": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz",
+ "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==",
+ "dev": true
+ },
+ "node_modules/fast-levenshtein": {
+ "version": "2.0.6",
+ "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz",
+ "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==",
+ "dev": true
+ },
+ "node_modules/fastq": {
+ "version": "1.17.1",
+ "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.17.1.tgz",
+ "integrity": "sha512-sRVD3lWVIXWg6By68ZN7vho9a1pQcN/WBFaAAsDDFzlJjvoGx0P8z7V1t72grFJfJhu3YPZBuu25f7Kaw2jN1w==",
+ "dev": true,
+ "dependencies": {
+ "reusify": "^1.0.4"
+ }
+ },
+ "node_modules/fd-slicer": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/fd-slicer/-/fd-slicer-1.1.0.tgz",
+ "integrity": "sha512-cE1qsB/VwyQozZ+q1dGxR8LBYNZeofhEdUNGSMbQD3Gw2lAzX9Zb3uIU6Ebc/Fmyjo9AWWfnn0AUCHqtevs/8g==",
+ "dev": true,
+ "dependencies": {
+ "pend": "~1.2.0"
+ }
+ },
+ "node_modules/figures": {
+ "version": "3.2.0",
+ "resolved": "https://registry.npmjs.org/figures/-/figures-3.2.0.tgz",
+ "integrity": "sha512-yaduQFRKLXYOGgEn6AZau90j3ggSOyiqXU0F9JZfeXYhNa+Jk4X+s45A2zg5jns87GAFa34BBm2kXw4XpNcbdg==",
+ "dev": true,
+ "dependencies": {
+ "escape-string-regexp": "^1.0.5"
+ },
+ "engines": {
+ "node": ">=8"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/figures/node_modules/escape-string-regexp": {
+ "version": "1.0.5",
+ "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz",
+ "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==",
+ "dev": true,
+ "engines": {
+ "node": ">=0.8.0"
+ }
+ },
+ "node_modules/file-entry-cache": {
+ "version": "6.0.1",
+ "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz",
+ "integrity": "sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==",
+ "dev": true,
+ "dependencies": {
+ "flat-cache": "^3.0.4"
+ },
+ "engines": {
+ "node": "^10.12.0 || >=12.0.0"
+ }
+ },
+ "node_modules/fill-range": {
+ "version": "7.0.1",
+ "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz",
+ "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==",
+ "dev": true,
+ "dependencies": {
+ "to-regex-range": "^5.0.1"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/find-root": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/find-root/-/find-root-1.1.0.tgz",
+ "integrity": "sha512-NKfW6bec6GfKc0SGx1e07QZY9PE99u0Bft/0rzSD5k3sO/vwkVUpDUKVm5Gpp5Ue3YfShPFTX2070tDs5kB9Ng=="
+ },
+ "node_modules/find-up": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz",
+ "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==",
+ "dev": true,
+ "dependencies": {
+ "locate-path": "^6.0.0",
+ "path-exists": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/flat-cache": {
+ "version": "3.2.0",
+ "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.2.0.tgz",
+ "integrity": "sha512-CYcENa+FtcUKLmhhqyctpclsq7QF38pKjZHsGNiSQF5r4FtoKDWabFDl3hzaEQMvT1LHEysw5twgLvpYYb4vbw==",
+ "dev": true,
+ "dependencies": {
+ "flatted": "^3.2.9",
+ "keyv": "^4.5.3",
+ "rimraf": "^3.0.2"
+ },
+ "engines": {
+ "node": "^10.12.0 || >=12.0.0"
+ }
+ },
+ "node_modules/flatted": {
+ "version": "3.3.1",
+ "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.1.tgz",
+ "integrity": "sha512-X8cqMLLie7KsNUDSdzeN8FYK9rEt4Dt67OsG/DNGnYTSDBG4uFAJFBnUeiV+zCVAvwFy56IjM9sH51jVaEhNxw==",
+ "dev": true
+ },
+ "node_modules/follow-redirects": {
+ "version": "1.15.6",
+ "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.6.tgz",
+ "integrity": "sha512-wWN62YITEaOpSK584EZXJafH1AGpO8RVgElfkuXbTOrPX4fIfOyEpW/CsiNd8JdYrAoOvafRTOEnvsO++qCqFA==",
+ "funding": [
+ {
+ "type": "individual",
+ "url": "https://github.com/sponsors/RubenVerborgh"
+ }
+ ],
+ "engines": {
+ "node": ">=4.0"
+ },
+ "peerDependenciesMeta": {
+ "debug": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/forever-agent": {
+ "version": "0.6.1",
+ "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz",
+ "integrity": "sha512-j0KLYPhm6zeac4lz3oJ3o65qvgQCcPubiyotZrXqEaG4hNagNYO8qdlUrX5vwqv9ohqeT/Z3j6+yW067yWWdUw==",
+ "dev": true,
+ "engines": {
+ "node": "*"
+ }
+ },
+ "node_modules/form-data": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz",
+ "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==",
+ "dependencies": {
+ "asynckit": "^0.4.0",
+ "combined-stream": "^1.0.8",
+ "mime-types": "^2.1.12"
+ },
+ "engines": {
+ "node": ">= 6"
+ }
+ },
+ "node_modules/fs-extra": {
+ "version": "9.1.0",
+ "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-9.1.0.tgz",
+ "integrity": "sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==",
+ "dev": true,
+ "dependencies": {
+ "at-least-node": "^1.0.0",
+ "graceful-fs": "^4.2.0",
+ "jsonfile": "^6.0.1",
+ "universalify": "^2.0.0"
+ },
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/fs.realpath": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
+ "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==",
+ "dev": true
+ },
+ "node_modules/fsevents": {
+ "version": "2.3.3",
+ "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz",
+ "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==",
+ "dev": true,
+ "hasInstallScript": true,
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": "^8.16.0 || ^10.6.0 || >=11.0.0"
+ }
+ },
+ "node_modules/function-bind": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz",
+ "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==",
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/gensync": {
+ "version": "1.0.0-beta.2",
+ "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz",
+ "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==",
+ "dev": true,
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/get-intrinsic": {
+ "version": "1.2.4",
+ "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.4.tgz",
+ "integrity": "sha512-5uYhsJH8VJBTv7oslg4BznJYhDoRI6waYCxMmCdnTrcCrHA/fCFKoTFz2JKKE0HdDFUF7/oQuhzumXJK7paBRQ==",
+ "dev": true,
+ "dependencies": {
+ "es-errors": "^1.3.0",
+ "function-bind": "^1.1.2",
+ "has-proto": "^1.0.1",
+ "has-symbols": "^1.0.3",
+ "hasown": "^2.0.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/get-stream": {
+ "version": "5.2.0",
+ "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.2.0.tgz",
+ "integrity": "sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==",
+ "dev": true,
+ "dependencies": {
+ "pump": "^3.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/getos": {
+ "version": "3.2.1",
+ "resolved": "https://registry.npmjs.org/getos/-/getos-3.2.1.tgz",
+ "integrity": "sha512-U56CfOK17OKgTVqozZjUKNdkfEv6jk5WISBJ8SHoagjE6L69zOwl3Z+O8myjY9MEW3i2HPWQBt/LTbCgcC973Q==",
+ "dev": true,
+ "dependencies": {
+ "async": "^3.2.0"
+ }
+ },
+ "node_modules/getpass": {
+ "version": "0.1.7",
+ "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz",
+ "integrity": "sha512-0fzj9JxOLfJ+XGLhR8ze3unN0KZCgZwiSSDz168VERjK8Wl8kVSdcu2kspd4s4wtAa1y/qrVRiAA0WclVsu0ng==",
+ "dev": true,
+ "dependencies": {
+ "assert-plus": "^1.0.0"
+ }
+ },
+ "node_modules/glob": {
+ "version": "7.2.3",
+ "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz",
+ "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==",
+ "dev": true,
+ "dependencies": {
+ "fs.realpath": "^1.0.0",
+ "inflight": "^1.0.4",
+ "inherits": "2",
+ "minimatch": "^3.1.1",
+ "once": "^1.3.0",
+ "path-is-absolute": "^1.0.0"
+ },
+ "engines": {
+ "node": "*"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/isaacs"
+ }
+ },
+ "node_modules/glob-parent": {
+ "version": "6.0.2",
+ "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz",
+ "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==",
+ "dev": true,
+ "dependencies": {
+ "is-glob": "^4.0.3"
+ },
+ "engines": {
+ "node": ">=10.13.0"
+ }
+ },
+ "node_modules/glob/node_modules/brace-expansion": {
+ "version": "1.1.11",
+ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
+ "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
+ "dev": true,
+ "dependencies": {
+ "balanced-match": "^1.0.0",
+ "concat-map": "0.0.1"
+ }
+ },
+ "node_modules/glob/node_modules/minimatch": {
+ "version": "3.1.2",
+ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
+ "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==",
+ "dev": true,
+ "dependencies": {
+ "brace-expansion": "^1.1.7"
+ },
+ "engines": {
+ "node": "*"
+ }
+ },
+ "node_modules/global-dirs": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/global-dirs/-/global-dirs-3.0.1.tgz",
+ "integrity": "sha512-NBcGGFbBA9s1VzD41QXDG+3++t9Mn5t1FpLdhESY6oKY4gYTFpX4wO3sqGUa0Srjtbfj3szX0RnemmrVRUdULA==",
+ "dev": true,
+ "dependencies": {
+ "ini": "2.0.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/globals": {
+ "version": "11.12.0",
+ "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz",
+ "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==",
+ "dev": true,
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/globby": {
+ "version": "11.1.0",
+ "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz",
+ "integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==",
+ "dev": true,
+ "dependencies": {
+ "array-union": "^2.1.0",
+ "dir-glob": "^3.0.1",
+ "fast-glob": "^3.2.9",
+ "ignore": "^5.2.0",
+ "merge2": "^1.4.1",
+ "slash": "^3.0.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/gopd": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz",
+ "integrity": "sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==",
+ "dev": true,
+ "dependencies": {
+ "get-intrinsic": "^1.1.3"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/graceful-fs": {
+ "version": "4.2.11",
+ "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz",
+ "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==",
+ "dev": true
+ },
+ "node_modules/graphemer": {
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz",
+ "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==",
+ "dev": true
+ },
+ "node_modules/has-flag": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz",
+ "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==",
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/has-property-descriptors": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz",
+ "integrity": "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==",
+ "dev": true,
+ "dependencies": {
+ "es-define-property": "^1.0.0"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/has-proto": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.0.3.tgz",
+ "integrity": "sha512-SJ1amZAJUiZS+PhsVLf5tGydlaVB8EdFpaSO4gmiUKUOxk8qzn5AIy4ZeJUmh22znIdk/uMAUT2pl3FxzVUH+Q==",
+ "dev": true,
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/has-symbols": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz",
+ "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==",
+ "dev": true,
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/hasown": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz",
+ "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==",
+ "dependencies": {
+ "function-bind": "^1.1.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/hoist-non-react-statics": {
+ "version": "3.3.2",
+ "resolved": "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz",
+ "integrity": "sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw==",
+ "dependencies": {
+ "react-is": "^16.7.0"
+ }
+ },
+ "node_modules/hoist-non-react-statics/node_modules/react-is": {
+ "version": "16.13.1",
+ "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz",
+ "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ=="
+ },
+ "node_modules/html-parse-stringify": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/html-parse-stringify/-/html-parse-stringify-3.0.1.tgz",
+ "integrity": "sha512-KknJ50kTInJ7qIScF3jeaFRpMpE8/lfiTdzf/twXyPBLAGrLRTmkz3AdTnKeh40X8k9L2fdYwEp/42WGXIRGcg==",
+ "dependencies": {
+ "void-elements": "3.1.0"
+ }
+ },
+ "node_modules/http-signature": {
+ "version": "1.3.6",
+ "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.3.6.tgz",
+ "integrity": "sha512-3adrsD6zqo4GsTqtO7FyrejHNv+NgiIfAfv68+jVlFmSr9OGy7zrxONceFRLKvnnZA5jbxQBX1u9PpB6Wi32Gw==",
+ "dev": true,
+ "dependencies": {
+ "assert-plus": "^1.0.0",
+ "jsprim": "^2.0.2",
+ "sshpk": "^1.14.1"
+ },
+ "engines": {
+ "node": ">=0.10"
+ }
+ },
+ "node_modules/human-signals": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-1.1.1.tgz",
+ "integrity": "sha512-SEQu7vl8KjNL2eoGBLF3+wAjpsNfA9XMlXAYj/3EdaNfAlxKthD1xjEQfGOUhllCGGJVNY34bRr6lPINhNjyZw==",
+ "dev": true,
+ "engines": {
+ "node": ">=8.12.0"
+ }
+ },
+ "node_modules/husky": {
+ "version": "7.0.4",
+ "resolved": "https://registry.npmjs.org/husky/-/husky-7.0.4.tgz",
+ "integrity": "sha512-vbaCKN2QLtP/vD4yvs6iz6hBEo6wkSzs8HpRah1Z6aGmF2KW5PdYuAd7uX5a+OyBZHBhd+TFLqgjUgytQr4RvQ==",
+ "dev": true,
+ "bin": {
+ "husky": "lib/bin.js"
+ },
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/typicode"
+ }
+ },
+ "node_modules/i18next": {
+ "version": "23.11.3",
+ "resolved": "https://registry.npmjs.org/i18next/-/i18next-23.11.3.tgz",
+ "integrity": "sha512-Pq/aSKowir7JM0rj+Wa23Kb6KKDUGno/HjG+wRQu0PxoTbpQ4N89MAT0rFGvXmLkRLNMb1BbBOKGozl01dabzg==",
+ "funding": [
+ {
+ "type": "individual",
+ "url": "https://locize.com"
+ },
+ {
+ "type": "individual",
+ "url": "https://locize.com/i18next.html"
+ },
+ {
+ "type": "individual",
+ "url": "https://www.i18next.com/how-to/faq#i18next-is-awesome.-how-can-i-support-the-project"
+ }
+ ],
+ "dependencies": {
+ "@babel/runtime": "^7.23.2"
+ }
+ },
+ "node_modules/i18next-browser-languagedetector": {
+ "version": "7.2.1",
+ "resolved": "https://registry.npmjs.org/i18next-browser-languagedetector/-/i18next-browser-languagedetector-7.2.1.tgz",
+ "integrity": "sha512-h/pM34bcH6tbz8WgGXcmWauNpQupCGr25XPp9cZwZInR9XHSjIFDYp1SIok7zSPsTOMxdvuLyu86V+g2Kycnfw==",
+ "dependencies": {
+ "@babel/runtime": "^7.23.2"
+ }
+ },
+ "node_modules/i18next-http-backend": {
+ "version": "2.5.1",
+ "resolved": "https://registry.npmjs.org/i18next-http-backend/-/i18next-http-backend-2.5.1.tgz",
+ "integrity": "sha512-+rNX1tghdVxdfjfPt0bI1sNg5ahGW9kA7OboG7b4t03Fp69NdDlRIze6yXhIbN8rbHxJ8IP4dzRm/okZ15lkQg==",
+ "dependencies": {
+ "cross-fetch": "4.0.0"
+ }
+ },
+ "node_modules/ieee754": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz",
+ "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/feross"
+ },
+ {
+ "type": "patreon",
+ "url": "https://www.patreon.com/feross"
+ },
+ {
+ "type": "consulting",
+ "url": "https://feross.org/support"
+ }
+ ]
+ },
+ "node_modules/ignore": {
+ "version": "5.3.1",
+ "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.1.tgz",
+ "integrity": "sha512-5Fytz/IraMjqpwfd34ke28PTVMjZjJG2MPn5t7OE4eUCUNf8BAa7b5WUS9/Qvr6mwOQS7Mk6vdsMno5he+T8Xw==",
+ "dev": true,
+ "engines": {
+ "node": ">= 4"
+ }
+ },
+ "node_modules/immediate": {
+ "version": "3.0.6",
+ "resolved": "https://registry.npmjs.org/immediate/-/immediate-3.0.6.tgz",
+ "integrity": "sha512-XXOFtyqDjNDAQxVfYxuF7g9Il/IbWmmlQg2MYKOH8ExIT1qg6xc4zyS3HaEEATgs1btfzxq15ciUiY7gjSXRGQ=="
+ },
+ "node_modules/import-fresh": {
+ "version": "3.3.0",
+ "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz",
+ "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==",
+ "dependencies": {
+ "parent-module": "^1.0.0",
+ "resolve-from": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=6"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/imurmurhash": {
+ "version": "0.1.4",
+ "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz",
+ "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==",
+ "dev": true,
+ "engines": {
+ "node": ">=0.8.19"
+ }
+ },
+ "node_modules/indent-string": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz",
+ "integrity": "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==",
+ "dev": true,
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/inflight": {
+ "version": "1.0.6",
+ "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz",
+ "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==",
+ "dev": true,
+ "dependencies": {
+ "once": "^1.3.0",
+ "wrappy": "1"
+ }
+ },
+ "node_modules/inherits": {
+ "version": "2.0.4",
+ "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
+ "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ=="
+ },
+ "node_modules/ini": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/ini/-/ini-2.0.0.tgz",
+ "integrity": "sha512-7PnF4oN3CvZF23ADhA5wRaYEQpJ8qygSkbtTXWBeXWXmEVRXK+1ITciHWwHhsjv1TmW0MgacIv6hEi5pX5NQdA==",
+ "dev": true,
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/invariant": {
+ "version": "2.2.4",
+ "resolved": "https://registry.npmjs.org/invariant/-/invariant-2.2.4.tgz",
+ "integrity": "sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA==",
+ "dependencies": {
+ "loose-envify": "^1.0.0"
+ }
+ },
+ "node_modules/is-arrayish": {
+ "version": "0.2.1",
+ "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz",
+ "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg=="
+ },
+ "node_modules/is-ci": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/is-ci/-/is-ci-3.0.1.tgz",
+ "integrity": "sha512-ZYvCgrefwqoQ6yTyYUbQu64HsITZ3NfKX1lzaEYdkTDcfKzzCI/wthRRYKkdjHKFVgNiXKAKm65Zo1pk2as/QQ==",
+ "dev": true,
+ "dependencies": {
+ "ci-info": "^3.2.0"
+ },
+ "bin": {
+ "is-ci": "bin.js"
+ }
+ },
+ "node_modules/is-core-module": {
+ "version": "2.13.1",
+ "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.13.1.tgz",
+ "integrity": "sha512-hHrIjvZsftOsvKSn2TRYl63zvxsgE0K+0mYMoH6gD4omR5IWB2KynivBQczo3+wF1cCkjzvptnI9Q0sPU66ilw==",
+ "dependencies": {
+ "hasown": "^2.0.0"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/is-extglob": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz",
+ "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==",
+ "dev": true,
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/is-fullwidth-code-point": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz",
+ "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==",
+ "dev": true,
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/is-glob": {
+ "version": "4.0.3",
+ "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz",
+ "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==",
+ "dev": true,
+ "dependencies": {
+ "is-extglob": "^2.1.1"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/is-installed-globally": {
+ "version": "0.4.0",
+ "resolved": "https://registry.npmjs.org/is-installed-globally/-/is-installed-globally-0.4.0.tgz",
+ "integrity": "sha512-iwGqO3J21aaSkC7jWnHP/difazwS7SFeIqxv6wEtLU8Y5KlzFTjyqcSIT0d8s4+dDhKytsk9PJZ2BkS5eZwQRQ==",
+ "dev": true,
+ "dependencies": {
+ "global-dirs": "^3.0.0",
+ "is-path-inside": "^3.0.2"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/is-number": {
+ "version": "7.0.0",
+ "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz",
+ "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==",
+ "dev": true,
+ "engines": {
+ "node": ">=0.12.0"
+ }
+ },
+ "node_modules/is-path-inside": {
+ "version": "3.0.3",
+ "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz",
+ "integrity": "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==",
+ "dev": true,
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/is-stream": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz",
+ "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==",
+ "dev": true,
+ "engines": {
+ "node": ">=8"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/is-typedarray": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz",
+ "integrity": "sha512-cyA56iCMHAh5CdzjJIa4aohJyeO1YbwLi3Jc35MmRU6poroFjIGZzUzupGiRPOjgHg9TLu43xbpwXk523fMxKA==",
+ "dev": true
+ },
+ "node_modules/is-unicode-supported": {
+ "version": "0.1.0",
+ "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz",
+ "integrity": "sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==",
+ "dev": true,
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/isarray": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz",
+ "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ=="
+ },
+ "node_modules/isexe": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz",
+ "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==",
+ "dev": true
+ },
+ "node_modules/isstream": {
+ "version": "0.1.2",
+ "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz",
+ "integrity": "sha512-Yljz7ffyPbrLpLngrMtZ7NduUgVvi6wG9RJ9IUcyCd59YQ911PBJphODUcbOVbqYfxe1wuYf/LJ8PauMRwsM/g==",
+ "dev": true
+ },
+ "node_modules/js-tokens": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz",
+ "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ=="
+ },
+ "node_modules/js-yaml": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz",
+ "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==",
+ "dev": true,
+ "dependencies": {
+ "argparse": "^2.0.1"
+ },
+ "bin": {
+ "js-yaml": "bin/js-yaml.js"
+ }
+ },
+ "node_modules/jsbn": {
+ "version": "0.1.1",
+ "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz",
+ "integrity": "sha512-UVU9dibq2JcFWxQPA6KCqj5O42VOmAY3zQUfEKxU0KpTGXwNoCjkX1e13eHNvw/xPynt6pU0rZ1htjWTNTSXsg==",
+ "dev": true
+ },
+ "node_modules/jsesc": {
+ "version": "2.5.2",
+ "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz",
+ "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==",
+ "dev": true,
+ "bin": {
+ "jsesc": "bin/jsesc"
+ },
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/json-buffer": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz",
+ "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==",
+ "dev": true
+ },
+ "node_modules/json-parse-even-better-errors": {
+ "version": "2.3.1",
+ "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz",
+ "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w=="
+ },
+ "node_modules/json-schema": {
+ "version": "0.4.0",
+ "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.4.0.tgz",
+ "integrity": "sha512-es94M3nTIfsEPisRafak+HDLfHXnKBhV3vU5eqPcS3flIWqcxJWgXHXiey3YrpaNsanY5ei1VoYEbOzijuq9BA==",
+ "dev": true
+ },
+ "node_modules/json-schema-traverse": {
+ "version": "0.4.1",
+ "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz",
+ "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==",
+ "dev": true
+ },
+ "node_modules/json-stable-stringify-without-jsonify": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz",
+ "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==",
+ "dev": true
+ },
+ "node_modules/json-stringify-safe": {
+ "version": "5.0.1",
+ "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz",
+ "integrity": "sha512-ZClg6AaYvamvYEE82d3Iyd3vSSIjQ+odgjaTzRuO3s7toCdFKczob2i0zCh7JE8kWn17yvAWhUVxvqGwUalsRA==",
+ "dev": true
+ },
+ "node_modules/json5": {
+ "version": "2.2.3",
+ "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz",
+ "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==",
+ "dev": true,
+ "bin": {
+ "json5": "lib/cli.js"
+ },
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/jsonfile": {
+ "version": "6.1.0",
+ "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz",
+ "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==",
+ "dev": true,
+ "dependencies": {
+ "universalify": "^2.0.0"
+ },
+ "optionalDependencies": {
+ "graceful-fs": "^4.1.6"
+ }
+ },
+ "node_modules/jsprim": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-2.0.2.tgz",
+ "integrity": "sha512-gqXddjPqQ6G40VdnI6T6yObEC+pDNvyP95wdQhkWkg7crHH3km5qP1FsOXEkzEQwnz6gz5qGTn1c2Y52wP3OyQ==",
+ "dev": true,
+ "engines": [
+ "node >=0.6.0"
+ ],
+ "dependencies": {
+ "assert-plus": "1.0.0",
+ "extsprintf": "1.3.0",
+ "json-schema": "0.4.0",
+ "verror": "1.10.0"
+ }
+ },
+ "node_modules/jszip": {
+ "version": "3.10.1",
+ "resolved": "https://registry.npmjs.org/jszip/-/jszip-3.10.1.tgz",
+ "integrity": "sha512-xXDvecyTpGLrqFrvkrUSoxxfJI5AH7U8zxxtVclpsUtMCq4JQ290LY8AW5c7Ggnr/Y/oK+bQMbqK2qmtk3pN4g==",
+ "dependencies": {
+ "lie": "~3.3.0",
+ "pako": "~1.0.2",
+ "readable-stream": "~2.3.6",
+ "setimmediate": "^1.0.5"
+ }
+ },
+ "node_modules/keyv": {
+ "version": "4.5.4",
+ "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz",
+ "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==",
+ "dev": true,
+ "dependencies": {
+ "json-buffer": "3.0.1"
+ }
+ },
+ "node_modules/lazy-ass": {
+ "version": "1.6.0",
+ "resolved": "https://registry.npmjs.org/lazy-ass/-/lazy-ass-1.6.0.tgz",
+ "integrity": "sha512-cc8oEVoctTvsFZ/Oje/kGnHbpWHYBe8IAJe4C0QNc3t8uM/0Y8+erSz/7Y1ALuXTEZTMvxXwO6YbX1ey3ujiZw==",
+ "dev": true,
+ "engines": {
+ "node": "> 0.8"
+ }
+ },
+ "node_modules/levn": {
+ "version": "0.4.1",
+ "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz",
+ "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==",
+ "dev": true,
+ "dependencies": {
+ "prelude-ls": "^1.2.1",
+ "type-check": "~0.4.0"
+ },
+ "engines": {
+ "node": ">= 0.8.0"
+ }
+ },
+ "node_modules/lie": {
+ "version": "3.3.0",
+ "resolved": "https://registry.npmjs.org/lie/-/lie-3.3.0.tgz",
+ "integrity": "sha512-UaiMJzeWRlEujzAuw5LokY1L5ecNQYZKfmyZ9L7wDHb/p5etKaxXhohBcrw0EYby+G/NA52vRSN4N39dxHAIwQ==",
+ "dependencies": {
+ "immediate": "~3.0.5"
+ }
+ },
+ "node_modules/lines-and-columns": {
+ "version": "1.2.4",
+ "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz",
+ "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg=="
+ },
+ "node_modules/listr2": {
+ "version": "3.14.0",
+ "resolved": "https://registry.npmjs.org/listr2/-/listr2-3.14.0.tgz",
+ "integrity": "sha512-TyWI8G99GX9GjE54cJ+RrNMcIFBfwMPxc3XTFiAYGN4s10hWROGtOg7+O6u6LE3mNkyld7RSLE6nrKBvTfcs3g==",
+ "dev": true,
+ "dependencies": {
+ "cli-truncate": "^2.1.0",
+ "colorette": "^2.0.16",
+ "log-update": "^4.0.0",
+ "p-map": "^4.0.0",
+ "rfdc": "^1.3.0",
+ "rxjs": "^7.5.1",
+ "through": "^2.3.8",
+ "wrap-ansi": "^7.0.0"
+ },
+ "engines": {
+ "node": ">=10.0.0"
+ },
+ "peerDependencies": {
+ "enquirer": ">= 2.3.0 < 3"
+ },
+ "peerDependenciesMeta": {
+ "enquirer": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/localforage": {
+ "version": "1.10.0",
+ "resolved": "https://registry.npmjs.org/localforage/-/localforage-1.10.0.tgz",
+ "integrity": "sha512-14/H1aX7hzBBmmh7sGPd+AOMkkIrHM3Z1PAyGgZigA1H1p5O5ANnMyWzvpAETtG68/dC4pC0ncy3+PPGzXZHPg==",
+ "dependencies": {
+ "lie": "3.1.1"
+ }
+ },
+ "node_modules/localforage/node_modules/lie": {
+ "version": "3.1.1",
+ "resolved": "https://registry.npmjs.org/lie/-/lie-3.1.1.tgz",
+ "integrity": "sha512-RiNhHysUjhrDQntfYSfY4MU24coXXdEOgw9WGcKHNeEwffDYbF//u87M1EWaMGzuFoSbqW0C9C6lEEhDOAswfw==",
+ "dependencies": {
+ "immediate": "~3.0.5"
+ }
+ },
+ "node_modules/locate-path": {
+ "version": "6.0.0",
+ "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz",
+ "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==",
+ "dev": true,
+ "dependencies": {
+ "p-locate": "^5.0.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/lodash": {
+ "version": "4.17.21",
+ "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz",
+ "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==",
+ "dev": true
+ },
+ "node_modules/lodash.merge": {
+ "version": "4.6.2",
+ "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz",
+ "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==",
+ "dev": true
+ },
+ "node_modules/lodash.once": {
+ "version": "4.1.1",
+ "resolved": "https://registry.npmjs.org/lodash.once/-/lodash.once-4.1.1.tgz",
+ "integrity": "sha512-Sb487aTOCr9drQVL8pIxOzVhafOjZN9UU54hiN8PU3uAiSV7lx1yYNpbNmex2PK6dSJoNTSJUUswT651yww3Mg==",
+ "dev": true
+ },
+ "node_modules/log-symbols": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz",
+ "integrity": "sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==",
+ "dev": true,
+ "dependencies": {
+ "chalk": "^4.1.0",
+ "is-unicode-supported": "^0.1.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/log-symbols/node_modules/ansi-styles": {
+ "version": "4.3.0",
+ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
+ "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
+ "dev": true,
+ "dependencies": {
+ "color-convert": "^2.0.1"
+ },
+ "engines": {
+ "node": ">=8"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/ansi-styles?sponsor=1"
+ }
+ },
+ "node_modules/log-symbols/node_modules/chalk": {
+ "version": "4.1.2",
+ "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
+ "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
+ "dev": true,
+ "dependencies": {
+ "ansi-styles": "^4.1.0",
+ "supports-color": "^7.1.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/chalk?sponsor=1"
+ }
+ },
+ "node_modules/log-symbols/node_modules/color-convert": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
+ "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
+ "dev": true,
+ "dependencies": {
+ "color-name": "~1.1.4"
+ },
+ "engines": {
+ "node": ">=7.0.0"
+ }
+ },
+ "node_modules/log-symbols/node_modules/color-name": {
+ "version": "1.1.4",
+ "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
+ "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
+ "dev": true
+ },
+ "node_modules/log-symbols/node_modules/has-flag": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
+ "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
+ "dev": true,
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/log-symbols/node_modules/supports-color": {
+ "version": "7.2.0",
+ "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
+ "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
+ "dev": true,
+ "dependencies": {
+ "has-flag": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/log-update": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/log-update/-/log-update-4.0.0.tgz",
+ "integrity": "sha512-9fkkDevMefjg0mmzWFBW8YkFP91OrizzkW3diF7CpG+S2EYdy4+TVfGwz1zeF8x7hCx1ovSPTOE9Ngib74qqUg==",
+ "dev": true,
+ "dependencies": {
+ "ansi-escapes": "^4.3.0",
+ "cli-cursor": "^3.1.0",
+ "slice-ansi": "^4.0.0",
+ "wrap-ansi": "^6.2.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/log-update/node_modules/ansi-styles": {
+ "version": "4.3.0",
+ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
+ "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
+ "dev": true,
+ "dependencies": {
+ "color-convert": "^2.0.1"
+ },
+ "engines": {
+ "node": ">=8"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/ansi-styles?sponsor=1"
+ }
+ },
+ "node_modules/log-update/node_modules/color-convert": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
+ "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
+ "dev": true,
+ "dependencies": {
+ "color-name": "~1.1.4"
+ },
+ "engines": {
+ "node": ">=7.0.0"
+ }
+ },
+ "node_modules/log-update/node_modules/color-name": {
+ "version": "1.1.4",
+ "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
+ "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
+ "dev": true
+ },
+ "node_modules/log-update/node_modules/slice-ansi": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-4.0.0.tgz",
+ "integrity": "sha512-qMCMfhY040cVHT43K9BFygqYbUPFZKHOg7K73mtTWJRb8pyP3fzf4Ixd5SzdEJQ6MRUg/WBnOLxghZtKKurENQ==",
+ "dev": true,
+ "dependencies": {
+ "ansi-styles": "^4.0.0",
+ "astral-regex": "^2.0.0",
+ "is-fullwidth-code-point": "^3.0.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/slice-ansi?sponsor=1"
+ }
+ },
+ "node_modules/log-update/node_modules/wrap-ansi": {
+ "version": "6.2.0",
+ "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz",
+ "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==",
+ "dev": true,
+ "dependencies": {
+ "ansi-styles": "^4.0.0",
+ "string-width": "^4.1.0",
+ "strip-ansi": "^6.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/loose-envify": {
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz",
+ "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==",
+ "dependencies": {
+ "js-tokens": "^3.0.0 || ^4.0.0"
+ },
+ "bin": {
+ "loose-envify": "cli.js"
+ }
+ },
+ "node_modules/lru-cache": {
+ "version": "5.1.1",
+ "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz",
+ "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==",
+ "dev": true,
+ "dependencies": {
+ "yallist": "^3.0.2"
+ }
+ },
+ "node_modules/match-sorter": {
+ "version": "6.3.4",
+ "resolved": "https://registry.npmjs.org/match-sorter/-/match-sorter-6.3.4.tgz",
+ "integrity": "sha512-jfZW7cWS5y/1xswZo8VBOdudUiSd9nifYRWphc9M5D/ee4w4AoXLgBEdRbgVaxbMuagBPeUC5y2Hi8DO6o9aDg==",
+ "dependencies": {
+ "@babel/runtime": "^7.23.8",
+ "remove-accents": "0.5.0"
+ }
+ },
+ "node_modules/merge-stream": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz",
+ "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==",
+ "dev": true
+ },
+ "node_modules/merge2": {
+ "version": "1.4.1",
+ "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz",
+ "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==",
+ "dev": true,
+ "engines": {
+ "node": ">= 8"
+ }
+ },
+ "node_modules/micromatch": {
+ "version": "4.0.5",
+ "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz",
+ "integrity": "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==",
+ "dev": true,
+ "dependencies": {
+ "braces": "^3.0.2",
+ "picomatch": "^2.3.1"
+ },
+ "engines": {
+ "node": ">=8.6"
+ }
+ },
+ "node_modules/mime-db": {
+ "version": "1.52.0",
+ "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz",
+ "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==",
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/mime-types": {
+ "version": "2.1.35",
+ "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz",
+ "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==",
+ "dependencies": {
+ "mime-db": "1.52.0"
+ },
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/mimic-fn": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz",
+ "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==",
+ "dev": true,
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/minimatch": {
+ "version": "9.0.4",
+ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.4.tgz",
+ "integrity": "sha512-KqWh+VchfxcMNRAJjj2tnsSJdNbHsVgnkBhTNrW7AjVo6OvLtxw8zfT9oLw1JSohlFzJ8jCoTgaoXvJ+kHt6fw==",
+ "dev": true,
+ "dependencies": {
+ "brace-expansion": "^2.0.1"
+ },
+ "engines": {
+ "node": ">=16 || 14 >=14.17"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/isaacs"
+ }
+ },
+ "node_modules/minimist": {
+ "version": "1.2.8",
+ "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz",
+ "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==",
+ "dev": true,
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/ms": {
+ "version": "2.1.2",
+ "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
+ "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==",
+ "dev": true
+ },
+ "node_modules/nanoid": {
+ "version": "3.3.7",
+ "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.7.tgz",
+ "integrity": "sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/ai"
+ }
+ ],
+ "bin": {
+ "nanoid": "bin/nanoid.cjs"
+ },
+ "engines": {
+ "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1"
+ }
+ },
+ "node_modules/natural-compare": {
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz",
+ "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==",
+ "dev": true
+ },
+ "node_modules/node-fetch": {
+ "version": "2.7.0",
+ "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz",
+ "integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==",
+ "dependencies": {
+ "whatwg-url": "^5.0.0"
+ },
+ "engines": {
+ "node": "4.x || >=6.0.0"
+ },
+ "peerDependencies": {
+ "encoding": "^0.1.0"
+ },
+ "peerDependenciesMeta": {
+ "encoding": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/node-releases": {
+ "version": "2.0.14",
+ "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.14.tgz",
+ "integrity": "sha512-y10wOWt8yZpqXmOgRo77WaHEmhYQYGNA6y421PKsKYWEK8aW+cqAphborZDhqfyKrbZEN92CN1X2KbafY2s7Yw==",
+ "dev": true
+ },
+ "node_modules/npm-run-path": {
+ "version": "4.0.1",
+ "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz",
+ "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==",
+ "dev": true,
+ "dependencies": {
+ "path-key": "^3.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/object-assign": {
+ "version": "4.1.1",
+ "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
+ "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/object-inspect": {
+ "version": "1.13.1",
+ "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.1.tgz",
+ "integrity": "sha512-5qoj1RUiKOMsCCNLV1CBiPYE10sziTsnmNxkAI/rZhiD63CF7IqdFGC/XzjWjpSgLf0LxXX3bDFIh0E18f6UhQ==",
+ "dev": true,
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/once": {
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
+ "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==",
+ "dev": true,
+ "dependencies": {
+ "wrappy": "1"
+ }
+ },
+ "node_modules/onetime": {
+ "version": "5.1.2",
+ "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz",
+ "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==",
+ "dev": true,
+ "dependencies": {
+ "mimic-fn": "^2.1.0"
+ },
+ "engines": {
+ "node": ">=6"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/optionator": {
+ "version": "0.9.4",
+ "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz",
+ "integrity": "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==",
+ "dev": true,
+ "dependencies": {
+ "deep-is": "^0.1.3",
+ "fast-levenshtein": "^2.0.6",
+ "levn": "^0.4.1",
+ "prelude-ls": "^1.2.1",
+ "type-check": "^0.4.0",
+ "word-wrap": "^1.2.5"
+ },
+ "engines": {
+ "node": ">= 0.8.0"
+ }
+ },
+ "node_modules/ospath": {
+ "version": "1.2.2",
+ "resolved": "https://registry.npmjs.org/ospath/-/ospath-1.2.2.tgz",
+ "integrity": "sha512-o6E5qJV5zkAbIDNhGSIlyOhScKXgQrSRMilfph0clDfM0nEnBOlKlH4sWDmG95BW/CvwNz0vmm7dJVtU2KlMiA==",
+ "dev": true
+ },
+ "node_modules/p-limit": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz",
+ "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==",
+ "dev": true,
+ "dependencies": {
+ "yocto-queue": "^0.1.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/p-locate": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz",
+ "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==",
+ "dev": true,
+ "dependencies": {
+ "p-limit": "^3.0.2"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/p-map": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/p-map/-/p-map-4.0.0.tgz",
+ "integrity": "sha512-/bjOqmgETBYB5BoEeGVea8dmvHb2m9GLy1E9W43yeyfP6QQCZGFNa+XRceJEuDB6zqr+gKpIAmlLebMpykw/MQ==",
+ "dev": true,
+ "dependencies": {
+ "aggregate-error": "^3.0.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/pako": {
+ "version": "1.0.11",
+ "resolved": "https://registry.npmjs.org/pako/-/pako-1.0.11.tgz",
+ "integrity": "sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw=="
+ },
+ "node_modules/papaparse": {
+ "version": "5.4.1",
+ "resolved": "https://registry.npmjs.org/papaparse/-/papaparse-5.4.1.tgz",
+ "integrity": "sha512-HipMsgJkZu8br23pW15uvo6sib6wne/4woLZPlFf3rpDyMe9ywEXUsuD7+6K9PRkJlVT51j/sCOYDKGGS3ZJrw=="
+ },
+ "node_modules/parent-module": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz",
+ "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==",
+ "dependencies": {
+ "callsites": "^3.0.0"
+ },
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/parse-json": {
+ "version": "5.2.0",
+ "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz",
+ "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==",
+ "dependencies": {
+ "@babel/code-frame": "^7.0.0",
+ "error-ex": "^1.3.1",
+ "json-parse-even-better-errors": "^2.3.0",
+ "lines-and-columns": "^1.1.6"
+ },
+ "engines": {
+ "node": ">=8"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/path-exists": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz",
+ "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==",
+ "dev": true,
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/path-is-absolute": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz",
+ "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==",
+ "dev": true,
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/path-key": {
+ "version": "3.1.1",
+ "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz",
+ "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==",
+ "dev": true,
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/path-parse": {
+ "version": "1.0.7",
+ "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz",
+ "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw=="
+ },
+ "node_modules/path-type": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz",
+ "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/pend": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/pend/-/pend-1.2.0.tgz",
+ "integrity": "sha512-F3asv42UuXchdzt+xXqfW1OGlVBe+mxa2mqI0pg5yAHZPvFmY3Y6drSf/GQ1A86WgWEN9Kzh/WrgKa6iGcHXLg==",
+ "dev": true
+ },
+ "node_modules/performance-now": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz",
+ "integrity": "sha512-7EAHlyLHI56VEIdK57uwHdHKIaAGbnXPiw0yWbarQZOKaKpvUIgW0jWRVLiatnM+XXlSwsanIBH/hzGMJulMow==",
+ "dev": true
+ },
+ "node_modules/picocolors": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz",
+ "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ=="
+ },
+ "node_modules/picomatch": {
+ "version": "2.3.1",
+ "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz",
+ "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==",
+ "dev": true,
+ "engines": {
+ "node": ">=8.6"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/jonschlinkert"
+ }
+ },
+ "node_modules/pify": {
+ "version": "2.3.0",
+ "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz",
+ "integrity": "sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==",
+ "dev": true,
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/postcss": {
+ "version": "8.4.38",
+ "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.38.tgz",
+ "integrity": "sha512-Wglpdk03BSfXkHoQa3b/oulrotAkwrlLDRSOb9D0bN86FdRyE9lppSp33aHNPgBa0JKCoB+drFLZkQoRRYae5A==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/postcss/"
+ },
+ {
+ "type": "tidelift",
+ "url": "https://tidelift.com/funding/github/npm/postcss"
+ },
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/ai"
+ }
+ ],
+ "dependencies": {
+ "nanoid": "^3.3.7",
+ "picocolors": "^1.0.0",
+ "source-map-js": "^1.2.0"
+ },
+ "engines": {
+ "node": "^10 || ^12 || >=14"
+ }
+ },
+ "node_modules/prelude-ls": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz",
+ "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==",
+ "dev": true,
+ "engines": {
+ "node": ">= 0.8.0"
+ }
+ },
+ "node_modules/prettier": {
+ "version": "3.2.5",
+ "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.2.5.tgz",
+ "integrity": "sha512-3/GWa9aOC0YeD7LUfvOG2NiDyhOWRvt1k+rcKhOuYnMY24iiCphgneUfJDyFXd6rZCAnuLBv6UeAULtrhT/F4A==",
+ "dev": true,
+ "bin": {
+ "prettier": "bin/prettier.cjs"
+ },
+ "engines": {
+ "node": ">=14"
+ },
+ "funding": {
+ "url": "https://github.com/prettier/prettier?sponsor=1"
+ }
+ },
+ "node_modules/prettier-linter-helpers": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/prettier-linter-helpers/-/prettier-linter-helpers-1.0.0.tgz",
+ "integrity": "sha512-GbK2cP9nraSSUF9N2XwUwqfzlAFlMNYYl+ShE/V+H8a9uNl/oUqB1w2EL54Jh0OlyRSd8RfWYJ3coVS4TROP2w==",
+ "dev": true,
+ "dependencies": {
+ "fast-diff": "^1.1.2"
+ },
+ "engines": {
+ "node": ">=6.0.0"
+ }
+ },
+ "node_modules/pretty-bytes": {
+ "version": "5.6.0",
+ "resolved": "https://registry.npmjs.org/pretty-bytes/-/pretty-bytes-5.6.0.tgz",
+ "integrity": "sha512-FFw039TmrBqFK8ma/7OL3sDz/VytdtJr044/QUJtH0wK9lb9jLq9tJyIxUwtQJHwar2BqtiA4iCWSwo9JLkzFg==",
+ "dev": true,
+ "engines": {
+ "node": ">=6"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/process": {
+ "version": "0.11.10",
+ "resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz",
+ "integrity": "sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A==",
+ "dev": true,
+ "engines": {
+ "node": ">= 0.6.0"
+ }
+ },
+ "node_modules/process-nextick-args": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz",
+ "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag=="
+ },
+ "node_modules/prop-types": {
+ "version": "15.8.1",
+ "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz",
+ "integrity": "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==",
+ "dependencies": {
+ "loose-envify": "^1.4.0",
+ "object-assign": "^4.1.1",
+ "react-is": "^16.13.1"
+ }
+ },
+ "node_modules/prop-types/node_modules/react-is": {
+ "version": "16.13.1",
+ "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz",
+ "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ=="
+ },
+ "node_modules/proxy-from-env": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz",
+ "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg=="
+ },
+ "node_modules/psl": {
+ "version": "1.9.0",
+ "resolved": "https://registry.npmjs.org/psl/-/psl-1.9.0.tgz",
+ "integrity": "sha512-E/ZsdU4HLs/68gYzgGTkMicWTLPdAftJLfJFlLUAAKZGkStNU72sZjT66SnMDVOfOWY/YAoiD7Jxa9iHvngcag==",
+ "dev": true
+ },
+ "node_modules/pump": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz",
+ "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==",
+ "dev": true,
+ "dependencies": {
+ "end-of-stream": "^1.1.0",
+ "once": "^1.3.1"
+ }
+ },
+ "node_modules/punycode": {
+ "version": "2.3.1",
+ "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz",
+ "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==",
+ "dev": true,
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/qs": {
+ "version": "6.10.4",
+ "resolved": "https://registry.npmjs.org/qs/-/qs-6.10.4.tgz",
+ "integrity": "sha512-OQiU+C+Ds5qiH91qh/mg0w+8nwQuLjM4F4M/PbmhDOoYehPh+Fb0bDjtR1sOvy7YKxvj28Y/M0PhP5uVX0kB+g==",
+ "dev": true,
+ "dependencies": {
+ "side-channel": "^1.0.4"
+ },
+ "engines": {
+ "node": ">=0.6"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/querystringify": {
+ "version": "2.2.0",
+ "resolved": "https://registry.npmjs.org/querystringify/-/querystringify-2.2.0.tgz",
+ "integrity": "sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ==",
+ "dev": true
+ },
+ "node_modules/queue-microtask": {
+ "version": "1.2.3",
+ "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz",
+ "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/feross"
+ },
+ {
+ "type": "patreon",
+ "url": "https://www.patreon.com/feross"
+ },
+ {
+ "type": "consulting",
+ "url": "https://feross.org/support"
+ }
+ ]
+ },
+ "node_modules/react": {
+ "version": "18.3.1",
+ "resolved": "https://registry.npmjs.org/react/-/react-18.3.1.tgz",
+ "integrity": "sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ==",
+ "dependencies": {
+ "loose-envify": "^1.1.0"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/react-dom": {
+ "version": "18.3.1",
+ "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.3.1.tgz",
+ "integrity": "sha512-5m4nQKp+rZRb09LNH59GM4BxTh9251/ylbKIbpe7TpGxfJ+9kv6BLkLBXIjjspbgbnIBNqlI23tRnTWT0snUIw==",
+ "dependencies": {
+ "loose-envify": "^1.1.0",
+ "scheduler": "^0.23.2"
+ },
+ "peerDependencies": {
+ "react": "^18.3.1"
+ }
+ },
+ "node_modules/react-fast-compare": {
+ "version": "3.2.2",
+ "resolved": "https://registry.npmjs.org/react-fast-compare/-/react-fast-compare-3.2.2.tgz",
+ "integrity": "sha512-nsO+KSNgo1SbJqJEYRE9ERzo7YtYbou/OqjSQKxV7jcKox7+usiUVZOAC+XnDOABXggQTno0Y1CpVnuWEc1boQ=="
+ },
+ "node_modules/react-helmet": {
+ "version": "6.1.0",
+ "resolved": "https://registry.npmjs.org/react-helmet/-/react-helmet-6.1.0.tgz",
+ "integrity": "sha512-4uMzEY9nlDlgxr61NL3XbKRy1hEkXmKNXhjbAIOVw5vcFrsdYbH2FEwcNyWvWinl103nXgzYNlns9ca+8kFiWw==",
+ "dependencies": {
+ "object-assign": "^4.1.1",
+ "prop-types": "^15.7.2",
+ "react-fast-compare": "^3.1.1",
+ "react-side-effect": "^2.1.0"
+ },
+ "peerDependencies": {
+ "react": ">=16.3.0"
+ }
+ },
+ "node_modules/react-helmet-async": {
+ "version": "2.0.4",
+ "resolved": "https://registry.npmjs.org/react-helmet-async/-/react-helmet-async-2.0.4.tgz",
+ "integrity": "sha512-yxjQMWposw+akRfvpl5+8xejl4JtUlHnEBcji6u8/e6oc7ozT+P9PNTWMhCbz2y9tc5zPegw2BvKjQA+NwdEjQ==",
+ "dependencies": {
+ "invariant": "^2.2.4",
+ "react-fast-compare": "^3.2.2",
+ "shallowequal": "^1.1.0"
+ },
+ "peerDependencies": {
+ "react": "^16.6.0 || ^17.0.0 || ^18.0.0",
+ "react-dom": "^16.6.0 || ^17.0.0 || ^18.0.0"
+ }
+ },
+ "node_modules/react-i18next": {
+ "version": "14.1.1",
+ "resolved": "https://registry.npmjs.org/react-i18next/-/react-i18next-14.1.1.tgz",
+ "integrity": "sha512-QSiKw+ihzJ/CIeIYWrarCmXJUySHDwQr5y8uaNIkbxoGRm/5DukkxZs+RPla79IKyyDPzC/DRlgQCABHtrQuQQ==",
+ "dependencies": {
+ "@babel/runtime": "^7.23.9",
+ "html-parse-stringify": "^3.0.1"
+ },
+ "peerDependencies": {
+ "i18next": ">= 23.2.3",
+ "react": ">= 16.8.0"
+ },
+ "peerDependenciesMeta": {
+ "react-dom": {
+ "optional": true
+ },
+ "react-native": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/react-is": {
+ "version": "18.3.1",
+ "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz",
+ "integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg=="
+ },
+ "node_modules/react-refresh": {
+ "version": "0.14.2",
+ "resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.14.2.tgz",
+ "integrity": "sha512-jCvmsr+1IUSMUyzOkRcvnVbX3ZYC6g9TDrDbFuFmRDq7PD4yaGbLKNQL6k2jnArV8hjYxh7hVhAZB6s9HDGpZA==",
+ "dev": true,
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/react-router": {
+ "version": "6.23.0",
+ "resolved": "https://registry.npmjs.org/react-router/-/react-router-6.23.0.tgz",
+ "integrity": "sha512-wPMZ8S2TuPadH0sF5irFGjkNLIcRvOSaEe7v+JER8508dyJumm6XZB1u5kztlX0RVq6AzRVndzqcUh6sFIauzA==",
+ "dependencies": {
+ "@remix-run/router": "1.16.0"
+ },
+ "engines": {
+ "node": ">=14.0.0"
+ },
+ "peerDependencies": {
+ "react": ">=16.8"
+ }
+ },
+ "node_modules/react-router-dom": {
+ "version": "6.23.0",
+ "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-6.23.0.tgz",
+ "integrity": "sha512-Q9YaSYvubwgbal2c9DJKfx6hTNoBp3iJDsl+Duva/DwxoJH+OTXkxGpql4iUK2sla/8z4RpjAm6EWx1qUDuopQ==",
+ "dependencies": {
+ "@remix-run/router": "1.16.0",
+ "react-router": "6.23.0"
+ },
+ "engines": {
+ "node": ">=14.0.0"
+ },
+ "peerDependencies": {
+ "react": ">=16.8",
+ "react-dom": ">=16.8"
+ }
+ },
+ "node_modules/react-side-effect": {
+ "version": "2.1.2",
+ "resolved": "https://registry.npmjs.org/react-side-effect/-/react-side-effect-2.1.2.tgz",
+ "integrity": "sha512-PVjOcvVOyIILrYoyGEpDN3vmYNLdy1CajSFNt4TDsVQC5KpTijDvWVoR+/7Rz2xT978D8/ZtFceXxzsPwZEDvw==",
+ "peerDependencies": {
+ "react": "^16.3.0 || ^17.0.0 || ^18.0.0"
+ }
+ },
+ "node_modules/react-transition-group": {
+ "version": "4.4.5",
+ "resolved": "https://registry.npmjs.org/react-transition-group/-/react-transition-group-4.4.5.tgz",
+ "integrity": "sha512-pZcd1MCJoiKiBR2NRxeCRg13uCXbydPnmB4EOeRrY7480qNWO8IIgQG6zlDkm6uRMsURXPuKq0GWtiM59a5Q6g==",
+ "dependencies": {
+ "@babel/runtime": "^7.5.5",
+ "dom-helpers": "^5.0.1",
+ "loose-envify": "^1.4.0",
+ "prop-types": "^15.6.2"
+ },
+ "peerDependencies": {
+ "react": ">=16.6.0",
+ "react-dom": ">=16.6.0"
+ }
+ },
+ "node_modules/readable-stream": {
+ "version": "2.3.8",
+ "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz",
+ "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==",
+ "dependencies": {
+ "core-util-is": "~1.0.0",
+ "inherits": "~2.0.3",
+ "isarray": "~1.0.0",
+ "process-nextick-args": "~2.0.0",
+ "safe-buffer": "~5.1.1",
+ "string_decoder": "~1.1.1",
+ "util-deprecate": "~1.0.1"
+ }
+ },
+ "node_modules/regenerator-runtime": {
+ "version": "0.14.1",
+ "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.14.1.tgz",
+ "integrity": "sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw=="
+ },
+ "node_modules/remove-accents": {
+ "version": "0.5.0",
+ "resolved": "https://registry.npmjs.org/remove-accents/-/remove-accents-0.5.0.tgz",
+ "integrity": "sha512-8g3/Otx1eJaVD12e31UbJj1YzdtVvzH85HV7t+9MJYk/u3XmkOUJ5Ys9wQrf9PCPK8+xn4ymzqYCiZl6QWKn+A=="
+ },
+ "node_modules/request-progress": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/request-progress/-/request-progress-3.0.0.tgz",
+ "integrity": "sha512-MnWzEHHaxHO2iWiQuHrUPBi/1WeBf5PkxQqNyNvLl9VAYSdXkP8tQ3pBSeCPD+yw0v0Aq1zosWLz0BdeXpWwZg==",
+ "dev": true,
+ "dependencies": {
+ "throttleit": "^1.0.0"
+ }
+ },
+ "node_modules/requires-port": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz",
+ "integrity": "sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==",
+ "dev": true
+ },
+ "node_modules/resolve": {
+ "version": "1.22.8",
+ "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.8.tgz",
+ "integrity": "sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==",
+ "dependencies": {
+ "is-core-module": "^2.13.0",
+ "path-parse": "^1.0.7",
+ "supports-preserve-symlinks-flag": "^1.0.0"
+ },
+ "bin": {
+ "resolve": "bin/resolve"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/resolve-from": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz",
+ "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==",
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/restore-cursor": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-3.1.0.tgz",
+ "integrity": "sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==",
+ "dev": true,
+ "dependencies": {
+ "onetime": "^5.1.0",
+ "signal-exit": "^3.0.2"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/reusify": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz",
+ "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==",
+ "dev": true,
+ "engines": {
+ "iojs": ">=1.0.0",
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/rfdc": {
+ "version": "1.3.1",
+ "resolved": "https://registry.npmjs.org/rfdc/-/rfdc-1.3.1.tgz",
+ "integrity": "sha512-r5a3l5HzYlIC68TpmYKlxWjmOP6wiPJ1vWv2HeLhNsRZMrCkxeqxiHlQ21oXmQ4F3SiryXBHhAD7JZqvOJjFmg==",
+ "dev": true
+ },
+ "node_modules/rimraf": {
+ "version": "3.0.2",
+ "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz",
+ "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==",
+ "dev": true,
+ "dependencies": {
+ "glob": "^7.1.3"
+ },
+ "bin": {
+ "rimraf": "bin.js"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/isaacs"
+ }
+ },
+ "node_modules/rollup": {
+ "version": "4.17.2",
+ "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.17.2.tgz",
+ "integrity": "sha512-/9ClTJPByC0U4zNLowV1tMBe8yMEAxewtR3cUNX5BoEpGH3dQEWpJLr6CLp0fPdYRF/fzVOgvDb1zXuakwF5kQ==",
+ "dev": true,
+ "dependencies": {
+ "@types/estree": "1.0.5"
+ },
+ "bin": {
+ "rollup": "dist/bin/rollup"
+ },
+ "engines": {
+ "node": ">=18.0.0",
+ "npm": ">=8.0.0"
+ },
+ "optionalDependencies": {
+ "@rollup/rollup-android-arm-eabi": "4.17.2",
+ "@rollup/rollup-android-arm64": "4.17.2",
+ "@rollup/rollup-darwin-arm64": "4.17.2",
+ "@rollup/rollup-darwin-x64": "4.17.2",
+ "@rollup/rollup-linux-arm-gnueabihf": "4.17.2",
+ "@rollup/rollup-linux-arm-musleabihf": "4.17.2",
+ "@rollup/rollup-linux-arm64-gnu": "4.17.2",
+ "@rollup/rollup-linux-arm64-musl": "4.17.2",
+ "@rollup/rollup-linux-powerpc64le-gnu": "4.17.2",
+ "@rollup/rollup-linux-riscv64-gnu": "4.17.2",
+ "@rollup/rollup-linux-s390x-gnu": "4.17.2",
+ "@rollup/rollup-linux-x64-gnu": "4.17.2",
+ "@rollup/rollup-linux-x64-musl": "4.17.2",
+ "@rollup/rollup-win32-arm64-msvc": "4.17.2",
+ "@rollup/rollup-win32-ia32-msvc": "4.17.2",
+ "@rollup/rollup-win32-x64-msvc": "4.17.2",
+ "fsevents": "~2.3.2"
+ }
+ },
+ "node_modules/run-parallel": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz",
+ "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/feross"
+ },
+ {
+ "type": "patreon",
+ "url": "https://www.patreon.com/feross"
+ },
+ {
+ "type": "consulting",
+ "url": "https://feross.org/support"
+ }
+ ],
+ "dependencies": {
+ "queue-microtask": "^1.2.2"
+ }
+ },
+ "node_modules/rxjs": {
+ "version": "7.8.1",
+ "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.1.tgz",
+ "integrity": "sha512-AA3TVj+0A2iuIoQkWEK/tqFjBq2j+6PO6Y0zJcvzLAFhEFIO3HL0vls9hWLncZbAAbK0mar7oZ4V079I/qPMxg==",
+ "dev": true,
+ "dependencies": {
+ "tslib": "^2.1.0"
+ }
+ },
+ "node_modules/safe-buffer": {
+ "version": "5.1.2",
+ "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
+ "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g=="
+ },
+ "node_modules/safer-buffer": {
+ "version": "2.1.2",
+ "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz",
+ "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==",
+ "dev": true
+ },
+ "node_modules/scheduler": {
+ "version": "0.23.2",
+ "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.23.2.tgz",
+ "integrity": "sha512-UOShsPwz7NrMUqhR6t0hWjFduvOzbtv7toDH1/hIrfRNIDBnnBWd0CwJTGvTpngVlmwGCdP9/Zl/tVrDqcuYzQ==",
+ "dependencies": {
+ "loose-envify": "^1.1.0"
+ }
+ },
+ "node_modules/semver": {
+ "version": "7.6.0",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.0.tgz",
+ "integrity": "sha512-EnwXhrlwXMk9gKu5/flx5sv/an57AkRplG3hTK68W7FRDN+k+OWBj65M7719OkA82XLBxrcX0KSHj+X5COhOVg==",
+ "dev": true,
+ "dependencies": {
+ "lru-cache": "^6.0.0"
+ },
+ "bin": {
+ "semver": "bin/semver.js"
+ },
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/semver/node_modules/lru-cache": {
+ "version": "6.0.0",
+ "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz",
+ "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==",
+ "dev": true,
+ "dependencies": {
+ "yallist": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/semver/node_modules/yallist": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz",
+ "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==",
+ "dev": true
+ },
+ "node_modules/set-function-length": {
+ "version": "1.2.2",
+ "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz",
+ "integrity": "sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==",
+ "dev": true,
+ "dependencies": {
+ "define-data-property": "^1.1.4",
+ "es-errors": "^1.3.0",
+ "function-bind": "^1.1.2",
+ "get-intrinsic": "^1.2.4",
+ "gopd": "^1.0.1",
+ "has-property-descriptors": "^1.0.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/setimmediate": {
+ "version": "1.0.5",
+ "resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz",
+ "integrity": "sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA=="
+ },
+ "node_modules/shallowequal": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/shallowequal/-/shallowequal-1.1.0.tgz",
+ "integrity": "sha512-y0m1JoUZSlPAjXVtPPW70aZWfIL/dSP7AFkRnniLCrK/8MDKog3TySTBmckD+RObVxH0v4Tox67+F14PdED2oQ=="
+ },
+ "node_modules/shebang-command": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz",
+ "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==",
+ "dev": true,
+ "dependencies": {
+ "shebang-regex": "^3.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/shebang-regex": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz",
+ "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==",
+ "dev": true,
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/side-channel": {
+ "version": "1.0.6",
+ "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.6.tgz",
+ "integrity": "sha512-fDW/EZ6Q9RiO8eFG8Hj+7u/oW+XrPTIChwCOM2+th2A6OblDtYYIpve9m+KvI9Z4C9qSEXlaGR6bTEYHReuglA==",
+ "dev": true,
+ "dependencies": {
+ "call-bind": "^1.0.7",
+ "es-errors": "^1.3.0",
+ "get-intrinsic": "^1.2.4",
+ "object-inspect": "^1.13.1"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/signal-exit": {
+ "version": "3.0.7",
+ "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz",
+ "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==",
+ "dev": true
+ },
+ "node_modules/slash": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz",
+ "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==",
+ "dev": true,
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/slice-ansi": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-3.0.0.tgz",
+ "integrity": "sha512-pSyv7bSTC7ig9Dcgbw9AuRNUb5k5V6oDudjZoMBSr13qpLBG7tB+zgCkARjq7xIUgdz5P1Qe8u+rSGdouOOIyQ==",
+ "dev": true,
+ "dependencies": {
+ "ansi-styles": "^4.0.0",
+ "astral-regex": "^2.0.0",
+ "is-fullwidth-code-point": "^3.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/slice-ansi/node_modules/ansi-styles": {
+ "version": "4.3.0",
+ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
+ "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
+ "dev": true,
+ "dependencies": {
+ "color-convert": "^2.0.1"
+ },
+ "engines": {
+ "node": ">=8"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/ansi-styles?sponsor=1"
+ }
+ },
+ "node_modules/slice-ansi/node_modules/color-convert": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
+ "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
+ "dev": true,
+ "dependencies": {
+ "color-name": "~1.1.4"
+ },
+ "engines": {
+ "node": ">=7.0.0"
+ }
+ },
+ "node_modules/slice-ansi/node_modules/color-name": {
+ "version": "1.1.4",
+ "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
+ "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
+ "dev": true
+ },
+ "node_modules/source-map": {
+ "version": "0.5.7",
+ "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz",
+ "integrity": "sha512-LbrmJOMUSdEVxIKvdcJzQC+nQhe8FUZQTXQy6+I75skNgn3OoQ0DZA8YnFa7gp8tqtL3KPf1kmo0R5DoApeSGQ==",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/source-map-js": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.0.tgz",
+ "integrity": "sha512-itJW8lvSA0TXEphiRoawsCksnlf8SyvmFzIhltqAHluXd88pkCd+cXJVHTDwdCr0IzwptSm035IHQktUu1QUMg==",
+ "dev": true,
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/sshpk": {
+ "version": "1.18.0",
+ "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.18.0.tgz",
+ "integrity": "sha512-2p2KJZTSqQ/I3+HX42EpYOa2l3f8Erv8MWKsy2I9uf4wA7yFIkXRffYdsx86y6z4vHtV8u7g+pPlr8/4ouAxsQ==",
+ "dev": true,
+ "dependencies": {
+ "asn1": "~0.2.3",
+ "assert-plus": "^1.0.0",
+ "bcrypt-pbkdf": "^1.0.0",
+ "dashdash": "^1.12.0",
+ "ecc-jsbn": "~0.1.1",
+ "getpass": "^0.1.1",
+ "jsbn": "~0.1.0",
+ "safer-buffer": "^2.0.2",
+ "tweetnacl": "~0.14.0"
+ },
+ "bin": {
+ "sshpk-conv": "bin/sshpk-conv",
+ "sshpk-sign": "bin/sshpk-sign",
+ "sshpk-verify": "bin/sshpk-verify"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/string_decoder": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz",
+ "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==",
+ "dependencies": {
+ "safe-buffer": "~5.1.0"
+ }
+ },
+ "node_modules/string-width": {
+ "version": "4.2.3",
+ "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz",
+ "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
+ "dev": true,
+ "dependencies": {
+ "emoji-regex": "^8.0.0",
+ "is-fullwidth-code-point": "^3.0.0",
+ "strip-ansi": "^6.0.1"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/strip-ansi": {
+ "version": "6.0.1",
+ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
+ "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
+ "dev": true,
+ "dependencies": {
+ "ansi-regex": "^5.0.1"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/strip-final-newline": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz",
+ "integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==",
+ "dev": true,
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/strip-json-comments": {
+ "version": "3.1.1",
+ "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz",
+ "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==",
+ "dev": true,
+ "engines": {
+ "node": ">=8"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/stylis": {
+ "version": "4.2.0",
+ "resolved": "https://registry.npmjs.org/stylis/-/stylis-4.2.0.tgz",
+ "integrity": "sha512-Orov6g6BB1sDfYgzWfTHDOxamtX1bE/zo104Dh9e6fqJ3PooipYyfJ0pUmrZO2wAvO8YbEyeFrkV91XTsGMSrw=="
+ },
+ "node_modules/supports-color": {
+ "version": "5.5.0",
+ "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz",
+ "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==",
+ "dependencies": {
+ "has-flag": "^3.0.0"
+ },
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/supports-preserve-symlinks-flag": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz",
+ "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==",
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/synckit": {
+ "version": "0.8.8",
+ "resolved": "https://registry.npmjs.org/synckit/-/synckit-0.8.8.tgz",
+ "integrity": "sha512-HwOKAP7Wc5aRGYdKH+dw0PRRpbO841v2DENBtjnR5HFWoiNByAl7vrx3p0G/rCyYXQsrxqtX48TImFtPcIHSpQ==",
+ "dev": true,
+ "dependencies": {
+ "@pkgr/core": "^0.1.0",
+ "tslib": "^2.6.2"
+ },
+ "engines": {
+ "node": "^14.18.0 || >=16.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/unts"
+ }
+ },
+ "node_modules/text-table": {
+ "version": "0.2.0",
+ "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz",
+ "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==",
+ "dev": true
+ },
+ "node_modules/throttleit": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/throttleit/-/throttleit-1.0.1.tgz",
+ "integrity": "sha512-vDZpf9Chs9mAdfY046mcPt8fg5QSZr37hEH4TXYBnDF+izxgrbRGUAAaBvIk/fJm9aOFCGFd1EsNg5AZCbnQCQ==",
+ "dev": true,
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/through": {
+ "version": "2.3.8",
+ "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz",
+ "integrity": "sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==",
+ "dev": true
+ },
+ "node_modules/tmp": {
+ "version": "0.2.3",
+ "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.2.3.tgz",
+ "integrity": "sha512-nZD7m9iCPC5g0pYmcaxogYKggSfLsdxl8of3Q/oIbqCqLLIO9IAF0GWjX1z9NZRHPiXv8Wex4yDCaZsgEw0Y8w==",
+ "dev": true,
+ "engines": {
+ "node": ">=14.14"
+ }
+ },
+ "node_modules/to-fast-properties": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz",
+ "integrity": "sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==",
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/to-regex-range": {
+ "version": "5.0.1",
+ "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz",
+ "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==",
+ "dev": true,
+ "dependencies": {
+ "is-number": "^7.0.0"
+ },
+ "engines": {
+ "node": ">=8.0"
+ }
+ },
+ "node_modules/tough-cookie": {
+ "version": "4.1.4",
+ "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-4.1.4.tgz",
+ "integrity": "sha512-Loo5UUvLD9ScZ6jh8beX1T6sO1w2/MpCRpEP7V280GKMVUQ0Jzar2U3UJPsrdbziLEMMhu3Ujnq//rhiFuIeag==",
+ "dev": true,
+ "dependencies": {
+ "psl": "^1.1.33",
+ "punycode": "^2.1.1",
+ "universalify": "^0.2.0",
+ "url-parse": "^1.5.3"
+ },
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/tough-cookie/node_modules/universalify": {
+ "version": "0.2.0",
+ "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.2.0.tgz",
+ "integrity": "sha512-CJ1QgKmNg3CwvAv/kOFmtnEN05f0D/cn9QntgNOQlQF9dgvVTHj3t+8JPdjqawCHk7V/KA+fbUqzZ9XWhcqPUg==",
+ "dev": true,
+ "engines": {
+ "node": ">= 4.0.0"
+ }
+ },
+ "node_modules/tr46": {
+ "version": "0.0.3",
+ "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz",
+ "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw=="
+ },
+ "node_modules/ts-api-utils": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-1.3.0.tgz",
+ "integrity": "sha512-UQMIo7pb8WRomKR1/+MFVLTroIvDVtMX3K6OUir8ynLyzB8Jeriont2bTAtmNPa1ekAgN7YPDyf6V+ygrdU+eQ==",
+ "dev": true,
+ "engines": {
+ "node": ">=16"
+ },
+ "peerDependencies": {
+ "typescript": ">=4.2.0"
+ }
+ },
+ "node_modules/tslib": {
+ "version": "2.6.2",
+ "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz",
+ "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==",
+ "dev": true
+ },
+ "node_modules/tunnel-agent": {
+ "version": "0.6.0",
+ "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz",
+ "integrity": "sha512-McnNiV1l8RYeY8tBgEpuodCC1mLUdbSN+CYBL7kJsJNInOP8UjDDEwdk6Mw60vdLLrr5NHKZhMAOSrR2NZuQ+w==",
+ "dev": true,
+ "dependencies": {
+ "safe-buffer": "^5.0.1"
+ },
+ "engines": {
+ "node": "*"
+ }
+ },
+ "node_modules/tweetnacl": {
+ "version": "0.14.5",
+ "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz",
+ "integrity": "sha512-KXXFFdAbFXY4geFIwoyNK+f5Z1b7swfXABfL7HXCmoIWMKU3dmS26672A4EeQtDzLKy7SXmfBu51JolvEKwtGA==",
+ "dev": true
+ },
+ "node_modules/type-check": {
+ "version": "0.4.0",
+ "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz",
+ "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==",
+ "dev": true,
+ "dependencies": {
+ "prelude-ls": "^1.2.1"
+ },
+ "engines": {
+ "node": ">= 0.8.0"
+ }
+ },
+ "node_modules/type-fest": {
+ "version": "0.20.2",
+ "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz",
+ "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==",
+ "dev": true,
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/typescript": {
+ "version": "5.4.5",
+ "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.4.5.tgz",
+ "integrity": "sha512-vcI4UpRgg81oIRUFwR0WSIHKt11nJ7SAVlYNIu+QpqeyXP+gpQJy/Z4+F0aGxSE4MqwjyXvW/TzgkLAx2AGHwQ==",
+ "dev": true,
+ "bin": {
+ "tsc": "bin/tsc",
+ "tsserver": "bin/tsserver"
+ },
+ "engines": {
+ "node": ">=14.17"
+ }
+ },
+ "node_modules/undici-types": {
+ "version": "5.26.5",
+ "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz",
+ "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==",
+ "dev": true
+ },
+ "node_modules/universalify": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz",
+ "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==",
+ "dev": true,
+ "engines": {
+ "node": ">= 10.0.0"
+ }
+ },
+ "node_modules/untildify": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/untildify/-/untildify-4.0.0.tgz",
+ "integrity": "sha512-KK8xQ1mkzZeg9inewmFVDNkg3l5LUhoq9kN6iWYB/CC9YMG8HA+c1Q8HwDe6dEX7kErrEVNVBO3fWsVq5iDgtw==",
+ "dev": true,
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/update-browserslist-db": {
+ "version": "1.0.15",
+ "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.15.tgz",
+ "integrity": "sha512-K9HWH62x3/EalU1U6sjSZiylm9C8tgq2mSvshZpqc7QE69RaA2qjhkW2HlNA0tFpEbtyFz7HTqbSdN4MSwUodA==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/browserslist"
+ },
+ {
+ "type": "tidelift",
+ "url": "https://tidelift.com/funding/github/npm/browserslist"
+ },
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/ai"
+ }
+ ],
+ "dependencies": {
+ "escalade": "^3.1.2",
+ "picocolors": "^1.0.0"
+ },
+ "bin": {
+ "update-browserslist-db": "cli.js"
+ },
+ "peerDependencies": {
+ "browserslist": ">= 4.21.0"
+ }
+ },
+ "node_modules/uri-js": {
+ "version": "4.4.1",
+ "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz",
+ "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==",
+ "dev": true,
+ "dependencies": {
+ "punycode": "^2.1.0"
+ }
+ },
+ "node_modules/url-parse": {
+ "version": "1.5.10",
+ "resolved": "https://registry.npmjs.org/url-parse/-/url-parse-1.5.10.tgz",
+ "integrity": "sha512-WypcfiRhfeUP9vvF0j6rw0J3hrWrw6iZv3+22h6iRMJ/8z1Tj6XfLP4DsUix5MhMPnXpiHDoKyoZ/bdCkwBCiQ==",
+ "dev": true,
+ "dependencies": {
+ "querystringify": "^2.1.1",
+ "requires-port": "^1.0.0"
+ }
+ },
+ "node_modules/util-deprecate": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
+ "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw=="
+ },
+ "node_modules/uuid": {
+ "version": "8.3.2",
+ "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz",
+ "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==",
+ "dev": true,
+ "bin": {
+ "uuid": "dist/bin/uuid"
+ }
+ },
+ "node_modules/verror": {
+ "version": "1.10.0",
+ "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz",
+ "integrity": "sha512-ZZKSmDAEFOijERBLkmYfJ+vmk3w+7hOLYDNkRCuRuMJGEmqYNCNLyBBFwWKVMhfwaEF3WOd0Zlw86U/WC/+nYw==",
+ "dev": true,
+ "engines": [
+ "node >=0.6.0"
+ ],
+ "dependencies": {
+ "assert-plus": "^1.0.0",
+ "core-util-is": "1.0.2",
+ "extsprintf": "^1.2.0"
+ }
+ },
+ "node_modules/verror/node_modules/core-util-is": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz",
+ "integrity": "sha512-3lqz5YjWTYnW6dlDa5TLaTCcShfar1e40rmcJVwCBJC6mWlFuj0eCHIElmG1g5kyuJ/GD+8Wn4FFCcz4gJPfaQ==",
+ "dev": true
+ },
+ "node_modules/vite": {
+ "version": "5.2.11",
+ "resolved": "https://registry.npmjs.org/vite/-/vite-5.2.11.tgz",
+ "integrity": "sha512-HndV31LWW05i1BLPMUCE1B9E9GFbOu1MbenhS58FuK6owSO5qHm7GiCotrNY1YE5rMeQSFBGmT5ZaLEjFizgiQ==",
+ "dev": true,
+ "dependencies": {
+ "esbuild": "^0.20.1",
+ "postcss": "^8.4.38",
+ "rollup": "^4.13.0"
+ },
+ "bin": {
+ "vite": "bin/vite.js"
+ },
+ "engines": {
+ "node": "^18.0.0 || >=20.0.0"
+ },
+ "funding": {
+ "url": "https://github.com/vitejs/vite?sponsor=1"
+ },
+ "optionalDependencies": {
+ "fsevents": "~2.3.3"
+ },
+ "peerDependencies": {
+ "@types/node": "^18.0.0 || >=20.0.0",
+ "less": "*",
+ "lightningcss": "^1.21.0",
+ "sass": "*",
+ "stylus": "*",
+ "sugarss": "*",
+ "terser": "^5.4.0"
+ },
+ "peerDependenciesMeta": {
+ "@types/node": {
+ "optional": true
+ },
+ "less": {
+ "optional": true
+ },
+ "lightningcss": {
+ "optional": true
+ },
+ "sass": {
+ "optional": true
+ },
+ "stylus": {
+ "optional": true
+ },
+ "sugarss": {
+ "optional": true
+ },
+ "terser": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/void-elements": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/void-elements/-/void-elements-3.1.0.tgz",
+ "integrity": "sha512-Dhxzh5HZuiHQhbvTW9AMetFfBHDMYpo23Uo9btPXgdYP+3T5S+p+jgNy7spra+veYhBP2dCSgxR/i2Y02h5/6w==",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/webidl-conversions": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz",
+ "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ=="
+ },
+ "node_modules/whatwg-url": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz",
+ "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==",
+ "dependencies": {
+ "tr46": "~0.0.3",
+ "webidl-conversions": "^3.0.0"
+ }
+ },
+ "node_modules/which": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz",
+ "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==",
+ "dev": true,
+ "dependencies": {
+ "isexe": "^2.0.0"
+ },
+ "bin": {
+ "node-which": "bin/node-which"
+ },
+ "engines": {
+ "node": ">= 8"
+ }
+ },
+ "node_modules/word-wrap": {
+ "version": "1.2.5",
+ "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz",
+ "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==",
+ "dev": true,
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/wrap-ansi": {
+ "version": "7.0.0",
+ "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz",
+ "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==",
+ "dev": true,
+ "dependencies": {
+ "ansi-styles": "^4.0.0",
+ "string-width": "^4.1.0",
+ "strip-ansi": "^6.0.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/wrap-ansi?sponsor=1"
+ }
+ },
+ "node_modules/wrap-ansi/node_modules/ansi-styles": {
+ "version": "4.3.0",
+ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
+ "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
+ "dev": true,
+ "dependencies": {
+ "color-convert": "^2.0.1"
+ },
+ "engines": {
+ "node": ">=8"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/ansi-styles?sponsor=1"
+ }
+ },
+ "node_modules/wrap-ansi/node_modules/color-convert": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
+ "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
+ "dev": true,
+ "dependencies": {
+ "color-name": "~1.1.4"
+ },
+ "engines": {
+ "node": ">=7.0.0"
+ }
+ },
+ "node_modules/wrap-ansi/node_modules/color-name": {
+ "version": "1.1.4",
+ "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
+ "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
+ "dev": true
+ },
+ "node_modules/wrappy": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
+ "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==",
+ "dev": true
+ },
+ "node_modules/yallist": {
+ "version": "3.1.1",
+ "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz",
+ "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==",
+ "dev": true
+ },
+ "node_modules/yaml": {
+ "version": "1.10.2",
+ "resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.2.tgz",
+ "integrity": "sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==",
+ "engines": {
+ "node": ">= 6"
+ }
+ },
+ "node_modules/yauzl": {
+ "version": "2.10.0",
+ "resolved": "https://registry.npmjs.org/yauzl/-/yauzl-2.10.0.tgz",
+ "integrity": "sha512-p4a9I6X6nu6IhoGmBqAcbJy1mlC4j27vEPZX9F4L4/vZT3Lyq1VkFHw/V/PUcB9Buo+DG3iHkT0x3Qya58zc3g==",
+ "dev": true,
+ "dependencies": {
+ "buffer-crc32": "~0.2.3",
+ "fd-slicer": "~1.1.0"
+ }
+ },
+ "node_modules/yocto-queue": {
+ "version": "0.1.0",
+ "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz",
+ "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==",
+ "dev": true,
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ }
+ },
+ "dependencies": {
+ "@ampproject/remapping": {
+ "version": "2.3.0",
+ "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.3.0.tgz",
+ "integrity": "sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==",
+ "dev": true,
+ "requires": {
+ "@jridgewell/gen-mapping": "^0.3.5",
+ "@jridgewell/trace-mapping": "^0.3.24"
+ }
+ },
+ "@azure/msal-browser": {
+ "version": "3.13.0",
+ "resolved": "https://registry.npmjs.org/@azure/msal-browser/-/msal-browser-3.13.0.tgz",
+ "integrity": "sha512-fD906nmJei3yE7la6DZTdUtXKvpwzJURkfsiz9747Icv4pit77cegSm6prJTKLQ1fw4iiZzrrWwxnhMLrTf5gQ==",
+ "requires": {
+ "@azure/msal-common": "14.9.0"
+ }
+ },
+ "@azure/msal-common": {
+ "version": "14.9.0",
+ "resolved": "https://registry.npmjs.org/@azure/msal-common/-/msal-common-14.9.0.tgz",
+ "integrity": "sha512-yzBPRlWPnTBeixxLNI3BBIgF5/bHpbhoRVuuDBnYjCyWRavaPUsKAHUDYLqpGkBLDciA6TCc6GOxN4/S3WiSxg=="
+ },
+ "@azure/msal-react": {
+ "version": "2.0.15",
+ "resolved": "https://registry.npmjs.org/@azure/msal-react/-/msal-react-2.0.15.tgz",
+ "integrity": "sha512-WtZGPxA6rWqGhovmWPUXjUyKTp/TUa4Oekk6FSa9ldCayd1QzYT9XcoAEqA0EfT0Fx12KiNvJyDDKckBU6J/+Q==",
+ "requires": {}
+ },
+ "@babel/code-frame": {
+ "version": "7.24.2",
+ "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.24.2.tgz",
+ "integrity": "sha512-y5+tLQyV8pg3fsiln67BVLD1P13Eg4lh5RW9mF0zUuvLrv9uIQ4MCL+CRT+FTsBlBjcIan6PGsLcBN0m3ClUyQ==",
+ "requires": {
+ "@babel/highlight": "^7.24.2",
+ "picocolors": "^1.0.0"
+ }
+ },
+ "@babel/compat-data": {
+ "version": "7.24.4",
+ "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.24.4.tgz",
+ "integrity": "sha512-vg8Gih2MLK+kOkHJp4gBEIkyaIi00jgWot2D9QOmmfLC8jINSOzmCLta6Bvz/JSBCqnegV0L80jhxkol5GWNfQ==",
+ "dev": true
+ },
+ "@babel/core": {
+ "version": "7.24.5",
+ "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.24.5.tgz",
+ "integrity": "sha512-tVQRucExLQ02Boi4vdPp49svNGcfL2GhdTCT9aldhXgCJVAI21EtRfBettiuLUwce/7r6bFdgs6JFkcdTiFttA==",
+ "dev": true,
+ "requires": {
+ "@ampproject/remapping": "^2.2.0",
+ "@babel/code-frame": "^7.24.2",
+ "@babel/generator": "^7.24.5",
+ "@babel/helper-compilation-targets": "^7.23.6",
+ "@babel/helper-module-transforms": "^7.24.5",
+ "@babel/helpers": "^7.24.5",
+ "@babel/parser": "^7.24.5",
+ "@babel/template": "^7.24.0",
+ "@babel/traverse": "^7.24.5",
+ "@babel/types": "^7.24.5",
+ "convert-source-map": "^2.0.0",
+ "debug": "^4.1.0",
+ "gensync": "^1.0.0-beta.2",
+ "json5": "^2.2.3",
+ "semver": "^6.3.1"
+ },
+ "dependencies": {
+ "convert-source-map": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz",
+ "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==",
+ "dev": true
+ },
+ "semver": {
+ "version": "6.3.1",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz",
+ "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==",
+ "dev": true
+ }
+ }
+ },
+ "@babel/generator": {
+ "version": "7.24.5",
+ "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.24.5.tgz",
+ "integrity": "sha512-x32i4hEXvr+iI0NEoEfDKzlemF8AmtOP8CcrRaEcpzysWuoEb1KknpcvMsHKPONoKZiDuItklgWhB18xEhr9PA==",
+ "dev": true,
+ "requires": {
+ "@babel/types": "^7.24.5",
+ "@jridgewell/gen-mapping": "^0.3.5",
+ "@jridgewell/trace-mapping": "^0.3.25",
+ "jsesc": "^2.5.1"
+ }
+ },
+ "@babel/helper-compilation-targets": {
+ "version": "7.23.6",
+ "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.23.6.tgz",
+ "integrity": "sha512-9JB548GZoQVmzrFgp8o7KxdgkTGm6xs9DW0o/Pim72UDjzr5ObUQ6ZzYPqA+g9OTS2bBQoctLJrky0RDCAWRgQ==",
+ "dev": true,
+ "requires": {
+ "@babel/compat-data": "^7.23.5",
+ "@babel/helper-validator-option": "^7.23.5",
+ "browserslist": "^4.22.2",
+ "lru-cache": "^5.1.1",
+ "semver": "^6.3.1"
+ },
+ "dependencies": {
+ "semver": {
+ "version": "6.3.1",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz",
+ "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==",
+ "dev": true
+ }
+ }
+ },
+ "@babel/helper-environment-visitor": {
+ "version": "7.22.20",
+ "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.22.20.tgz",
+ "integrity": "sha512-zfedSIzFhat/gFhWfHtgWvlec0nqB9YEIVrpuwjruLlXfUSnA8cJB0miHKwqDnQ7d32aKo2xt88/xZptwxbfhA==",
+ "dev": true
+ },
+ "@babel/helper-function-name": {
+ "version": "7.23.0",
+ "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.23.0.tgz",
+ "integrity": "sha512-OErEqsrxjZTJciZ4Oo+eoZqeW9UIiOcuYKRJA4ZAgV9myA+pOXhhmpfNCKjEH/auVfEYVFJ6y1Tc4r0eIApqiw==",
+ "dev": true,
+ "requires": {
+ "@babel/template": "^7.22.15",
+ "@babel/types": "^7.23.0"
+ }
+ },
+ "@babel/helper-hoist-variables": {
+ "version": "7.22.5",
+ "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.22.5.tgz",
+ "integrity": "sha512-wGjk9QZVzvknA6yKIUURb8zY3grXCcOZt+/7Wcy8O2uctxhplmUPkOdlgoNhmdVee2c92JXbf1xpMtVNbfoxRw==",
+ "dev": true,
+ "requires": {
+ "@babel/types": "^7.22.5"
+ }
+ },
+ "@babel/helper-module-imports": {
+ "version": "7.24.3",
+ "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.24.3.tgz",
+ "integrity": "sha512-viKb0F9f2s0BCS22QSF308z/+1YWKV/76mwt61NBzS5izMzDPwdq1pTrzf+Li3npBWX9KdQbkeCt1jSAM7lZqg==",
+ "requires": {
+ "@babel/types": "^7.24.0"
+ }
+ },
+ "@babel/helper-module-transforms": {
+ "version": "7.24.5",
+ "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.24.5.tgz",
+ "integrity": "sha512-9GxeY8c2d2mdQUP1Dye0ks3VDyIMS98kt/llQ2nUId8IsWqTF0l1LkSX0/uP7l7MCDrzXS009Hyhe2gzTiGW8A==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-environment-visitor": "^7.22.20",
+ "@babel/helper-module-imports": "^7.24.3",
+ "@babel/helper-simple-access": "^7.24.5",
+ "@babel/helper-split-export-declaration": "^7.24.5",
+ "@babel/helper-validator-identifier": "^7.24.5"
+ }
+ },
+ "@babel/helper-plugin-utils": {
+ "version": "7.24.5",
+ "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.24.5.tgz",
+ "integrity": "sha512-xjNLDopRzW2o6ba0gKbkZq5YWEBaK3PCyTOY1K2P/O07LGMhMqlMXPxwN4S5/RhWuCobT8z0jrlKGlYmeR1OhQ==",
+ "dev": true
+ },
+ "@babel/helper-simple-access": {
+ "version": "7.24.5",
+ "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.24.5.tgz",
+ "integrity": "sha512-uH3Hmf5q5n7n8mz7arjUlDOCbttY/DW4DYhE6FUsjKJ/oYC1kQQUvwEQWxRwUpX9qQKRXeqLwWxrqilMrf32sQ==",
+ "dev": true,
+ "requires": {
+ "@babel/types": "^7.24.5"
+ }
+ },
+ "@babel/helper-split-export-declaration": {
+ "version": "7.24.5",
+ "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.24.5.tgz",
+ "integrity": "sha512-5CHncttXohrHk8GWOFCcCl4oRD9fKosWlIRgWm4ql9VYioKm52Mk2xsmoohvm7f3JoiLSM5ZgJuRaf5QZZYd3Q==",
+ "dev": true,
+ "requires": {
+ "@babel/types": "^7.24.5"
+ }
+ },
+ "@babel/helper-string-parser": {
+ "version": "7.24.1",
+ "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.24.1.tgz",
+ "integrity": "sha512-2ofRCjnnA9y+wk8b9IAREroeUP02KHp431N2mhKniy2yKIDKpbrHv9eXwm8cBeWQYcJmzv5qKCu65P47eCF7CQ=="
+ },
+ "@babel/helper-validator-identifier": {
+ "version": "7.24.5",
+ "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.24.5.tgz",
+ "integrity": "sha512-3q93SSKX2TWCG30M2G2kwaKeTYgEUp5Snjuj8qm729SObL6nbtUldAi37qbxkD5gg3xnBio+f9nqpSepGZMvxA=="
+ },
+ "@babel/helper-validator-option": {
+ "version": "7.23.5",
+ "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.23.5.tgz",
+ "integrity": "sha512-85ttAOMLsr53VgXkTbkx8oA6YTfT4q7/HzXSLEYmjcSTJPMPQtvq1BD79Byep5xMUYbGRzEpDsjUf3dyp54IKw==",
+ "dev": true
+ },
+ "@babel/helpers": {
+ "version": "7.24.5",
+ "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.24.5.tgz",
+ "integrity": "sha512-CiQmBMMpMQHwM5m01YnrM6imUG1ebgYJ+fAIW4FZe6m4qHTPaRHti+R8cggAwkdz4oXhtO4/K9JWlh+8hIfR2Q==",
+ "dev": true,
+ "requires": {
+ "@babel/template": "^7.24.0",
+ "@babel/traverse": "^7.24.5",
+ "@babel/types": "^7.24.5"
+ }
+ },
+ "@babel/highlight": {
+ "version": "7.24.5",
+ "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.24.5.tgz",
+ "integrity": "sha512-8lLmua6AVh/8SLJRRVD6V8p73Hir9w5mJrhE+IPpILG31KKlI9iz5zmBYKcWPS59qSfgP9RaSBQSHHE81WKuEw==",
+ "requires": {
+ "@babel/helper-validator-identifier": "^7.24.5",
+ "chalk": "^2.4.2",
+ "js-tokens": "^4.0.0",
+ "picocolors": "^1.0.0"
+ }
+ },
+ "@babel/parser": {
+ "version": "7.24.5",
+ "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.24.5.tgz",
+ "integrity": "sha512-EOv5IK8arwh3LI47dz1b0tKUb/1uhHAnHJOrjgtQMIpu1uXd9mlFrJg9IUgGUgZ41Ch0K8REPTYpO7B76b4vJg==",
+ "dev": true
+ },
+ "@babel/plugin-transform-react-jsx-self": {
+ "version": "7.24.5",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-self/-/plugin-transform-react-jsx-self-7.24.5.tgz",
+ "integrity": "sha512-RtCJoUO2oYrYwFPtR1/jkoBEcFuI1ae9a9IMxeyAVa3a1Ap4AnxmyIKG2b2FaJKqkidw/0cxRbWN+HOs6ZWd1w==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-plugin-utils": "^7.24.5"
+ }
+ },
+ "@babel/plugin-transform-react-jsx-source": {
+ "version": "7.24.1",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-source/-/plugin-transform-react-jsx-source-7.24.1.tgz",
+ "integrity": "sha512-1v202n7aUq4uXAieRTKcwPzNyphlCuqHHDcdSNc+vdhoTEZcFMh+L5yZuCmGaIO7bs1nJUNfHB89TZyoL48xNA==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-plugin-utils": "^7.24.0"
+ }
+ },
+ "@babel/runtime": {
+ "version": "7.24.5",
+ "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.24.5.tgz",
+ "integrity": "sha512-Nms86NXrsaeU9vbBJKni6gXiEXZ4CVpYVzEjDH9Sb8vmZ3UljyA1GSOJl/6LGPO8EHLuSF9H+IxNXHPX8QHJ4g==",
+ "requires": {
+ "regenerator-runtime": "^0.14.0"
+ }
+ },
+ "@babel/template": {
+ "version": "7.24.0",
+ "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.24.0.tgz",
+ "integrity": "sha512-Bkf2q8lMB0AFpX0NFEqSbx1OkTHf0f+0j82mkw+ZpzBnkk7e9Ql0891vlfgi+kHwOk8tQjiQHpqh4LaSa0fKEA==",
+ "dev": true,
+ "requires": {
+ "@babel/code-frame": "^7.23.5",
+ "@babel/parser": "^7.24.0",
+ "@babel/types": "^7.24.0"
+ }
+ },
+ "@babel/traverse": {
+ "version": "7.24.5",
+ "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.24.5.tgz",
+ "integrity": "sha512-7aaBLeDQ4zYcUFDUD41lJc1fG8+5IU9DaNSJAgal866FGvmD5EbWQgnEC6kO1gGLsX0esNkfnJSndbTXA3r7UA==",
+ "dev": true,
+ "requires": {
+ "@babel/code-frame": "^7.24.2",
+ "@babel/generator": "^7.24.5",
+ "@babel/helper-environment-visitor": "^7.22.20",
+ "@babel/helper-function-name": "^7.23.0",
+ "@babel/helper-hoist-variables": "^7.22.5",
+ "@babel/helper-split-export-declaration": "^7.24.5",
+ "@babel/parser": "^7.24.5",
+ "@babel/types": "^7.24.5",
+ "debug": "^4.3.1",
+ "globals": "^11.1.0"
+ }
+ },
+ "@babel/types": {
+ "version": "7.24.5",
+ "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.24.5.tgz",
+ "integrity": "sha512-6mQNsaLeXTw0nxYUYu+NSa4Hx4BlF1x1x8/PMFbiR+GBSr+2DkECc69b8hgy2frEodNcvPffeH8YfWd3LI6jhQ==",
+ "requires": {
+ "@babel/helper-string-parser": "^7.24.1",
+ "@babel/helper-validator-identifier": "^7.24.5",
+ "to-fast-properties": "^2.0.0"
+ }
+ },
+ "@colors/colors": {
+ "version": "1.5.0",
+ "resolved": "https://registry.npmjs.org/@colors/colors/-/colors-1.5.0.tgz",
+ "integrity": "sha512-ooWCrlZP11i8GImSjTHYHLkvFDP48nS4+204nGb1RiX/WXYHmJA2III9/e2DWVabCESdW7hBAEzHRqUn9OUVvQ==",
+ "dev": true,
+ "optional": true
+ },
+ "@cypress/request": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/@cypress/request/-/request-3.0.1.tgz",
+ "integrity": "sha512-TWivJlJi8ZDx2wGOw1dbLuHJKUYX7bWySw377nlnGOW3hP9/MUKIsEdXT/YngWxVdgNCHRBmFlBipE+5/2ZZlQ==",
+ "dev": true,
+ "requires": {
+ "aws-sign2": "~0.7.0",
+ "aws4": "^1.8.0",
+ "caseless": "~0.12.0",
+ "combined-stream": "~1.0.6",
+ "extend": "~3.0.2",
+ "forever-agent": "~0.6.1",
+ "form-data": "~2.3.2",
+ "http-signature": "~1.3.6",
+ "is-typedarray": "~1.0.0",
+ "isstream": "~0.1.2",
+ "json-stringify-safe": "~5.0.1",
+ "mime-types": "~2.1.19",
+ "performance-now": "^2.1.0",
+ "qs": "6.10.4",
+ "safe-buffer": "^5.1.2",
+ "tough-cookie": "^4.1.3",
+ "tunnel-agent": "^0.6.0",
+ "uuid": "^8.3.2"
+ },
+ "dependencies": {
+ "form-data": {
+ "version": "2.3.3",
+ "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.3.tgz",
+ "integrity": "sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ==",
+ "dev": true,
+ "requires": {
+ "asynckit": "^0.4.0",
+ "combined-stream": "^1.0.6",
+ "mime-types": "^2.1.12"
+ }
+ }
+ }
+ },
+ "@cypress/xvfb": {
+ "version": "1.2.4",
+ "resolved": "https://registry.npmjs.org/@cypress/xvfb/-/xvfb-1.2.4.tgz",
+ "integrity": "sha512-skbBzPggOVYCbnGgV+0dmBdW/s77ZkAOXIC1knS8NagwDjBrNC1LuXtQJeiN6l+m7lzmHtaoUw/ctJKdqkG57Q==",
+ "dev": true,
+ "requires": {
+ "debug": "^3.1.0",
+ "lodash.once": "^4.1.1"
+ },
+ "dependencies": {
+ "debug": {
+ "version": "3.2.7",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz",
+ "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==",
+ "dev": true,
+ "requires": {
+ "ms": "^2.1.1"
+ }
+ }
+ }
+ },
+ "@emotion/babel-plugin": {
+ "version": "11.11.0",
+ "resolved": "https://registry.npmjs.org/@emotion/babel-plugin/-/babel-plugin-11.11.0.tgz",
+ "integrity": "sha512-m4HEDZleaaCH+XgDDsPF15Ht6wTLsgDTeR3WYj9Q/k76JtWhrJjcP4+/XlG8LGT/Rol9qUfOIztXeA84ATpqPQ==",
+ "requires": {
+ "@babel/helper-module-imports": "^7.16.7",
+ "@babel/runtime": "^7.18.3",
+ "@emotion/hash": "^0.9.1",
+ "@emotion/memoize": "^0.8.1",
+ "@emotion/serialize": "^1.1.2",
+ "babel-plugin-macros": "^3.1.0",
+ "convert-source-map": "^1.5.0",
+ "escape-string-regexp": "^4.0.0",
+ "find-root": "^1.1.0",
+ "source-map": "^0.5.7",
+ "stylis": "4.2.0"
+ }
+ },
+ "@emotion/cache": {
+ "version": "11.11.0",
+ "resolved": "https://registry.npmjs.org/@emotion/cache/-/cache-11.11.0.tgz",
+ "integrity": "sha512-P34z9ssTCBi3e9EI1ZsWpNHcfY1r09ZO0rZbRO2ob3ZQMnFI35jB536qoXbkdesr5EUhYi22anuEJuyxifaqAQ==",
+ "requires": {
+ "@emotion/memoize": "^0.8.1",
+ "@emotion/sheet": "^1.2.2",
+ "@emotion/utils": "^1.2.1",
+ "@emotion/weak-memoize": "^0.3.1",
+ "stylis": "4.2.0"
+ }
+ },
+ "@emotion/hash": {
+ "version": "0.9.1",
+ "resolved": "https://registry.npmjs.org/@emotion/hash/-/hash-0.9.1.tgz",
+ "integrity": "sha512-gJB6HLm5rYwSLI6PQa+X1t5CFGrv1J1TWG+sOyMCeKz2ojaj6Fnl/rZEspogG+cvqbt4AE/2eIyD2QfLKTBNlQ=="
+ },
+ "@emotion/is-prop-valid": {
+ "version": "1.2.2",
+ "resolved": "https://registry.npmjs.org/@emotion/is-prop-valid/-/is-prop-valid-1.2.2.tgz",
+ "integrity": "sha512-uNsoYd37AFmaCdXlg6EYD1KaPOaRWRByMCYzbKUX4+hhMfrxdVSelShywL4JVaAeM/eHUOSprYBQls+/neX3pw==",
+ "requires": {
+ "@emotion/memoize": "^0.8.1"
+ }
+ },
+ "@emotion/memoize": {
+ "version": "0.8.1",
+ "resolved": "https://registry.npmjs.org/@emotion/memoize/-/memoize-0.8.1.tgz",
+ "integrity": "sha512-W2P2c/VRW1/1tLox0mVUalvnWXxavmv/Oum2aPsRcoDJuob75FC3Y8FbpfLwUegRcxINtGUMPq0tFCvYNTBXNA=="
+ },
+ "@emotion/react": {
+ "version": "11.11.4",
+ "resolved": "https://registry.npmjs.org/@emotion/react/-/react-11.11.4.tgz",
+ "integrity": "sha512-t8AjMlF0gHpvvxk5mAtCqR4vmxiGHCeJBaQO6gncUSdklELOgtwjerNY2yuJNfwnc6vi16U/+uMF+afIawJ9iw==",
+ "requires": {
+ "@babel/runtime": "^7.18.3",
+ "@emotion/babel-plugin": "^11.11.0",
+ "@emotion/cache": "^11.11.0",
+ "@emotion/serialize": "^1.1.3",
+ "@emotion/use-insertion-effect-with-fallbacks": "^1.0.1",
+ "@emotion/utils": "^1.2.1",
+ "@emotion/weak-memoize": "^0.3.1",
+ "hoist-non-react-statics": "^3.3.1"
+ }
+ },
+ "@emotion/serialize": {
+ "version": "1.1.4",
+ "resolved": "https://registry.npmjs.org/@emotion/serialize/-/serialize-1.1.4.tgz",
+ "integrity": "sha512-RIN04MBT8g+FnDwgvIUi8czvr1LU1alUMI05LekWB5DGyTm8cCBMCRpq3GqaiyEDRptEXOyXnvZ58GZYu4kBxQ==",
+ "requires": {
+ "@emotion/hash": "^0.9.1",
+ "@emotion/memoize": "^0.8.1",
+ "@emotion/unitless": "^0.8.1",
+ "@emotion/utils": "^1.2.1",
+ "csstype": "^3.0.2"
+ }
+ },
+ "@emotion/sheet": {
+ "version": "1.2.2",
+ "resolved": "https://registry.npmjs.org/@emotion/sheet/-/sheet-1.2.2.tgz",
+ "integrity": "sha512-0QBtGvaqtWi+nx6doRwDdBIzhNdZrXUppvTM4dtZZWEGTXL/XE/yJxLMGlDT1Gt+UHH5IX1n+jkXyytE/av7OA=="
+ },
+ "@emotion/styled": {
+ "version": "11.11.5",
+ "resolved": "https://registry.npmjs.org/@emotion/styled/-/styled-11.11.5.tgz",
+ "integrity": "sha512-/ZjjnaNKvuMPxcIiUkf/9SHoG4Q196DRl1w82hQ3WCsjo1IUR8uaGWrC6a87CrYAW0Kb/pK7hk8BnLgLRi9KoQ==",
+ "requires": {
+ "@babel/runtime": "^7.18.3",
+ "@emotion/babel-plugin": "^11.11.0",
+ "@emotion/is-prop-valid": "^1.2.2",
+ "@emotion/serialize": "^1.1.4",
+ "@emotion/use-insertion-effect-with-fallbacks": "^1.0.1",
+ "@emotion/utils": "^1.2.1"
+ }
+ },
+ "@emotion/unitless": {
+ "version": "0.8.1",
+ "resolved": "https://registry.npmjs.org/@emotion/unitless/-/unitless-0.8.1.tgz",
+ "integrity": "sha512-KOEGMu6dmJZtpadb476IsZBclKvILjopjUii3V+7MnXIQCYh8W3NgNcgwo21n9LXZX6EDIKvqfjYxXebDwxKmQ=="
+ },
+ "@emotion/use-insertion-effect-with-fallbacks": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/@emotion/use-insertion-effect-with-fallbacks/-/use-insertion-effect-with-fallbacks-1.0.1.tgz",
+ "integrity": "sha512-jT/qyKZ9rzLErtrjGgdkMBn2OP8wl0G3sQlBb3YPryvKHsjvINUhVaPFfP+fpBcOkmrVOVEEHQFJ7nbj2TH2gw==",
+ "requires": {}
+ },
+ "@emotion/utils": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/@emotion/utils/-/utils-1.2.1.tgz",
+ "integrity": "sha512-Y2tGf3I+XVnajdItskUCn6LX+VUDmP6lTL4fcqsXAv43dnlbZiuW4MWQW38rW/BVWSE7Q/7+XQocmpnRYILUmg=="
+ },
+ "@emotion/weak-memoize": {
+ "version": "0.3.1",
+ "resolved": "https://registry.npmjs.org/@emotion/weak-memoize/-/weak-memoize-0.3.1.tgz",
+ "integrity": "sha512-EsBwpc7hBUJWAsNPBmJy4hxWx12v6bshQsldrVmjxJoc3isbxhOrF2IcCpaXxfvq03NwkI7sbsOLXbYuqF/8Ww=="
+ },
+ "@esbuild/aix-ppc64": {
+ "version": "0.20.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.20.2.tgz",
+ "integrity": "sha512-D+EBOJHXdNZcLJRBkhENNG8Wji2kgc9AZ9KiPr1JuZjsNtyHzrsfLRrY0tk2H2aoFu6RANO1y1iPPUCDYWkb5g==",
+ "dev": true,
+ "optional": true
+ },
+ "@esbuild/android-arm": {
+ "version": "0.20.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.20.2.tgz",
+ "integrity": "sha512-t98Ra6pw2VaDhqNWO2Oph2LXbz/EJcnLmKLGBJwEwXX/JAN83Fym1rU8l0JUWK6HkIbWONCSSatf4sf2NBRx/w==",
+ "dev": true,
+ "optional": true
+ },
+ "@esbuild/android-arm64": {
+ "version": "0.20.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.20.2.tgz",
+ "integrity": "sha512-mRzjLacRtl/tWU0SvD8lUEwb61yP9cqQo6noDZP/O8VkwafSYwZ4yWy24kan8jE/IMERpYncRt2dw438LP3Xmg==",
+ "dev": true,
+ "optional": true
+ },
+ "@esbuild/android-x64": {
+ "version": "0.20.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.20.2.tgz",
+ "integrity": "sha512-btzExgV+/lMGDDa194CcUQm53ncxzeBrWJcncOBxuC6ndBkKxnHdFJn86mCIgTELsooUmwUm9FkhSp5HYu00Rg==",
+ "dev": true,
+ "optional": true
+ },
+ "@esbuild/darwin-arm64": {
+ "version": "0.20.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.20.2.tgz",
+ "integrity": "sha512-4J6IRT+10J3aJH3l1yzEg9y3wkTDgDk7TSDFX+wKFiWjqWp/iCfLIYzGyasx9l0SAFPT1HwSCR+0w/h1ES/MjA==",
+ "dev": true,
+ "optional": true
+ },
+ "@esbuild/darwin-x64": {
+ "version": "0.20.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.20.2.tgz",
+ "integrity": "sha512-tBcXp9KNphnNH0dfhv8KYkZhjc+H3XBkF5DKtswJblV7KlT9EI2+jeA8DgBjp908WEuYll6pF+UStUCfEpdysA==",
+ "dev": true,
+ "optional": true
+ },
+ "@esbuild/freebsd-arm64": {
+ "version": "0.20.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.20.2.tgz",
+ "integrity": "sha512-d3qI41G4SuLiCGCFGUrKsSeTXyWG6yem1KcGZVS+3FYlYhtNoNgYrWcvkOoaqMhwXSMrZRl69ArHsGJ9mYdbbw==",
+ "dev": true,
+ "optional": true
+ },
+ "@esbuild/freebsd-x64": {
+ "version": "0.20.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.20.2.tgz",
+ "integrity": "sha512-d+DipyvHRuqEeM5zDivKV1KuXn9WeRX6vqSqIDgwIfPQtwMP4jaDsQsDncjTDDsExT4lR/91OLjRo8bmC1e+Cw==",
+ "dev": true,
+ "optional": true
+ },
+ "@esbuild/linux-arm": {
+ "version": "0.20.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.20.2.tgz",
+ "integrity": "sha512-VhLPeR8HTMPccbuWWcEUD1Az68TqaTYyj6nfE4QByZIQEQVWBB8vup8PpR7y1QHL3CpcF6xd5WVBU/+SBEvGTg==",
+ "dev": true,
+ "optional": true
+ },
+ "@esbuild/linux-arm64": {
+ "version": "0.20.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.20.2.tgz",
+ "integrity": "sha512-9pb6rBjGvTFNira2FLIWqDk/uaf42sSyLE8j1rnUpuzsODBq7FvpwHYZxQ/It/8b+QOS1RYfqgGFNLRI+qlq2A==",
+ "dev": true,
+ "optional": true
+ },
+ "@esbuild/linux-ia32": {
+ "version": "0.20.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.20.2.tgz",
+ "integrity": "sha512-o10utieEkNPFDZFQm9CoP7Tvb33UutoJqg3qKf1PWVeeJhJw0Q347PxMvBgVVFgouYLGIhFYG0UGdBumROyiig==",
+ "dev": true,
+ "optional": true
+ },
+ "@esbuild/linux-loong64": {
+ "version": "0.20.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.20.2.tgz",
+ "integrity": "sha512-PR7sp6R/UC4CFVomVINKJ80pMFlfDfMQMYynX7t1tNTeivQ6XdX5r2XovMmha/VjR1YN/HgHWsVcTRIMkymrgQ==",
+ "dev": true,
+ "optional": true
+ },
+ "@esbuild/linux-mips64el": {
+ "version": "0.20.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.20.2.tgz",
+ "integrity": "sha512-4BlTqeutE/KnOiTG5Y6Sb/Hw6hsBOZapOVF6njAESHInhlQAghVVZL1ZpIctBOoTFbQyGW+LsVYZ8lSSB3wkjA==",
+ "dev": true,
+ "optional": true
+ },
+ "@esbuild/linux-ppc64": {
+ "version": "0.20.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.20.2.tgz",
+ "integrity": "sha512-rD3KsaDprDcfajSKdn25ooz5J5/fWBylaaXkuotBDGnMnDP1Uv5DLAN/45qfnf3JDYyJv/ytGHQaziHUdyzaAg==",
+ "dev": true,
+ "optional": true
+ },
+ "@esbuild/linux-riscv64": {
+ "version": "0.20.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.20.2.tgz",
+ "integrity": "sha512-snwmBKacKmwTMmhLlz/3aH1Q9T8v45bKYGE3j26TsaOVtjIag4wLfWSiZykXzXuE1kbCE+zJRmwp+ZbIHinnVg==",
+ "dev": true,
+ "optional": true
+ },
+ "@esbuild/linux-s390x": {
+ "version": "0.20.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.20.2.tgz",
+ "integrity": "sha512-wcWISOobRWNm3cezm5HOZcYz1sKoHLd8VL1dl309DiixxVFoFe/o8HnwuIwn6sXre88Nwj+VwZUvJf4AFxkyrQ==",
+ "dev": true,
+ "optional": true
+ },
+ "@esbuild/linux-x64": {
+ "version": "0.20.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.20.2.tgz",
+ "integrity": "sha512-1MdwI6OOTsfQfek8sLwgyjOXAu+wKhLEoaOLTjbijk6E2WONYpH9ZU2mNtR+lZ2B4uwr+usqGuVfFT9tMtGvGw==",
+ "dev": true,
+ "optional": true
+ },
+ "@esbuild/netbsd-x64": {
+ "version": "0.20.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.20.2.tgz",
+ "integrity": "sha512-K8/DhBxcVQkzYc43yJXDSyjlFeHQJBiowJ0uVL6Tor3jGQfSGHNNJcWxNbOI8v5k82prYqzPuwkzHt3J1T1iZQ==",
+ "dev": true,
+ "optional": true
+ },
+ "@esbuild/openbsd-x64": {
+ "version": "0.20.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.20.2.tgz",
+ "integrity": "sha512-eMpKlV0SThJmmJgiVyN9jTPJ2VBPquf6Kt/nAoo6DgHAoN57K15ZghiHaMvqjCye/uU4X5u3YSMgVBI1h3vKrQ==",
+ "dev": true,
+ "optional": true
+ },
+ "@esbuild/sunos-x64": {
+ "version": "0.20.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.20.2.tgz",
+ "integrity": "sha512-2UyFtRC6cXLyejf/YEld4Hajo7UHILetzE1vsRcGL3earZEW77JxrFjH4Ez2qaTiEfMgAXxfAZCm1fvM/G/o8w==",
+ "dev": true,
+ "optional": true
+ },
+ "@esbuild/win32-arm64": {
+ "version": "0.20.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.20.2.tgz",
+ "integrity": "sha512-GRibxoawM9ZCnDxnP3usoUDO9vUkpAxIIZ6GQI+IlVmr5kP3zUq+l17xELTHMWTWzjxa2guPNyrpq1GWmPvcGQ==",
+ "dev": true,
+ "optional": true
+ },
+ "@esbuild/win32-ia32": {
+ "version": "0.20.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.20.2.tgz",
+ "integrity": "sha512-HfLOfn9YWmkSKRQqovpnITazdtquEW8/SoHW7pWpuEeguaZI4QnCRW6b+oZTztdBnZOS2hqJ6im/D5cPzBTTlQ==",
+ "dev": true,
+ "optional": true
+ },
+ "@esbuild/win32-x64": {
+ "version": "0.20.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.20.2.tgz",
+ "integrity": "sha512-N49X4lJX27+l9jbLKSqZ6bKNjzQvHaT8IIFUy+YIqmXQdjYCToGWwOItDrfby14c78aDd5NHQl29xingXfCdLQ==",
+ "dev": true,
+ "optional": true
+ },
+ "@eslint-community/eslint-utils": {
+ "version": "4.4.0",
+ "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz",
+ "integrity": "sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==",
+ "dev": true,
+ "requires": {
+ "eslint-visitor-keys": "^3.3.0"
+ }
+ },
+ "@eslint-community/regexpp": {
+ "version": "4.10.0",
+ "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.10.0.tgz",
+ "integrity": "sha512-Cu96Sd2By9mCNTx2iyKOmq10v22jUVQv0lQnlGNy16oE9589yE+QADPbrMGCkA51cKZSg3Pu/aTJVTGfL/qjUA==",
+ "dev": true
+ },
+ "@eslint/eslintrc": {
+ "version": "2.1.4",
+ "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.1.4.tgz",
+ "integrity": "sha512-269Z39MS6wVJtsoUl10L60WdkhJVdPG24Q4eZTH3nnF6lpvSShEK3wQjDX9JRWAUPvPh7COouPpU9IrqaZFvtQ==",
+ "dev": true,
+ "requires": {
+ "ajv": "^6.12.4",
+ "debug": "^4.3.2",
+ "espree": "^9.6.0",
+ "globals": "^13.19.0",
+ "ignore": "^5.2.0",
+ "import-fresh": "^3.2.1",
+ "js-yaml": "^4.1.0",
+ "minimatch": "^3.1.2",
+ "strip-json-comments": "^3.1.1"
+ },
+ "dependencies": {
+ "brace-expansion": {
+ "version": "1.1.11",
+ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
+ "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
+ "dev": true,
+ "requires": {
+ "balanced-match": "^1.0.0",
+ "concat-map": "0.0.1"
+ }
+ },
+ "globals": {
+ "version": "13.24.0",
+ "resolved": "https://registry.npmjs.org/globals/-/globals-13.24.0.tgz",
+ "integrity": "sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==",
+ "dev": true,
+ "requires": {
+ "type-fest": "^0.20.2"
+ }
+ },
+ "minimatch": {
+ "version": "3.1.2",
+ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
+ "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==",
+ "dev": true,
+ "requires": {
+ "brace-expansion": "^1.1.7"
+ }
+ }
+ }
+ },
+ "@eslint/js": {
+ "version": "8.57.0",
+ "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.57.0.tgz",
+ "integrity": "sha512-Ys+3g2TaW7gADOJzPt83SJtCDhMjndcDMFVQ/Tj9iA1BfJzFKD9mAUXT3OenpuPHbI6P/myECxRJrofUsDx/5g==",
+ "dev": true
+ },
+ "@floating-ui/core": {
+ "version": "1.6.1",
+ "resolved": "https://registry.npmjs.org/@floating-ui/core/-/core-1.6.1.tgz",
+ "integrity": "sha512-42UH54oPZHPdRHdw6BgoBD6cg/eVTmVrFcgeRDM3jbO7uxSoipVcmcIGFcA5jmOHO5apcyvBhkSKES3fQJnu7A==",
+ "requires": {
+ "@floating-ui/utils": "^0.2.0"
+ }
+ },
+ "@floating-ui/dom": {
+ "version": "1.6.5",
+ "resolved": "https://registry.npmjs.org/@floating-ui/dom/-/dom-1.6.5.tgz",
+ "integrity": "sha512-Nsdud2X65Dz+1RHjAIP0t8z5e2ff/IRbei6BqFrl1urT8sDVzM1HMQ+R0XcU5ceRfyO3I6ayeqIfh+6Wb8LGTw==",
+ "requires": {
+ "@floating-ui/core": "^1.0.0",
+ "@floating-ui/utils": "^0.2.0"
+ }
+ },
+ "@floating-ui/react-dom": {
+ "version": "2.0.9",
+ "resolved": "https://registry.npmjs.org/@floating-ui/react-dom/-/react-dom-2.0.9.tgz",
+ "integrity": "sha512-q0umO0+LQK4+p6aGyvzASqKbKOJcAHJ7ycE9CuUvfx3s9zTHWmGJTPOIlM/hmSBfUfg/XfY5YhLBLR/LHwShQQ==",
+ "requires": {
+ "@floating-ui/dom": "^1.0.0"
+ }
+ },
+ "@floating-ui/utils": {
+ "version": "0.2.2",
+ "resolved": "https://registry.npmjs.org/@floating-ui/utils/-/utils-0.2.2.tgz",
+ "integrity": "sha512-J4yDIIthosAsRZ5CPYP/jQvUAQtlZTTD/4suA08/FEnlxqW3sKS9iAhgsa9VYLZ6vDHn/ixJgIqRQPotoBjxIw=="
+ },
+ "@fontsource/roboto": {
+ "version": "5.0.13",
+ "resolved": "https://registry.npmjs.org/@fontsource/roboto/-/roboto-5.0.13.tgz",
+ "integrity": "sha512-j61DHjsdUCKMXSdNLTOxcG701FWnF0jcqNNQi2iPCDxU8seN/sMxeh62dC++UiagCWq9ghTypX+Pcy7kX+QOeQ=="
+ },
+ "@humanwhocodes/config-array": {
+ "version": "0.11.14",
+ "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.14.tgz",
+ "integrity": "sha512-3T8LkOmg45BV5FICb15QQMsyUSWrQ8AygVfC7ZG32zOalnqrilm018ZVCw0eapXux8FtA33q8PSRSstjee3jSg==",
+ "dev": true,
+ "requires": {
+ "@humanwhocodes/object-schema": "^2.0.2",
+ "debug": "^4.3.1",
+ "minimatch": "^3.0.5"
+ },
+ "dependencies": {
+ "brace-expansion": {
+ "version": "1.1.11",
+ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
+ "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
+ "dev": true,
+ "requires": {
+ "balanced-match": "^1.0.0",
+ "concat-map": "0.0.1"
+ }
+ },
+ "minimatch": {
+ "version": "3.1.2",
+ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
+ "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==",
+ "dev": true,
+ "requires": {
+ "brace-expansion": "^1.1.7"
+ }
+ }
+ }
+ },
+ "@humanwhocodes/module-importer": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz",
+ "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==",
+ "dev": true
+ },
+ "@humanwhocodes/object-schema": {
+ "version": "2.0.3",
+ "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-2.0.3.tgz",
+ "integrity": "sha512-93zYdMES/c1D69yZiKDBj0V24vqNzB/koF26KPaagAfd3P/4gUlh3Dys5ogAK+Exi9QyzlD8x/08Zt7wIKcDcA==",
+ "dev": true
+ },
+ "@jridgewell/gen-mapping": {
+ "version": "0.3.5",
+ "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.5.tgz",
+ "integrity": "sha512-IzL8ZoEDIBRWEzlCcRhOaCupYyN5gdIK+Q6fbFdPDg6HqX6jpkItn7DFIpW9LQzXG6Df9sA7+OKnq0qlz/GaQg==",
+ "dev": true,
+ "requires": {
+ "@jridgewell/set-array": "^1.2.1",
+ "@jridgewell/sourcemap-codec": "^1.4.10",
+ "@jridgewell/trace-mapping": "^0.3.24"
+ }
+ },
+ "@jridgewell/resolve-uri": {
+ "version": "3.1.2",
+ "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz",
+ "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==",
+ "dev": true
+ },
+ "@jridgewell/set-array": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.2.1.tgz",
+ "integrity": "sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==",
+ "dev": true
+ },
+ "@jridgewell/sourcemap-codec": {
+ "version": "1.4.15",
+ "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz",
+ "integrity": "sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==",
+ "dev": true
+ },
+ "@jridgewell/trace-mapping": {
+ "version": "0.3.25",
+ "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz",
+ "integrity": "sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==",
+ "dev": true,
+ "requires": {
+ "@jridgewell/resolve-uri": "^3.1.0",
+ "@jridgewell/sourcemap-codec": "^1.4.14"
+ }
+ },
+ "@mui/base": {
+ "version": "5.0.0-beta.40",
+ "resolved": "https://registry.npmjs.org/@mui/base/-/base-5.0.0-beta.40.tgz",
+ "integrity": "sha512-I/lGHztkCzvwlXpjD2+SNmvNQvB4227xBXhISPjEaJUXGImOQ9f3D2Yj/T3KasSI/h0MLWy74X0J6clhPmsRbQ==",
+ "requires": {
+ "@babel/runtime": "^7.23.9",
+ "@floating-ui/react-dom": "^2.0.8",
+ "@mui/types": "^7.2.14",
+ "@mui/utils": "^5.15.14",
+ "@popperjs/core": "^2.11.8",
+ "clsx": "^2.1.0",
+ "prop-types": "^15.8.1"
+ }
+ },
+ "@mui/core-downloads-tracker": {
+ "version": "5.15.16",
+ "resolved": "https://registry.npmjs.org/@mui/core-downloads-tracker/-/core-downloads-tracker-5.15.16.tgz",
+ "integrity": "sha512-PTIbMJs5C/vYMfyJNW8ArOezh4eyHkg2pTeA7bBxh2kLP1Uzs0Nm+krXWbWGJPwTWjM8EhnDrr4aCF26+2oleg=="
+ },
+ "@mui/icons-material": {
+ "version": "5.15.16",
+ "resolved": "https://registry.npmjs.org/@mui/icons-material/-/icons-material-5.15.16.tgz",
+ "integrity": "sha512-s8vYbyACzTNZRKv+20fCfVXJwJqNcVotns2EKnu1wmAga6wv2LAo5kB1d5yqQqZlMFtp34EJvRXf7cy8X0tJVA==",
+ "requires": {
+ "@babel/runtime": "^7.23.9"
+ }
+ },
+ "@mui/material": {
+ "version": "5.15.16",
+ "resolved": "https://registry.npmjs.org/@mui/material/-/material-5.15.16.tgz",
+ "integrity": "sha512-ery2hFReewko9gpDBqOr2VmXwQG9ifXofPhGzIx09/b9JqCQC/06kZXZDGGrOTpIddK9HlIf4yrS+G70jPAzUQ==",
+ "requires": {
+ "@babel/runtime": "^7.23.9",
+ "@mui/base": "5.0.0-beta.40",
+ "@mui/core-downloads-tracker": "^5.15.16",
+ "@mui/system": "^5.15.15",
+ "@mui/types": "^7.2.14",
+ "@mui/utils": "^5.15.14",
+ "@types/react-transition-group": "^4.4.10",
+ "clsx": "^2.1.0",
+ "csstype": "^3.1.3",
+ "prop-types": "^15.8.1",
+ "react-is": "^18.2.0",
+ "react-transition-group": "^4.4.5"
+ }
+ },
+ "@mui/private-theming": {
+ "version": "5.15.14",
+ "resolved": "https://registry.npmjs.org/@mui/private-theming/-/private-theming-5.15.14.tgz",
+ "integrity": "sha512-UH0EiZckOWcxiXLX3Jbb0K7rC8mxTr9L9l6QhOZxYc4r8FHUkefltV9VDGLrzCaWh30SQiJvAEd7djX3XXY6Xw==",
+ "requires": {
+ "@babel/runtime": "^7.23.9",
+ "@mui/utils": "^5.15.14",
+ "prop-types": "^15.8.1"
+ }
+ },
+ "@mui/styled-engine": {
+ "version": "5.15.14",
+ "resolved": "https://registry.npmjs.org/@mui/styled-engine/-/styled-engine-5.15.14.tgz",
+ "integrity": "sha512-RILkuVD8gY6PvjZjqnWhz8fu68dVkqhM5+jYWfB5yhlSQKg+2rHkmEwm75XIeAqI3qwOndK6zELK5H6Zxn4NHw==",
+ "requires": {
+ "@babel/runtime": "^7.23.9",
+ "@emotion/cache": "^11.11.0",
+ "csstype": "^3.1.3",
+ "prop-types": "^15.8.1"
+ }
+ },
+ "@mui/system": {
+ "version": "5.15.15",
+ "resolved": "https://registry.npmjs.org/@mui/system/-/system-5.15.15.tgz",
+ "integrity": "sha512-aulox6N1dnu5PABsfxVGOZffDVmlxPOVgj56HrUnJE8MCSh8lOvvkd47cebIVQQYAjpwieXQXiDPj5pwM40jTQ==",
+ "requires": {
+ "@babel/runtime": "^7.23.9",
+ "@mui/private-theming": "^5.15.14",
+ "@mui/styled-engine": "^5.15.14",
+ "@mui/types": "^7.2.14",
+ "@mui/utils": "^5.15.14",
+ "clsx": "^2.1.0",
+ "csstype": "^3.1.3",
+ "prop-types": "^15.8.1"
+ }
+ },
+ "@mui/types": {
+ "version": "7.2.14",
+ "resolved": "https://registry.npmjs.org/@mui/types/-/types-7.2.14.tgz",
+ "integrity": "sha512-MZsBZ4q4HfzBsywtXgM1Ksj6HDThtiwmOKUXH1pKYISI9gAVXCNHNpo7TlGoGrBaYWZTdNoirIN7JsQcQUjmQQ==",
+ "requires": {}
+ },
+ "@mui/utils": {
+ "version": "5.15.14",
+ "resolved": "https://registry.npmjs.org/@mui/utils/-/utils-5.15.14.tgz",
+ "integrity": "sha512-0lF/7Hh/ezDv5X7Pry6enMsbYyGKjADzvHyo3Qrc/SSlTsQ1VkbDMbH0m2t3OR5iIVLwMoxwM7yGd+6FCMtTFA==",
+ "requires": {
+ "@babel/runtime": "^7.23.9",
+ "@types/prop-types": "^15.7.11",
+ "prop-types": "^15.8.1",
+ "react-is": "^18.2.0"
+ }
+ },
+ "@mui/x-date-pickers": {
+ "version": "6.19.9",
+ "resolved": "https://registry.npmjs.org/@mui/x-date-pickers/-/x-date-pickers-6.19.9.tgz",
+ "integrity": "sha512-B2m4Fv/fOme5qmV6zuE3QnWQSvj3zKtI2OvikPz5prpiCcIxqpeytkQ7VfrWH3/Aqd5yhG1Yr4IgbqG0ymIXGg==",
+ "requires": {
+ "@babel/runtime": "^7.23.2",
+ "@mui/base": "^5.0.0-beta.22",
+ "@mui/utils": "^5.14.16",
+ "@types/react-transition-group": "^4.4.8",
+ "clsx": "^2.0.0",
+ "prop-types": "^15.8.1",
+ "react-transition-group": "^4.4.5"
+ }
+ },
+ "@nodelib/fs.scandir": {
+ "version": "2.1.5",
+ "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz",
+ "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==",
+ "dev": true,
+ "requires": {
+ "@nodelib/fs.stat": "2.0.5",
+ "run-parallel": "^1.1.9"
+ }
+ },
+ "@nodelib/fs.stat": {
+ "version": "2.0.5",
+ "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz",
+ "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==",
+ "dev": true
+ },
+ "@nodelib/fs.walk": {
+ "version": "1.2.8",
+ "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz",
+ "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==",
+ "dev": true,
+ "requires": {
+ "@nodelib/fs.scandir": "2.1.5",
+ "fastq": "^1.6.0"
+ }
+ },
+ "@pkgr/core": {
+ "version": "0.1.1",
+ "resolved": "https://registry.npmjs.org/@pkgr/core/-/core-0.1.1.tgz",
+ "integrity": "sha512-cq8o4cWH0ibXh9VGi5P20Tu9XF/0fFXl9EUinr9QfTM7a7p0oTA4iJRCQWppXR1Pg8dSM0UCItCkPwsk9qWWYA==",
+ "dev": true
+ },
+ "@popperjs/core": {
+ "version": "2.11.8",
+ "resolved": "https://registry.npmjs.org/@popperjs/core/-/core-2.11.8.tgz",
+ "integrity": "sha512-P1st0aksCrn9sGZhp8GMYwBnQsbvAWsZAX44oXNNvLHGqAOcoVxmjZiohstwQ7SqKnbR47akdNi+uleWD8+g6A=="
+ },
+ "@remix-run/router": {
+ "version": "1.16.0",
+ "resolved": "https://registry.npmjs.org/@remix-run/router/-/router-1.16.0.tgz",
+ "integrity": "sha512-Quz1KOffeEf/zwkCBM3kBtH4ZoZ+pT3xIXBG4PPW/XFtDP7EGhtTiC2+gpL9GnR7+Qdet5Oa6cYSvwKYg6kN9Q=="
+ },
+ "@rollup/rollup-android-arm-eabi": {
+ "version": "4.17.2",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.17.2.tgz",
+ "integrity": "sha512-NM0jFxY8bB8QLkoKxIQeObCaDlJKewVlIEkuyYKm5An1tdVZ966w2+MPQ2l8LBZLjR+SgyV+nRkTIunzOYBMLQ==",
+ "dev": true,
+ "optional": true
+ },
+ "@rollup/rollup-android-arm64": {
+ "version": "4.17.2",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.17.2.tgz",
+ "integrity": "sha512-yeX/Usk7daNIVwkq2uGoq2BYJKZY1JfyLTaHO/jaiSwi/lsf8fTFoQW/n6IdAsx5tx+iotu2zCJwz8MxI6D/Bw==",
+ "dev": true,
+ "optional": true
+ },
+ "@rollup/rollup-darwin-arm64": {
+ "version": "4.17.2",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.17.2.tgz",
+ "integrity": "sha512-kcMLpE6uCwls023+kknm71ug7MZOrtXo+y5p/tsg6jltpDtgQY1Eq5sGfHcQfb+lfuKwhBmEURDga9N0ol4YPw==",
+ "dev": true,
+ "optional": true
+ },
+ "@rollup/rollup-darwin-x64": {
+ "version": "4.17.2",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.17.2.tgz",
+ "integrity": "sha512-AtKwD0VEx0zWkL0ZjixEkp5tbNLzX+FCqGG1SvOu993HnSz4qDI6S4kGzubrEJAljpVkhRSlg5bzpV//E6ysTQ==",
+ "dev": true,
+ "optional": true
+ },
+ "@rollup/rollup-linux-arm-gnueabihf": {
+ "version": "4.17.2",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.17.2.tgz",
+ "integrity": "sha512-3reX2fUHqN7sffBNqmEyMQVj/CKhIHZd4y631duy0hZqI8Qoqf6lTtmAKvJFYa6bhU95B1D0WgzHkmTg33In0A==",
+ "dev": true,
+ "optional": true
+ },
+ "@rollup/rollup-linux-arm-musleabihf": {
+ "version": "4.17.2",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.17.2.tgz",
+ "integrity": "sha512-uSqpsp91mheRgw96xtyAGP9FW5ChctTFEoXP0r5FAzj/3ZRv3Uxjtc7taRQSaQM/q85KEKjKsZuiZM3GyUivRg==",
+ "dev": true,
+ "optional": true
+ },
+ "@rollup/rollup-linux-arm64-gnu": {
+ "version": "4.17.2",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.17.2.tgz",
+ "integrity": "sha512-EMMPHkiCRtE8Wdk3Qhtciq6BndLtstqZIroHiiGzB3C5LDJmIZcSzVtLRbwuXuUft1Cnv+9fxuDtDxz3k3EW2A==",
+ "dev": true,
+ "optional": true
+ },
+ "@rollup/rollup-linux-arm64-musl": {
+ "version": "4.17.2",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.17.2.tgz",
+ "integrity": "sha512-NMPylUUZ1i0z/xJUIx6VUhISZDRT+uTWpBcjdv0/zkp7b/bQDF+NfnfdzuTiB1G6HTodgoFa93hp0O1xl+/UbA==",
+ "dev": true,
+ "optional": true
+ },
+ "@rollup/rollup-linux-powerpc64le-gnu": {
+ "version": "4.17.2",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.17.2.tgz",
+ "integrity": "sha512-T19My13y8uYXPw/L/k0JYaX1fJKFT/PWdXiHr8mTbXWxjVF1t+8Xl31DgBBvEKclw+1b00Chg0hxE2O7bTG7GQ==",
+ "dev": true,
+ "optional": true
+ },
+ "@rollup/rollup-linux-riscv64-gnu": {
+ "version": "4.17.2",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.17.2.tgz",
+ "integrity": "sha512-BOaNfthf3X3fOWAB+IJ9kxTgPmMqPPH5f5k2DcCsRrBIbWnaJCgX2ll77dV1TdSy9SaXTR5iDXRL8n7AnoP5cg==",
+ "dev": true,
+ "optional": true
+ },
+ "@rollup/rollup-linux-s390x-gnu": {
+ "version": "4.17.2",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.17.2.tgz",
+ "integrity": "sha512-W0UP/x7bnn3xN2eYMql2T/+wpASLE5SjObXILTMPUBDB/Fg/FxC+gX4nvCfPBCbNhz51C+HcqQp2qQ4u25ok6g==",
+ "dev": true,
+ "optional": true
+ },
+ "@rollup/rollup-linux-x64-gnu": {
+ "version": "4.17.2",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.17.2.tgz",
+ "integrity": "sha512-Hy7pLwByUOuyaFC6mAr7m+oMC+V7qyifzs/nW2OJfC8H4hbCzOX07Ov0VFk/zP3kBsELWNFi7rJtgbKYsav9QQ==",
+ "dev": true,
+ "optional": true
+ },
+ "@rollup/rollup-linux-x64-musl": {
+ "version": "4.17.2",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.17.2.tgz",
+ "integrity": "sha512-h1+yTWeYbRdAyJ/jMiVw0l6fOOm/0D1vNLui9iPuqgRGnXA0u21gAqOyB5iHjlM9MMfNOm9RHCQ7zLIzT0x11Q==",
+ "dev": true,
+ "optional": true
+ },
+ "@rollup/rollup-win32-arm64-msvc": {
+ "version": "4.17.2",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.17.2.tgz",
+ "integrity": "sha512-tmdtXMfKAjy5+IQsVtDiCfqbynAQE/TQRpWdVataHmhMb9DCoJxp9vLcCBjEQWMiUYxO1QprH/HbY9ragCEFLA==",
+ "dev": true,
+ "optional": true
+ },
+ "@rollup/rollup-win32-ia32-msvc": {
+ "version": "4.17.2",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.17.2.tgz",
+ "integrity": "sha512-7II/QCSTAHuE5vdZaQEwJq2ZACkBpQDOmQsE6D6XUbnBHW8IAhm4eTufL6msLJorzrHDFv3CF8oCA/hSIRuZeQ==",
+ "dev": true,
+ "optional": true
+ },
+ "@rollup/rollup-win32-x64-msvc": {
+ "version": "4.17.2",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.17.2.tgz",
+ "integrity": "sha512-TGGO7v7qOq4CYmSBVEYpI1Y5xDuCEnbVC5Vth8mOsW0gDSzxNrVERPc790IGHsrT2dQSimgMr9Ub3Y1Jci5/8w==",
+ "dev": true,
+ "optional": true
+ },
+ "@types/babel__core": {
+ "version": "7.20.5",
+ "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.5.tgz",
+ "integrity": "sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==",
+ "dev": true,
+ "requires": {
+ "@babel/parser": "^7.20.7",
+ "@babel/types": "^7.20.7",
+ "@types/babel__generator": "*",
+ "@types/babel__template": "*",
+ "@types/babel__traverse": "*"
+ }
+ },
+ "@types/babel__generator": {
+ "version": "7.6.8",
+ "resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.6.8.tgz",
+ "integrity": "sha512-ASsj+tpEDsEiFr1arWrlN6V3mdfjRMZt6LtK/Vp/kreFLnr5QH5+DhvD5nINYZXzwJvXeGq+05iUXcAzVrqWtw==",
+ "dev": true,
+ "requires": {
+ "@babel/types": "^7.0.0"
+ }
+ },
+ "@types/babel__template": {
+ "version": "7.4.4",
+ "resolved": "https://registry.npmjs.org/@types/babel__template/-/babel__template-7.4.4.tgz",
+ "integrity": "sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A==",
+ "dev": true,
+ "requires": {
+ "@babel/parser": "^7.1.0",
+ "@babel/types": "^7.0.0"
+ }
+ },
+ "@types/babel__traverse": {
+ "version": "7.20.5",
+ "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.20.5.tgz",
+ "integrity": "sha512-WXCyOcRtH37HAUkpXhUduaxdm82b4GSlyTqajXviN4EfiuPgNYR109xMCKvpl6zPIpua0DGlMEDCq+g8EdoheQ==",
+ "dev": true,
+ "requires": {
+ "@babel/types": "^7.20.7"
+ }
+ },
+ "@types/estree": {
+ "version": "1.0.5",
+ "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.5.tgz",
+ "integrity": "sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==",
+ "dev": true
+ },
+ "@types/json-schema": {
+ "version": "7.0.15",
+ "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz",
+ "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==",
+ "dev": true
+ },
+ "@types/node": {
+ "version": "20.12.8",
+ "resolved": "https://registry.npmjs.org/@types/node/-/node-20.12.8.tgz",
+ "integrity": "sha512-NU0rJLJnshZWdE/097cdCBbyW1h4hEg0xpovcoAQYHl8dnEyp/NAOiE45pvc+Bd1Dt+2r94v2eGFpQJ4R7g+2w==",
+ "dev": true,
+ "requires": {
+ "undici-types": "~5.26.4"
+ }
+ },
+ "@types/papaparse": {
+ "version": "5.3.14",
+ "resolved": "https://registry.npmjs.org/@types/papaparse/-/papaparse-5.3.14.tgz",
+ "integrity": "sha512-LxJ4iEFcpqc6METwp9f6BV6VVc43m6MfH0VqFosHvrUgfXiFe6ww7R3itkOQ+TCK6Y+Iv/+RnnvtRZnkc5Kc9g==",
+ "dev": true,
+ "requires": {
+ "@types/node": "*"
+ }
+ },
+ "@types/parse-json": {
+ "version": "4.0.2",
+ "resolved": "https://registry.npmjs.org/@types/parse-json/-/parse-json-4.0.2.tgz",
+ "integrity": "sha512-dISoDXWWQwUquiKsyZ4Ng+HX2KsPL7LyHKHQwgGFEA3IaKac4Obd+h2a/a6waisAoepJlBcx9paWqjA8/HVjCw=="
+ },
+ "@types/prop-types": {
+ "version": "15.7.12",
+ "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.12.tgz",
+ "integrity": "sha512-5zvhXYtRNRluoE/jAp4GVsSduVUzNWKkOZrCDBWYtE7biZywwdC2AcEzg+cSMLFRfVgeAFqpfNabiPjxFddV1Q=="
+ },
+ "@types/react": {
+ "version": "18.3.1",
+ "resolved": "https://registry.npmjs.org/@types/react/-/react-18.3.1.tgz",
+ "integrity": "sha512-V0kuGBX3+prX+DQ/7r2qsv1NsdfnCLnTgnRJ1pYnxykBhGMz+qj+box5lq7XsO5mtZsBqpjwwTu/7wszPfMBcw==",
+ "requires": {
+ "@types/prop-types": "*",
+ "csstype": "^3.0.2"
+ }
+ },
+ "@types/react-dom": {
+ "version": "18.3.0",
+ "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-18.3.0.tgz",
+ "integrity": "sha512-EhwApuTmMBmXuFOikhQLIBUn6uFg81SwLMOAUgodJF14SOBOCMdU04gDoYi0WOJJHD144TL32z4yDqCW3dnkQg==",
+ "dev": true,
+ "requires": {
+ "@types/react": "*"
+ }
+ },
+ "@types/react-helmet": {
+ "version": "6.1.11",
+ "resolved": "https://registry.npmjs.org/@types/react-helmet/-/react-helmet-6.1.11.tgz",
+ "integrity": "sha512-0QcdGLddTERotCXo3VFlUSWO3ztraw8nZ6e3zJSgG7apwV5xt+pJUS8ewPBqT4NYB1optGLprNQzFleIY84u/g==",
+ "dev": true,
+ "requires": {
+ "@types/react": "*"
+ }
+ },
+ "@types/react-transition-group": {
+ "version": "4.4.10",
+ "resolved": "https://registry.npmjs.org/@types/react-transition-group/-/react-transition-group-4.4.10.tgz",
+ "integrity": "sha512-hT/+s0VQs2ojCX823m60m5f0sL5idt9SO6Tj6Dg+rdphGPIeJbJ6CxvBYkgkGKrYeDjvIpKTR38UzmtHJOGW3Q==",
+ "requires": {
+ "@types/react": "*"
+ }
+ },
+ "@types/semver": {
+ "version": "7.5.8",
+ "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.5.8.tgz",
+ "integrity": "sha512-I8EUhyrgfLrcTkzV3TSsGyl1tSuPrEDzr0yd5m90UgNxQkyDXULk3b6MlQqTCpZpNtWe1K0hzclnZkTcLBe2UQ==",
+ "dev": true
+ },
+ "@types/sinonjs__fake-timers": {
+ "version": "8.1.1",
+ "resolved": "https://registry.npmjs.org/@types/sinonjs__fake-timers/-/sinonjs__fake-timers-8.1.1.tgz",
+ "integrity": "sha512-0kSuKjAS0TrGLJ0M/+8MaFkGsQhZpB6pxOmvS3K8FYI72K//YmdfoW9X2qPsAKh1mkwxGD5zib9s1FIFed6E8g==",
+ "dev": true
+ },
+ "@types/sizzle": {
+ "version": "2.3.8",
+ "resolved": "https://registry.npmjs.org/@types/sizzle/-/sizzle-2.3.8.tgz",
+ "integrity": "sha512-0vWLNK2D5MT9dg0iOo8GlKguPAU02QjmZitPEsXRuJXU/OGIOt9vT9Fc26wtYuavLxtO45v9PGleoL9Z0k1LHg==",
+ "dev": true
+ },
+ "@types/yauzl": {
+ "version": "2.10.3",
+ "resolved": "https://registry.npmjs.org/@types/yauzl/-/yauzl-2.10.3.tgz",
+ "integrity": "sha512-oJoftv0LSuaDZE3Le4DbKX+KS9G36NzOeSap90UIK0yMA/NhKJhqlSGtNDORNRaIbQfzjXDrQa0ytJ6mNRGz/Q==",
+ "dev": true,
+ "optional": true,
+ "requires": {
+ "@types/node": "*"
+ }
+ },
+ "@typescript-eslint/eslint-plugin": {
+ "version": "7.8.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-7.8.0.tgz",
+ "integrity": "sha512-gFTT+ezJmkwutUPmB0skOj3GZJtlEGnlssems4AjkVweUPGj7jRwwqg0Hhg7++kPGJqKtTYx+R05Ftww372aIg==",
+ "dev": true,
+ "requires": {
+ "@eslint-community/regexpp": "^4.10.0",
+ "@typescript-eslint/scope-manager": "7.8.0",
+ "@typescript-eslint/type-utils": "7.8.0",
+ "@typescript-eslint/utils": "7.8.0",
+ "@typescript-eslint/visitor-keys": "7.8.0",
+ "debug": "^4.3.4",
+ "graphemer": "^1.4.0",
+ "ignore": "^5.3.1",
+ "natural-compare": "^1.4.0",
+ "semver": "^7.6.0",
+ "ts-api-utils": "^1.3.0"
+ }
+ },
+ "@typescript-eslint/parser": {
+ "version": "7.8.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-7.8.0.tgz",
+ "integrity": "sha512-KgKQly1pv0l4ltcftP59uQZCi4HUYswCLbTqVZEJu7uLX8CTLyswqMLqLN+2QFz4jCptqWVV4SB7vdxcH2+0kQ==",
+ "dev": true,
+ "requires": {
+ "@typescript-eslint/scope-manager": "7.8.0",
+ "@typescript-eslint/types": "7.8.0",
+ "@typescript-eslint/typescript-estree": "7.8.0",
+ "@typescript-eslint/visitor-keys": "7.8.0",
+ "debug": "^4.3.4"
+ }
+ },
+ "@typescript-eslint/scope-manager": {
+ "version": "7.8.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-7.8.0.tgz",
+ "integrity": "sha512-viEmZ1LmwsGcnr85gIq+FCYI7nO90DVbE37/ll51hjv9aG+YZMb4WDE2fyWpUR4O/UrhGRpYXK/XajcGTk2B8g==",
+ "dev": true,
+ "requires": {
+ "@typescript-eslint/types": "7.8.0",
+ "@typescript-eslint/visitor-keys": "7.8.0"
+ }
+ },
+ "@typescript-eslint/type-utils": {
+ "version": "7.8.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-7.8.0.tgz",
+ "integrity": "sha512-H70R3AefQDQpz9mGv13Uhi121FNMh+WEaRqcXTX09YEDky21km4dV1ZXJIp8QjXc4ZaVkXVdohvWDzbnbHDS+A==",
+ "dev": true,
+ "requires": {
+ "@typescript-eslint/typescript-estree": "7.8.0",
+ "@typescript-eslint/utils": "7.8.0",
+ "debug": "^4.3.4",
+ "ts-api-utils": "^1.3.0"
+ }
+ },
+ "@typescript-eslint/types": {
+ "version": "7.8.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-7.8.0.tgz",
+ "integrity": "sha512-wf0peJ+ZGlcH+2ZS23aJbOv+ztjeeP8uQ9GgwMJGVLx/Nj9CJt17GWgWWoSmoRVKAX2X+7fzEnAjxdvK2gqCLw==",
+ "dev": true
+ },
+ "@typescript-eslint/typescript-estree": {
+ "version": "7.8.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-7.8.0.tgz",
+ "integrity": "sha512-5pfUCOwK5yjPaJQNy44prjCwtr981dO8Qo9J9PwYXZ0MosgAbfEMB008dJ5sNo3+/BN6ytBPuSvXUg9SAqB0dg==",
+ "dev": true,
+ "requires": {
+ "@typescript-eslint/types": "7.8.0",
+ "@typescript-eslint/visitor-keys": "7.8.0",
+ "debug": "^4.3.4",
+ "globby": "^11.1.0",
+ "is-glob": "^4.0.3",
+ "minimatch": "^9.0.4",
+ "semver": "^7.6.0",
+ "ts-api-utils": "^1.3.0"
+ }
+ },
+ "@typescript-eslint/utils": {
+ "version": "7.8.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-7.8.0.tgz",
+ "integrity": "sha512-L0yFqOCflVqXxiZyXrDr80lnahQfSOfc9ELAAZ75sqicqp2i36kEZZGuUymHNFoYOqxRT05up760b4iGsl02nQ==",
+ "dev": true,
+ "requires": {
+ "@eslint-community/eslint-utils": "^4.4.0",
+ "@types/json-schema": "^7.0.15",
+ "@types/semver": "^7.5.8",
+ "@typescript-eslint/scope-manager": "7.8.0",
+ "@typescript-eslint/types": "7.8.0",
+ "@typescript-eslint/typescript-estree": "7.8.0",
+ "semver": "^7.6.0"
+ }
+ },
+ "@typescript-eslint/visitor-keys": {
+ "version": "7.8.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-7.8.0.tgz",
+ "integrity": "sha512-q4/gibTNBQNA0lGyYQCmWRS5D15n8rXh4QjK3KV+MBPlTYHpfBUT3D3PaPR/HeNiI9W6R7FvlkcGhNyAoP+caA==",
+ "dev": true,
+ "requires": {
+ "@typescript-eslint/types": "7.8.0",
+ "eslint-visitor-keys": "^3.4.3"
+ }
+ },
+ "@ungap/structured-clone": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.2.0.tgz",
+ "integrity": "sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ==",
+ "dev": true
+ },
+ "@vitejs/plugin-react": {
+ "version": "4.2.1",
+ "resolved": "https://registry.npmjs.org/@vitejs/plugin-react/-/plugin-react-4.2.1.tgz",
+ "integrity": "sha512-oojO9IDc4nCUUi8qIR11KoQm0XFFLIwsRBwHRR4d/88IWghn1y6ckz/bJ8GHDCsYEJee8mDzqtJxh15/cisJNQ==",
+ "dev": true,
+ "requires": {
+ "@babel/core": "^7.23.5",
+ "@babel/plugin-transform-react-jsx-self": "^7.23.3",
+ "@babel/plugin-transform-react-jsx-source": "^7.23.3",
+ "@types/babel__core": "^7.20.5",
+ "react-refresh": "^0.14.0"
+ }
+ },
+ "acorn": {
+ "version": "8.11.3",
+ "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.11.3.tgz",
+ "integrity": "sha512-Y9rRfJG5jcKOE0CLisYbojUjIrIEE7AGMzA/Sm4BslANhbS+cDMpgBdcPT91oJ7OuJ9hYJBx59RjbhxVnrF8Xg==",
+ "dev": true
+ },
+ "acorn-jsx": {
+ "version": "5.3.2",
+ "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz",
+ "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==",
+ "dev": true,
+ "requires": {}
+ },
+ "aggregate-error": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/aggregate-error/-/aggregate-error-3.1.0.tgz",
+ "integrity": "sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA==",
+ "dev": true,
+ "requires": {
+ "clean-stack": "^2.0.0",
+ "indent-string": "^4.0.0"
+ }
+ },
+ "ajv": {
+ "version": "6.12.6",
+ "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz",
+ "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==",
+ "dev": true,
+ "requires": {
+ "fast-deep-equal": "^3.1.1",
+ "fast-json-stable-stringify": "^2.0.0",
+ "json-schema-traverse": "^0.4.1",
+ "uri-js": "^4.2.2"
+ }
+ },
+ "ansi-colors": {
+ "version": "4.1.3",
+ "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.3.tgz",
+ "integrity": "sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw==",
+ "dev": true
+ },
+ "ansi-escapes": {
+ "version": "4.3.2",
+ "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz",
+ "integrity": "sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==",
+ "dev": true,
+ "requires": {
+ "type-fest": "^0.21.3"
+ },
+ "dependencies": {
+ "type-fest": {
+ "version": "0.21.3",
+ "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz",
+ "integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==",
+ "dev": true
+ }
+ }
+ },
+ "ansi-regex": {
+ "version": "5.0.1",
+ "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
+ "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==",
+ "dev": true
+ },
+ "ansi-styles": {
+ "version": "3.2.1",
+ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz",
+ "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==",
+ "requires": {
+ "color-convert": "^1.9.0"
+ }
+ },
+ "arch": {
+ "version": "2.2.0",
+ "resolved": "https://registry.npmjs.org/arch/-/arch-2.2.0.tgz",
+ "integrity": "sha512-Of/R0wqp83cgHozfIYLbBMnej79U/SVGOOyuB3VVFv1NRM/PSFMK12x9KVtiYzJqmnU5WR2qp0Z5rHb7sWGnFQ==",
+ "dev": true
+ },
+ "argparse": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz",
+ "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==",
+ "dev": true
+ },
+ "array-union": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz",
+ "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==",
+ "dev": true
+ },
+ "asn1": {
+ "version": "0.2.6",
+ "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.6.tgz",
+ "integrity": "sha512-ix/FxPn0MDjeyJ7i/yoHGFt/EX6LyNbxSEhPPXODPL+KB0VPk86UYfL0lMdy+KCnv+fmvIzySwaK5COwqVbWTQ==",
+ "dev": true,
+ "requires": {
+ "safer-buffer": "~2.1.0"
+ }
+ },
+ "assert-plus": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz",
+ "integrity": "sha512-NfJ4UzBCcQGLDlQq7nHxH+tv3kyZ0hHQqF5BO6J7tNJeP5do1llPr8dZ8zHonfhAu0PHAdMkSo+8o0wxg9lZWw==",
+ "dev": true
+ },
+ "astral-regex": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-2.0.0.tgz",
+ "integrity": "sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ==",
+ "dev": true
+ },
+ "async": {
+ "version": "3.2.5",
+ "resolved": "https://registry.npmjs.org/async/-/async-3.2.5.tgz",
+ "integrity": "sha512-baNZyqaaLhyLVKm/DlvdW051MSgO6b8eVfIezl9E5PqWxFgzLm/wQntEW4zOytVburDEr0JlALEpdOFwvErLsg==",
+ "dev": true
+ },
+ "asynckit": {
+ "version": "0.4.0",
+ "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz",
+ "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q=="
+ },
+ "at-least-node": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/at-least-node/-/at-least-node-1.0.0.tgz",
+ "integrity": "sha512-+q/t7Ekv1EDY2l6Gda6LLiX14rU9TV20Wa3ofeQmwPFZbOMo9DXrLbOjFaaclkXKWidIaopwAObQDqwWtGUjqg==",
+ "dev": true
+ },
+ "aws-sign2": {
+ "version": "0.7.0",
+ "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz",
+ "integrity": "sha512-08kcGqnYf/YmjoRhfxyu+CLxBjUtHLXLXX/vUfx9l2LYzG3c1m61nrpyFUZI6zeS+Li/wWMMidD9KgrqtGq3mA==",
+ "dev": true
+ },
+ "aws4": {
+ "version": "1.12.0",
+ "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.12.0.tgz",
+ "integrity": "sha512-NmWvPnx0F1SfrQbYwOi7OeaNGokp9XhzNioJ/CSBs8Qa4vxug81mhJEAVZwxXuBmYB5KDRfMq/F3RR0BIU7sWg==",
+ "dev": true
+ },
+ "axios": {
+ "version": "1.6.8",
+ "resolved": "https://registry.npmjs.org/axios/-/axios-1.6.8.tgz",
+ "integrity": "sha512-v/ZHtJDU39mDpyBoFVkETcd/uNdxrWRrg3bKpOKzXFA6Bvqopts6ALSMU3y6ijYxbw2B+wPrIv46egTzJXCLGQ==",
+ "requires": {
+ "follow-redirects": "^1.15.6",
+ "form-data": "^4.0.0",
+ "proxy-from-env": "^1.1.0"
+ }
+ },
+ "babel-plugin-macros": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/babel-plugin-macros/-/babel-plugin-macros-3.1.0.tgz",
+ "integrity": "sha512-Cg7TFGpIr01vOQNODXOOaGz2NpCU5gl8x1qJFbb6hbZxR7XrcE2vtbAsTAbJ7/xwJtUuJEw8K8Zr/AE0LHlesg==",
+ "requires": {
+ "@babel/runtime": "^7.12.5",
+ "cosmiconfig": "^7.0.0",
+ "resolve": "^1.19.0"
+ }
+ },
+ "balanced-match": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
+ "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==",
+ "dev": true
+ },
+ "base64-js": {
+ "version": "1.5.1",
+ "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz",
+ "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==",
+ "dev": true
+ },
+ "bcrypt-pbkdf": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz",
+ "integrity": "sha512-qeFIXtP4MSoi6NLqO12WfqARWWuCKi2Rn/9hJLEmtB5yTNr9DqFWkJRCf2qShWzPeAMRnOgCrq0sg/KLv5ES9w==",
+ "dev": true,
+ "requires": {
+ "tweetnacl": "^0.14.3"
+ }
+ },
+ "blob-util": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/blob-util/-/blob-util-2.0.2.tgz",
+ "integrity": "sha512-T7JQa+zsXXEa6/8ZhHcQEW1UFfVM49Ts65uBkFL6fz2QmrElqmbajIDJvuA0tEhRe5eIjpV9ZF+0RfZR9voJFQ==",
+ "dev": true
+ },
+ "bluebird": {
+ "version": "3.7.2",
+ "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.7.2.tgz",
+ "integrity": "sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg==",
+ "dev": true
+ },
+ "brace-expansion": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz",
+ "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==",
+ "dev": true,
+ "requires": {
+ "balanced-match": "^1.0.0"
+ }
+ },
+ "braces": {
+ "version": "3.0.2",
+ "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz",
+ "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==",
+ "dev": true,
+ "requires": {
+ "fill-range": "^7.0.1"
+ }
+ },
+ "browserslist": {
+ "version": "4.23.0",
+ "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.23.0.tgz",
+ "integrity": "sha512-QW8HiM1shhT2GuzkvklfjcKDiWFXHOeFCIA/huJPwHsslwcydgk7X+z2zXpEijP98UCY7HbubZt5J2Zgvf0CaQ==",
+ "dev": true,
+ "requires": {
+ "caniuse-lite": "^1.0.30001587",
+ "electron-to-chromium": "^1.4.668",
+ "node-releases": "^2.0.14",
+ "update-browserslist-db": "^1.0.13"
+ }
+ },
+ "buffer": {
+ "version": "5.7.1",
+ "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz",
+ "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==",
+ "dev": true,
+ "requires": {
+ "base64-js": "^1.3.1",
+ "ieee754": "^1.1.13"
+ }
+ },
+ "buffer-crc32": {
+ "version": "0.2.13",
+ "resolved": "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-0.2.13.tgz",
+ "integrity": "sha512-VO9Ht/+p3SN7SKWqcrgEzjGbRSJYTx+Q1pTQC0wrWqHx0vpJraQ6GtHx8tvcg1rlK1byhU5gccxgOgj7B0TDkQ==",
+ "dev": true
+ },
+ "cachedir": {
+ "version": "2.4.0",
+ "resolved": "https://registry.npmjs.org/cachedir/-/cachedir-2.4.0.tgz",
+ "integrity": "sha512-9EtFOZR8g22CL7BWjJ9BUx1+A/djkofnyW3aOXZORNW2kxoUpx2h+uN2cOqwPmFhnpVmxg+KW2OjOSgChTEvsQ==",
+ "dev": true
+ },
+ "call-bind": {
+ "version": "1.0.7",
+ "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.7.tgz",
+ "integrity": "sha512-GHTSNSYICQ7scH7sZ+M2rFopRoLh8t2bLSW6BbgrtLsahOIB5iyAVJf9GjWK3cYTDaMj4XdBpM1cA6pIS0Kv2w==",
+ "dev": true,
+ "requires": {
+ "es-define-property": "^1.0.0",
+ "es-errors": "^1.3.0",
+ "function-bind": "^1.1.2",
+ "get-intrinsic": "^1.2.4",
+ "set-function-length": "^1.2.1"
+ }
+ },
+ "callsites": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz",
+ "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ=="
+ },
+ "caniuse-lite": {
+ "version": "1.0.30001616",
+ "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001616.tgz",
+ "integrity": "sha512-RHVYKov7IcdNjVHJFNY/78RdG4oGVjbayxv8u5IO74Wv7Hlq4PnJE6mo/OjFijjVFNy5ijnCt6H3IIo4t+wfEw==",
+ "dev": true
+ },
+ "caseless": {
+ "version": "0.12.0",
+ "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz",
+ "integrity": "sha512-4tYFyifaFfGacoiObjJegolkwSU4xQNGbVgUiNYVUxbQ2x2lUsFvY4hVgVzGiIe6WLOPqycWXA40l+PWsxthUw==",
+ "dev": true
+ },
+ "chalk": {
+ "version": "2.4.2",
+ "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz",
+ "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==",
+ "requires": {
+ "ansi-styles": "^3.2.1",
+ "escape-string-regexp": "^1.0.5",
+ "supports-color": "^5.3.0"
+ },
+ "dependencies": {
+ "escape-string-regexp": {
+ "version": "1.0.5",
+ "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz",
+ "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg=="
+ }
+ }
+ },
+ "check-more-types": {
+ "version": "2.24.0",
+ "resolved": "https://registry.npmjs.org/check-more-types/-/check-more-types-2.24.0.tgz",
+ "integrity": "sha512-Pj779qHxV2tuapviy1bSZNEL1maXr13bPYpsvSDB68HlYcYuhlDrmGd63i0JHMCLKzc7rUSNIrpdJlhVlNwrxA==",
+ "dev": true
+ },
+ "ci-info": {
+ "version": "3.9.0",
+ "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.9.0.tgz",
+ "integrity": "sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ==",
+ "dev": true
+ },
+ "clean-stack": {
+ "version": "2.2.0",
+ "resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-2.2.0.tgz",
+ "integrity": "sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A==",
+ "dev": true
+ },
+ "cli-cursor": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-3.1.0.tgz",
+ "integrity": "sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==",
+ "dev": true,
+ "requires": {
+ "restore-cursor": "^3.1.0"
+ }
+ },
+ "cli-table3": {
+ "version": "0.6.5",
+ "resolved": "https://registry.npmjs.org/cli-table3/-/cli-table3-0.6.5.tgz",
+ "integrity": "sha512-+W/5efTR7y5HRD7gACw9yQjqMVvEMLBHmboM/kPWam+H+Hmyrgjh6YncVKK122YZkXrLudzTuAukUw9FnMf7IQ==",
+ "dev": true,
+ "requires": {
+ "@colors/colors": "1.5.0",
+ "string-width": "^4.2.0"
+ }
+ },
+ "cli-truncate": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/cli-truncate/-/cli-truncate-2.1.0.tgz",
+ "integrity": "sha512-n8fOixwDD6b/ObinzTrp1ZKFzbgvKZvuz/TvejnLn1aQfC6r52XEx85FmuC+3HI+JM7coBRXUvNqEU2PHVrHpg==",
+ "dev": true,
+ "requires": {
+ "slice-ansi": "^3.0.0",
+ "string-width": "^4.2.0"
+ }
+ },
+ "clsx": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz",
+ "integrity": "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA=="
+ },
+ "color-convert": {
+ "version": "1.9.3",
+ "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz",
+ "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==",
+ "requires": {
+ "color-name": "1.1.3"
+ }
+ },
+ "color-name": {
+ "version": "1.1.3",
+ "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz",
+ "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw=="
+ },
+ "colorette": {
+ "version": "2.0.20",
+ "resolved": "https://registry.npmjs.org/colorette/-/colorette-2.0.20.tgz",
+ "integrity": "sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w==",
+ "dev": true
+ },
+ "combined-stream": {
+ "version": "1.0.8",
+ "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz",
+ "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==",
+ "requires": {
+ "delayed-stream": "~1.0.0"
+ }
+ },
+ "commander": {
+ "version": "6.2.1",
+ "resolved": "https://registry.npmjs.org/commander/-/commander-6.2.1.tgz",
+ "integrity": "sha512-U7VdrJFnJgo4xjrHpTzu0yrHPGImdsmD95ZlgYSEajAn2JKzDhDTPG9kBTefmObL2w/ngeZnilk+OV9CG3d7UA==",
+ "dev": true
+ },
+ "common-tags": {
+ "version": "1.8.2",
+ "resolved": "https://registry.npmjs.org/common-tags/-/common-tags-1.8.2.tgz",
+ "integrity": "sha512-gk/Z852D2Wtb//0I+kRFNKKE9dIIVirjoqPoA1wJU+XePVXZfGeBpk45+A1rKO4Q43prqWBNY/MiIeRLbPWUaA==",
+ "dev": true
+ },
+ "concat-map": {
+ "version": "0.0.1",
+ "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
+ "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==",
+ "dev": true
+ },
+ "convert-source-map": {
+ "version": "1.9.0",
+ "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.9.0.tgz",
+ "integrity": "sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A=="
+ },
+ "core-util-is": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz",
+ "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ=="
+ },
+ "cosmiconfig": {
+ "version": "7.1.0",
+ "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-7.1.0.tgz",
+ "integrity": "sha512-AdmX6xUzdNASswsFtmwSt7Vj8po9IuqXm0UXz7QKPuEUmPB4XyjGfaAr2PSuELMwkRMVH1EpIkX5bTZGRB3eCA==",
+ "requires": {
+ "@types/parse-json": "^4.0.0",
+ "import-fresh": "^3.2.1",
+ "parse-json": "^5.0.0",
+ "path-type": "^4.0.0",
+ "yaml": "^1.10.0"
+ }
+ },
+ "cross-fetch": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/cross-fetch/-/cross-fetch-4.0.0.tgz",
+ "integrity": "sha512-e4a5N8lVvuLgAWgnCrLr2PP0YyDOTHa9H/Rj54dirp61qXnNq46m82bRhNqIA5VccJtWBvPTFRV3TtvHUKPB1g==",
+ "requires": {
+ "node-fetch": "^2.6.12"
+ }
+ },
+ "cross-spawn": {
+ "version": "7.0.3",
+ "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz",
+ "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==",
+ "dev": true,
+ "requires": {
+ "path-key": "^3.1.0",
+ "shebang-command": "^2.0.0",
+ "which": "^2.0.1"
+ }
+ },
+ "csstype": {
+ "version": "3.1.3",
+ "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz",
+ "integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw=="
+ },
+ "cypress": {
+ "version": "13.9.0",
+ "resolved": "https://registry.npmjs.org/cypress/-/cypress-13.9.0.tgz",
+ "integrity": "sha512-atNjmYfHsvTuCaxTxLZr9xGoHz53LLui3266WWxXJHY7+N6OdwJdg/feEa3T+buez9dmUXHT1izCOklqG82uCQ==",
+ "dev": true,
+ "requires": {
+ "@cypress/request": "^3.0.0",
+ "@cypress/xvfb": "^1.2.4",
+ "@types/sinonjs__fake-timers": "8.1.1",
+ "@types/sizzle": "^2.3.2",
+ "arch": "^2.2.0",
+ "blob-util": "^2.0.2",
+ "bluebird": "^3.7.2",
+ "buffer": "^5.7.1",
+ "cachedir": "^2.3.0",
+ "chalk": "^4.1.0",
+ "check-more-types": "^2.24.0",
+ "cli-cursor": "^3.1.0",
+ "cli-table3": "~0.6.1",
+ "commander": "^6.2.1",
+ "common-tags": "^1.8.0",
+ "dayjs": "^1.10.4",
+ "debug": "^4.3.4",
+ "enquirer": "^2.3.6",
+ "eventemitter2": "6.4.7",
+ "execa": "4.1.0",
+ "executable": "^4.1.1",
+ "extract-zip": "2.0.1",
+ "figures": "^3.2.0",
+ "fs-extra": "^9.1.0",
+ "getos": "^3.2.1",
+ "is-ci": "^3.0.1",
+ "is-installed-globally": "~0.4.0",
+ "lazy-ass": "^1.6.0",
+ "listr2": "^3.8.3",
+ "lodash": "^4.17.21",
+ "log-symbols": "^4.0.0",
+ "minimist": "^1.2.8",
+ "ospath": "^1.2.2",
+ "pretty-bytes": "^5.6.0",
+ "process": "^0.11.10",
+ "proxy-from-env": "1.0.0",
+ "request-progress": "^3.0.0",
+ "semver": "^7.5.3",
+ "supports-color": "^8.1.1",
+ "tmp": "~0.2.1",
+ "untildify": "^4.0.0",
+ "yauzl": "^2.10.0"
+ },
+ "dependencies": {
+ "ansi-styles": {
+ "version": "4.3.0",
+ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
+ "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
+ "dev": true,
+ "requires": {
+ "color-convert": "^2.0.1"
+ }
+ },
+ "chalk": {
+ "version": "4.1.2",
+ "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
+ "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
+ "dev": true,
+ "requires": {
+ "ansi-styles": "^4.1.0",
+ "supports-color": "^7.1.0"
+ },
+ "dependencies": {
+ "supports-color": {
+ "version": "7.2.0",
+ "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
+ "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
+ "dev": true,
+ "requires": {
+ "has-flag": "^4.0.0"
+ }
+ }
+ }
+ },
+ "color-convert": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
+ "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
+ "dev": true,
+ "requires": {
+ "color-name": "~1.1.4"
+ }
+ },
+ "color-name": {
+ "version": "1.1.4",
+ "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
+ "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
+ "dev": true
+ },
+ "has-flag": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
+ "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
+ "dev": true
+ },
+ "proxy-from-env": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.0.0.tgz",
+ "integrity": "sha512-F2JHgJQ1iqwnHDcQjVBsq3n/uoaFL+iPW/eAeL7kVxy/2RrWaN4WroKjjvbsoRtv0ftelNyC01bjRhn/bhcf4A==",
+ "dev": true
+ },
+ "supports-color": {
+ "version": "8.1.1",
+ "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz",
+ "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==",
+ "dev": true,
+ "requires": {
+ "has-flag": "^4.0.0"
+ }
+ }
+ }
+ },
+ "dashdash": {
+ "version": "1.14.1",
+ "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz",
+ "integrity": "sha512-jRFi8UDGo6j+odZiEpjazZaWqEal3w/basFjQHQEwVtZJGDpxbH1MeYluwCS8Xq5wmLJooDlMgvVarmWfGM44g==",
+ "dev": true,
+ "requires": {
+ "assert-plus": "^1.0.0"
+ }
+ },
+ "dayjs": {
+ "version": "1.11.11",
+ "resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.11.11.tgz",
+ "integrity": "sha512-okzr3f11N6WuqYtZSvm+F776mB41wRZMhKP+hc34YdW+KmtYYK9iqvHSwo2k9FEH3fhGXvOPV6yz2IcSrfRUDg=="
+ },
+ "debug": {
+ "version": "4.3.4",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz",
+ "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==",
+ "dev": true,
+ "requires": {
+ "ms": "2.1.2"
+ }
+ },
+ "deep-is": {
+ "version": "0.1.4",
+ "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz",
+ "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==",
+ "dev": true
+ },
+ "define-data-property": {
+ "version": "1.1.4",
+ "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz",
+ "integrity": "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==",
+ "dev": true,
+ "requires": {
+ "es-define-property": "^1.0.0",
+ "es-errors": "^1.3.0",
+ "gopd": "^1.0.1"
+ }
+ },
+ "delayed-stream": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz",
+ "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ=="
+ },
+ "dir-glob": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz",
+ "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==",
+ "dev": true,
+ "requires": {
+ "path-type": "^4.0.0"
+ }
+ },
+ "doctrine": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz",
+ "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==",
+ "dev": true,
+ "requires": {
+ "esutils": "^2.0.2"
+ }
+ },
+ "dom-helpers": {
+ "version": "5.2.1",
+ "resolved": "https://registry.npmjs.org/dom-helpers/-/dom-helpers-5.2.1.tgz",
+ "integrity": "sha512-nRCa7CK3VTrM2NmGkIy4cbK7IZlgBE/PYMn55rrXefr5xXDP0LdtfPnblFDoVdcAfslJ7or6iqAUnx0CCGIWQA==",
+ "requires": {
+ "@babel/runtime": "^7.8.7",
+ "csstype": "^3.0.2"
+ }
+ },
+ "ecc-jsbn": {
+ "version": "0.1.2",
+ "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz",
+ "integrity": "sha512-eh9O+hwRHNbG4BLTjEl3nw044CkGm5X6LoaCf7LPp7UU8Qrt47JYNi6nPX8xjW97TKGKm1ouctg0QSpZe9qrnw==",
+ "dev": true,
+ "requires": {
+ "jsbn": "~0.1.0",
+ "safer-buffer": "^2.1.0"
+ }
+ },
+ "electron-to-chromium": {
+ "version": "1.4.756",
+ "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.756.tgz",
+ "integrity": "sha512-RJKZ9+vEBMeiPAvKNWyZjuYyUqMndcP1f335oHqn3BEQbs2NFtVrnK5+6Xg5wSM9TknNNpWghGDUCKGYF+xWXw==",
+ "dev": true
+ },
+ "emoji-regex": {
+ "version": "8.0.0",
+ "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
+ "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==",
+ "dev": true
+ },
+ "end-of-stream": {
+ "version": "1.4.4",
+ "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz",
+ "integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==",
+ "dev": true,
+ "requires": {
+ "once": "^1.4.0"
+ }
+ },
+ "enquirer": {
+ "version": "2.4.1",
+ "resolved": "https://registry.npmjs.org/enquirer/-/enquirer-2.4.1.tgz",
+ "integrity": "sha512-rRqJg/6gd538VHvR3PSrdRBb/1Vy2YfzHqzvbhGIQpDRKIa4FgV/54b5Q1xYSxOOwKvjXweS26E0Q+nAMwp2pQ==",
+ "dev": true,
+ "requires": {
+ "ansi-colors": "^4.1.1",
+ "strip-ansi": "^6.0.1"
+ }
+ },
+ "error-ex": {
+ "version": "1.3.2",
+ "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz",
+ "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==",
+ "requires": {
+ "is-arrayish": "^0.2.1"
+ }
+ },
+ "es-define-property": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.0.tgz",
+ "integrity": "sha512-jxayLKShrEqqzJ0eumQbVhTYQM27CfT1T35+gCgDFoL82JLsXqTJ76zv6A0YLOgEnLUMvLzsDsGIrl8NFpT2gQ==",
+ "dev": true,
+ "requires": {
+ "get-intrinsic": "^1.2.4"
+ }
+ },
+ "es-errors": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz",
+ "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==",
+ "dev": true
+ },
+ "esbuild": {
+ "version": "0.20.2",
+ "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.20.2.tgz",
+ "integrity": "sha512-WdOOppmUNU+IbZ0PaDiTst80zjnrOkyJNHoKupIcVyU8Lvla3Ugx94VzkQ32Ijqd7UhHJy75gNWDMUekcrSJ6g==",
+ "dev": true,
+ "requires": {
+ "@esbuild/aix-ppc64": "0.20.2",
+ "@esbuild/android-arm": "0.20.2",
+ "@esbuild/android-arm64": "0.20.2",
+ "@esbuild/android-x64": "0.20.2",
+ "@esbuild/darwin-arm64": "0.20.2",
+ "@esbuild/darwin-x64": "0.20.2",
+ "@esbuild/freebsd-arm64": "0.20.2",
+ "@esbuild/freebsd-x64": "0.20.2",
+ "@esbuild/linux-arm": "0.20.2",
+ "@esbuild/linux-arm64": "0.20.2",
+ "@esbuild/linux-ia32": "0.20.2",
+ "@esbuild/linux-loong64": "0.20.2",
+ "@esbuild/linux-mips64el": "0.20.2",
+ "@esbuild/linux-ppc64": "0.20.2",
+ "@esbuild/linux-riscv64": "0.20.2",
+ "@esbuild/linux-s390x": "0.20.2",
+ "@esbuild/linux-x64": "0.20.2",
+ "@esbuild/netbsd-x64": "0.20.2",
+ "@esbuild/openbsd-x64": "0.20.2",
+ "@esbuild/sunos-x64": "0.20.2",
+ "@esbuild/win32-arm64": "0.20.2",
+ "@esbuild/win32-ia32": "0.20.2",
+ "@esbuild/win32-x64": "0.20.2"
+ }
+ },
+ "escalade": {
+ "version": "3.1.2",
+ "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.2.tgz",
+ "integrity": "sha512-ErCHMCae19vR8vQGe50xIsVomy19rg6gFu3+r3jkEO46suLMWBksvVyoGgQV+jOfl84ZSOSlmv6Gxa89PmTGmA==",
+ "dev": true
+ },
+ "escape-string-regexp": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz",
+ "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA=="
+ },
+ "eslint": {
+ "version": "8.57.0",
+ "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.57.0.tgz",
+ "integrity": "sha512-dZ6+mexnaTIbSBZWgou51U6OmzIhYM2VcNdtiTtI7qPNZm35Akpr0f6vtw3w1Kmn5PYo+tZVfh13WrhpS6oLqQ==",
+ "dev": true,
+ "requires": {
+ "@eslint-community/eslint-utils": "^4.2.0",
+ "@eslint-community/regexpp": "^4.6.1",
+ "@eslint/eslintrc": "^2.1.4",
+ "@eslint/js": "8.57.0",
+ "@humanwhocodes/config-array": "^0.11.14",
+ "@humanwhocodes/module-importer": "^1.0.1",
+ "@nodelib/fs.walk": "^1.2.8",
+ "@ungap/structured-clone": "^1.2.0",
+ "ajv": "^6.12.4",
+ "chalk": "^4.0.0",
+ "cross-spawn": "^7.0.2",
+ "debug": "^4.3.2",
+ "doctrine": "^3.0.0",
+ "escape-string-regexp": "^4.0.0",
+ "eslint-scope": "^7.2.2",
+ "eslint-visitor-keys": "^3.4.3",
+ "espree": "^9.6.1",
+ "esquery": "^1.4.2",
+ "esutils": "^2.0.2",
+ "fast-deep-equal": "^3.1.3",
+ "file-entry-cache": "^6.0.1",
+ "find-up": "^5.0.0",
+ "glob-parent": "^6.0.2",
+ "globals": "^13.19.0",
+ "graphemer": "^1.4.0",
+ "ignore": "^5.2.0",
+ "imurmurhash": "^0.1.4",
+ "is-glob": "^4.0.0",
+ "is-path-inside": "^3.0.3",
+ "js-yaml": "^4.1.0",
+ "json-stable-stringify-without-jsonify": "^1.0.1",
+ "levn": "^0.4.1",
+ "lodash.merge": "^4.6.2",
+ "minimatch": "^3.1.2",
+ "natural-compare": "^1.4.0",
+ "optionator": "^0.9.3",
+ "strip-ansi": "^6.0.1",
+ "text-table": "^0.2.0"
+ },
+ "dependencies": {
+ "ansi-styles": {
+ "version": "4.3.0",
+ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
+ "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
+ "dev": true,
+ "requires": {
+ "color-convert": "^2.0.1"
+ }
+ },
+ "brace-expansion": {
+ "version": "1.1.11",
+ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
+ "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
+ "dev": true,
+ "requires": {
+ "balanced-match": "^1.0.0",
+ "concat-map": "0.0.1"
+ }
+ },
+ "chalk": {
+ "version": "4.1.2",
+ "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
+ "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
+ "dev": true,
+ "requires": {
+ "ansi-styles": "^4.1.0",
+ "supports-color": "^7.1.0"
+ }
+ },
+ "color-convert": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
+ "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
+ "dev": true,
+ "requires": {
+ "color-name": "~1.1.4"
+ }
+ },
+ "color-name": {
+ "version": "1.1.4",
+ "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
+ "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
+ "dev": true
+ },
+ "globals": {
+ "version": "13.24.0",
+ "resolved": "https://registry.npmjs.org/globals/-/globals-13.24.0.tgz",
+ "integrity": "sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==",
+ "dev": true,
+ "requires": {
+ "type-fest": "^0.20.2"
+ }
+ },
+ "has-flag": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
+ "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
+ "dev": true
+ },
+ "minimatch": {
+ "version": "3.1.2",
+ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
+ "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==",
+ "dev": true,
+ "requires": {
+ "brace-expansion": "^1.1.7"
+ }
+ },
+ "supports-color": {
+ "version": "7.2.0",
+ "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
+ "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
+ "dev": true,
+ "requires": {
+ "has-flag": "^4.0.0"
+ }
+ }
+ }
+ },
+ "eslint-config-prettier": {
+ "version": "9.1.0",
+ "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-9.1.0.tgz",
+ "integrity": "sha512-NSWl5BFQWEPi1j4TjVNItzYV7dZXZ+wP6I6ZhrBGpChQhZRUaElihE9uRRkcbRnNb76UMKDF3r+WTmNcGPKsqw==",
+ "dev": true,
+ "requires": {}
+ },
+ "eslint-plugin-prettier": {
+ "version": "5.1.3",
+ "resolved": "https://registry.npmjs.org/eslint-plugin-prettier/-/eslint-plugin-prettier-5.1.3.tgz",
+ "integrity": "sha512-C9GCVAs4Eq7ZC/XFQHITLiHJxQngdtraXaM+LoUFoFp/lHNl2Zn8f3WQbe9HvTBBQ9YnKFB0/2Ajdqwo5D1EAw==",
+ "dev": true,
+ "requires": {
+ "prettier-linter-helpers": "^1.0.0",
+ "synckit": "^0.8.6"
+ }
+ },
+ "eslint-plugin-react-hooks": {
+ "version": "4.6.2",
+ "resolved": "https://registry.npmjs.org/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-4.6.2.tgz",
+ "integrity": "sha512-QzliNJq4GinDBcD8gPB5v0wh6g8q3SUi6EFF0x8N/BL9PoVs0atuGc47ozMRyOWAKdwaZ5OnbOEa3WR+dSGKuQ==",
+ "dev": true,
+ "requires": {}
+ },
+ "eslint-plugin-react-refresh": {
+ "version": "0.4.6",
+ "resolved": "https://registry.npmjs.org/eslint-plugin-react-refresh/-/eslint-plugin-react-refresh-0.4.6.tgz",
+ "integrity": "sha512-NjGXdm7zgcKRkKMua34qVO9doI7VOxZ6ancSvBELJSSoX97jyndXcSoa8XBh69JoB31dNz3EEzlMcizZl7LaMA==",
+ "dev": true,
+ "requires": {}
+ },
+ "eslint-scope": {
+ "version": "7.2.2",
+ "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.2.2.tgz",
+ "integrity": "sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==",
+ "dev": true,
+ "requires": {
+ "esrecurse": "^4.3.0",
+ "estraverse": "^5.2.0"
+ }
+ },
+ "eslint-visitor-keys": {
+ "version": "3.4.3",
+ "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz",
+ "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==",
+ "dev": true
+ },
+ "espree": {
+ "version": "9.6.1",
+ "resolved": "https://registry.npmjs.org/espree/-/espree-9.6.1.tgz",
+ "integrity": "sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==",
+ "dev": true,
+ "requires": {
+ "acorn": "^8.9.0",
+ "acorn-jsx": "^5.3.2",
+ "eslint-visitor-keys": "^3.4.1"
+ }
+ },
+ "esquery": {
+ "version": "1.5.0",
+ "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.5.0.tgz",
+ "integrity": "sha512-YQLXUplAwJgCydQ78IMJywZCceoqk1oH01OERdSAJc/7U2AylwjhSCLDEtqwg811idIS/9fIU5GjG73IgjKMVg==",
+ "dev": true,
+ "requires": {
+ "estraverse": "^5.1.0"
+ }
+ },
+ "esrecurse": {
+ "version": "4.3.0",
+ "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz",
+ "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==",
+ "dev": true,
+ "requires": {
+ "estraverse": "^5.2.0"
+ }
+ },
+ "estraverse": {
+ "version": "5.3.0",
+ "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz",
+ "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==",
+ "dev": true
+ },
+ "esutils": {
+ "version": "2.0.3",
+ "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz",
+ "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==",
+ "dev": true
+ },
+ "eventemitter2": {
+ "version": "6.4.7",
+ "resolved": "https://registry.npmjs.org/eventemitter2/-/eventemitter2-6.4.7.tgz",
+ "integrity": "sha512-tYUSVOGeQPKt/eC1ABfhHy5Xd96N3oIijJvN3O9+TsC28T5V9yX9oEfEK5faP0EFSNVOG97qtAS68GBrQB2hDg==",
+ "dev": true
+ },
+ "execa": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/execa/-/execa-4.1.0.tgz",
+ "integrity": "sha512-j5W0//W7f8UxAn8hXVnwG8tLwdiUy4FJLcSupCg6maBYZDpyBvTApK7KyuI4bKj8KOh1r2YH+6ucuYtJv1bTZA==",
+ "dev": true,
+ "requires": {
+ "cross-spawn": "^7.0.0",
+ "get-stream": "^5.0.0",
+ "human-signals": "^1.1.1",
+ "is-stream": "^2.0.0",
+ "merge-stream": "^2.0.0",
+ "npm-run-path": "^4.0.0",
+ "onetime": "^5.1.0",
+ "signal-exit": "^3.0.2",
+ "strip-final-newline": "^2.0.0"
+ }
+ },
+ "executable": {
+ "version": "4.1.1",
+ "resolved": "https://registry.npmjs.org/executable/-/executable-4.1.1.tgz",
+ "integrity": "sha512-8iA79xD3uAch729dUG8xaaBBFGaEa0wdD2VkYLFHwlqosEj/jT66AzcreRDSgV7ehnNLBW2WR5jIXwGKjVdTLg==",
+ "dev": true,
+ "requires": {
+ "pify": "^2.2.0"
+ }
+ },
+ "extend": {
+ "version": "3.0.2",
+ "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz",
+ "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==",
+ "dev": true
+ },
+ "extract-zip": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/extract-zip/-/extract-zip-2.0.1.tgz",
+ "integrity": "sha512-GDhU9ntwuKyGXdZBUgTIe+vXnWj0fppUEtMDL0+idd5Sta8TGpHssn/eusA9mrPr9qNDym6SxAYZjNvCn/9RBg==",
+ "dev": true,
+ "requires": {
+ "@types/yauzl": "^2.9.1",
+ "debug": "^4.1.1",
+ "get-stream": "^5.1.0",
+ "yauzl": "^2.10.0"
+ }
+ },
+ "extsprintf": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz",
+ "integrity": "sha512-11Ndz7Nv+mvAC1j0ktTa7fAb0vLyGGX+rMHNBYQviQDGU0Hw7lhctJANqbPhu9nV9/izT/IntTgZ7Im/9LJs9g==",
+ "dev": true
+ },
+ "fast-deep-equal": {
+ "version": "3.1.3",
+ "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz",
+ "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==",
+ "dev": true
+ },
+ "fast-diff": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/fast-diff/-/fast-diff-1.3.0.tgz",
+ "integrity": "sha512-VxPP4NqbUjj6MaAOafWeUn2cXWLcCtljklUtZf0Ind4XQ+QPtmA0b18zZy0jIQx+ExRVCR/ZQpBmik5lXshNsw==",
+ "dev": true
+ },
+ "fast-glob": {
+ "version": "3.3.2",
+ "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.2.tgz",
+ "integrity": "sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow==",
+ "dev": true,
+ "requires": {
+ "@nodelib/fs.stat": "^2.0.2",
+ "@nodelib/fs.walk": "^1.2.3",
+ "glob-parent": "^5.1.2",
+ "merge2": "^1.3.0",
+ "micromatch": "^4.0.4"
+ },
+ "dependencies": {
+ "glob-parent": {
+ "version": "5.1.2",
+ "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz",
+ "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==",
+ "dev": true,
+ "requires": {
+ "is-glob": "^4.0.1"
+ }
+ }
+ }
+ },
+ "fast-json-stable-stringify": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz",
+ "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==",
+ "dev": true
+ },
+ "fast-levenshtein": {
+ "version": "2.0.6",
+ "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz",
+ "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==",
+ "dev": true
+ },
+ "fastq": {
+ "version": "1.17.1",
+ "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.17.1.tgz",
+ "integrity": "sha512-sRVD3lWVIXWg6By68ZN7vho9a1pQcN/WBFaAAsDDFzlJjvoGx0P8z7V1t72grFJfJhu3YPZBuu25f7Kaw2jN1w==",
+ "dev": true,
+ "requires": {
+ "reusify": "^1.0.4"
+ }
+ },
+ "fd-slicer": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/fd-slicer/-/fd-slicer-1.1.0.tgz",
+ "integrity": "sha512-cE1qsB/VwyQozZ+q1dGxR8LBYNZeofhEdUNGSMbQD3Gw2lAzX9Zb3uIU6Ebc/Fmyjo9AWWfnn0AUCHqtevs/8g==",
+ "dev": true,
+ "requires": {
+ "pend": "~1.2.0"
+ }
+ },
+ "figures": {
+ "version": "3.2.0",
+ "resolved": "https://registry.npmjs.org/figures/-/figures-3.2.0.tgz",
+ "integrity": "sha512-yaduQFRKLXYOGgEn6AZau90j3ggSOyiqXU0F9JZfeXYhNa+Jk4X+s45A2zg5jns87GAFa34BBm2kXw4XpNcbdg==",
+ "dev": true,
+ "requires": {
+ "escape-string-regexp": "^1.0.5"
+ },
+ "dependencies": {
+ "escape-string-regexp": {
+ "version": "1.0.5",
+ "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz",
+ "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==",
+ "dev": true
+ }
+ }
+ },
+ "file-entry-cache": {
+ "version": "6.0.1",
+ "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz",
+ "integrity": "sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==",
+ "dev": true,
+ "requires": {
+ "flat-cache": "^3.0.4"
+ }
+ },
+ "fill-range": {
+ "version": "7.0.1",
+ "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz",
+ "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==",
+ "dev": true,
+ "requires": {
+ "to-regex-range": "^5.0.1"
+ }
+ },
+ "find-root": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/find-root/-/find-root-1.1.0.tgz",
+ "integrity": "sha512-NKfW6bec6GfKc0SGx1e07QZY9PE99u0Bft/0rzSD5k3sO/vwkVUpDUKVm5Gpp5Ue3YfShPFTX2070tDs5kB9Ng=="
+ },
+ "find-up": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz",
+ "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==",
+ "dev": true,
+ "requires": {
+ "locate-path": "^6.0.0",
+ "path-exists": "^4.0.0"
+ }
+ },
+ "flat-cache": {
+ "version": "3.2.0",
+ "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.2.0.tgz",
+ "integrity": "sha512-CYcENa+FtcUKLmhhqyctpclsq7QF38pKjZHsGNiSQF5r4FtoKDWabFDl3hzaEQMvT1LHEysw5twgLvpYYb4vbw==",
+ "dev": true,
+ "requires": {
+ "flatted": "^3.2.9",
+ "keyv": "^4.5.3",
+ "rimraf": "^3.0.2"
+ }
+ },
+ "flatted": {
+ "version": "3.3.1",
+ "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.1.tgz",
+ "integrity": "sha512-X8cqMLLie7KsNUDSdzeN8FYK9rEt4Dt67OsG/DNGnYTSDBG4uFAJFBnUeiV+zCVAvwFy56IjM9sH51jVaEhNxw==",
+ "dev": true
+ },
+ "follow-redirects": {
+ "version": "1.15.6",
+ "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.6.tgz",
+ "integrity": "sha512-wWN62YITEaOpSK584EZXJafH1AGpO8RVgElfkuXbTOrPX4fIfOyEpW/CsiNd8JdYrAoOvafRTOEnvsO++qCqFA=="
+ },
+ "forever-agent": {
+ "version": "0.6.1",
+ "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz",
+ "integrity": "sha512-j0KLYPhm6zeac4lz3oJ3o65qvgQCcPubiyotZrXqEaG4hNagNYO8qdlUrX5vwqv9ohqeT/Z3j6+yW067yWWdUw==",
+ "dev": true
+ },
+ "form-data": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz",
+ "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==",
+ "requires": {
+ "asynckit": "^0.4.0",
+ "combined-stream": "^1.0.8",
+ "mime-types": "^2.1.12"
+ }
+ },
+ "fs-extra": {
+ "version": "9.1.0",
+ "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-9.1.0.tgz",
+ "integrity": "sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==",
+ "dev": true,
+ "requires": {
+ "at-least-node": "^1.0.0",
+ "graceful-fs": "^4.2.0",
+ "jsonfile": "^6.0.1",
+ "universalify": "^2.0.0"
+ }
+ },
+ "fs.realpath": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
+ "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==",
+ "dev": true
+ },
+ "fsevents": {
+ "version": "2.3.3",
+ "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz",
+ "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==",
+ "dev": true,
+ "optional": true
+ },
+ "function-bind": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz",
+ "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA=="
+ },
+ "gensync": {
+ "version": "1.0.0-beta.2",
+ "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz",
+ "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==",
+ "dev": true
+ },
+ "get-intrinsic": {
+ "version": "1.2.4",
+ "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.4.tgz",
+ "integrity": "sha512-5uYhsJH8VJBTv7oslg4BznJYhDoRI6waYCxMmCdnTrcCrHA/fCFKoTFz2JKKE0HdDFUF7/oQuhzumXJK7paBRQ==",
+ "dev": true,
+ "requires": {
+ "es-errors": "^1.3.0",
+ "function-bind": "^1.1.2",
+ "has-proto": "^1.0.1",
+ "has-symbols": "^1.0.3",
+ "hasown": "^2.0.0"
+ }
+ },
+ "get-stream": {
+ "version": "5.2.0",
+ "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.2.0.tgz",
+ "integrity": "sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==",
+ "dev": true,
+ "requires": {
+ "pump": "^3.0.0"
+ }
+ },
+ "getos": {
+ "version": "3.2.1",
+ "resolved": "https://registry.npmjs.org/getos/-/getos-3.2.1.tgz",
+ "integrity": "sha512-U56CfOK17OKgTVqozZjUKNdkfEv6jk5WISBJ8SHoagjE6L69zOwl3Z+O8myjY9MEW3i2HPWQBt/LTbCgcC973Q==",
+ "dev": true,
+ "requires": {
+ "async": "^3.2.0"
+ }
+ },
+ "getpass": {
+ "version": "0.1.7",
+ "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz",
+ "integrity": "sha512-0fzj9JxOLfJ+XGLhR8ze3unN0KZCgZwiSSDz168VERjK8Wl8kVSdcu2kspd4s4wtAa1y/qrVRiAA0WclVsu0ng==",
+ "dev": true,
+ "requires": {
+ "assert-plus": "^1.0.0"
+ }
+ },
+ "glob": {
+ "version": "7.2.3",
+ "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz",
+ "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==",
+ "dev": true,
+ "requires": {
+ "fs.realpath": "^1.0.0",
+ "inflight": "^1.0.4",
+ "inherits": "2",
+ "minimatch": "^3.1.1",
+ "once": "^1.3.0",
+ "path-is-absolute": "^1.0.0"
+ },
+ "dependencies": {
+ "brace-expansion": {
+ "version": "1.1.11",
+ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
+ "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
+ "dev": true,
+ "requires": {
+ "balanced-match": "^1.0.0",
+ "concat-map": "0.0.1"
+ }
+ },
+ "minimatch": {
+ "version": "3.1.2",
+ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
+ "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==",
+ "dev": true,
+ "requires": {
+ "brace-expansion": "^1.1.7"
+ }
+ }
+ }
+ },
+ "glob-parent": {
+ "version": "6.0.2",
+ "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz",
+ "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==",
+ "dev": true,
+ "requires": {
+ "is-glob": "^4.0.3"
+ }
+ },
+ "global-dirs": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/global-dirs/-/global-dirs-3.0.1.tgz",
+ "integrity": "sha512-NBcGGFbBA9s1VzD41QXDG+3++t9Mn5t1FpLdhESY6oKY4gYTFpX4wO3sqGUa0Srjtbfj3szX0RnemmrVRUdULA==",
+ "dev": true,
+ "requires": {
+ "ini": "2.0.0"
+ }
+ },
+ "globals": {
+ "version": "11.12.0",
+ "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz",
+ "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==",
+ "dev": true
+ },
+ "globby": {
+ "version": "11.1.0",
+ "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz",
+ "integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==",
+ "dev": true,
+ "requires": {
+ "array-union": "^2.1.0",
+ "dir-glob": "^3.0.1",
+ "fast-glob": "^3.2.9",
+ "ignore": "^5.2.0",
+ "merge2": "^1.4.1",
+ "slash": "^3.0.0"
+ }
+ },
+ "gopd": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz",
+ "integrity": "sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==",
+ "dev": true,
+ "requires": {
+ "get-intrinsic": "^1.1.3"
+ }
+ },
+ "graceful-fs": {
+ "version": "4.2.11",
+ "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz",
+ "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==",
+ "dev": true
+ },
+ "graphemer": {
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz",
+ "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==",
+ "dev": true
+ },
+ "has-flag": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz",
+ "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw=="
+ },
+ "has-property-descriptors": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz",
+ "integrity": "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==",
+ "dev": true,
+ "requires": {
+ "es-define-property": "^1.0.0"
+ }
+ },
+ "has-proto": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.0.3.tgz",
+ "integrity": "sha512-SJ1amZAJUiZS+PhsVLf5tGydlaVB8EdFpaSO4gmiUKUOxk8qzn5AIy4ZeJUmh22znIdk/uMAUT2pl3FxzVUH+Q==",
+ "dev": true
+ },
+ "has-symbols": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz",
+ "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==",
+ "dev": true
+ },
+ "hasown": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz",
+ "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==",
+ "requires": {
+ "function-bind": "^1.1.2"
+ }
+ },
+ "hoist-non-react-statics": {
+ "version": "3.3.2",
+ "resolved": "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz",
+ "integrity": "sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw==",
+ "requires": {
+ "react-is": "^16.7.0"
+ },
+ "dependencies": {
+ "react-is": {
+ "version": "16.13.1",
+ "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz",
+ "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ=="
+ }
+ }
+ },
+ "html-parse-stringify": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/html-parse-stringify/-/html-parse-stringify-3.0.1.tgz",
+ "integrity": "sha512-KknJ50kTInJ7qIScF3jeaFRpMpE8/lfiTdzf/twXyPBLAGrLRTmkz3AdTnKeh40X8k9L2fdYwEp/42WGXIRGcg==",
+ "requires": {
+ "void-elements": "3.1.0"
+ }
+ },
+ "http-signature": {
+ "version": "1.3.6",
+ "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.3.6.tgz",
+ "integrity": "sha512-3adrsD6zqo4GsTqtO7FyrejHNv+NgiIfAfv68+jVlFmSr9OGy7zrxONceFRLKvnnZA5jbxQBX1u9PpB6Wi32Gw==",
+ "dev": true,
+ "requires": {
+ "assert-plus": "^1.0.0",
+ "jsprim": "^2.0.2",
+ "sshpk": "^1.14.1"
+ }
+ },
+ "human-signals": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-1.1.1.tgz",
+ "integrity": "sha512-SEQu7vl8KjNL2eoGBLF3+wAjpsNfA9XMlXAYj/3EdaNfAlxKthD1xjEQfGOUhllCGGJVNY34bRr6lPINhNjyZw==",
+ "dev": true
+ },
+ "husky": {
+ "version": "7.0.4",
+ "resolved": "https://registry.npmjs.org/husky/-/husky-7.0.4.tgz",
+ "integrity": "sha512-vbaCKN2QLtP/vD4yvs6iz6hBEo6wkSzs8HpRah1Z6aGmF2KW5PdYuAd7uX5a+OyBZHBhd+TFLqgjUgytQr4RvQ==",
+ "dev": true
+ },
+ "i18next": {
+ "version": "23.11.3",
+ "resolved": "https://registry.npmjs.org/i18next/-/i18next-23.11.3.tgz",
+ "integrity": "sha512-Pq/aSKowir7JM0rj+Wa23Kb6KKDUGno/HjG+wRQu0PxoTbpQ4N89MAT0rFGvXmLkRLNMb1BbBOKGozl01dabzg==",
+ "requires": {
+ "@babel/runtime": "^7.23.2"
+ }
+ },
+ "i18next-browser-languagedetector": {
+ "version": "7.2.1",
+ "resolved": "https://registry.npmjs.org/i18next-browser-languagedetector/-/i18next-browser-languagedetector-7.2.1.tgz",
+ "integrity": "sha512-h/pM34bcH6tbz8WgGXcmWauNpQupCGr25XPp9cZwZInR9XHSjIFDYp1SIok7zSPsTOMxdvuLyu86V+g2Kycnfw==",
+ "requires": {
+ "@babel/runtime": "^7.23.2"
+ }
+ },
+ "i18next-http-backend": {
+ "version": "2.5.1",
+ "resolved": "https://registry.npmjs.org/i18next-http-backend/-/i18next-http-backend-2.5.1.tgz",
+ "integrity": "sha512-+rNX1tghdVxdfjfPt0bI1sNg5ahGW9kA7OboG7b4t03Fp69NdDlRIze6yXhIbN8rbHxJ8IP4dzRm/okZ15lkQg==",
+ "requires": {
+ "cross-fetch": "4.0.0"
+ }
+ },
+ "ieee754": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz",
+ "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==",
+ "dev": true
+ },
+ "ignore": {
+ "version": "5.3.1",
+ "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.1.tgz",
+ "integrity": "sha512-5Fytz/IraMjqpwfd34ke28PTVMjZjJG2MPn5t7OE4eUCUNf8BAa7b5WUS9/Qvr6mwOQS7Mk6vdsMno5he+T8Xw==",
+ "dev": true
+ },
+ "immediate": {
+ "version": "3.0.6",
+ "resolved": "https://registry.npmjs.org/immediate/-/immediate-3.0.6.tgz",
+ "integrity": "sha512-XXOFtyqDjNDAQxVfYxuF7g9Il/IbWmmlQg2MYKOH8ExIT1qg6xc4zyS3HaEEATgs1btfzxq15ciUiY7gjSXRGQ=="
+ },
+ "import-fresh": {
+ "version": "3.3.0",
+ "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz",
+ "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==",
+ "requires": {
+ "parent-module": "^1.0.0",
+ "resolve-from": "^4.0.0"
+ }
+ },
+ "imurmurhash": {
+ "version": "0.1.4",
+ "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz",
+ "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==",
+ "dev": true
+ },
+ "indent-string": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz",
+ "integrity": "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==",
+ "dev": true
+ },
+ "inflight": {
+ "version": "1.0.6",
+ "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz",
+ "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==",
+ "dev": true,
+ "requires": {
+ "once": "^1.3.0",
+ "wrappy": "1"
+ }
+ },
+ "inherits": {
+ "version": "2.0.4",
+ "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
+ "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ=="
+ },
+ "ini": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/ini/-/ini-2.0.0.tgz",
+ "integrity": "sha512-7PnF4oN3CvZF23ADhA5wRaYEQpJ8qygSkbtTXWBeXWXmEVRXK+1ITciHWwHhsjv1TmW0MgacIv6hEi5pX5NQdA==",
+ "dev": true
+ },
+ "invariant": {
+ "version": "2.2.4",
+ "resolved": "https://registry.npmjs.org/invariant/-/invariant-2.2.4.tgz",
+ "integrity": "sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA==",
+ "requires": {
+ "loose-envify": "^1.0.0"
+ }
+ },
+ "is-arrayish": {
+ "version": "0.2.1",
+ "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz",
+ "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg=="
+ },
+ "is-ci": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/is-ci/-/is-ci-3.0.1.tgz",
+ "integrity": "sha512-ZYvCgrefwqoQ6yTyYUbQu64HsITZ3NfKX1lzaEYdkTDcfKzzCI/wthRRYKkdjHKFVgNiXKAKm65Zo1pk2as/QQ==",
+ "dev": true,
+ "requires": {
+ "ci-info": "^3.2.0"
+ }
+ },
+ "is-core-module": {
+ "version": "2.13.1",
+ "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.13.1.tgz",
+ "integrity": "sha512-hHrIjvZsftOsvKSn2TRYl63zvxsgE0K+0mYMoH6gD4omR5IWB2KynivBQczo3+wF1cCkjzvptnI9Q0sPU66ilw==",
+ "requires": {
+ "hasown": "^2.0.0"
+ }
+ },
+ "is-extglob": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz",
+ "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==",
+ "dev": true
+ },
+ "is-fullwidth-code-point": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz",
+ "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==",
+ "dev": true
+ },
+ "is-glob": {
+ "version": "4.0.3",
+ "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz",
+ "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==",
+ "dev": true,
+ "requires": {
+ "is-extglob": "^2.1.1"
+ }
+ },
+ "is-installed-globally": {
+ "version": "0.4.0",
+ "resolved": "https://registry.npmjs.org/is-installed-globally/-/is-installed-globally-0.4.0.tgz",
+ "integrity": "sha512-iwGqO3J21aaSkC7jWnHP/difazwS7SFeIqxv6wEtLU8Y5KlzFTjyqcSIT0d8s4+dDhKytsk9PJZ2BkS5eZwQRQ==",
+ "dev": true,
+ "requires": {
+ "global-dirs": "^3.0.0",
+ "is-path-inside": "^3.0.2"
+ }
+ },
+ "is-number": {
+ "version": "7.0.0",
+ "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz",
+ "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==",
+ "dev": true
+ },
+ "is-path-inside": {
+ "version": "3.0.3",
+ "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz",
+ "integrity": "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==",
+ "dev": true
+ },
+ "is-stream": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz",
+ "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==",
+ "dev": true
+ },
+ "is-typedarray": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz",
+ "integrity": "sha512-cyA56iCMHAh5CdzjJIa4aohJyeO1YbwLi3Jc35MmRU6poroFjIGZzUzupGiRPOjgHg9TLu43xbpwXk523fMxKA==",
+ "dev": true
+ },
+ "is-unicode-supported": {
+ "version": "0.1.0",
+ "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz",
+ "integrity": "sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==",
+ "dev": true
+ },
+ "isarray": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz",
+ "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ=="
+ },
+ "isexe": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz",
+ "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==",
+ "dev": true
+ },
+ "isstream": {
+ "version": "0.1.2",
+ "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz",
+ "integrity": "sha512-Yljz7ffyPbrLpLngrMtZ7NduUgVvi6wG9RJ9IUcyCd59YQ911PBJphODUcbOVbqYfxe1wuYf/LJ8PauMRwsM/g==",
+ "dev": true
+ },
+ "js-tokens": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz",
+ "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ=="
+ },
+ "js-yaml": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz",
+ "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==",
+ "dev": true,
+ "requires": {
+ "argparse": "^2.0.1"
+ }
+ },
+ "jsbn": {
+ "version": "0.1.1",
+ "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz",
+ "integrity": "sha512-UVU9dibq2JcFWxQPA6KCqj5O42VOmAY3zQUfEKxU0KpTGXwNoCjkX1e13eHNvw/xPynt6pU0rZ1htjWTNTSXsg==",
+ "dev": true
+ },
+ "jsesc": {
+ "version": "2.5.2",
+ "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz",
+ "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==",
+ "dev": true
+ },
+ "json-buffer": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz",
+ "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==",
+ "dev": true
+ },
+ "json-parse-even-better-errors": {
+ "version": "2.3.1",
+ "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz",
+ "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w=="
+ },
+ "json-schema": {
+ "version": "0.4.0",
+ "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.4.0.tgz",
+ "integrity": "sha512-es94M3nTIfsEPisRafak+HDLfHXnKBhV3vU5eqPcS3flIWqcxJWgXHXiey3YrpaNsanY5ei1VoYEbOzijuq9BA==",
+ "dev": true
+ },
+ "json-schema-traverse": {
+ "version": "0.4.1",
+ "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz",
+ "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==",
+ "dev": true
+ },
+ "json-stable-stringify-without-jsonify": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz",
+ "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==",
+ "dev": true
+ },
+ "json-stringify-safe": {
+ "version": "5.0.1",
+ "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz",
+ "integrity": "sha512-ZClg6AaYvamvYEE82d3Iyd3vSSIjQ+odgjaTzRuO3s7toCdFKczob2i0zCh7JE8kWn17yvAWhUVxvqGwUalsRA==",
+ "dev": true
+ },
+ "json5": {
+ "version": "2.2.3",
+ "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz",
+ "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==",
+ "dev": true
+ },
+ "jsonfile": {
+ "version": "6.1.0",
+ "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz",
+ "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==",
+ "dev": true,
+ "requires": {
+ "graceful-fs": "^4.1.6",
+ "universalify": "^2.0.0"
+ }
+ },
+ "jsprim": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-2.0.2.tgz",
+ "integrity": "sha512-gqXddjPqQ6G40VdnI6T6yObEC+pDNvyP95wdQhkWkg7crHH3km5qP1FsOXEkzEQwnz6gz5qGTn1c2Y52wP3OyQ==",
+ "dev": true,
+ "requires": {
+ "assert-plus": "1.0.0",
+ "extsprintf": "1.3.0",
+ "json-schema": "0.4.0",
+ "verror": "1.10.0"
+ }
+ },
+ "jszip": {
+ "version": "3.10.1",
+ "resolved": "https://registry.npmjs.org/jszip/-/jszip-3.10.1.tgz",
+ "integrity": "sha512-xXDvecyTpGLrqFrvkrUSoxxfJI5AH7U8zxxtVclpsUtMCq4JQ290LY8AW5c7Ggnr/Y/oK+bQMbqK2qmtk3pN4g==",
+ "requires": {
+ "lie": "~3.3.0",
+ "pako": "~1.0.2",
+ "readable-stream": "~2.3.6",
+ "setimmediate": "^1.0.5"
+ }
+ },
+ "keyv": {
+ "version": "4.5.4",
+ "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz",
+ "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==",
+ "dev": true,
+ "requires": {
+ "json-buffer": "3.0.1"
+ }
+ },
+ "lazy-ass": {
+ "version": "1.6.0",
+ "resolved": "https://registry.npmjs.org/lazy-ass/-/lazy-ass-1.6.0.tgz",
+ "integrity": "sha512-cc8oEVoctTvsFZ/Oje/kGnHbpWHYBe8IAJe4C0QNc3t8uM/0Y8+erSz/7Y1ALuXTEZTMvxXwO6YbX1ey3ujiZw==",
+ "dev": true
+ },
+ "levn": {
+ "version": "0.4.1",
+ "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz",
+ "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==",
+ "dev": true,
+ "requires": {
+ "prelude-ls": "^1.2.1",
+ "type-check": "~0.4.0"
+ }
+ },
+ "lie": {
+ "version": "3.3.0",
+ "resolved": "https://registry.npmjs.org/lie/-/lie-3.3.0.tgz",
+ "integrity": "sha512-UaiMJzeWRlEujzAuw5LokY1L5ecNQYZKfmyZ9L7wDHb/p5etKaxXhohBcrw0EYby+G/NA52vRSN4N39dxHAIwQ==",
+ "requires": {
+ "immediate": "~3.0.5"
+ }
+ },
+ "lines-and-columns": {
+ "version": "1.2.4",
+ "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz",
+ "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg=="
+ },
+ "listr2": {
+ "version": "3.14.0",
+ "resolved": "https://registry.npmjs.org/listr2/-/listr2-3.14.0.tgz",
+ "integrity": "sha512-TyWI8G99GX9GjE54cJ+RrNMcIFBfwMPxc3XTFiAYGN4s10hWROGtOg7+O6u6LE3mNkyld7RSLE6nrKBvTfcs3g==",
+ "dev": true,
+ "requires": {
+ "cli-truncate": "^2.1.0",
+ "colorette": "^2.0.16",
+ "log-update": "^4.0.0",
+ "p-map": "^4.0.0",
+ "rfdc": "^1.3.0",
+ "rxjs": "^7.5.1",
+ "through": "^2.3.8",
+ "wrap-ansi": "^7.0.0"
+ }
+ },
+ "localforage": {
+ "version": "1.10.0",
+ "resolved": "https://registry.npmjs.org/localforage/-/localforage-1.10.0.tgz",
+ "integrity": "sha512-14/H1aX7hzBBmmh7sGPd+AOMkkIrHM3Z1PAyGgZigA1H1p5O5ANnMyWzvpAETtG68/dC4pC0ncy3+PPGzXZHPg==",
+ "requires": {
+ "lie": "3.1.1"
+ },
+ "dependencies": {
+ "lie": {
+ "version": "3.1.1",
+ "resolved": "https://registry.npmjs.org/lie/-/lie-3.1.1.tgz",
+ "integrity": "sha512-RiNhHysUjhrDQntfYSfY4MU24coXXdEOgw9WGcKHNeEwffDYbF//u87M1EWaMGzuFoSbqW0C9C6lEEhDOAswfw==",
+ "requires": {
+ "immediate": "~3.0.5"
+ }
+ }
+ }
+ },
+ "locate-path": {
+ "version": "6.0.0",
+ "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz",
+ "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==",
+ "dev": true,
+ "requires": {
+ "p-locate": "^5.0.0"
+ }
+ },
+ "lodash": {
+ "version": "4.17.21",
+ "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz",
+ "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==",
+ "dev": true
+ },
+ "lodash.merge": {
+ "version": "4.6.2",
+ "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz",
+ "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==",
+ "dev": true
+ },
+ "lodash.once": {
+ "version": "4.1.1",
+ "resolved": "https://registry.npmjs.org/lodash.once/-/lodash.once-4.1.1.tgz",
+ "integrity": "sha512-Sb487aTOCr9drQVL8pIxOzVhafOjZN9UU54hiN8PU3uAiSV7lx1yYNpbNmex2PK6dSJoNTSJUUswT651yww3Mg==",
+ "dev": true
+ },
+ "log-symbols": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz",
+ "integrity": "sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==",
+ "dev": true,
+ "requires": {
+ "chalk": "^4.1.0",
+ "is-unicode-supported": "^0.1.0"
+ },
+ "dependencies": {
+ "ansi-styles": {
+ "version": "4.3.0",
+ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
+ "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
+ "dev": true,
+ "requires": {
+ "color-convert": "^2.0.1"
+ }
+ },
+ "chalk": {
+ "version": "4.1.2",
+ "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
+ "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
+ "dev": true,
+ "requires": {
+ "ansi-styles": "^4.1.0",
+ "supports-color": "^7.1.0"
+ }
+ },
+ "color-convert": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
+ "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
+ "dev": true,
+ "requires": {
+ "color-name": "~1.1.4"
+ }
+ },
+ "color-name": {
+ "version": "1.1.4",
+ "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
+ "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
+ "dev": true
+ },
+ "has-flag": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
+ "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
+ "dev": true
+ },
+ "supports-color": {
+ "version": "7.2.0",
+ "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
+ "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
+ "dev": true,
+ "requires": {
+ "has-flag": "^4.0.0"
+ }
+ }
+ }
+ },
+ "log-update": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/log-update/-/log-update-4.0.0.tgz",
+ "integrity": "sha512-9fkkDevMefjg0mmzWFBW8YkFP91OrizzkW3diF7CpG+S2EYdy4+TVfGwz1zeF8x7hCx1ovSPTOE9Ngib74qqUg==",
+ "dev": true,
+ "requires": {
+ "ansi-escapes": "^4.3.0",
+ "cli-cursor": "^3.1.0",
+ "slice-ansi": "^4.0.0",
+ "wrap-ansi": "^6.2.0"
+ },
+ "dependencies": {
+ "ansi-styles": {
+ "version": "4.3.0",
+ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
+ "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
+ "dev": true,
+ "requires": {
+ "color-convert": "^2.0.1"
+ }
+ },
+ "color-convert": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
+ "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
+ "dev": true,
+ "requires": {
+ "color-name": "~1.1.4"
+ }
+ },
+ "color-name": {
+ "version": "1.1.4",
+ "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
+ "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
+ "dev": true
+ },
+ "slice-ansi": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-4.0.0.tgz",
+ "integrity": "sha512-qMCMfhY040cVHT43K9BFygqYbUPFZKHOg7K73mtTWJRb8pyP3fzf4Ixd5SzdEJQ6MRUg/WBnOLxghZtKKurENQ==",
+ "dev": true,
+ "requires": {
+ "ansi-styles": "^4.0.0",
+ "astral-regex": "^2.0.0",
+ "is-fullwidth-code-point": "^3.0.0"
+ }
+ },
+ "wrap-ansi": {
+ "version": "6.2.0",
+ "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz",
+ "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==",
+ "dev": true,
+ "requires": {
+ "ansi-styles": "^4.0.0",
+ "string-width": "^4.1.0",
+ "strip-ansi": "^6.0.0"
+ }
+ }
+ }
+ },
+ "loose-envify": {
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz",
+ "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==",
+ "requires": {
+ "js-tokens": "^3.0.0 || ^4.0.0"
+ }
+ },
+ "lru-cache": {
+ "version": "5.1.1",
+ "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz",
+ "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==",
+ "dev": true,
+ "requires": {
+ "yallist": "^3.0.2"
+ }
+ },
+ "match-sorter": {
+ "version": "6.3.4",
+ "resolved": "https://registry.npmjs.org/match-sorter/-/match-sorter-6.3.4.tgz",
+ "integrity": "sha512-jfZW7cWS5y/1xswZo8VBOdudUiSd9nifYRWphc9M5D/ee4w4AoXLgBEdRbgVaxbMuagBPeUC5y2Hi8DO6o9aDg==",
+ "requires": {
+ "@babel/runtime": "^7.23.8",
+ "remove-accents": "0.5.0"
+ }
+ },
+ "merge-stream": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz",
+ "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==",
+ "dev": true
+ },
+ "merge2": {
+ "version": "1.4.1",
+ "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz",
+ "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==",
+ "dev": true
+ },
+ "micromatch": {
+ "version": "4.0.5",
+ "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz",
+ "integrity": "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==",
+ "dev": true,
+ "requires": {
+ "braces": "^3.0.2",
+ "picomatch": "^2.3.1"
+ }
+ },
+ "mime-db": {
+ "version": "1.52.0",
+ "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz",
+ "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg=="
+ },
+ "mime-types": {
+ "version": "2.1.35",
+ "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz",
+ "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==",
+ "requires": {
+ "mime-db": "1.52.0"
+ }
+ },
+ "mimic-fn": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz",
+ "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==",
+ "dev": true
+ },
+ "minimatch": {
+ "version": "9.0.4",
+ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.4.tgz",
+ "integrity": "sha512-KqWh+VchfxcMNRAJjj2tnsSJdNbHsVgnkBhTNrW7AjVo6OvLtxw8zfT9oLw1JSohlFzJ8jCoTgaoXvJ+kHt6fw==",
+ "dev": true,
+ "requires": {
+ "brace-expansion": "^2.0.1"
+ }
+ },
+ "minimist": {
+ "version": "1.2.8",
+ "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz",
+ "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==",
+ "dev": true
+ },
+ "ms": {
+ "version": "2.1.2",
+ "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
+ "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==",
+ "dev": true
+ },
+ "nanoid": {
+ "version": "3.3.7",
+ "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.7.tgz",
+ "integrity": "sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==",
+ "dev": true
+ },
+ "natural-compare": {
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz",
+ "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==",
+ "dev": true
+ },
+ "node-fetch": {
+ "version": "2.7.0",
+ "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz",
+ "integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==",
+ "requires": {
+ "whatwg-url": "^5.0.0"
+ }
+ },
+ "node-releases": {
+ "version": "2.0.14",
+ "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.14.tgz",
+ "integrity": "sha512-y10wOWt8yZpqXmOgRo77WaHEmhYQYGNA6y421PKsKYWEK8aW+cqAphborZDhqfyKrbZEN92CN1X2KbafY2s7Yw==",
+ "dev": true
+ },
+ "npm-run-path": {
+ "version": "4.0.1",
+ "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz",
+ "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==",
+ "dev": true,
+ "requires": {
+ "path-key": "^3.0.0"
+ }
+ },
+ "object-assign": {
+ "version": "4.1.1",
+ "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
+ "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg=="
+ },
+ "object-inspect": {
+ "version": "1.13.1",
+ "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.1.tgz",
+ "integrity": "sha512-5qoj1RUiKOMsCCNLV1CBiPYE10sziTsnmNxkAI/rZhiD63CF7IqdFGC/XzjWjpSgLf0LxXX3bDFIh0E18f6UhQ==",
+ "dev": true
+ },
+ "once": {
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
+ "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==",
+ "dev": true,
+ "requires": {
+ "wrappy": "1"
+ }
+ },
+ "onetime": {
+ "version": "5.1.2",
+ "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz",
+ "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==",
+ "dev": true,
+ "requires": {
+ "mimic-fn": "^2.1.0"
+ }
+ },
+ "optionator": {
+ "version": "0.9.4",
+ "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz",
+ "integrity": "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==",
+ "dev": true,
+ "requires": {
+ "deep-is": "^0.1.3",
+ "fast-levenshtein": "^2.0.6",
+ "levn": "^0.4.1",
+ "prelude-ls": "^1.2.1",
+ "type-check": "^0.4.0",
+ "word-wrap": "^1.2.5"
+ }
+ },
+ "ospath": {
+ "version": "1.2.2",
+ "resolved": "https://registry.npmjs.org/ospath/-/ospath-1.2.2.tgz",
+ "integrity": "sha512-o6E5qJV5zkAbIDNhGSIlyOhScKXgQrSRMilfph0clDfM0nEnBOlKlH4sWDmG95BW/CvwNz0vmm7dJVtU2KlMiA==",
+ "dev": true
+ },
+ "p-limit": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz",
+ "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==",
+ "dev": true,
+ "requires": {
+ "yocto-queue": "^0.1.0"
+ }
+ },
+ "p-locate": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz",
+ "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==",
+ "dev": true,
+ "requires": {
+ "p-limit": "^3.0.2"
+ }
+ },
+ "p-map": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/p-map/-/p-map-4.0.0.tgz",
+ "integrity": "sha512-/bjOqmgETBYB5BoEeGVea8dmvHb2m9GLy1E9W43yeyfP6QQCZGFNa+XRceJEuDB6zqr+gKpIAmlLebMpykw/MQ==",
+ "dev": true,
+ "requires": {
+ "aggregate-error": "^3.0.0"
+ }
+ },
+ "pako": {
+ "version": "1.0.11",
+ "resolved": "https://registry.npmjs.org/pako/-/pako-1.0.11.tgz",
+ "integrity": "sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw=="
+ },
+ "papaparse": {
+ "version": "5.4.1",
+ "resolved": "https://registry.npmjs.org/papaparse/-/papaparse-5.4.1.tgz",
+ "integrity": "sha512-HipMsgJkZu8br23pW15uvo6sib6wne/4woLZPlFf3rpDyMe9ywEXUsuD7+6K9PRkJlVT51j/sCOYDKGGS3ZJrw=="
+ },
+ "parent-module": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz",
+ "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==",
+ "requires": {
+ "callsites": "^3.0.0"
+ }
+ },
+ "parse-json": {
+ "version": "5.2.0",
+ "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz",
+ "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==",
+ "requires": {
+ "@babel/code-frame": "^7.0.0",
+ "error-ex": "^1.3.1",
+ "json-parse-even-better-errors": "^2.3.0",
+ "lines-and-columns": "^1.1.6"
+ }
+ },
+ "path-exists": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz",
+ "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==",
+ "dev": true
+ },
+ "path-is-absolute": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz",
+ "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==",
+ "dev": true
+ },
+ "path-key": {
+ "version": "3.1.1",
+ "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz",
+ "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==",
+ "dev": true
+ },
+ "path-parse": {
+ "version": "1.0.7",
+ "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz",
+ "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw=="
+ },
+ "path-type": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz",
+ "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw=="
+ },
+ "pend": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/pend/-/pend-1.2.0.tgz",
+ "integrity": "sha512-F3asv42UuXchdzt+xXqfW1OGlVBe+mxa2mqI0pg5yAHZPvFmY3Y6drSf/GQ1A86WgWEN9Kzh/WrgKa6iGcHXLg==",
+ "dev": true
+ },
+ "performance-now": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz",
+ "integrity": "sha512-7EAHlyLHI56VEIdK57uwHdHKIaAGbnXPiw0yWbarQZOKaKpvUIgW0jWRVLiatnM+XXlSwsanIBH/hzGMJulMow==",
+ "dev": true
+ },
+ "picocolors": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz",
+ "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ=="
+ },
+ "picomatch": {
+ "version": "2.3.1",
+ "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz",
+ "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==",
+ "dev": true
+ },
+ "pify": {
+ "version": "2.3.0",
+ "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz",
+ "integrity": "sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==",
+ "dev": true
+ },
+ "postcss": {
+ "version": "8.4.38",
+ "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.38.tgz",
+ "integrity": "sha512-Wglpdk03BSfXkHoQa3b/oulrotAkwrlLDRSOb9D0bN86FdRyE9lppSp33aHNPgBa0JKCoB+drFLZkQoRRYae5A==",
+ "dev": true,
+ "requires": {
+ "nanoid": "^3.3.7",
+ "picocolors": "^1.0.0",
+ "source-map-js": "^1.2.0"
+ }
+ },
+ "prelude-ls": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz",
+ "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==",
+ "dev": true
+ },
+ "prettier": {
+ "version": "3.2.5",
+ "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.2.5.tgz",
+ "integrity": "sha512-3/GWa9aOC0YeD7LUfvOG2NiDyhOWRvt1k+rcKhOuYnMY24iiCphgneUfJDyFXd6rZCAnuLBv6UeAULtrhT/F4A==",
+ "dev": true
+ },
+ "prettier-linter-helpers": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/prettier-linter-helpers/-/prettier-linter-helpers-1.0.0.tgz",
+ "integrity": "sha512-GbK2cP9nraSSUF9N2XwUwqfzlAFlMNYYl+ShE/V+H8a9uNl/oUqB1w2EL54Jh0OlyRSd8RfWYJ3coVS4TROP2w==",
+ "dev": true,
+ "requires": {
+ "fast-diff": "^1.1.2"
+ }
+ },
+ "pretty-bytes": {
+ "version": "5.6.0",
+ "resolved": "https://registry.npmjs.org/pretty-bytes/-/pretty-bytes-5.6.0.tgz",
+ "integrity": "sha512-FFw039TmrBqFK8ma/7OL3sDz/VytdtJr044/QUJtH0wK9lb9jLq9tJyIxUwtQJHwar2BqtiA4iCWSwo9JLkzFg==",
+ "dev": true
+ },
+ "process": {
+ "version": "0.11.10",
+ "resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz",
+ "integrity": "sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A==",
+ "dev": true
+ },
+ "process-nextick-args": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz",
+ "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag=="
+ },
+ "prop-types": {
+ "version": "15.8.1",
+ "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz",
+ "integrity": "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==",
+ "requires": {
+ "loose-envify": "^1.4.0",
+ "object-assign": "^4.1.1",
+ "react-is": "^16.13.1"
+ },
+ "dependencies": {
+ "react-is": {
+ "version": "16.13.1",
+ "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz",
+ "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ=="
+ }
+ }
+ },
+ "proxy-from-env": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz",
+ "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg=="
+ },
+ "psl": {
+ "version": "1.9.0",
+ "resolved": "https://registry.npmjs.org/psl/-/psl-1.9.0.tgz",
+ "integrity": "sha512-E/ZsdU4HLs/68gYzgGTkMicWTLPdAftJLfJFlLUAAKZGkStNU72sZjT66SnMDVOfOWY/YAoiD7Jxa9iHvngcag==",
+ "dev": true
+ },
+ "pump": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz",
+ "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==",
+ "dev": true,
+ "requires": {
+ "end-of-stream": "^1.1.0",
+ "once": "^1.3.1"
+ }
+ },
+ "punycode": {
+ "version": "2.3.1",
+ "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz",
+ "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==",
+ "dev": true
+ },
+ "qs": {
+ "version": "6.10.4",
+ "resolved": "https://registry.npmjs.org/qs/-/qs-6.10.4.tgz",
+ "integrity": "sha512-OQiU+C+Ds5qiH91qh/mg0w+8nwQuLjM4F4M/PbmhDOoYehPh+Fb0bDjtR1sOvy7YKxvj28Y/M0PhP5uVX0kB+g==",
+ "dev": true,
+ "requires": {
+ "side-channel": "^1.0.4"
+ }
+ },
+ "querystringify": {
+ "version": "2.2.0",
+ "resolved": "https://registry.npmjs.org/querystringify/-/querystringify-2.2.0.tgz",
+ "integrity": "sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ==",
+ "dev": true
+ },
+ "queue-microtask": {
+ "version": "1.2.3",
+ "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz",
+ "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==",
+ "dev": true
+ },
+ "react": {
+ "version": "18.3.1",
+ "resolved": "https://registry.npmjs.org/react/-/react-18.3.1.tgz",
+ "integrity": "sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ==",
+ "requires": {
+ "loose-envify": "^1.1.0"
+ }
+ },
+ "react-dom": {
+ "version": "18.3.1",
+ "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.3.1.tgz",
+ "integrity": "sha512-5m4nQKp+rZRb09LNH59GM4BxTh9251/ylbKIbpe7TpGxfJ+9kv6BLkLBXIjjspbgbnIBNqlI23tRnTWT0snUIw==",
+ "requires": {
+ "loose-envify": "^1.1.0",
+ "scheduler": "^0.23.2"
+ }
+ },
+ "react-fast-compare": {
+ "version": "3.2.2",
+ "resolved": "https://registry.npmjs.org/react-fast-compare/-/react-fast-compare-3.2.2.tgz",
+ "integrity": "sha512-nsO+KSNgo1SbJqJEYRE9ERzo7YtYbou/OqjSQKxV7jcKox7+usiUVZOAC+XnDOABXggQTno0Y1CpVnuWEc1boQ=="
+ },
+ "react-helmet": {
+ "version": "6.1.0",
+ "resolved": "https://registry.npmjs.org/react-helmet/-/react-helmet-6.1.0.tgz",
+ "integrity": "sha512-4uMzEY9nlDlgxr61NL3XbKRy1hEkXmKNXhjbAIOVw5vcFrsdYbH2FEwcNyWvWinl103nXgzYNlns9ca+8kFiWw==",
+ "requires": {
+ "object-assign": "^4.1.1",
+ "prop-types": "^15.7.2",
+ "react-fast-compare": "^3.1.1",
+ "react-side-effect": "^2.1.0"
+ }
+ },
+ "react-helmet-async": {
+ "version": "2.0.4",
+ "resolved": "https://registry.npmjs.org/react-helmet-async/-/react-helmet-async-2.0.4.tgz",
+ "integrity": "sha512-yxjQMWposw+akRfvpl5+8xejl4JtUlHnEBcji6u8/e6oc7ozT+P9PNTWMhCbz2y9tc5zPegw2BvKjQA+NwdEjQ==",
+ "requires": {
+ "invariant": "^2.2.4",
+ "react-fast-compare": "^3.2.2",
+ "shallowequal": "^1.1.0"
+ }
+ },
+ "react-i18next": {
+ "version": "14.1.1",
+ "resolved": "https://registry.npmjs.org/react-i18next/-/react-i18next-14.1.1.tgz",
+ "integrity": "sha512-QSiKw+ihzJ/CIeIYWrarCmXJUySHDwQr5y8uaNIkbxoGRm/5DukkxZs+RPla79IKyyDPzC/DRlgQCABHtrQuQQ==",
+ "requires": {
+ "@babel/runtime": "^7.23.9",
+ "html-parse-stringify": "^3.0.1"
+ }
+ },
+ "react-is": {
+ "version": "18.3.1",
+ "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz",
+ "integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg=="
+ },
+ "react-refresh": {
+ "version": "0.14.2",
+ "resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.14.2.tgz",
+ "integrity": "sha512-jCvmsr+1IUSMUyzOkRcvnVbX3ZYC6g9TDrDbFuFmRDq7PD4yaGbLKNQL6k2jnArV8hjYxh7hVhAZB6s9HDGpZA==",
+ "dev": true
+ },
+ "react-router": {
+ "version": "6.23.0",
+ "resolved": "https://registry.npmjs.org/react-router/-/react-router-6.23.0.tgz",
+ "integrity": "sha512-wPMZ8S2TuPadH0sF5irFGjkNLIcRvOSaEe7v+JER8508dyJumm6XZB1u5kztlX0RVq6AzRVndzqcUh6sFIauzA==",
+ "requires": {
+ "@remix-run/router": "1.16.0"
+ }
+ },
+ "react-router-dom": {
+ "version": "6.23.0",
+ "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-6.23.0.tgz",
+ "integrity": "sha512-Q9YaSYvubwgbal2c9DJKfx6hTNoBp3iJDsl+Duva/DwxoJH+OTXkxGpql4iUK2sla/8z4RpjAm6EWx1qUDuopQ==",
+ "requires": {
+ "@remix-run/router": "1.16.0",
+ "react-router": "6.23.0"
+ }
+ },
+ "react-side-effect": {
+ "version": "2.1.2",
+ "resolved": "https://registry.npmjs.org/react-side-effect/-/react-side-effect-2.1.2.tgz",
+ "integrity": "sha512-PVjOcvVOyIILrYoyGEpDN3vmYNLdy1CajSFNt4TDsVQC5KpTijDvWVoR+/7Rz2xT978D8/ZtFceXxzsPwZEDvw==",
+ "requires": {}
+ },
+ "react-transition-group": {
+ "version": "4.4.5",
+ "resolved": "https://registry.npmjs.org/react-transition-group/-/react-transition-group-4.4.5.tgz",
+ "integrity": "sha512-pZcd1MCJoiKiBR2NRxeCRg13uCXbydPnmB4EOeRrY7480qNWO8IIgQG6zlDkm6uRMsURXPuKq0GWtiM59a5Q6g==",
+ "requires": {
+ "@babel/runtime": "^7.5.5",
+ "dom-helpers": "^5.0.1",
+ "loose-envify": "^1.4.0",
+ "prop-types": "^15.6.2"
+ }
+ },
+ "readable-stream": {
+ "version": "2.3.8",
+ "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz",
+ "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==",
+ "requires": {
+ "core-util-is": "~1.0.0",
+ "inherits": "~2.0.3",
+ "isarray": "~1.0.0",
+ "process-nextick-args": "~2.0.0",
+ "safe-buffer": "~5.1.1",
+ "string_decoder": "~1.1.1",
+ "util-deprecate": "~1.0.1"
+ }
+ },
+ "regenerator-runtime": {
+ "version": "0.14.1",
+ "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.14.1.tgz",
+ "integrity": "sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw=="
+ },
+ "remove-accents": {
+ "version": "0.5.0",
+ "resolved": "https://registry.npmjs.org/remove-accents/-/remove-accents-0.5.0.tgz",
+ "integrity": "sha512-8g3/Otx1eJaVD12e31UbJj1YzdtVvzH85HV7t+9MJYk/u3XmkOUJ5Ys9wQrf9PCPK8+xn4ymzqYCiZl6QWKn+A=="
+ },
+ "request-progress": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/request-progress/-/request-progress-3.0.0.tgz",
+ "integrity": "sha512-MnWzEHHaxHO2iWiQuHrUPBi/1WeBf5PkxQqNyNvLl9VAYSdXkP8tQ3pBSeCPD+yw0v0Aq1zosWLz0BdeXpWwZg==",
+ "dev": true,
+ "requires": {
+ "throttleit": "^1.0.0"
+ }
+ },
+ "requires-port": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz",
+ "integrity": "sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==",
+ "dev": true
+ },
+ "resolve": {
+ "version": "1.22.8",
+ "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.8.tgz",
+ "integrity": "sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==",
+ "requires": {
+ "is-core-module": "^2.13.0",
+ "path-parse": "^1.0.7",
+ "supports-preserve-symlinks-flag": "^1.0.0"
+ }
+ },
+ "resolve-from": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz",
+ "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g=="
+ },
+ "restore-cursor": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-3.1.0.tgz",
+ "integrity": "sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==",
+ "dev": true,
+ "requires": {
+ "onetime": "^5.1.0",
+ "signal-exit": "^3.0.2"
+ }
+ },
+ "reusify": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz",
+ "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==",
+ "dev": true
+ },
+ "rfdc": {
+ "version": "1.3.1",
+ "resolved": "https://registry.npmjs.org/rfdc/-/rfdc-1.3.1.tgz",
+ "integrity": "sha512-r5a3l5HzYlIC68TpmYKlxWjmOP6wiPJ1vWv2HeLhNsRZMrCkxeqxiHlQ21oXmQ4F3SiryXBHhAD7JZqvOJjFmg==",
+ "dev": true
+ },
+ "rimraf": {
+ "version": "3.0.2",
+ "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz",
+ "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==",
+ "dev": true,
+ "requires": {
+ "glob": "^7.1.3"
+ }
+ },
+ "rollup": {
+ "version": "4.17.2",
+ "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.17.2.tgz",
+ "integrity": "sha512-/9ClTJPByC0U4zNLowV1tMBe8yMEAxewtR3cUNX5BoEpGH3dQEWpJLr6CLp0fPdYRF/fzVOgvDb1zXuakwF5kQ==",
+ "dev": true,
+ "requires": {
+ "@rollup/rollup-android-arm-eabi": "4.17.2",
+ "@rollup/rollup-android-arm64": "4.17.2",
+ "@rollup/rollup-darwin-arm64": "4.17.2",
+ "@rollup/rollup-darwin-x64": "4.17.2",
+ "@rollup/rollup-linux-arm-gnueabihf": "4.17.2",
+ "@rollup/rollup-linux-arm-musleabihf": "4.17.2",
+ "@rollup/rollup-linux-arm64-gnu": "4.17.2",
+ "@rollup/rollup-linux-arm64-musl": "4.17.2",
+ "@rollup/rollup-linux-powerpc64le-gnu": "4.17.2",
+ "@rollup/rollup-linux-riscv64-gnu": "4.17.2",
+ "@rollup/rollup-linux-s390x-gnu": "4.17.2",
+ "@rollup/rollup-linux-x64-gnu": "4.17.2",
+ "@rollup/rollup-linux-x64-musl": "4.17.2",
+ "@rollup/rollup-win32-arm64-msvc": "4.17.2",
+ "@rollup/rollup-win32-ia32-msvc": "4.17.2",
+ "@rollup/rollup-win32-x64-msvc": "4.17.2",
+ "@types/estree": "1.0.5",
+ "fsevents": "~2.3.2"
+ }
+ },
+ "run-parallel": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz",
+ "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==",
+ "dev": true,
+ "requires": {
+ "queue-microtask": "^1.2.2"
+ }
+ },
+ "rxjs": {
+ "version": "7.8.1",
+ "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.1.tgz",
+ "integrity": "sha512-AA3TVj+0A2iuIoQkWEK/tqFjBq2j+6PO6Y0zJcvzLAFhEFIO3HL0vls9hWLncZbAAbK0mar7oZ4V079I/qPMxg==",
+ "dev": true,
+ "requires": {
+ "tslib": "^2.1.0"
+ }
+ },
+ "safe-buffer": {
+ "version": "5.1.2",
+ "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
+ "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g=="
+ },
+ "safer-buffer": {
+ "version": "2.1.2",
+ "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz",
+ "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==",
+ "dev": true
+ },
+ "scheduler": {
+ "version": "0.23.2",
+ "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.23.2.tgz",
+ "integrity": "sha512-UOShsPwz7NrMUqhR6t0hWjFduvOzbtv7toDH1/hIrfRNIDBnnBWd0CwJTGvTpngVlmwGCdP9/Zl/tVrDqcuYzQ==",
+ "requires": {
+ "loose-envify": "^1.1.0"
+ }
+ },
+ "semver": {
+ "version": "7.6.0",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.0.tgz",
+ "integrity": "sha512-EnwXhrlwXMk9gKu5/flx5sv/an57AkRplG3hTK68W7FRDN+k+OWBj65M7719OkA82XLBxrcX0KSHj+X5COhOVg==",
+ "dev": true,
+ "requires": {
+ "lru-cache": "^6.0.0"
+ },
+ "dependencies": {
+ "lru-cache": {
+ "version": "6.0.0",
+ "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz",
+ "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==",
+ "dev": true,
+ "requires": {
+ "yallist": "^4.0.0"
+ }
+ },
+ "yallist": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz",
+ "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==",
+ "dev": true
+ }
+ }
+ },
+ "set-function-length": {
+ "version": "1.2.2",
+ "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz",
+ "integrity": "sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==",
+ "dev": true,
+ "requires": {
+ "define-data-property": "^1.1.4",
+ "es-errors": "^1.3.0",
+ "function-bind": "^1.1.2",
+ "get-intrinsic": "^1.2.4",
+ "gopd": "^1.0.1",
+ "has-property-descriptors": "^1.0.2"
+ }
+ },
+ "setimmediate": {
+ "version": "1.0.5",
+ "resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz",
+ "integrity": "sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA=="
+ },
+ "shallowequal": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/shallowequal/-/shallowequal-1.1.0.tgz",
+ "integrity": "sha512-y0m1JoUZSlPAjXVtPPW70aZWfIL/dSP7AFkRnniLCrK/8MDKog3TySTBmckD+RObVxH0v4Tox67+F14PdED2oQ=="
+ },
+ "shebang-command": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz",
+ "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==",
+ "dev": true,
+ "requires": {
+ "shebang-regex": "^3.0.0"
+ }
+ },
+ "shebang-regex": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz",
+ "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==",
+ "dev": true
+ },
+ "side-channel": {
+ "version": "1.0.6",
+ "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.6.tgz",
+ "integrity": "sha512-fDW/EZ6Q9RiO8eFG8Hj+7u/oW+XrPTIChwCOM2+th2A6OblDtYYIpve9m+KvI9Z4C9qSEXlaGR6bTEYHReuglA==",
+ "dev": true,
+ "requires": {
+ "call-bind": "^1.0.7",
+ "es-errors": "^1.3.0",
+ "get-intrinsic": "^1.2.4",
+ "object-inspect": "^1.13.1"
+ }
+ },
+ "signal-exit": {
+ "version": "3.0.7",
+ "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz",
+ "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==",
+ "dev": true
+ },
+ "slash": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz",
+ "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==",
+ "dev": true
+ },
+ "slice-ansi": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-3.0.0.tgz",
+ "integrity": "sha512-pSyv7bSTC7ig9Dcgbw9AuRNUb5k5V6oDudjZoMBSr13qpLBG7tB+zgCkARjq7xIUgdz5P1Qe8u+rSGdouOOIyQ==",
+ "dev": true,
+ "requires": {
+ "ansi-styles": "^4.0.0",
+ "astral-regex": "^2.0.0",
+ "is-fullwidth-code-point": "^3.0.0"
+ },
+ "dependencies": {
+ "ansi-styles": {
+ "version": "4.3.0",
+ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
+ "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
+ "dev": true,
+ "requires": {
+ "color-convert": "^2.0.1"
+ }
+ },
+ "color-convert": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
+ "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
+ "dev": true,
+ "requires": {
+ "color-name": "~1.1.4"
+ }
+ },
+ "color-name": {
+ "version": "1.1.4",
+ "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
+ "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
+ "dev": true
+ }
+ }
+ },
+ "source-map": {
+ "version": "0.5.7",
+ "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz",
+ "integrity": "sha512-LbrmJOMUSdEVxIKvdcJzQC+nQhe8FUZQTXQy6+I75skNgn3OoQ0DZA8YnFa7gp8tqtL3KPf1kmo0R5DoApeSGQ=="
+ },
+ "source-map-js": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.0.tgz",
+ "integrity": "sha512-itJW8lvSA0TXEphiRoawsCksnlf8SyvmFzIhltqAHluXd88pkCd+cXJVHTDwdCr0IzwptSm035IHQktUu1QUMg==",
+ "dev": true
+ },
+ "sshpk": {
+ "version": "1.18.0",
+ "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.18.0.tgz",
+ "integrity": "sha512-2p2KJZTSqQ/I3+HX42EpYOa2l3f8Erv8MWKsy2I9uf4wA7yFIkXRffYdsx86y6z4vHtV8u7g+pPlr8/4ouAxsQ==",
+ "dev": true,
+ "requires": {
+ "asn1": "~0.2.3",
+ "assert-plus": "^1.0.0",
+ "bcrypt-pbkdf": "^1.0.0",
+ "dashdash": "^1.12.0",
+ "ecc-jsbn": "~0.1.1",
+ "getpass": "^0.1.1",
+ "jsbn": "~0.1.0",
+ "safer-buffer": "^2.0.2",
+ "tweetnacl": "~0.14.0"
+ }
+ },
+ "string_decoder": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz",
+ "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==",
+ "requires": {
+ "safe-buffer": "~5.1.0"
+ }
+ },
+ "string-width": {
+ "version": "4.2.3",
+ "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz",
+ "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
+ "dev": true,
+ "requires": {
+ "emoji-regex": "^8.0.0",
+ "is-fullwidth-code-point": "^3.0.0",
+ "strip-ansi": "^6.0.1"
+ }
+ },
+ "strip-ansi": {
+ "version": "6.0.1",
+ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
+ "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
+ "dev": true,
+ "requires": {
+ "ansi-regex": "^5.0.1"
+ }
+ },
+ "strip-final-newline": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz",
+ "integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==",
+ "dev": true
+ },
+ "strip-json-comments": {
+ "version": "3.1.1",
+ "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz",
+ "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==",
+ "dev": true
+ },
+ "stylis": {
+ "version": "4.2.0",
+ "resolved": "https://registry.npmjs.org/stylis/-/stylis-4.2.0.tgz",
+ "integrity": "sha512-Orov6g6BB1sDfYgzWfTHDOxamtX1bE/zo104Dh9e6fqJ3PooipYyfJ0pUmrZO2wAvO8YbEyeFrkV91XTsGMSrw=="
+ },
+ "supports-color": {
+ "version": "5.5.0",
+ "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz",
+ "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==",
+ "requires": {
+ "has-flag": "^3.0.0"
+ }
+ },
+ "supports-preserve-symlinks-flag": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz",
+ "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w=="
+ },
+ "synckit": {
+ "version": "0.8.8",
+ "resolved": "https://registry.npmjs.org/synckit/-/synckit-0.8.8.tgz",
+ "integrity": "sha512-HwOKAP7Wc5aRGYdKH+dw0PRRpbO841v2DENBtjnR5HFWoiNByAl7vrx3p0G/rCyYXQsrxqtX48TImFtPcIHSpQ==",
+ "dev": true,
+ "requires": {
+ "@pkgr/core": "^0.1.0",
+ "tslib": "^2.6.2"
+ }
+ },
+ "text-table": {
+ "version": "0.2.0",
+ "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz",
+ "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==",
+ "dev": true
+ },
+ "throttleit": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/throttleit/-/throttleit-1.0.1.tgz",
+ "integrity": "sha512-vDZpf9Chs9mAdfY046mcPt8fg5QSZr37hEH4TXYBnDF+izxgrbRGUAAaBvIk/fJm9aOFCGFd1EsNg5AZCbnQCQ==",
+ "dev": true
+ },
+ "through": {
+ "version": "2.3.8",
+ "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz",
+ "integrity": "sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==",
+ "dev": true
+ },
+ "tmp": {
+ "version": "0.2.3",
+ "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.2.3.tgz",
+ "integrity": "sha512-nZD7m9iCPC5g0pYmcaxogYKggSfLsdxl8of3Q/oIbqCqLLIO9IAF0GWjX1z9NZRHPiXv8Wex4yDCaZsgEw0Y8w==",
+ "dev": true
+ },
+ "to-fast-properties": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz",
+ "integrity": "sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog=="
+ },
+ "to-regex-range": {
+ "version": "5.0.1",
+ "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz",
+ "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==",
+ "dev": true,
+ "requires": {
+ "is-number": "^7.0.0"
+ }
+ },
+ "tough-cookie": {
+ "version": "4.1.4",
+ "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-4.1.4.tgz",
+ "integrity": "sha512-Loo5UUvLD9ScZ6jh8beX1T6sO1w2/MpCRpEP7V280GKMVUQ0Jzar2U3UJPsrdbziLEMMhu3Ujnq//rhiFuIeag==",
+ "dev": true,
+ "requires": {
+ "psl": "^1.1.33",
+ "punycode": "^2.1.1",
+ "universalify": "^0.2.0",
+ "url-parse": "^1.5.3"
+ },
+ "dependencies": {
+ "universalify": {
+ "version": "0.2.0",
+ "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.2.0.tgz",
+ "integrity": "sha512-CJ1QgKmNg3CwvAv/kOFmtnEN05f0D/cn9QntgNOQlQF9dgvVTHj3t+8JPdjqawCHk7V/KA+fbUqzZ9XWhcqPUg==",
+ "dev": true
+ }
+ }
+ },
+ "tr46": {
+ "version": "0.0.3",
+ "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz",
+ "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw=="
+ },
+ "ts-api-utils": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-1.3.0.tgz",
+ "integrity": "sha512-UQMIo7pb8WRomKR1/+MFVLTroIvDVtMX3K6OUir8ynLyzB8Jeriont2bTAtmNPa1ekAgN7YPDyf6V+ygrdU+eQ==",
+ "dev": true,
+ "requires": {}
+ },
+ "tslib": {
+ "version": "2.6.2",
+ "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz",
+ "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==",
+ "dev": true
+ },
+ "tunnel-agent": {
+ "version": "0.6.0",
+ "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz",
+ "integrity": "sha512-McnNiV1l8RYeY8tBgEpuodCC1mLUdbSN+CYBL7kJsJNInOP8UjDDEwdk6Mw60vdLLrr5NHKZhMAOSrR2NZuQ+w==",
+ "dev": true,
+ "requires": {
+ "safe-buffer": "^5.0.1"
+ }
+ },
+ "tweetnacl": {
+ "version": "0.14.5",
+ "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz",
+ "integrity": "sha512-KXXFFdAbFXY4geFIwoyNK+f5Z1b7swfXABfL7HXCmoIWMKU3dmS26672A4EeQtDzLKy7SXmfBu51JolvEKwtGA==",
+ "dev": true
+ },
+ "type-check": {
+ "version": "0.4.0",
+ "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz",
+ "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==",
+ "dev": true,
+ "requires": {
+ "prelude-ls": "^1.2.1"
+ }
+ },
+ "type-fest": {
+ "version": "0.20.2",
+ "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz",
+ "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==",
+ "dev": true
+ },
+ "typescript": {
+ "version": "5.4.5",
+ "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.4.5.tgz",
+ "integrity": "sha512-vcI4UpRgg81oIRUFwR0WSIHKt11nJ7SAVlYNIu+QpqeyXP+gpQJy/Z4+F0aGxSE4MqwjyXvW/TzgkLAx2AGHwQ==",
+ "dev": true
+ },
+ "undici-types": {
+ "version": "5.26.5",
+ "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz",
+ "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==",
+ "dev": true
+ },
+ "universalify": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz",
+ "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==",
+ "dev": true
+ },
+ "untildify": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/untildify/-/untildify-4.0.0.tgz",
+ "integrity": "sha512-KK8xQ1mkzZeg9inewmFVDNkg3l5LUhoq9kN6iWYB/CC9YMG8HA+c1Q8HwDe6dEX7kErrEVNVBO3fWsVq5iDgtw==",
+ "dev": true
+ },
+ "update-browserslist-db": {
+ "version": "1.0.15",
+ "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.15.tgz",
+ "integrity": "sha512-K9HWH62x3/EalU1U6sjSZiylm9C8tgq2mSvshZpqc7QE69RaA2qjhkW2HlNA0tFpEbtyFz7HTqbSdN4MSwUodA==",
+ "dev": true,
+ "requires": {
+ "escalade": "^3.1.2",
+ "picocolors": "^1.0.0"
+ }
+ },
+ "uri-js": {
+ "version": "4.4.1",
+ "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz",
+ "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==",
+ "dev": true,
+ "requires": {
+ "punycode": "^2.1.0"
+ }
+ },
+ "url-parse": {
+ "version": "1.5.10",
+ "resolved": "https://registry.npmjs.org/url-parse/-/url-parse-1.5.10.tgz",
+ "integrity": "sha512-WypcfiRhfeUP9vvF0j6rw0J3hrWrw6iZv3+22h6iRMJ/8z1Tj6XfLP4DsUix5MhMPnXpiHDoKyoZ/bdCkwBCiQ==",
+ "dev": true,
+ "requires": {
+ "querystringify": "^2.1.1",
+ "requires-port": "^1.0.0"
+ }
+ },
+ "util-deprecate": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
+ "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw=="
+ },
+ "uuid": {
+ "version": "8.3.2",
+ "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz",
+ "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==",
+ "dev": true
+ },
+ "verror": {
+ "version": "1.10.0",
+ "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz",
+ "integrity": "sha512-ZZKSmDAEFOijERBLkmYfJ+vmk3w+7hOLYDNkRCuRuMJGEmqYNCNLyBBFwWKVMhfwaEF3WOd0Zlw86U/WC/+nYw==",
+ "dev": true,
+ "requires": {
+ "assert-plus": "^1.0.0",
+ "core-util-is": "1.0.2",
+ "extsprintf": "^1.2.0"
+ },
+ "dependencies": {
+ "core-util-is": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz",
+ "integrity": "sha512-3lqz5YjWTYnW6dlDa5TLaTCcShfar1e40rmcJVwCBJC6mWlFuj0eCHIElmG1g5kyuJ/GD+8Wn4FFCcz4gJPfaQ==",
+ "dev": true
+ }
+ }
+ },
+ "vite": {
+ "version": "5.2.11",
+ "resolved": "https://registry.npmjs.org/vite/-/vite-5.2.11.tgz",
+ "integrity": "sha512-HndV31LWW05i1BLPMUCE1B9E9GFbOu1MbenhS58FuK6owSO5qHm7GiCotrNY1YE5rMeQSFBGmT5ZaLEjFizgiQ==",
+ "dev": true,
+ "requires": {
+ "esbuild": "^0.20.1",
+ "fsevents": "~2.3.3",
+ "postcss": "^8.4.38",
+ "rollup": "^4.13.0"
+ }
+ },
+ "void-elements": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/void-elements/-/void-elements-3.1.0.tgz",
+ "integrity": "sha512-Dhxzh5HZuiHQhbvTW9AMetFfBHDMYpo23Uo9btPXgdYP+3T5S+p+jgNy7spra+veYhBP2dCSgxR/i2Y02h5/6w=="
+ },
+ "webidl-conversions": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz",
+ "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ=="
+ },
+ "whatwg-url": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz",
+ "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==",
+ "requires": {
+ "tr46": "~0.0.3",
+ "webidl-conversions": "^3.0.0"
+ }
+ },
+ "which": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz",
+ "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==",
+ "dev": true,
+ "requires": {
+ "isexe": "^2.0.0"
+ }
+ },
+ "word-wrap": {
+ "version": "1.2.5",
+ "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz",
+ "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==",
+ "dev": true
+ },
+ "wrap-ansi": {
+ "version": "7.0.0",
+ "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz",
+ "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==",
+ "dev": true,
+ "requires": {
+ "ansi-styles": "^4.0.0",
+ "string-width": "^4.1.0",
+ "strip-ansi": "^6.0.0"
+ },
+ "dependencies": {
+ "ansi-styles": {
+ "version": "4.3.0",
+ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
+ "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
+ "dev": true,
+ "requires": {
+ "color-convert": "^2.0.1"
+ }
+ },
+ "color-convert": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
+ "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
+ "dev": true,
+ "requires": {
+ "color-name": "~1.1.4"
+ }
+ },
+ "color-name": {
+ "version": "1.1.4",
+ "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
+ "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
+ "dev": true
+ }
+ }
+ },
+ "wrappy": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
+ "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==",
+ "dev": true
+ },
+ "yallist": {
+ "version": "3.1.1",
+ "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz",
+ "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==",
+ "dev": true
+ },
+ "yaml": {
+ "version": "1.10.2",
+ "resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.2.tgz",
+ "integrity": "sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg=="
+ },
+ "yauzl": {
+ "version": "2.10.0",
+ "resolved": "https://registry.npmjs.org/yauzl/-/yauzl-2.10.0.tgz",
+ "integrity": "sha512-p4a9I6X6nu6IhoGmBqAcbJy1mlC4j27vEPZX9F4L4/vZT3Lyq1VkFHw/V/PUcB9Buo+DG3iHkT0x3Qya58zc3g==",
+ "dev": true,
+ "requires": {
+ "buffer-crc32": "~0.2.3",
+ "fd-slicer": "~1.1.0"
+ }
+ },
+ "yocto-queue": {
+ "version": "0.1.0",
+ "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz",
+ "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==",
+ "dev": true
+ }
+ }
+}
diff --git a/frontend/frontend/package-lock.json.orig b/frontend/frontend/package-lock.json.orig
deleted file mode 100644
index c033f4c0f..000000000
--- a/frontend/frontend/package-lock.json.orig
+++ /dev/null
@@ -1,4382 +0,0 @@
-{
- "name": "frontend",
- "version": "0.0.0",
- "lockfileVersion": 3,
- "requires": true,
- "packages": {
- "": {
- "name": "frontend",
- "version": "0.0.0",
- "dependencies": {
- "@azure/msal-browser": "^3.11.0",
- "@azure/msal-react": "^2.0.13",
- "@emotion/react": "^11.11.4",
- "@emotion/styled": "^11.11.0",
- "@fontsource/roboto": "^5.0.8",
- "@mui/icons-material": "^5.15.11",
- "@mui/material": "^5.15.12",
- "@mui/x-date-pickers": "^6.19.6",
- "axios": "^1.6.7",
- "dayjs": "^1.11.10",
- "i18next": "^23.10.0",
- "i18next-browser-languagedetector": "^7.2.0",
- "i18next-http-backend": "^2.5.0",
- "localforage": "^1.10.0",
- "match-sorter": "^6.3.4",
- "react": "^18.2.0",
- "react-dom": "^18.2.0",
- "react-helmet": "^6.1.0",
- "react-helmet-async": "^2.0.4",
- "react-i18next": "^14.0.5",
- "react-router-dom": "^6.22.2"
- },
- "devDependencies": {
- "@types/react": "^18.2.56",
- "@types/react-dom": "^18.2.19",
- "@types/react-helmet": "^6.1.11",
- "@typescript-eslint/eslint-plugin": "^7.0.2",
- "@typescript-eslint/parser": "^7.0.2",
- "@vitejs/plugin-react": "^4.2.1",
- "eslint": "^8.56.0",
- "eslint-plugin-react-hooks": "^4.6.0",
- "eslint-plugin-react-refresh": "^0.4.5",
- "typescript": "^5.2.2",
- "vite": "^5.1.7"
- }
- },
- "node_modules/@aashutoshrathi/word-wrap": {
- "version": "1.2.6",
- "resolved": "https://registry.npmjs.org/@aashutoshrathi/word-wrap/-/word-wrap-1.2.6.tgz",
- "integrity": "sha512-1Yjs2SvM8TflER/OD3cOjhWWOZb58A2t7wpE2S9XfBYTiIl+XFhQG2bjy4Pu1I+EAlCNUzRDYDdFwFYUKvXcIA==",
- "dev": true,
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/@ampproject/remapping": {
- "version": "2.3.0",
- "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.3.0.tgz",
- "integrity": "sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==",
- "dev": true,
- "dependencies": {
- "@jridgewell/gen-mapping": "^0.3.5",
- "@jridgewell/trace-mapping": "^0.3.24"
- },
- "engines": {
- "node": ">=6.0.0"
- }
- },
- "node_modules/@azure/msal-browser": {
- "version": "3.11.0",
- "resolved": "https://registry.npmjs.org/@azure/msal-browser/-/msal-browser-3.11.0.tgz",
- "integrity": "sha512-Xc0g1gdB2gdscPeuUGKmlGMgP1L/AWDeuxaToWkeautPdZqKvzeE82ggqLMctKZ0yq6e7F1XfGhUDCcUo7Db9w==",
- "dependencies": {
- "@azure/msal-common": "14.8.0"
- },
- "engines": {
- "node": ">=0.8.0"
- }
- },
- "node_modules/@azure/msal-common": {
- "version": "14.8.0",
- "resolved": "https://registry.npmjs.org/@azure/msal-common/-/msal-common-14.8.0.tgz",
- "integrity": "sha512-FIghuAzpgmc5ZAW2rCTAHKdhGcCRqg/UyroidTgGgSRrG1gOsEbUTW+7lmEFTz84ttCv5RnjOAUUi/SQjUTw0w==",
- "engines": {
- "node": ">=0.8.0"
- }
- },
- "node_modules/@azure/msal-react": {
- "version": "2.0.13",
- "resolved": "https://registry.npmjs.org/@azure/msal-react/-/msal-react-2.0.13.tgz",
- "integrity": "sha512-M+xmk6Avkrt/Gmxr9TusrGHHcMTr3aqLVm1S9l/mRAvSRlpuvnHE2gCFKSjWP9G3kDoiPPLADXj7Q+r/3VMa6A==",
- "engines": {
- "node": ">=10"
- },
- "peerDependencies": {
- "@azure/msal-browser": "^3.11.0",
- "react": "^16.8.0 || ^17 || ^18"
- }
- },
- "node_modules/@babel/code-frame": {
- "version": "7.23.5",
- "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.23.5.tgz",
- "integrity": "sha512-CgH3s1a96LipHCmSUmYFPwY7MNx8C3avkq7i4Wl3cfa662ldtUe4VM1TPXX70pfmrlWTb6jLqTYrZyT2ZTJBgA==",
- "dependencies": {
- "@babel/highlight": "^7.23.4",
- "chalk": "^2.4.2"
- },
- "engines": {
- "node": ">=6.9.0"
- }
- },
- "node_modules/@babel/compat-data": {
- "version": "7.23.5",
- "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.23.5.tgz",
- "integrity": "sha512-uU27kfDRlhfKl+w1U6vp16IuvSLtjAxdArVXPa9BvLkrr7CYIsxH5adpHObeAGY/41+syctUWOZ140a2Rvkgjw==",
- "dev": true,
- "engines": {
- "node": ">=6.9.0"
- }
- },
- "node_modules/@babel/core": {
- "version": "7.24.0",
- "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.24.0.tgz",
- "integrity": "sha512-fQfkg0Gjkza3nf0c7/w6Xf34BW4YvzNfACRLmmb7XRLa6XHdR+K9AlJlxneFfWYf6uhOzuzZVTjF/8KfndZANw==",
- "dev": true,
- "dependencies": {
- "@ampproject/remapping": "^2.2.0",
- "@babel/code-frame": "^7.23.5",
- "@babel/generator": "^7.23.6",
- "@babel/helper-compilation-targets": "^7.23.6",
- "@babel/helper-module-transforms": "^7.23.3",
- "@babel/helpers": "^7.24.0",
- "@babel/parser": "^7.24.0",
- "@babel/template": "^7.24.0",
- "@babel/traverse": "^7.24.0",
- "@babel/types": "^7.24.0",
- "convert-source-map": "^2.0.0",
- "debug": "^4.1.0",
- "gensync": "^1.0.0-beta.2",
- "json5": "^2.2.3",
- "semver": "^6.3.1"
- },
- "engines": {
- "node": ">=6.9.0"
- },
- "funding": {
- "type": "opencollective",
- "url": "https://opencollective.com/babel"
- }
- },
- "node_modules/@babel/core/node_modules/convert-source-map": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz",
- "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==",
- "dev": true
- },
- "node_modules/@babel/core/node_modules/semver": {
- "version": "6.3.1",
- "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz",
- "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==",
- "dev": true,
- "bin": {
- "semver": "bin/semver.js"
- }
- },
- "node_modules/@babel/generator": {
- "version": "7.23.6",
- "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.23.6.tgz",
- "integrity": "sha512-qrSfCYxYQB5owCmGLbl8XRpX1ytXlpueOb0N0UmQwA073KZxejgQTzAmJezxvpwQD9uGtK2shHdi55QT+MbjIw==",
- "dev": true,
- "dependencies": {
- "@babel/types": "^7.23.6",
- "@jridgewell/gen-mapping": "^0.3.2",
- "@jridgewell/trace-mapping": "^0.3.17",
- "jsesc": "^2.5.1"
- },
- "engines": {
- "node": ">=6.9.0"
- }
- },
- "node_modules/@babel/helper-compilation-targets": {
- "version": "7.23.6",
- "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.23.6.tgz",
- "integrity": "sha512-9JB548GZoQVmzrFgp8o7KxdgkTGm6xs9DW0o/Pim72UDjzr5ObUQ6ZzYPqA+g9OTS2bBQoctLJrky0RDCAWRgQ==",
- "dev": true,
- "dependencies": {
- "@babel/compat-data": "^7.23.5",
- "@babel/helper-validator-option": "^7.23.5",
- "browserslist": "^4.22.2",
- "lru-cache": "^5.1.1",
- "semver": "^6.3.1"
- },
- "engines": {
- "node": ">=6.9.0"
- }
- },
- "node_modules/@babel/helper-compilation-targets/node_modules/semver": {
- "version": "6.3.1",
- "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz",
- "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==",
- "dev": true,
- "bin": {
- "semver": "bin/semver.js"
- }
- },
- "node_modules/@babel/helper-environment-visitor": {
- "version": "7.22.20",
- "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.22.20.tgz",
- "integrity": "sha512-zfedSIzFhat/gFhWfHtgWvlec0nqB9YEIVrpuwjruLlXfUSnA8cJB0miHKwqDnQ7d32aKo2xt88/xZptwxbfhA==",
- "dev": true,
- "engines": {
- "node": ">=6.9.0"
- }
- },
- "node_modules/@babel/helper-function-name": {
- "version": "7.23.0",
- "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.23.0.tgz",
- "integrity": "sha512-OErEqsrxjZTJciZ4Oo+eoZqeW9UIiOcuYKRJA4ZAgV9myA+pOXhhmpfNCKjEH/auVfEYVFJ6y1Tc4r0eIApqiw==",
- "dev": true,
- "dependencies": {
- "@babel/template": "^7.22.15",
- "@babel/types": "^7.23.0"
- },
- "engines": {
- "node": ">=6.9.0"
- }
- },
- "node_modules/@babel/helper-hoist-variables": {
- "version": "7.22.5",
- "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.22.5.tgz",
- "integrity": "sha512-wGjk9QZVzvknA6yKIUURb8zY3grXCcOZt+/7Wcy8O2uctxhplmUPkOdlgoNhmdVee2c92JXbf1xpMtVNbfoxRw==",
- "dev": true,
- "dependencies": {
- "@babel/types": "^7.22.5"
- },
- "engines": {
- "node": ">=6.9.0"
- }
- },
- "node_modules/@babel/helper-module-imports": {
- "version": "7.22.15",
- "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.22.15.tgz",
- "integrity": "sha512-0pYVBnDKZO2fnSPCrgM/6WMc7eS20Fbok+0r88fp+YtWVLZrp4CkafFGIp+W0VKw4a22sgebPT99y+FDNMdP4w==",
- "dependencies": {
- "@babel/types": "^7.22.15"
- },
- "engines": {
- "node": ">=6.9.0"
- }
- },
- "node_modules/@babel/helper-module-transforms": {
- "version": "7.23.3",
- "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.23.3.tgz",
- "integrity": "sha512-7bBs4ED9OmswdfDzpz4MpWgSrV7FXlc3zIagvLFjS5H+Mk7Snr21vQ6QwrsoCGMfNC4e4LQPdoULEt4ykz0SRQ==",
- "dev": true,
- "dependencies": {
- "@babel/helper-environment-visitor": "^7.22.20",
- "@babel/helper-module-imports": "^7.22.15",
- "@babel/helper-simple-access": "^7.22.5",
- "@babel/helper-split-export-declaration": "^7.22.6",
- "@babel/helper-validator-identifier": "^7.22.20"
- },
- "engines": {
- "node": ">=6.9.0"
- },
- "peerDependencies": {
- "@babel/core": "^7.0.0"
- }
- },
- "node_modules/@babel/helper-plugin-utils": {
- "version": "7.24.0",
- "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.24.0.tgz",
- "integrity": "sha512-9cUznXMG0+FxRuJfvL82QlTqIzhVW9sL0KjMPHhAOOvpQGL8QtdxnBKILjBqxlHyliz0yCa1G903ZXI/FuHy2w==",
- "dev": true,
- "engines": {
- "node": ">=6.9.0"
- }
- },
- "node_modules/@babel/helper-simple-access": {
- "version": "7.22.5",
- "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.22.5.tgz",
- "integrity": "sha512-n0H99E/K+Bika3++WNL17POvo4rKWZ7lZEp1Q+fStVbUi8nxPQEBOlTmCOxW/0JsS56SKKQ+ojAe2pHKJHN35w==",
- "dev": true,
- "dependencies": {
- "@babel/types": "^7.22.5"
- },
- "engines": {
- "node": ">=6.9.0"
- }
- },
- "node_modules/@babel/helper-split-export-declaration": {
- "version": "7.22.6",
- "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.22.6.tgz",
- "integrity": "sha512-AsUnxuLhRYsisFiaJwvp1QF+I3KjD5FOxut14q/GzovUe6orHLesW2C7d754kRm53h5gqrz6sFl6sxc4BVtE/g==",
- "dev": true,
- "dependencies": {
- "@babel/types": "^7.22.5"
- },
- "engines": {
- "node": ">=6.9.0"
- }
- },
- "node_modules/@babel/helper-string-parser": {
- "version": "7.23.4",
- "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.23.4.tgz",
- "integrity": "sha512-803gmbQdqwdf4olxrX4AJyFBV/RTr3rSmOj0rKwesmzlfhYNDEs+/iOcznzpNWlJlIlTJC2QfPFcHB6DlzdVLQ==",
- "engines": {
- "node": ">=6.9.0"
- }
- },
- "node_modules/@babel/helper-validator-identifier": {
- "version": "7.22.20",
- "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.20.tgz",
- "integrity": "sha512-Y4OZ+ytlatR8AI+8KZfKuL5urKp7qey08ha31L8b3BwewJAoJamTzyvxPR/5D+KkdJCGPq/+8TukHBlY10FX9A==",
- "engines": {
- "node": ">=6.9.0"
- }
- },
- "node_modules/@babel/helper-validator-option": {
- "version": "7.23.5",
- "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.23.5.tgz",
- "integrity": "sha512-85ttAOMLsr53VgXkTbkx8oA6YTfT4q7/HzXSLEYmjcSTJPMPQtvq1BD79Byep5xMUYbGRzEpDsjUf3dyp54IKw==",
- "dev": true,
- "engines": {
- "node": ">=6.9.0"
- }
- },
- "node_modules/@babel/helpers": {
- "version": "7.24.0",
- "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.24.0.tgz",
- "integrity": "sha512-ulDZdc0Aj5uLc5nETsa7EPx2L7rM0YJM8r7ck7U73AXi7qOV44IHHRAYZHY6iU1rr3C5N4NtTmMRUJP6kwCWeA==",
- "dev": true,
- "dependencies": {
- "@babel/template": "^7.24.0",
- "@babel/traverse": "^7.24.0",
- "@babel/types": "^7.24.0"
- },
- "engines": {
- "node": ">=6.9.0"
- }
- },
- "node_modules/@babel/highlight": {
- "version": "7.23.4",
- "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.23.4.tgz",
- "integrity": "sha512-acGdbYSfp2WheJoJm/EBBBLh/ID8KDc64ISZ9DYtBmC8/Q204PZJLHyzeB5qMzJ5trcOkybd78M4x2KWsUq++A==",
- "dependencies": {
- "@babel/helper-validator-identifier": "^7.22.20",
- "chalk": "^2.4.2",
- "js-tokens": "^4.0.0"
- },
- "engines": {
- "node": ">=6.9.0"
- }
- },
- "node_modules/@babel/parser": {
- "version": "7.24.0",
- "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.24.0.tgz",
- "integrity": "sha512-QuP/FxEAzMSjXygs8v4N9dvdXzEHN4W1oF3PxuWAtPo08UdM17u89RDMgjLn/mlc56iM0HlLmVkO/wgR+rDgHg==",
- "dev": true,
- "bin": {
- "parser": "bin/babel-parser.js"
- },
- "engines": {
- "node": ">=6.0.0"
- }
- },
- "node_modules/@babel/plugin-transform-react-jsx-self": {
- "version": "7.23.3",
- "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-self/-/plugin-transform-react-jsx-self-7.23.3.tgz",
- "integrity": "sha512-qXRvbeKDSfwnlJnanVRp0SfuWE5DQhwQr5xtLBzp56Wabyo+4CMosF6Kfp+eOD/4FYpql64XVJ2W0pVLlJZxOQ==",
- "dev": true,
- "dependencies": {
- "@babel/helper-plugin-utils": "^7.22.5"
- },
- "engines": {
- "node": ">=6.9.0"
- },
- "peerDependencies": {
- "@babel/core": "^7.0.0-0"
- }
- },
- "node_modules/@babel/plugin-transform-react-jsx-source": {
- "version": "7.23.3",
- "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-source/-/plugin-transform-react-jsx-source-7.23.3.tgz",
- "integrity": "sha512-91RS0MDnAWDNvGC6Wio5XYkyWI39FMFO+JK9+4AlgaTH+yWwVTsw7/sn6LK0lH7c5F+TFkpv/3LfCJ1Ydwof/g==",
- "dev": true,
- "dependencies": {
- "@babel/helper-plugin-utils": "^7.22.5"
- },
- "engines": {
- "node": ">=6.9.0"
- },
- "peerDependencies": {
- "@babel/core": "^7.0.0-0"
- }
- },
- "node_modules/@babel/runtime": {
- "version": "7.24.0",
- "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.24.0.tgz",
- "integrity": "sha512-Chk32uHMg6TnQdvw2e9IlqPpFX/6NLuK0Ys2PqLb7/gL5uFn9mXvK715FGLlOLQrcO4qIkNHkvPGktzzXexsFw==",
- "dependencies": {
- "regenerator-runtime": "^0.14.0"
- },
- "engines": {
- "node": ">=6.9.0"
- }
- },
- "node_modules/@babel/template": {
- "version": "7.24.0",
- "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.24.0.tgz",
- "integrity": "sha512-Bkf2q8lMB0AFpX0NFEqSbx1OkTHf0f+0j82mkw+ZpzBnkk7e9Ql0891vlfgi+kHwOk8tQjiQHpqh4LaSa0fKEA==",
- "dev": true,
- "dependencies": {
- "@babel/code-frame": "^7.23.5",
- "@babel/parser": "^7.24.0",
- "@babel/types": "^7.24.0"
- },
- "engines": {
- "node": ">=6.9.0"
- }
- },
- "node_modules/@babel/traverse": {
- "version": "7.24.0",
- "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.24.0.tgz",
- "integrity": "sha512-HfuJlI8qq3dEDmNU5ChzzpZRWq+oxCZQyMzIMEqLho+AQnhMnKQUzH6ydo3RBl/YjPCuk68Y6s0Gx0AeyULiWw==",
- "dev": true,
- "dependencies": {
- "@babel/code-frame": "^7.23.5",
- "@babel/generator": "^7.23.6",
- "@babel/helper-environment-visitor": "^7.22.20",
- "@babel/helper-function-name": "^7.23.0",
- "@babel/helper-hoist-variables": "^7.22.5",
- "@babel/helper-split-export-declaration": "^7.22.6",
- "@babel/parser": "^7.24.0",
- "@babel/types": "^7.24.0",
- "debug": "^4.3.1",
- "globals": "^11.1.0"
- },
- "engines": {
- "node": ">=6.9.0"
- }
- },
- "node_modules/@babel/types": {
- "version": "7.24.0",
- "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.24.0.tgz",
- "integrity": "sha512-+j7a5c253RfKh8iABBhywc8NSfP5LURe7Uh4qpsh6jc+aLJguvmIUBdjSdEMQv2bENrCR5MfRdjGo7vzS/ob7w==",
- "dependencies": {
- "@babel/helper-string-parser": "^7.23.4",
- "@babel/helper-validator-identifier": "^7.22.20",
- "to-fast-properties": "^2.0.0"
- },
- "engines": {
- "node": ">=6.9.0"
- }
- },
- "node_modules/@emotion/babel-plugin": {
- "version": "11.11.0",
- "resolved": "https://registry.npmjs.org/@emotion/babel-plugin/-/babel-plugin-11.11.0.tgz",
- "integrity": "sha512-m4HEDZleaaCH+XgDDsPF15Ht6wTLsgDTeR3WYj9Q/k76JtWhrJjcP4+/XlG8LGT/Rol9qUfOIztXeA84ATpqPQ==",
- "dependencies": {
- "@babel/helper-module-imports": "^7.16.7",
- "@babel/runtime": "^7.18.3",
- "@emotion/hash": "^0.9.1",
- "@emotion/memoize": "^0.8.1",
- "@emotion/serialize": "^1.1.2",
- "babel-plugin-macros": "^3.1.0",
- "convert-source-map": "^1.5.0",
- "escape-string-regexp": "^4.0.0",
- "find-root": "^1.1.0",
- "source-map": "^0.5.7",
- "stylis": "4.2.0"
- }
- },
- "node_modules/@emotion/cache": {
- "version": "11.11.0",
- "resolved": "https://registry.npmjs.org/@emotion/cache/-/cache-11.11.0.tgz",
- "integrity": "sha512-P34z9ssTCBi3e9EI1ZsWpNHcfY1r09ZO0rZbRO2ob3ZQMnFI35jB536qoXbkdesr5EUhYi22anuEJuyxifaqAQ==",
- "dependencies": {
- "@emotion/memoize": "^0.8.1",
- "@emotion/sheet": "^1.2.2",
- "@emotion/utils": "^1.2.1",
- "@emotion/weak-memoize": "^0.3.1",
- "stylis": "4.2.0"
- }
- },
- "node_modules/@emotion/hash": {
- "version": "0.9.1",
- "resolved": "https://registry.npmjs.org/@emotion/hash/-/hash-0.9.1.tgz",
- "integrity": "sha512-gJB6HLm5rYwSLI6PQa+X1t5CFGrv1J1TWG+sOyMCeKz2ojaj6Fnl/rZEspogG+cvqbt4AE/2eIyD2QfLKTBNlQ=="
- },
- "node_modules/@emotion/is-prop-valid": {
- "version": "1.2.2",
- "resolved": "https://registry.npmjs.org/@emotion/is-prop-valid/-/is-prop-valid-1.2.2.tgz",
- "integrity": "sha512-uNsoYd37AFmaCdXlg6EYD1KaPOaRWRByMCYzbKUX4+hhMfrxdVSelShywL4JVaAeM/eHUOSprYBQls+/neX3pw==",
- "dependencies": {
- "@emotion/memoize": "^0.8.1"
- }
- },
- "node_modules/@emotion/memoize": {
- "version": "0.8.1",
- "resolved": "https://registry.npmjs.org/@emotion/memoize/-/memoize-0.8.1.tgz",
- "integrity": "sha512-W2P2c/VRW1/1tLox0mVUalvnWXxavmv/Oum2aPsRcoDJuob75FC3Y8FbpfLwUegRcxINtGUMPq0tFCvYNTBXNA=="
- },
- "node_modules/@emotion/react": {
- "version": "11.11.4",
- "resolved": "https://registry.npmjs.org/@emotion/react/-/react-11.11.4.tgz",
- "integrity": "sha512-t8AjMlF0gHpvvxk5mAtCqR4vmxiGHCeJBaQO6gncUSdklELOgtwjerNY2yuJNfwnc6vi16U/+uMF+afIawJ9iw==",
- "dependencies": {
- "@babel/runtime": "^7.18.3",
- "@emotion/babel-plugin": "^11.11.0",
- "@emotion/cache": "^11.11.0",
- "@emotion/serialize": "^1.1.3",
- "@emotion/use-insertion-effect-with-fallbacks": "^1.0.1",
- "@emotion/utils": "^1.2.1",
- "@emotion/weak-memoize": "^0.3.1",
- "hoist-non-react-statics": "^3.3.1"
- },
- "peerDependencies": {
- "react": ">=16.8.0"
- },
- "peerDependenciesMeta": {
- "@types/react": {
- "optional": true
- }
- }
- },
- "node_modules/@emotion/serialize": {
- "version": "1.1.3",
- "resolved": "https://registry.npmjs.org/@emotion/serialize/-/serialize-1.1.3.tgz",
- "integrity": "sha512-iD4D6QVZFDhcbH0RAG1uVu1CwVLMWUkCvAqqlewO/rxf8+87yIBAlt4+AxMiiKPLs5hFc0owNk/sLLAOROw3cA==",
- "dependencies": {
- "@emotion/hash": "^0.9.1",
- "@emotion/memoize": "^0.8.1",
- "@emotion/unitless": "^0.8.1",
- "@emotion/utils": "^1.2.1",
- "csstype": "^3.0.2"
- }
- },
- "node_modules/@emotion/sheet": {
- "version": "1.2.2",
- "resolved": "https://registry.npmjs.org/@emotion/sheet/-/sheet-1.2.2.tgz",
- "integrity": "sha512-0QBtGvaqtWi+nx6doRwDdBIzhNdZrXUppvTM4dtZZWEGTXL/XE/yJxLMGlDT1Gt+UHH5IX1n+jkXyytE/av7OA=="
- },
- "node_modules/@emotion/styled": {
- "version": "11.11.0",
- "resolved": "https://registry.npmjs.org/@emotion/styled/-/styled-11.11.0.tgz",
- "integrity": "sha512-hM5Nnvu9P3midq5aaXj4I+lnSfNi7Pmd4EWk1fOZ3pxookaQTNew6bp4JaCBYM4HVFZF9g7UjJmsUmC2JlxOng==",
- "dependencies": {
- "@babel/runtime": "^7.18.3",
- "@emotion/babel-plugin": "^11.11.0",
- "@emotion/is-prop-valid": "^1.2.1",
- "@emotion/serialize": "^1.1.2",
- "@emotion/use-insertion-effect-with-fallbacks": "^1.0.1",
- "@emotion/utils": "^1.2.1"
- },
- "peerDependencies": {
- "@emotion/react": "^11.0.0-rc.0",
- "react": ">=16.8.0"
- },
- "peerDependenciesMeta": {
- "@types/react": {
- "optional": true
- }
- }
- },
- "node_modules/@emotion/unitless": {
- "version": "0.8.1",
- "resolved": "https://registry.npmjs.org/@emotion/unitless/-/unitless-0.8.1.tgz",
- "integrity": "sha512-KOEGMu6dmJZtpadb476IsZBclKvILjopjUii3V+7MnXIQCYh8W3NgNcgwo21n9LXZX6EDIKvqfjYxXebDwxKmQ=="
- },
- "node_modules/@emotion/use-insertion-effect-with-fallbacks": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/@emotion/use-insertion-effect-with-fallbacks/-/use-insertion-effect-with-fallbacks-1.0.1.tgz",
- "integrity": "sha512-jT/qyKZ9rzLErtrjGgdkMBn2OP8wl0G3sQlBb3YPryvKHsjvINUhVaPFfP+fpBcOkmrVOVEEHQFJ7nbj2TH2gw==",
- "peerDependencies": {
- "react": ">=16.8.0"
- }
- },
- "node_modules/@emotion/utils": {
- "version": "1.2.1",
- "resolved": "https://registry.npmjs.org/@emotion/utils/-/utils-1.2.1.tgz",
- "integrity": "sha512-Y2tGf3I+XVnajdItskUCn6LX+VUDmP6lTL4fcqsXAv43dnlbZiuW4MWQW38rW/BVWSE7Q/7+XQocmpnRYILUmg=="
- },
- "node_modules/@emotion/weak-memoize": {
- "version": "0.3.1",
- "resolved": "https://registry.npmjs.org/@emotion/weak-memoize/-/weak-memoize-0.3.1.tgz",
- "integrity": "sha512-EsBwpc7hBUJWAsNPBmJy4hxWx12v6bshQsldrVmjxJoc3isbxhOrF2IcCpaXxfvq03NwkI7sbsOLXbYuqF/8Ww=="
- },
- "node_modules/@esbuild/aix-ppc64": {
- "version": "0.19.12",
- "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.19.12.tgz",
- "integrity": "sha512-bmoCYyWdEL3wDQIVbcyzRyeKLgk2WtWLTWz1ZIAZF/EGbNOwSA6ew3PftJ1PqMiOOGu0OyFMzG53L0zqIpPeNA==",
- "cpu": [
- "ppc64"
- ],
- "dev": true,
- "optional": true,
- "os": [
- "aix"
- ],
- "engines": {
- "node": ">=12"
- }
- },
- "node_modules/@esbuild/android-arm": {
- "version": "0.19.12",
- "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.19.12.tgz",
- "integrity": "sha512-qg/Lj1mu3CdQlDEEiWrlC4eaPZ1KztwGJ9B6J+/6G+/4ewxJg7gqj8eVYWvao1bXrqGiW2rsBZFSX3q2lcW05w==",
- "cpu": [
- "arm"
- ],
- "dev": true,
- "optional": true,
- "os": [
- "android"
- ],
- "engines": {
- "node": ">=12"
- }
- },
- "node_modules/@esbuild/android-arm64": {
- "version": "0.19.12",
- "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.19.12.tgz",
- "integrity": "sha512-P0UVNGIienjZv3f5zq0DP3Nt2IE/3plFzuaS96vihvD0Hd6H/q4WXUGpCxD/E8YrSXfNyRPbpTq+T8ZQioSuPA==",
- "cpu": [
- "arm64"
- ],
- "dev": true,
- "optional": true,
- "os": [
- "android"
- ],
- "engines": {
- "node": ">=12"
- }
- },
- "node_modules/@esbuild/android-x64": {
- "version": "0.19.12",
- "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.19.12.tgz",
- "integrity": "sha512-3k7ZoUW6Q6YqhdhIaq/WZ7HwBpnFBlW905Fa4s4qWJyiNOgT1dOqDiVAQFwBH7gBRZr17gLrlFCRzF6jFh7Kew==",
- "cpu": [
- "x64"
- ],
- "dev": true,
- "optional": true,
- "os": [
- "android"
- ],
- "engines": {
- "node": ">=12"
- }
- },
- "node_modules/@esbuild/darwin-arm64": {
- "version": "0.19.12",
- "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.19.12.tgz",
- "integrity": "sha512-B6IeSgZgtEzGC42jsI+YYu9Z3HKRxp8ZT3cqhvliEHovq8HSX2YX8lNocDn79gCKJXOSaEot9MVYky7AKjCs8g==",
- "cpu": [
- "arm64"
- ],
- "dev": true,
- "optional": true,
- "os": [
- "darwin"
- ],
- "engines": {
- "node": ">=12"
- }
- },
- "node_modules/@esbuild/darwin-x64": {
- "version": "0.19.12",
- "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.19.12.tgz",
- "integrity": "sha512-hKoVkKzFiToTgn+41qGhsUJXFlIjxI/jSYeZf3ugemDYZldIXIxhvwN6erJGlX4t5h417iFuheZ7l+YVn05N3A==",
- "cpu": [
- "x64"
- ],
- "dev": true,
- "optional": true,
- "os": [
- "darwin"
- ],
- "engines": {
- "node": ">=12"
- }
- },
- "node_modules/@esbuild/freebsd-arm64": {
- "version": "0.19.12",
- "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.19.12.tgz",
- "integrity": "sha512-4aRvFIXmwAcDBw9AueDQ2YnGmz5L6obe5kmPT8Vd+/+x/JMVKCgdcRwH6APrbpNXsPz+K653Qg8HB/oXvXVukA==",
- "cpu": [
- "arm64"
- ],
- "dev": true,
- "optional": true,
- "os": [
- "freebsd"
- ],
- "engines": {
- "node": ">=12"
- }
- },
- "node_modules/@esbuild/freebsd-x64": {
- "version": "0.19.12",
- "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.19.12.tgz",
- "integrity": "sha512-EYoXZ4d8xtBoVN7CEwWY2IN4ho76xjYXqSXMNccFSx2lgqOG/1TBPW0yPx1bJZk94qu3tX0fycJeeQsKovA8gg==",
- "cpu": [
- "x64"
- ],
- "dev": true,
- "optional": true,
- "os": [
- "freebsd"
- ],
- "engines": {
- "node": ">=12"
- }
- },
- "node_modules/@esbuild/linux-arm": {
- "version": "0.19.12",
- "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.19.12.tgz",
- "integrity": "sha512-J5jPms//KhSNv+LO1S1TX1UWp1ucM6N6XuL6ITdKWElCu8wXP72l9MM0zDTzzeikVyqFE6U8YAV9/tFyj0ti+w==",
- "cpu": [
- "arm"
- ],
- "dev": true,
- "optional": true,
- "os": [
- "linux"
- ],
- "engines": {
- "node": ">=12"
- }
- },
- "node_modules/@esbuild/linux-arm64": {
- "version": "0.19.12",
- "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.19.12.tgz",
- "integrity": "sha512-EoTjyYyLuVPfdPLsGVVVC8a0p1BFFvtpQDB/YLEhaXyf/5bczaGeN15QkR+O4S5LeJ92Tqotve7i1jn35qwvdA==",
- "cpu": [
- "arm64"
- ],
- "dev": true,
- "optional": true,
- "os": [
- "linux"
- ],
- "engines": {
- "node": ">=12"
- }
- },
- "node_modules/@esbuild/linux-ia32": {
- "version": "0.19.12",
- "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.19.12.tgz",
- "integrity": "sha512-Thsa42rrP1+UIGaWz47uydHSBOgTUnwBwNq59khgIwktK6x60Hivfbux9iNR0eHCHzOLjLMLfUMLCypBkZXMHA==",
- "cpu": [
- "ia32"
- ],
- "dev": true,
- "optional": true,
- "os": [
- "linux"
- ],
- "engines": {
- "node": ">=12"
- }
- },
- "node_modules/@esbuild/linux-loong64": {
- "version": "0.19.12",
- "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.19.12.tgz",
- "integrity": "sha512-LiXdXA0s3IqRRjm6rV6XaWATScKAXjI4R4LoDlvO7+yQqFdlr1Bax62sRwkVvRIrwXxvtYEHHI4dm50jAXkuAA==",
- "cpu": [
- "loong64"
- ],
- "dev": true,
- "optional": true,
- "os": [
- "linux"
- ],
- "engines": {
- "node": ">=12"
- }
- },
- "node_modules/@esbuild/linux-mips64el": {
- "version": "0.19.12",
- "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.19.12.tgz",
- "integrity": "sha512-fEnAuj5VGTanfJ07ff0gOA6IPsvrVHLVb6Lyd1g2/ed67oU1eFzL0r9WL7ZzscD+/N6i3dWumGE1Un4f7Amf+w==",
- "cpu": [
- "mips64el"
- ],
- "dev": true,
- "optional": true,
- "os": [
- "linux"
- ],
- "engines": {
- "node": ">=12"
- }
- },
- "node_modules/@esbuild/linux-ppc64": {
- "version": "0.19.12",
- "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.19.12.tgz",
- "integrity": "sha512-nYJA2/QPimDQOh1rKWedNOe3Gfc8PabU7HT3iXWtNUbRzXS9+vgB0Fjaqr//XNbd82mCxHzik2qotuI89cfixg==",
- "cpu": [
- "ppc64"
- ],
- "dev": true,
- "optional": true,
- "os": [
- "linux"
- ],
- "engines": {
- "node": ">=12"
- }
- },
- "node_modules/@esbuild/linux-riscv64": {
- "version": "0.19.12",
- "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.19.12.tgz",
- "integrity": "sha512-2MueBrlPQCw5dVJJpQdUYgeqIzDQgw3QtiAHUC4RBz9FXPrskyyU3VI1hw7C0BSKB9OduwSJ79FTCqtGMWqJHg==",
- "cpu": [
- "riscv64"
- ],
- "dev": true,
- "optional": true,
- "os": [
- "linux"
- ],
- "engines": {
- "node": ">=12"
- }
- },
- "node_modules/@esbuild/linux-s390x": {
- "version": "0.19.12",
- "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.19.12.tgz",
- "integrity": "sha512-+Pil1Nv3Umes4m3AZKqA2anfhJiVmNCYkPchwFJNEJN5QxmTs1uzyy4TvmDrCRNT2ApwSari7ZIgrPeUx4UZDg==",
- "cpu": [
- "s390x"
- ],
- "dev": true,
- "optional": true,
- "os": [
- "linux"
- ],
- "engines": {
- "node": ">=12"
- }
- },
- "node_modules/@esbuild/linux-x64": {
- "version": "0.19.12",
- "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.19.12.tgz",
- "integrity": "sha512-B71g1QpxfwBvNrfyJdVDexenDIt1CiDN1TIXLbhOw0KhJzE78KIFGX6OJ9MrtC0oOqMWf+0xop4qEU8JrJTwCg==",
- "cpu": [
- "x64"
- ],
- "dev": true,
- "optional": true,
- "os": [
- "linux"
- ],
- "engines": {
- "node": ">=12"
- }
- },
- "node_modules/@esbuild/netbsd-x64": {
- "version": "0.19.12",
- "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.19.12.tgz",
- "integrity": "sha512-3ltjQ7n1owJgFbuC61Oj++XhtzmymoCihNFgT84UAmJnxJfm4sYCiSLTXZtE00VWYpPMYc+ZQmB6xbSdVh0JWA==",
- "cpu": [
- "x64"
- ],
- "dev": true,
- "optional": true,
- "os": [
- "netbsd"
- ],
- "engines": {
- "node": ">=12"
- }
- },
- "node_modules/@esbuild/openbsd-x64": {
- "version": "0.19.12",
- "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.19.12.tgz",
- "integrity": "sha512-RbrfTB9SWsr0kWmb9srfF+L933uMDdu9BIzdA7os2t0TXhCRjrQyCeOt6wVxr79CKD4c+p+YhCj31HBkYcXebw==",
- "cpu": [
- "x64"
- ],
- "dev": true,
- "optional": true,
- "os": [
- "openbsd"
- ],
- "engines": {
- "node": ">=12"
- }
- },
- "node_modules/@esbuild/sunos-x64": {
- "version": "0.19.12",
- "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.19.12.tgz",
- "integrity": "sha512-HKjJwRrW8uWtCQnQOz9qcU3mUZhTUQvi56Q8DPTLLB+DawoiQdjsYq+j+D3s9I8VFtDr+F9CjgXKKC4ss89IeA==",
- "cpu": [
- "x64"
- ],
- "dev": true,
- "optional": true,
- "os": [
- "sunos"
- ],
- "engines": {
- "node": ">=12"
- }
- },
- "node_modules/@esbuild/win32-arm64": {
- "version": "0.19.12",
- "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.19.12.tgz",
- "integrity": "sha512-URgtR1dJnmGvX864pn1B2YUYNzjmXkuJOIqG2HdU62MVS4EHpU2946OZoTMnRUHklGtJdJZ33QfzdjGACXhn1A==",
- "cpu": [
- "arm64"
- ],
- "dev": true,
- "optional": true,
- "os": [
- "win32"
- ],
- "engines": {
- "node": ">=12"
- }
- },
- "node_modules/@esbuild/win32-ia32": {
- "version": "0.19.12",
- "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.19.12.tgz",
- "integrity": "sha512-+ZOE6pUkMOJfmxmBZElNOx72NKpIa/HFOMGzu8fqzQJ5kgf6aTGrcJaFsNiVMH4JKpMipyK+7k0n2UXN7a8YKQ==",
- "cpu": [
- "ia32"
- ],
- "dev": true,
- "optional": true,
- "os": [
- "win32"
- ],
- "engines": {
- "node": ">=12"
- }
- },
- "node_modules/@esbuild/win32-x64": {
- "version": "0.19.12",
- "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.19.12.tgz",
- "integrity": "sha512-T1QyPSDCyMXaO3pzBkF96E8xMkiRYbUEZADd29SyPGabqxMViNoii+NcK7eWJAEoU6RZyEm5lVSIjTmcdoB9HA==",
- "cpu": [
- "x64"
- ],
- "dev": true,
- "optional": true,
- "os": [
- "win32"
- ],
- "engines": {
- "node": ">=12"
- }
- },
- "node_modules/@eslint-community/eslint-utils": {
- "version": "4.4.0",
- "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz",
- "integrity": "sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==",
- "dev": true,
- "dependencies": {
- "eslint-visitor-keys": "^3.3.0"
- },
- "engines": {
- "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
- },
- "peerDependencies": {
- "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0"
- }
- },
- "node_modules/@eslint-community/regexpp": {
- "version": "4.10.0",
- "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.10.0.tgz",
- "integrity": "sha512-Cu96Sd2By9mCNTx2iyKOmq10v22jUVQv0lQnlGNy16oE9589yE+QADPbrMGCkA51cKZSg3Pu/aTJVTGfL/qjUA==",
- "dev": true,
- "engines": {
- "node": "^12.0.0 || ^14.0.0 || >=16.0.0"
- }
- },
- "node_modules/@eslint/eslintrc": {
- "version": "2.1.4",
- "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.1.4.tgz",
- "integrity": "sha512-269Z39MS6wVJtsoUl10L60WdkhJVdPG24Q4eZTH3nnF6lpvSShEK3wQjDX9JRWAUPvPh7COouPpU9IrqaZFvtQ==",
- "dev": true,
- "dependencies": {
- "ajv": "^6.12.4",
- "debug": "^4.3.2",
- "espree": "^9.6.0",
- "globals": "^13.19.0",
- "ignore": "^5.2.0",
- "import-fresh": "^3.2.1",
- "js-yaml": "^4.1.0",
- "minimatch": "^3.1.2",
- "strip-json-comments": "^3.1.1"
- },
- "engines": {
- "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
- },
- "funding": {
- "url": "https://opencollective.com/eslint"
- }
- },
- "node_modules/@eslint/eslintrc/node_modules/brace-expansion": {
- "version": "1.1.11",
- "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
- "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
- "dev": true,
- "dependencies": {
- "balanced-match": "^1.0.0",
- "concat-map": "0.0.1"
- }
- },
- "node_modules/@eslint/eslintrc/node_modules/globals": {
- "version": "13.24.0",
- "resolved": "https://registry.npmjs.org/globals/-/globals-13.24.0.tgz",
- "integrity": "sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==",
- "dev": true,
- "dependencies": {
- "type-fest": "^0.20.2"
- },
- "engines": {
- "node": ">=8"
- },
- "funding": {
- "url": "https://github.com/sponsors/sindresorhus"
- }
- },
- "node_modules/@eslint/eslintrc/node_modules/minimatch": {
- "version": "3.1.2",
- "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
- "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==",
- "dev": true,
- "dependencies": {
- "brace-expansion": "^1.1.7"
- },
- "engines": {
- "node": "*"
- }
- },
- "node_modules/@eslint/js": {
- "version": "8.57.0",
- "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.57.0.tgz",
- "integrity": "sha512-Ys+3g2TaW7gADOJzPt83SJtCDhMjndcDMFVQ/Tj9iA1BfJzFKD9mAUXT3OenpuPHbI6P/myECxRJrofUsDx/5g==",
- "dev": true,
- "engines": {
- "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
- }
- },
- "node_modules/@floating-ui/core": {
- "version": "1.6.0",
- "resolved": "https://registry.npmjs.org/@floating-ui/core/-/core-1.6.0.tgz",
- "integrity": "sha512-PcF++MykgmTj3CIyOQbKA/hDzOAiqI3mhuoN44WRCopIs1sgoDoU4oty4Jtqaj/y3oDU6fnVSm4QG0a3t5i0+g==",
- "dependencies": {
- "@floating-ui/utils": "^0.2.1"
- }
- },
- "node_modules/@floating-ui/dom": {
- "version": "1.6.3",
- "resolved": "https://registry.npmjs.org/@floating-ui/dom/-/dom-1.6.3.tgz",
- "integrity": "sha512-RnDthu3mzPlQ31Ss/BTwQ1zjzIhr3lk1gZB1OC56h/1vEtaXkESrOqL5fQVMfXpwGtRwX+YsZBdyHtJMQnkArw==",
- "dependencies": {
- "@floating-ui/core": "^1.0.0",
- "@floating-ui/utils": "^0.2.0"
- }
- },
- "node_modules/@floating-ui/react-dom": {
- "version": "2.0.8",
- "resolved": "https://registry.npmjs.org/@floating-ui/react-dom/-/react-dom-2.0.8.tgz",
- "integrity": "sha512-HOdqOt3R3OGeTKidaLvJKcgg75S6tibQ3Tif4eyd91QnIJWr0NLvoXFpJA/j8HqkFSL68GDca9AuyWEHlhyClw==",
- "dependencies": {
- "@floating-ui/dom": "^1.6.1"
- },
- "peerDependencies": {
- "react": ">=16.8.0",
- "react-dom": ">=16.8.0"
- }
- },
- "node_modules/@floating-ui/utils": {
- "version": "0.2.1",
- "resolved": "https://registry.npmjs.org/@floating-ui/utils/-/utils-0.2.1.tgz",
- "integrity": "sha512-9TANp6GPoMtYzQdt54kfAyMmz1+osLlXdg2ENroU7zzrtflTLrrC/lgrIfaSe+Wu0b89GKccT7vxXA0MoAIO+Q=="
- },
- "node_modules/@fontsource/roboto": {
- "version": "5.0.12",
- "resolved": "https://registry.npmjs.org/@fontsource/roboto/-/roboto-5.0.12.tgz",
- "integrity": "sha512-x0o17jvgoSSbS9OZnUX2+xJmVRvVCfeaYJjkS7w62iN7CuJWtMf5vJj8LqgC7ibqIkitOHVW+XssRjgrcHn62g=="
- },
- "node_modules/@humanwhocodes/config-array": {
- "version": "0.11.14",
- "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.14.tgz",
- "integrity": "sha512-3T8LkOmg45BV5FICb15QQMsyUSWrQ8AygVfC7ZG32zOalnqrilm018ZVCw0eapXux8FtA33q8PSRSstjee3jSg==",
- "dev": true,
- "dependencies": {
- "@humanwhocodes/object-schema": "^2.0.2",
- "debug": "^4.3.1",
- "minimatch": "^3.0.5"
- },
- "engines": {
- "node": ">=10.10.0"
- }
- },
- "node_modules/@humanwhocodes/config-array/node_modules/brace-expansion": {
- "version": "1.1.11",
- "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
- "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
- "dev": true,
- "dependencies": {
- "balanced-match": "^1.0.0",
- "concat-map": "0.0.1"
- }
- },
- "node_modules/@humanwhocodes/config-array/node_modules/minimatch": {
- "version": "3.1.2",
- "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
- "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==",
- "dev": true,
- "dependencies": {
- "brace-expansion": "^1.1.7"
- },
- "engines": {
- "node": "*"
- }
- },
- "node_modules/@humanwhocodes/module-importer": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz",
- "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==",
- "dev": true,
- "engines": {
- "node": ">=12.22"
- },
- "funding": {
- "type": "github",
- "url": "https://github.com/sponsors/nzakas"
- }
- },
- "node_modules/@humanwhocodes/object-schema": {
- "version": "2.0.2",
- "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-2.0.2.tgz",
- "integrity": "sha512-6EwiSjwWYP7pTckG6I5eyFANjPhmPjUX9JRLUSfNPC7FX7zK9gyZAfUEaECL6ALTpGX5AjnBq3C9XmVWPitNpw==",
- "dev": true
- },
- "node_modules/@jridgewell/gen-mapping": {
- "version": "0.3.5",
- "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.5.tgz",
- "integrity": "sha512-IzL8ZoEDIBRWEzlCcRhOaCupYyN5gdIK+Q6fbFdPDg6HqX6jpkItn7DFIpW9LQzXG6Df9sA7+OKnq0qlz/GaQg==",
- "dev": true,
- "dependencies": {
- "@jridgewell/set-array": "^1.2.1",
- "@jridgewell/sourcemap-codec": "^1.4.10",
- "@jridgewell/trace-mapping": "^0.3.24"
- },
- "engines": {
- "node": ">=6.0.0"
- }
- },
- "node_modules/@jridgewell/resolve-uri": {
- "version": "3.1.2",
- "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz",
- "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==",
- "dev": true,
- "engines": {
- "node": ">=6.0.0"
- }
- },
- "node_modules/@jridgewell/set-array": {
- "version": "1.2.1",
- "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.2.1.tgz",
- "integrity": "sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==",
- "dev": true,
- "engines": {
- "node": ">=6.0.0"
- }
- },
- "node_modules/@jridgewell/sourcemap-codec": {
- "version": "1.4.15",
- "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz",
- "integrity": "sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==",
- "dev": true
- },
- "node_modules/@jridgewell/trace-mapping": {
- "version": "0.3.25",
- "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz",
- "integrity": "sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==",
- "dev": true,
- "dependencies": {
- "@jridgewell/resolve-uri": "^3.1.0",
- "@jridgewell/sourcemap-codec": "^1.4.14"
- }
- },
- "node_modules/@mui/base": {
- "version": "5.0.0-beta.38",
- "resolved": "https://registry.npmjs.org/@mui/base/-/base-5.0.0-beta.38.tgz",
- "integrity": "sha512-AsjD6Y1X5A1qndxz8xCcR8LDqv31aiwlgWMPxFAX/kCKiIGKlK65yMeVZ62iQr/6LBz+9hSKLiD1i4TZdAHKcQ==",
- "dependencies": {
- "@babel/runtime": "^7.23.9",
- "@floating-ui/react-dom": "^2.0.8",
- "@mui/types": "^7.2.13",
- "@mui/utils": "^5.15.12",
- "@popperjs/core": "^2.11.8",
- "clsx": "^2.1.0",
- "prop-types": "^15.8.1"
- },
- "engines": {
- "node": ">=12.0.0"
- },
- "funding": {
- "type": "opencollective",
- "url": "https://opencollective.com/mui-org"
- },
- "peerDependencies": {
- "@types/react": "^17.0.0 || ^18.0.0",
- "react": "^17.0.0 || ^18.0.0",
- "react-dom": "^17.0.0 || ^18.0.0"
- },
- "peerDependenciesMeta": {
- "@types/react": {
- "optional": true
- }
- }
- },
- "node_modules/@mui/core-downloads-tracker": {
- "version": "5.15.12",
- "resolved": "https://registry.npmjs.org/@mui/core-downloads-tracker/-/core-downloads-tracker-5.15.12.tgz",
- "integrity": "sha512-brRO+tMFLpGyjEYHrX97bzqeF6jZmKpqqe1rY0LyIHAwP6xRVzh++zSecOQorDOCaZJg4XkGT9xfD+RWOWxZBA==",
- "funding": {
- "type": "opencollective",
- "url": "https://opencollective.com/mui-org"
- }
- },
- "node_modules/@mui/icons-material": {
- "version": "5.15.12",
- "resolved": "https://registry.npmjs.org/@mui/icons-material/-/icons-material-5.15.12.tgz",
- "integrity": "sha512-3BXiDlOd3AexZoEXa/VqpIpVIvosCzjLHsdMWzKMXbZdnBiJjmb9ECdqfjn5SpTClO49qvkKLhkTqdBH3fSFGw==",
- "dependencies": {
- "@babel/runtime": "^7.23.9"
- },
- "engines": {
- "node": ">=12.0.0"
- },
- "funding": {
- "type": "opencollective",
- "url": "https://opencollective.com/mui-org"
- },
- "peerDependencies": {
- "@mui/material": "^5.0.0",
- "@types/react": "^17.0.0 || ^18.0.0",
- "react": "^17.0.0 || ^18.0.0"
- },
- "peerDependenciesMeta": {
- "@types/react": {
- "optional": true
- }
- }
- },
- "node_modules/@mui/material": {
- "version": "5.15.12",
- "resolved": "https://registry.npmjs.org/@mui/material/-/material-5.15.12.tgz",
- "integrity": "sha512-vXJGg6KNKucsvbW6l7w9zafnpOp0CWc0Wx4mDykuABTpQ5QQBnZxP7+oB4yAS1hDZQ1WobbeIl0CjxK4EEahkA==",
- "dependencies": {
- "@babel/runtime": "^7.23.9",
- "@mui/base": "5.0.0-beta.38",
- "@mui/core-downloads-tracker": "^5.15.12",
- "@mui/system": "^5.15.12",
- "@mui/types": "^7.2.13",
- "@mui/utils": "^5.15.12",
- "@types/react-transition-group": "^4.4.10",
- "clsx": "^2.1.0",
- "csstype": "^3.1.3",
- "prop-types": "^15.8.1",
- "react-is": "^18.2.0",
- "react-transition-group": "^4.4.5"
- },
- "engines": {
- "node": ">=12.0.0"
- },
- "funding": {
- "type": "opencollective",
- "url": "https://opencollective.com/mui-org"
- },
- "peerDependencies": {
- "@emotion/react": "^11.5.0",
- "@emotion/styled": "^11.3.0",
- "@types/react": "^17.0.0 || ^18.0.0",
- "react": "^17.0.0 || ^18.0.0",
- "react-dom": "^17.0.0 || ^18.0.0"
- },
- "peerDependenciesMeta": {
- "@emotion/react": {
- "optional": true
- },
- "@emotion/styled": {
- "optional": true
- },
- "@types/react": {
- "optional": true
- }
- }
- },
- "node_modules/@mui/private-theming": {
- "version": "5.15.12",
- "resolved": "https://registry.npmjs.org/@mui/private-theming/-/private-theming-5.15.12.tgz",
- "integrity": "sha512-cqoSo9sgA5HE+8vZClbLrq9EkyOnYysooepi5eKaKvJ41lReT2c5wOZAeDDM1+xknrMDos+0mT2zr3sZmUiRRA==",
- "dependencies": {
- "@babel/runtime": "^7.23.9",
- "@mui/utils": "^5.15.12",
- "prop-types": "^15.8.1"
- },
- "engines": {
- "node": ">=12.0.0"
- },
- "funding": {
- "type": "opencollective",
- "url": "https://opencollective.com/mui-org"
- },
- "peerDependencies": {
- "@types/react": "^17.0.0 || ^18.0.0",
- "react": "^17.0.0 || ^18.0.0"
- },
- "peerDependenciesMeta": {
- "@types/react": {
- "optional": true
- }
- }
- },
- "node_modules/@mui/styled-engine": {
- "version": "5.15.11",
- "resolved": "https://registry.npmjs.org/@mui/styled-engine/-/styled-engine-5.15.11.tgz",
- "integrity": "sha512-So21AhAngqo07ces4S/JpX5UaMU2RHXpEA6hNzI6IQjd/1usMPxpgK8wkGgTe3JKmC2KDmH8cvoycq5H3Ii7/w==",
- "dependencies": {
- "@babel/runtime": "^7.23.9",
- "@emotion/cache": "^11.11.0",
- "csstype": "^3.1.3",
- "prop-types": "^15.8.1"
- },
- "engines": {
- "node": ">=12.0.0"
- },
- "funding": {
- "type": "opencollective",
- "url": "https://opencollective.com/mui-org"
- },
- "peerDependencies": {
- "@emotion/react": "^11.4.1",
- "@emotion/styled": "^11.3.0",
- "react": "^17.0.0 || ^18.0.0"
- },
- "peerDependenciesMeta": {
- "@emotion/react": {
- "optional": true
- },
- "@emotion/styled": {
- "optional": true
- }
- }
- },
- "node_modules/@mui/system": {
- "version": "5.15.12",
- "resolved": "https://registry.npmjs.org/@mui/system/-/system-5.15.12.tgz",
- "integrity": "sha512-/pq+GO6yN3X7r3hAwFTrzkAh7K1bTF5r8IzS79B9eyKJg7v6B/t4/zZYMR6OT9qEPtwf6rYN2Utg1e6Z7F1OgQ==",
- "dependencies": {
- "@babel/runtime": "^7.23.9",
- "@mui/private-theming": "^5.15.12",
- "@mui/styled-engine": "^5.15.11",
- "@mui/types": "^7.2.13",
- "@mui/utils": "^5.15.12",
- "clsx": "^2.1.0",
- "csstype": "^3.1.3",
- "prop-types": "^15.8.1"
- },
- "engines": {
- "node": ">=12.0.0"
- },
- "funding": {
- "type": "opencollective",
- "url": "https://opencollective.com/mui-org"
- },
- "peerDependencies": {
- "@emotion/react": "^11.5.0",
- "@emotion/styled": "^11.3.0",
- "@types/react": "^17.0.0 || ^18.0.0",
- "react": "^17.0.0 || ^18.0.0"
- },
- "peerDependenciesMeta": {
- "@emotion/react": {
- "optional": true
- },
- "@emotion/styled": {
- "optional": true
- },
- "@types/react": {
- "optional": true
- }
- }
- },
- "node_modules/@mui/types": {
- "version": "7.2.13",
- "resolved": "https://registry.npmjs.org/@mui/types/-/types-7.2.13.tgz",
- "integrity": "sha512-qP9OgacN62s+l8rdDhSFRe05HWtLLJ5TGclC9I1+tQngbssu0m2dmFZs+Px53AcOs9fD7TbYd4gc9AXzVqO/+g==",
- "peerDependencies": {
- "@types/react": "^17.0.0 || ^18.0.0"
- },
- "peerDependenciesMeta": {
- "@types/react": {
- "optional": true
- }
- }
- },
- "node_modules/@mui/utils": {
- "version": "5.15.12",
- "resolved": "https://registry.npmjs.org/@mui/utils/-/utils-5.15.12.tgz",
- "integrity": "sha512-8SDGCnO2DY9Yy+5bGzu00NZowSDtuyHP4H8gunhHGQoIlhlY2Z3w64wBzAOLpYw/ZhJNzksDTnS/i8qdJvxuow==",
- "dependencies": {
- "@babel/runtime": "^7.23.9",
- "@types/prop-types": "^15.7.11",
- "prop-types": "^15.8.1",
- "react-is": "^18.2.0"
- },
- "engines": {
- "node": ">=12.0.0"
- },
- "funding": {
- "type": "opencollective",
- "url": "https://opencollective.com/mui-org"
- },
- "peerDependencies": {
- "@types/react": "^17.0.0 || ^18.0.0",
- "react": "^17.0.0 || ^18.0.0"
- },
- "peerDependenciesMeta": {
- "@types/react": {
- "optional": true
- }
- }
- },
- "node_modules/@mui/x-date-pickers": {
- "version": "6.19.6",
- "resolved": "https://registry.npmjs.org/@mui/x-date-pickers/-/x-date-pickers-6.19.6.tgz",
- "integrity": "sha512-QW9AFcPi0vLpkUhmquhhyhLaBvB0AZJuu3NTrE173qNKx3Z3n51aCLY9bc7c6i4ltZMMsVRHlvzQjsve04TC8A==",
- "dependencies": {
- "@babel/runtime": "^7.23.2",
- "@mui/base": "^5.0.0-beta.22",
- "@mui/utils": "^5.14.16",
- "@types/react-transition-group": "^4.4.8",
- "clsx": "^2.0.0",
- "prop-types": "^15.8.1",
- "react-transition-group": "^4.4.5"
- },
- "engines": {
- "node": ">=14.0.0"
- },
- "funding": {
- "type": "opencollective",
- "url": "https://opencollective.com/mui"
- },
- "peerDependencies": {
- "@emotion/react": "^11.9.0",
- "@emotion/styled": "^11.8.1",
- "@mui/material": "^5.8.6",
- "@mui/system": "^5.8.0",
- "date-fns": "^2.25.0 || ^3.2.0",
- "date-fns-jalali": "^2.13.0-0",
- "dayjs": "^1.10.7",
- "luxon": "^3.0.2",
- "moment": "^2.29.4",
- "moment-hijri": "^2.1.2",
- "moment-jalaali": "^0.7.4 || ^0.8.0 || ^0.9.0 || ^0.10.0",
- "react": "^17.0.0 || ^18.0.0",
- "react-dom": "^17.0.0 || ^18.0.0"
- },
- "peerDependenciesMeta": {
- "@emotion/react": {
- "optional": true
- },
- "@emotion/styled": {
- "optional": true
- },
- "date-fns": {
- "optional": true
- },
- "date-fns-jalali": {
- "optional": true
- },
- "dayjs": {
- "optional": true
- },
- "luxon": {
- "optional": true
- },
- "moment": {
- "optional": true
- },
- "moment-hijri": {
- "optional": true
- },
- "moment-jalaali": {
- "optional": true
- }
- }
- },
- "node_modules/@nodelib/fs.scandir": {
- "version": "2.1.5",
- "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz",
- "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==",
- "dev": true,
- "dependencies": {
- "@nodelib/fs.stat": "2.0.5",
- "run-parallel": "^1.1.9"
- },
- "engines": {
- "node": ">= 8"
- }
- },
- "node_modules/@nodelib/fs.stat": {
- "version": "2.0.5",
- "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz",
- "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==",
- "dev": true,
- "engines": {
- "node": ">= 8"
- }
- },
- "node_modules/@nodelib/fs.walk": {
- "version": "1.2.8",
- "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz",
- "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==",
- "dev": true,
- "dependencies": {
- "@nodelib/fs.scandir": "2.1.5",
- "fastq": "^1.6.0"
- },
- "engines": {
- "node": ">= 8"
- }
- },
- "node_modules/@popperjs/core": {
- "version": "2.11.8",
- "resolved": "https://registry.npmjs.org/@popperjs/core/-/core-2.11.8.tgz",
- "integrity": "sha512-P1st0aksCrn9sGZhp8GMYwBnQsbvAWsZAX44oXNNvLHGqAOcoVxmjZiohstwQ7SqKnbR47akdNi+uleWD8+g6A==",
- "funding": {
- "type": "opencollective",
- "url": "https://opencollective.com/popperjs"
- }
- },
- "node_modules/@remix-run/router": {
- "version": "1.15.2",
- "resolved": "https://registry.npmjs.org/@remix-run/router/-/router-1.15.2.tgz",
- "integrity": "sha512-+Rnav+CaoTE5QJc4Jcwh5toUpnVLKYbpU6Ys0zqbakqbaLQHeglLVHPfxOiQqdNmUy5C2lXz5dwC6tQNX2JW2Q==",
- "engines": {
- "node": ">=14.0.0"
- }
- },
- "node_modules/@rollup/rollup-android-arm-eabi": {
- "version": "4.12.1",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.12.1.tgz",
- "integrity": "sha512-iU2Sya8hNn1LhsYyf0N+L4Gf9Qc+9eBTJJJsaOGUp+7x4n2M9dxTt8UvhJl3oeftSjblSlpCfvjA/IfP3g5VjQ==",
- "cpu": [
- "arm"
- ],
- "dev": true,
- "optional": true,
- "os": [
- "android"
- ]
- },
- "node_modules/@rollup/rollup-android-arm64": {
- "version": "4.12.1",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.12.1.tgz",
- "integrity": "sha512-wlzcWiH2Ir7rdMELxFE5vuM7D6TsOcJ2Yw0c3vaBR3VOsJFVTx9xvwnAvhgU5Ii8Gd6+I11qNHwndDscIm0HXg==",
- "cpu": [
- "arm64"
- ],
- "dev": true,
- "optional": true,
- "os": [
- "android"
- ]
- },
- "node_modules/@rollup/rollup-darwin-arm64": {
- "version": "4.12.1",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.12.1.tgz",
- "integrity": "sha512-YRXa1+aZIFN5BaImK+84B3uNK8C6+ynKLPgvn29X9s0LTVCByp54TB7tdSMHDR7GTV39bz1lOmlLDuedgTwwHg==",
- "cpu": [
- "arm64"
- ],
- "dev": true,
- "optional": true,
- "os": [
- "darwin"
- ]
- },
- "node_modules/@rollup/rollup-darwin-x64": {
- "version": "4.12.1",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.12.1.tgz",
- "integrity": "sha512-opjWJ4MevxeA8FhlngQWPBOvVWYNPFkq6/25rGgG+KOy0r8clYwL1CFd+PGwRqqMFVQ4/Qd3sQu5t7ucP7C/Uw==",
- "cpu": [
- "x64"
- ],
- "dev": true,
- "optional": true,
- "os": [
- "darwin"
- ]
- },
- "node_modules/@rollup/rollup-linux-arm-gnueabihf": {
- "version": "4.12.1",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.12.1.tgz",
- "integrity": "sha512-uBkwaI+gBUlIe+EfbNnY5xNyXuhZbDSx2nzzW8tRMjUmpScd6lCQYKY2V9BATHtv5Ef2OBq6SChEP8h+/cxifQ==",
- "cpu": [
- "arm"
- ],
- "dev": true,
- "optional": true,
- "os": [
- "linux"
- ]
- },
- "node_modules/@rollup/rollup-linux-arm64-gnu": {
- "version": "4.12.1",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.12.1.tgz",
- "integrity": "sha512-0bK9aG1kIg0Su7OcFTlexkVeNZ5IzEsnz1ept87a0TUgZ6HplSgkJAnFpEVRW7GRcikT4GlPV0pbtVedOaXHQQ==",
- "cpu": [
- "arm64"
- ],
- "dev": true,
- "optional": true,
- "os": [
- "linux"
- ]
- },
- "node_modules/@rollup/rollup-linux-arm64-musl": {
- "version": "4.12.1",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.12.1.tgz",
- "integrity": "sha512-qB6AFRXuP8bdkBI4D7UPUbE7OQf7u5OL+R94JE42Z2Qjmyj74FtDdLGeriRyBDhm4rQSvqAGCGC01b8Fu2LthQ==",
- "cpu": [
- "arm64"
- ],
- "dev": true,
- "optional": true,
- "os": [
- "linux"
- ]
- },
- "node_modules/@rollup/rollup-linux-riscv64-gnu": {
- "version": "4.12.1",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.12.1.tgz",
- "integrity": "sha512-sHig3LaGlpNgDj5o8uPEoGs98RII8HpNIqFtAI8/pYABO8i0nb1QzT0JDoXF/pxzqO+FkxvwkHZo9k0NJYDedg==",
- "cpu": [
- "riscv64"
- ],
- "dev": true,
- "optional": true,
- "os": [
- "linux"
- ]
- },
- "node_modules/@rollup/rollup-linux-x64-gnu": {
- "version": "4.12.1",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.12.1.tgz",
- "integrity": "sha512-nD3YcUv6jBJbBNFvSbp0IV66+ba/1teuBcu+fBBPZ33sidxitc6ErhON3JNavaH8HlswhWMC3s5rgZpM4MtPqQ==",
- "cpu": [
- "x64"
- ],
- "dev": true,
- "optional": true,
- "os": [
- "linux"
- ]
- },
- "node_modules/@rollup/rollup-linux-x64-musl": {
- "version": "4.12.1",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.12.1.tgz",
- "integrity": "sha512-7/XVZqgBby2qp/cO0TQ8uJK+9xnSdJ9ct6gSDdEr4MfABrjTyrW6Bau7HQ73a2a5tPB7hno49A0y1jhWGDN9OQ==",
- "cpu": [
- "x64"
- ],
- "dev": true,
- "optional": true,
- "os": [
- "linux"
- ]
- },
- "node_modules/@rollup/rollup-win32-arm64-msvc": {
- "version": "4.12.1",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.12.1.tgz",
- "integrity": "sha512-CYc64bnICG42UPL7TrhIwsJW4QcKkIt9gGlj21gq3VV0LL6XNb1yAdHVp1pIi9gkts9gGcT3OfUYHjGP7ETAiw==",
- "cpu": [
- "arm64"
- ],
- "dev": true,
- "optional": true,
- "os": [
- "win32"
- ]
- },
- "node_modules/@rollup/rollup-win32-ia32-msvc": {
- "version": "4.12.1",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.12.1.tgz",
- "integrity": "sha512-LN+vnlZ9g0qlHGlS920GR4zFCqAwbv2lULrR29yGaWP9u7wF5L7GqWu9Ah6/kFZPXPUkpdZwd//TNR+9XC9hvA==",
- "cpu": [
- "ia32"
- ],
- "dev": true,
- "optional": true,
- "os": [
- "win32"
- ]
- },
- "node_modules/@rollup/rollup-win32-x64-msvc": {
- "version": "4.12.1",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.12.1.tgz",
- "integrity": "sha512-n+vkrSyphvmU0qkQ6QBNXCGr2mKjhP08mPRM/Xp5Ck2FV4NrHU+y6axzDeixUrCBHVUS51TZhjqrKBBsHLKb2Q==",
- "cpu": [
- "x64"
- ],
- "dev": true,
- "optional": true,
- "os": [
- "win32"
- ]
- },
- "node_modules/@types/babel__core": {
- "version": "7.20.5",
- "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.5.tgz",
- "integrity": "sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==",
- "dev": true,
- "dependencies": {
- "@babel/parser": "^7.20.7",
- "@babel/types": "^7.20.7",
- "@types/babel__generator": "*",
- "@types/babel__template": "*",
- "@types/babel__traverse": "*"
- }
- },
- "node_modules/@types/babel__generator": {
- "version": "7.6.8",
- "resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.6.8.tgz",
- "integrity": "sha512-ASsj+tpEDsEiFr1arWrlN6V3mdfjRMZt6LtK/Vp/kreFLnr5QH5+DhvD5nINYZXzwJvXeGq+05iUXcAzVrqWtw==",
- "dev": true,
- "dependencies": {
- "@babel/types": "^7.0.0"
- }
- },
- "node_modules/@types/babel__template": {
- "version": "7.4.4",
- "resolved": "https://registry.npmjs.org/@types/babel__template/-/babel__template-7.4.4.tgz",
- "integrity": "sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A==",
- "dev": true,
- "dependencies": {
- "@babel/parser": "^7.1.0",
- "@babel/types": "^7.0.0"
- }
- },
- "node_modules/@types/babel__traverse": {
- "version": "7.20.5",
- "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.20.5.tgz",
- "integrity": "sha512-WXCyOcRtH37HAUkpXhUduaxdm82b4GSlyTqajXviN4EfiuPgNYR109xMCKvpl6zPIpua0DGlMEDCq+g8EdoheQ==",
- "dev": true,
- "dependencies": {
- "@babel/types": "^7.20.7"
- }
- },
- "node_modules/@types/estree": {
- "version": "1.0.5",
- "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.5.tgz",
- "integrity": "sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==",
- "dev": true
- },
- "node_modules/@types/json-schema": {
- "version": "7.0.15",
- "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz",
- "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==",
- "dev": true
- },
- "node_modules/@types/parse-json": {
- "version": "4.0.2",
- "resolved": "https://registry.npmjs.org/@types/parse-json/-/parse-json-4.0.2.tgz",
- "integrity": "sha512-dISoDXWWQwUquiKsyZ4Ng+HX2KsPL7LyHKHQwgGFEA3IaKac4Obd+h2a/a6waisAoepJlBcx9paWqjA8/HVjCw=="
- },
- "node_modules/@types/prop-types": {
- "version": "15.7.11",
- "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.11.tgz",
- "integrity": "sha512-ga8y9v9uyeiLdpKddhxYQkxNDrfvuPrlFb0N1qnZZByvcElJaXthF1UhvCh9TLWJBEHeNtdnbysW7Y6Uq8CVng=="
- },
- "node_modules/@types/react": {
- "version": "18.2.63",
- "resolved": "https://registry.npmjs.org/@types/react/-/react-18.2.63.tgz",
- "integrity": "sha512-ppaqODhs15PYL2nGUOaOu2RSCCB4Difu4UFrP4I3NHLloXC/ESQzQMi9nvjfT1+rudd0d2L3fQPJxRSey+rGlQ==",
- "dependencies": {
- "@types/prop-types": "*",
- "@types/scheduler": "*",
- "csstype": "^3.0.2"
- }
- },
- "node_modules/@types/react-dom": {
- "version": "18.2.20",
- "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-18.2.20.tgz",
- "integrity": "sha512-HXN/biJY8nv20Cn9ZbCFq3liERd4CozVZmKbaiZ9KiKTrWqsP7eoGDO6OOGvJQwoVFuiXaiJ7nBBjiFFbRmQMQ==",
- "dev": true,
- "dependencies": {
- "@types/react": "*"
- }
- },
- "node_modules/@types/react-helmet": {
- "version": "6.1.11",
- "resolved": "https://registry.npmjs.org/@types/react-helmet/-/react-helmet-6.1.11.tgz",
- "integrity": "sha512-0QcdGLddTERotCXo3VFlUSWO3ztraw8nZ6e3zJSgG7apwV5xt+pJUS8ewPBqT4NYB1optGLprNQzFleIY84u/g==",
- "dev": true,
- "dependencies": {
- "@types/react": "*"
- }
- },
- "node_modules/@types/react-transition-group": {
- "version": "4.4.10",
- "resolved": "https://registry.npmjs.org/@types/react-transition-group/-/react-transition-group-4.4.10.tgz",
- "integrity": "sha512-hT/+s0VQs2ojCX823m60m5f0sL5idt9SO6Tj6Dg+rdphGPIeJbJ6CxvBYkgkGKrYeDjvIpKTR38UzmtHJOGW3Q==",
- "dependencies": {
- "@types/react": "*"
- }
- },
- "node_modules/@types/scheduler": {
- "version": "0.16.8",
- "resolved": "https://registry.npmjs.org/@types/scheduler/-/scheduler-0.16.8.tgz",
- "integrity": "sha512-WZLiwShhwLRmeV6zH+GkbOFT6Z6VklCItrDioxUnv+u4Ll+8vKeFySoFyK/0ctcRpOmwAicELfmys1sDc/Rw+A=="
- },
- "node_modules/@types/semver": {
- "version": "7.5.8",
- "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.5.8.tgz",
- "integrity": "sha512-I8EUhyrgfLrcTkzV3TSsGyl1tSuPrEDzr0yd5m90UgNxQkyDXULk3b6MlQqTCpZpNtWe1K0hzclnZkTcLBe2UQ==",
- "dev": true
- },
- "node_modules/@typescript-eslint/eslint-plugin": {
- "version": "7.1.1",
- "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-7.1.1.tgz",
- "integrity": "sha512-zioDz623d0RHNhvx0eesUmGfIjzrk18nSBC8xewepKXbBvN/7c1qImV7Hg8TI1URTxKax7/zxfxj3Uph8Chcuw==",
- "dev": true,
- "dependencies": {
- "@eslint-community/regexpp": "^4.5.1",
- "@typescript-eslint/scope-manager": "7.1.1",
- "@typescript-eslint/type-utils": "7.1.1",
- "@typescript-eslint/utils": "7.1.1",
- "@typescript-eslint/visitor-keys": "7.1.1",
- "debug": "^4.3.4",
- "graphemer": "^1.4.0",
- "ignore": "^5.2.4",
- "natural-compare": "^1.4.0",
- "semver": "^7.5.4",
- "ts-api-utils": "^1.0.1"
- },
- "engines": {
- "node": "^16.0.0 || >=18.0.0"
- },
- "funding": {
- "type": "opencollective",
- "url": "https://opencollective.com/typescript-eslint"
- },
- "peerDependencies": {
- "@typescript-eslint/parser": "^7.0.0",
- "eslint": "^8.56.0"
- },
- "peerDependenciesMeta": {
- "typescript": {
- "optional": true
- }
- }
- },
- "node_modules/@typescript-eslint/parser": {
- "version": "7.1.1",
- "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-7.1.1.tgz",
- "integrity": "sha512-ZWUFyL0z04R1nAEgr9e79YtV5LbafdOtN7yapNbn1ansMyaegl2D4bL7vHoJ4HPSc4CaLwuCVas8CVuneKzplQ==",
- "dev": true,
- "dependencies": {
- "@typescript-eslint/scope-manager": "7.1.1",
- "@typescript-eslint/types": "7.1.1",
- "@typescript-eslint/typescript-estree": "7.1.1",
- "@typescript-eslint/visitor-keys": "7.1.1",
- "debug": "^4.3.4"
- },
- "engines": {
- "node": "^16.0.0 || >=18.0.0"
- },
- "funding": {
- "type": "opencollective",
- "url": "https://opencollective.com/typescript-eslint"
- },
- "peerDependencies": {
- "eslint": "^8.56.0"
- },
- "peerDependenciesMeta": {
- "typescript": {
- "optional": true
- }
- }
- },
- "node_modules/@typescript-eslint/scope-manager": {
- "version": "7.1.1",
- "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-7.1.1.tgz",
- "integrity": "sha512-cirZpA8bJMRb4WZ+rO6+mnOJrGFDd38WoXCEI57+CYBqta8Yc8aJym2i7vyqLL1vVYljgw0X27axkUXz32T8TA==",
- "dev": true,
- "dependencies": {
- "@typescript-eslint/types": "7.1.1",
- "@typescript-eslint/visitor-keys": "7.1.1"
- },
- "engines": {
- "node": "^16.0.0 || >=18.0.0"
- },
- "funding": {
- "type": "opencollective",
- "url": "https://opencollective.com/typescript-eslint"
- }
- },
- "node_modules/@typescript-eslint/type-utils": {
- "version": "7.1.1",
- "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-7.1.1.tgz",
- "integrity": "sha512-5r4RKze6XHEEhlZnJtR3GYeCh1IueUHdbrukV2KSlLXaTjuSfeVF8mZUVPLovidCuZfbVjfhi4c0DNSa/Rdg5g==",
- "dev": true,
- "dependencies": {
- "@typescript-eslint/typescript-estree": "7.1.1",
- "@typescript-eslint/utils": "7.1.1",
- "debug": "^4.3.4",
- "ts-api-utils": "^1.0.1"
- },
- "engines": {
- "node": "^16.0.0 || >=18.0.0"
- },
- "funding": {
- "type": "opencollective",
- "url": "https://opencollective.com/typescript-eslint"
- },
- "peerDependencies": {
- "eslint": "^8.56.0"
- },
- "peerDependenciesMeta": {
- "typescript": {
- "optional": true
- }
- }
- },
- "node_modules/@typescript-eslint/types": {
- "version": "7.1.1",
- "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-7.1.1.tgz",
- "integrity": "sha512-KhewzrlRMrgeKm1U9bh2z5aoL4s7K3tK5DwHDn8MHv0yQfWFz/0ZR6trrIHHa5CsF83j/GgHqzdbzCXJ3crx0Q==",
- "dev": true,
- "engines": {
- "node": "^16.0.0 || >=18.0.0"
- },
- "funding": {
- "type": "opencollective",
- "url": "https://opencollective.com/typescript-eslint"
- }
- },
- "node_modules/@typescript-eslint/typescript-estree": {
- "version": "7.1.1",
- "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-7.1.1.tgz",
- "integrity": "sha512-9ZOncVSfr+sMXVxxca2OJOPagRwT0u/UHikM2Rd6L/aB+kL/QAuTnsv6MeXtjzCJYb8PzrXarypSGIPx3Jemxw==",
- "dev": true,
- "dependencies": {
- "@typescript-eslint/types": "7.1.1",
- "@typescript-eslint/visitor-keys": "7.1.1",
- "debug": "^4.3.4",
- "globby": "^11.1.0",
- "is-glob": "^4.0.3",
- "minimatch": "9.0.3",
- "semver": "^7.5.4",
- "ts-api-utils": "^1.0.1"
- },
- "engines": {
- "node": "^16.0.0 || >=18.0.0"
- },
- "funding": {
- "type": "opencollective",
- "url": "https://opencollective.com/typescript-eslint"
- },
- "peerDependenciesMeta": {
- "typescript": {
- "optional": true
- }
- }
- },
- "node_modules/@typescript-eslint/utils": {
- "version": "7.1.1",
- "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-7.1.1.tgz",
- "integrity": "sha512-thOXM89xA03xAE0lW7alstvnyoBUbBX38YtY+zAUcpRPcq9EIhXPuJ0YTv948MbzmKh6e1AUszn5cBFK49Umqg==",
- "dev": true,
- "dependencies": {
- "@eslint-community/eslint-utils": "^4.4.0",
- "@types/json-schema": "^7.0.12",
- "@types/semver": "^7.5.0",
- "@typescript-eslint/scope-manager": "7.1.1",
- "@typescript-eslint/types": "7.1.1",
- "@typescript-eslint/typescript-estree": "7.1.1",
- "semver": "^7.5.4"
- },
- "engines": {
- "node": "^16.0.0 || >=18.0.0"
- },
- "funding": {
- "type": "opencollective",
- "url": "https://opencollective.com/typescript-eslint"
- },
- "peerDependencies": {
- "eslint": "^8.56.0"
- }
- },
- "node_modules/@typescript-eslint/visitor-keys": {
- "version": "7.1.1",
- "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-7.1.1.tgz",
- "integrity": "sha512-yTdHDQxY7cSoCcAtiBzVzxleJhkGB9NncSIyMYe2+OGON1ZsP9zOPws/Pqgopa65jvknOjlk/w7ulPlZ78PiLQ==",
- "dev": true,
- "dependencies": {
- "@typescript-eslint/types": "7.1.1",
- "eslint-visitor-keys": "^3.4.1"
- },
- "engines": {
- "node": "^16.0.0 || >=18.0.0"
- },
- "funding": {
- "type": "opencollective",
- "url": "https://opencollective.com/typescript-eslint"
- }
- },
- "node_modules/@ungap/structured-clone": {
- "version": "1.2.0",
- "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.2.0.tgz",
- "integrity": "sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ==",
- "dev": true
- },
- "node_modules/@vitejs/plugin-react": {
- "version": "4.2.1",
- "resolved": "https://registry.npmjs.org/@vitejs/plugin-react/-/plugin-react-4.2.1.tgz",
- "integrity": "sha512-oojO9IDc4nCUUi8qIR11KoQm0XFFLIwsRBwHRR4d/88IWghn1y6ckz/bJ8GHDCsYEJee8mDzqtJxh15/cisJNQ==",
- "dev": true,
- "dependencies": {
- "@babel/core": "^7.23.5",
- "@babel/plugin-transform-react-jsx-self": "^7.23.3",
- "@babel/plugin-transform-react-jsx-source": "^7.23.3",
- "@types/babel__core": "^7.20.5",
- "react-refresh": "^0.14.0"
- },
- "engines": {
- "node": "^14.18.0 || >=16.0.0"
- },
- "peerDependencies": {
- "vite": "^4.2.0 || ^5.0.0"
- }
- },
- "node_modules/acorn": {
- "version": "8.11.3",
- "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.11.3.tgz",
- "integrity": "sha512-Y9rRfJG5jcKOE0CLisYbojUjIrIEE7AGMzA/Sm4BslANhbS+cDMpgBdcPT91oJ7OuJ9hYJBx59RjbhxVnrF8Xg==",
- "dev": true,
- "bin": {
- "acorn": "bin/acorn"
- },
- "engines": {
- "node": ">=0.4.0"
- }
- },
- "node_modules/acorn-jsx": {
- "version": "5.3.2",
- "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz",
- "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==",
- "dev": true,
- "peerDependencies": {
- "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0"
- }
- },
- "node_modules/ajv": {
- "version": "6.12.6",
- "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz",
- "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==",
- "dev": true,
- "dependencies": {
- "fast-deep-equal": "^3.1.1",
- "fast-json-stable-stringify": "^2.0.0",
- "json-schema-traverse": "^0.4.1",
- "uri-js": "^4.2.2"
- },
- "funding": {
- "type": "github",
- "url": "https://github.com/sponsors/epoberezkin"
- }
- },
- "node_modules/ansi-regex": {
- "version": "5.0.1",
- "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
- "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==",
- "dev": true,
- "engines": {
- "node": ">=8"
- }
- },
- "node_modules/ansi-styles": {
- "version": "3.2.1",
- "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz",
- "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==",
- "dependencies": {
- "color-convert": "^1.9.0"
- },
- "engines": {
- "node": ">=4"
- }
- },
- "node_modules/argparse": {
- "version": "2.0.1",
- "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz",
- "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==",
- "dev": true
- },
- "node_modules/array-union": {
- "version": "2.1.0",
- "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz",
- "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==",
- "dev": true,
- "engines": {
- "node": ">=8"
- }
- },
- "node_modules/asynckit": {
- "version": "0.4.0",
- "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz",
- "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q=="
- },
- "node_modules/axios": {
- "version": "1.6.7",
- "resolved": "https://registry.npmjs.org/axios/-/axios-1.6.7.tgz",
- "integrity": "sha512-/hDJGff6/c7u0hDkvkGxR/oy6CbCs8ziCsC7SqmhjfozqiJGc8Z11wrv9z9lYfY4K8l+H9TpjcMDX0xOZmx+RA==",
- "dependencies": {
- "follow-redirects": "^1.15.4",
- "form-data": "^4.0.0",
- "proxy-from-env": "^1.1.0"
- }
- },
- "node_modules/babel-plugin-macros": {
- "version": "3.1.0",
- "resolved": "https://registry.npmjs.org/babel-plugin-macros/-/babel-plugin-macros-3.1.0.tgz",
- "integrity": "sha512-Cg7TFGpIr01vOQNODXOOaGz2NpCU5gl8x1qJFbb6hbZxR7XrcE2vtbAsTAbJ7/xwJtUuJEw8K8Zr/AE0LHlesg==",
- "dependencies": {
- "@babel/runtime": "^7.12.5",
- "cosmiconfig": "^7.0.0",
- "resolve": "^1.19.0"
- },
- "engines": {
- "node": ">=10",
- "npm": ">=6"
- }
- },
- "node_modules/balanced-match": {
- "version": "1.0.2",
- "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
- "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==",
- "dev": true
- },
- "node_modules/brace-expansion": {
- "version": "2.0.1",
- "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz",
- "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==",
- "dev": true,
- "dependencies": {
- "balanced-match": "^1.0.0"
- }
- },
- "node_modules/braces": {
- "version": "3.0.2",
- "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz",
- "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==",
- "dev": true,
- "dependencies": {
- "fill-range": "^7.0.1"
- },
- "engines": {
- "node": ">=8"
- }
- },
- "node_modules/browserslist": {
- "version": "4.23.0",
- "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.23.0.tgz",
- "integrity": "sha512-QW8HiM1shhT2GuzkvklfjcKDiWFXHOeFCIA/huJPwHsslwcydgk7X+z2zXpEijP98UCY7HbubZt5J2Zgvf0CaQ==",
- "dev": true,
- "funding": [
- {
- "type": "opencollective",
- "url": "https://opencollective.com/browserslist"
- },
- {
- "type": "tidelift",
- "url": "https://tidelift.com/funding/github/npm/browserslist"
- },
- {
- "type": "github",
- "url": "https://github.com/sponsors/ai"
- }
- ],
- "dependencies": {
- "caniuse-lite": "^1.0.30001587",
- "electron-to-chromium": "^1.4.668",
- "node-releases": "^2.0.14",
- "update-browserslist-db": "^1.0.13"
- },
- "bin": {
- "browserslist": "cli.js"
- },
- "engines": {
- "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7"
- }
- },
- "node_modules/callsites": {
- "version": "3.1.0",
- "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz",
- "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==",
- "engines": {
- "node": ">=6"
- }
- },
- "node_modules/caniuse-lite": {
- "version": "1.0.30001594",
- "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001594.tgz",
- "integrity": "sha512-VblSX6nYqyJVs8DKFMldE2IVCJjZ225LW00ydtUWwh5hk9IfkTOffO6r8gJNsH0qqqeAF8KrbMYA2VEwTlGW5g==",
- "dev": true,
- "funding": [
- {
- "type": "opencollective",
- "url": "https://opencollective.com/browserslist"
- },
- {
- "type": "tidelift",
- "url": "https://tidelift.com/funding/github/npm/caniuse-lite"
- },
- {
- "type": "github",
- "url": "https://github.com/sponsors/ai"
- }
- ]
- },
- "node_modules/chalk": {
- "version": "2.4.2",
- "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz",
- "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==",
- "dependencies": {
- "ansi-styles": "^3.2.1",
- "escape-string-regexp": "^1.0.5",
- "supports-color": "^5.3.0"
- },
- "engines": {
- "node": ">=4"
- }
- },
- "node_modules/chalk/node_modules/escape-string-regexp": {
- "version": "1.0.5",
- "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz",
- "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==",
- "engines": {
- "node": ">=0.8.0"
- }
- },
- "node_modules/clsx": {
- "version": "2.1.0",
- "resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.0.tgz",
- "integrity": "sha512-m3iNNWpd9rl3jvvcBnu70ylMdrXt8Vlq4HYadnU5fwcOtvkSQWPmj7amUcDT2qYI7risszBjI5AUIUox9D16pg==",
- "engines": {
- "node": ">=6"
- }
- },
- "node_modules/color-convert": {
- "version": "1.9.3",
- "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz",
- "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==",
- "dependencies": {
- "color-name": "1.1.3"
- }
- },
- "node_modules/color-name": {
- "version": "1.1.3",
- "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz",
- "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw=="
- },
- "node_modules/combined-stream": {
- "version": "1.0.8",
- "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz",
- "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==",
- "dependencies": {
- "delayed-stream": "~1.0.0"
- },
- "engines": {
- "node": ">= 0.8"
- }
- },
- "node_modules/concat-map": {
- "version": "0.0.1",
- "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
- "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==",
- "dev": true
- },
- "node_modules/convert-source-map": {
- "version": "1.9.0",
- "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.9.0.tgz",
- "integrity": "sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A=="
- },
- "node_modules/cosmiconfig": {
- "version": "7.1.0",
- "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-7.1.0.tgz",
- "integrity": "sha512-AdmX6xUzdNASswsFtmwSt7Vj8po9IuqXm0UXz7QKPuEUmPB4XyjGfaAr2PSuELMwkRMVH1EpIkX5bTZGRB3eCA==",
- "dependencies": {
- "@types/parse-json": "^4.0.0",
- "import-fresh": "^3.2.1",
- "parse-json": "^5.0.0",
- "path-type": "^4.0.0",
- "yaml": "^1.10.0"
- },
- "engines": {
- "node": ">=10"
- }
- },
- "node_modules/cross-fetch": {
- "version": "4.0.0",
- "resolved": "https://registry.npmjs.org/cross-fetch/-/cross-fetch-4.0.0.tgz",
- "integrity": "sha512-e4a5N8lVvuLgAWgnCrLr2PP0YyDOTHa9H/Rj54dirp61qXnNq46m82bRhNqIA5VccJtWBvPTFRV3TtvHUKPB1g==",
- "dependencies": {
- "node-fetch": "^2.6.12"
- }
- },
- "node_modules/cross-spawn": {
- "version": "7.0.3",
- "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz",
- "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==",
- "dev": true,
- "dependencies": {
- "path-key": "^3.1.0",
- "shebang-command": "^2.0.0",
- "which": "^2.0.1"
- },
- "engines": {
- "node": ">= 8"
- }
- },
- "node_modules/csstype": {
- "version": "3.1.3",
- "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz",
- "integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw=="
- },
- "node_modules/dayjs": {
- "version": "1.11.10",
- "resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.11.10.tgz",
- "integrity": "sha512-vjAczensTgRcqDERK0SR2XMwsF/tSvnvlv6VcF2GIhg6Sx4yOIt/irsr1RDJsKiIyBzJDpCoXiWWq28MqH2cnQ=="
- },
- "node_modules/debug": {
- "version": "4.3.4",
- "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz",
- "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==",
- "dev": true,
- "dependencies": {
- "ms": "2.1.2"
- },
- "engines": {
- "node": ">=6.0"
- },
- "peerDependenciesMeta": {
- "supports-color": {
- "optional": true
- }
- }
- },
- "node_modules/deep-is": {
- "version": "0.1.4",
- "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz",
- "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==",
- "dev": true
- },
- "node_modules/delayed-stream": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz",
- "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==",
- "engines": {
- "node": ">=0.4.0"
- }
- },
- "node_modules/dir-glob": {
- "version": "3.0.1",
- "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz",
- "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==",
- "dev": true,
- "dependencies": {
- "path-type": "^4.0.0"
- },
- "engines": {
- "node": ">=8"
- }
- },
- "node_modules/doctrine": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz",
- "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==",
- "dev": true,
- "dependencies": {
- "esutils": "^2.0.2"
- },
- "engines": {
- "node": ">=6.0.0"
- }
- },
- "node_modules/dom-helpers": {
- "version": "5.2.1",
- "resolved": "https://registry.npmjs.org/dom-helpers/-/dom-helpers-5.2.1.tgz",
- "integrity": "sha512-nRCa7CK3VTrM2NmGkIy4cbK7IZlgBE/PYMn55rrXefr5xXDP0LdtfPnblFDoVdcAfslJ7or6iqAUnx0CCGIWQA==",
- "dependencies": {
- "@babel/runtime": "^7.8.7",
- "csstype": "^3.0.2"
- }
- },
- "node_modules/electron-to-chromium": {
- "version": "1.4.693",
- "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.693.tgz",
- "integrity": "sha512-/if4Ueg0GUQlhCrW2ZlXwDAm40ipuKo+OgeHInlL8sbjt+hzISxZK949fZeJaVsheamrzANXvw1zQTvbxTvSHw==",
- "dev": true
- },
- "node_modules/error-ex": {
- "version": "1.3.2",
- "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz",
- "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==",
- "dependencies": {
- "is-arrayish": "^0.2.1"
- }
- },
- "node_modules/esbuild": {
- "version": "0.19.12",
- "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.19.12.tgz",
- "integrity": "sha512-aARqgq8roFBj054KvQr5f1sFu0D65G+miZRCuJyJ0G13Zwx7vRar5Zhn2tkQNzIXcBrNVsv/8stehpj+GAjgbg==",
- "dev": true,
- "hasInstallScript": true,
- "bin": {
- "esbuild": "bin/esbuild"
- },
- "engines": {
- "node": ">=12"
- },
- "optionalDependencies": {
- "@esbuild/aix-ppc64": "0.19.12",
- "@esbuild/android-arm": "0.19.12",
- "@esbuild/android-arm64": "0.19.12",
- "@esbuild/android-x64": "0.19.12",
- "@esbuild/darwin-arm64": "0.19.12",
- "@esbuild/darwin-x64": "0.19.12",
- "@esbuild/freebsd-arm64": "0.19.12",
- "@esbuild/freebsd-x64": "0.19.12",
- "@esbuild/linux-arm": "0.19.12",
- "@esbuild/linux-arm64": "0.19.12",
- "@esbuild/linux-ia32": "0.19.12",
- "@esbuild/linux-loong64": "0.19.12",
- "@esbuild/linux-mips64el": "0.19.12",
- "@esbuild/linux-ppc64": "0.19.12",
- "@esbuild/linux-riscv64": "0.19.12",
- "@esbuild/linux-s390x": "0.19.12",
- "@esbuild/linux-x64": "0.19.12",
- "@esbuild/netbsd-x64": "0.19.12",
- "@esbuild/openbsd-x64": "0.19.12",
- "@esbuild/sunos-x64": "0.19.12",
- "@esbuild/win32-arm64": "0.19.12",
- "@esbuild/win32-ia32": "0.19.12",
- "@esbuild/win32-x64": "0.19.12"
- }
- },
- "node_modules/escalade": {
- "version": "3.1.2",
- "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.2.tgz",
- "integrity": "sha512-ErCHMCae19vR8vQGe50xIsVomy19rg6gFu3+r3jkEO46suLMWBksvVyoGgQV+jOfl84ZSOSlmv6Gxa89PmTGmA==",
- "dev": true,
- "engines": {
- "node": ">=6"
- }
- },
- "node_modules/escape-string-regexp": {
- "version": "4.0.0",
- "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz",
- "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==",
- "engines": {
- "node": ">=10"
- },
- "funding": {
- "url": "https://github.com/sponsors/sindresorhus"
- }
- },
- "node_modules/eslint": {
- "version": "8.57.0",
- "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.57.0.tgz",
- "integrity": "sha512-dZ6+mexnaTIbSBZWgou51U6OmzIhYM2VcNdtiTtI7qPNZm35Akpr0f6vtw3w1Kmn5PYo+tZVfh13WrhpS6oLqQ==",
- "dev": true,
- "dependencies": {
- "@eslint-community/eslint-utils": "^4.2.0",
- "@eslint-community/regexpp": "^4.6.1",
- "@eslint/eslintrc": "^2.1.4",
- "@eslint/js": "8.57.0",
- "@humanwhocodes/config-array": "^0.11.14",
- "@humanwhocodes/module-importer": "^1.0.1",
- "@nodelib/fs.walk": "^1.2.8",
- "@ungap/structured-clone": "^1.2.0",
- "ajv": "^6.12.4",
- "chalk": "^4.0.0",
- "cross-spawn": "^7.0.2",
- "debug": "^4.3.2",
- "doctrine": "^3.0.0",
- "escape-string-regexp": "^4.0.0",
- "eslint-scope": "^7.2.2",
- "eslint-visitor-keys": "^3.4.3",
- "espree": "^9.6.1",
- "esquery": "^1.4.2",
- "esutils": "^2.0.2",
- "fast-deep-equal": "^3.1.3",
- "file-entry-cache": "^6.0.1",
- "find-up": "^5.0.0",
- "glob-parent": "^6.0.2",
- "globals": "^13.19.0",
- "graphemer": "^1.4.0",
- "ignore": "^5.2.0",
- "imurmurhash": "^0.1.4",
- "is-glob": "^4.0.0",
- "is-path-inside": "^3.0.3",
- "js-yaml": "^4.1.0",
- "json-stable-stringify-without-jsonify": "^1.0.1",
- "levn": "^0.4.1",
- "lodash.merge": "^4.6.2",
- "minimatch": "^3.1.2",
- "natural-compare": "^1.4.0",
- "optionator": "^0.9.3",
- "strip-ansi": "^6.0.1",
- "text-table": "^0.2.0"
- },
- "bin": {
- "eslint": "bin/eslint.js"
- },
- "engines": {
- "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
- },
- "funding": {
- "url": "https://opencollective.com/eslint"
- }
- },
- "node_modules/eslint-plugin-react-hooks": {
- "version": "4.6.0",
- "resolved": "https://registry.npmjs.org/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-4.6.0.tgz",
- "integrity": "sha512-oFc7Itz9Qxh2x4gNHStv3BqJq54ExXmfC+a1NjAta66IAN87Wu0R/QArgIS9qKzX3dXKPI9H5crl9QchNMY9+g==",
- "dev": true,
- "engines": {
- "node": ">=10"
- },
- "peerDependencies": {
- "eslint": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0"
- }
- },
- "node_modules/eslint-plugin-react-refresh": {
- "version": "0.4.5",
- "resolved": "https://registry.npmjs.org/eslint-plugin-react-refresh/-/eslint-plugin-react-refresh-0.4.5.tgz",
- "integrity": "sha512-D53FYKJa+fDmZMtriODxvhwrO+IOqrxoEo21gMA0sjHdU6dPVH4OhyFip9ypl8HOF5RV5KdTo+rBQLvnY2cO8w==",
- "dev": true,
- "peerDependencies": {
- "eslint": ">=7"
- }
- },
- "node_modules/eslint-scope": {
- "version": "7.2.2",
- "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.2.2.tgz",
- "integrity": "sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==",
- "dev": true,
- "dependencies": {
- "esrecurse": "^4.3.0",
- "estraverse": "^5.2.0"
- },
- "engines": {
- "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
- },
- "funding": {
- "url": "https://opencollective.com/eslint"
- }
- },
- "node_modules/eslint-visitor-keys": {
- "version": "3.4.3",
- "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz",
- "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==",
- "dev": true,
- "engines": {
- "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
- },
- "funding": {
- "url": "https://opencollective.com/eslint"
- }
- },
- "node_modules/eslint/node_modules/ansi-styles": {
- "version": "4.3.0",
- "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
- "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
- "dev": true,
- "dependencies": {
- "color-convert": "^2.0.1"
- },
- "engines": {
- "node": ">=8"
- },
- "funding": {
- "url": "https://github.com/chalk/ansi-styles?sponsor=1"
- }
- },
- "node_modules/eslint/node_modules/brace-expansion": {
- "version": "1.1.11",
- "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
- "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
- "dev": true,
- "dependencies": {
- "balanced-match": "^1.0.0",
- "concat-map": "0.0.1"
- }
- },
- "node_modules/eslint/node_modules/chalk": {
- "version": "4.1.2",
- "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
- "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
- "dev": true,
- "dependencies": {
- "ansi-styles": "^4.1.0",
- "supports-color": "^7.1.0"
- },
- "engines": {
- "node": ">=10"
- },
- "funding": {
- "url": "https://github.com/chalk/chalk?sponsor=1"
- }
- },
- "node_modules/eslint/node_modules/color-convert": {
- "version": "2.0.1",
- "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
- "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
- "dev": true,
- "dependencies": {
- "color-name": "~1.1.4"
- },
- "engines": {
- "node": ">=7.0.0"
- }
- },
- "node_modules/eslint/node_modules/color-name": {
- "version": "1.1.4",
- "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
- "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
- "dev": true
- },
- "node_modules/eslint/node_modules/globals": {
- "version": "13.24.0",
- "resolved": "https://registry.npmjs.org/globals/-/globals-13.24.0.tgz",
- "integrity": "sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==",
- "dev": true,
- "dependencies": {
- "type-fest": "^0.20.2"
- },
- "engines": {
- "node": ">=8"
- },
- "funding": {
- "url": "https://github.com/sponsors/sindresorhus"
- }
- },
- "node_modules/eslint/node_modules/has-flag": {
- "version": "4.0.0",
- "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
- "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
- "dev": true,
- "engines": {
- "node": ">=8"
- }
- },
- "node_modules/eslint/node_modules/minimatch": {
- "version": "3.1.2",
- "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
- "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==",
- "dev": true,
- "dependencies": {
- "brace-expansion": "^1.1.7"
- },
- "engines": {
- "node": "*"
- }
- },
- "node_modules/eslint/node_modules/supports-color": {
- "version": "7.2.0",
- "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
- "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
- "dev": true,
- "dependencies": {
- "has-flag": "^4.0.0"
- },
- "engines": {
- "node": ">=8"
- }
- },
- "node_modules/espree": {
- "version": "9.6.1",
- "resolved": "https://registry.npmjs.org/espree/-/espree-9.6.1.tgz",
- "integrity": "sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==",
- "dev": true,
- "dependencies": {
- "acorn": "^8.9.0",
- "acorn-jsx": "^5.3.2",
- "eslint-visitor-keys": "^3.4.1"
- },
- "engines": {
- "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
- },
- "funding": {
- "url": "https://opencollective.com/eslint"
- }
- },
- "node_modules/esquery": {
- "version": "1.5.0",
- "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.5.0.tgz",
- "integrity": "sha512-YQLXUplAwJgCydQ78IMJywZCceoqk1oH01OERdSAJc/7U2AylwjhSCLDEtqwg811idIS/9fIU5GjG73IgjKMVg==",
- "dev": true,
- "dependencies": {
- "estraverse": "^5.1.0"
- },
- "engines": {
- "node": ">=0.10"
- }
- },
- "node_modules/esrecurse": {
- "version": "4.3.0",
- "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz",
- "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==",
- "dev": true,
- "dependencies": {
- "estraverse": "^5.2.0"
- },
- "engines": {
- "node": ">=4.0"
- }
- },
- "node_modules/estraverse": {
- "version": "5.3.0",
- "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz",
- "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==",
- "dev": true,
- "engines": {
- "node": ">=4.0"
- }
- },
- "node_modules/esutils": {
- "version": "2.0.3",
- "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz",
- "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==",
- "dev": true,
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/fast-deep-equal": {
- "version": "3.1.3",
- "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz",
- "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==",
- "dev": true
- },
- "node_modules/fast-glob": {
- "version": "3.3.2",
- "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.2.tgz",
- "integrity": "sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow==",
- "dev": true,
- "dependencies": {
- "@nodelib/fs.stat": "^2.0.2",
- "@nodelib/fs.walk": "^1.2.3",
- "glob-parent": "^5.1.2",
- "merge2": "^1.3.0",
- "micromatch": "^4.0.4"
- },
- "engines": {
- "node": ">=8.6.0"
- }
- },
- "node_modules/fast-glob/node_modules/glob-parent": {
- "version": "5.1.2",
- "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz",
- "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==",
- "dev": true,
- "dependencies": {
- "is-glob": "^4.0.1"
- },
- "engines": {
- "node": ">= 6"
- }
- },
- "node_modules/fast-json-stable-stringify": {
- "version": "2.1.0",
- "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz",
- "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==",
- "dev": true
- },
- "node_modules/fast-levenshtein": {
- "version": "2.0.6",
- "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz",
- "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==",
- "dev": true
- },
- "node_modules/fastq": {
- "version": "1.17.1",
- "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.17.1.tgz",
- "integrity": "sha512-sRVD3lWVIXWg6By68ZN7vho9a1pQcN/WBFaAAsDDFzlJjvoGx0P8z7V1t72grFJfJhu3YPZBuu25f7Kaw2jN1w==",
- "dev": true,
- "dependencies": {
- "reusify": "^1.0.4"
- }
- },
- "node_modules/file-entry-cache": {
- "version": "6.0.1",
- "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz",
- "integrity": "sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==",
- "dev": true,
- "dependencies": {
- "flat-cache": "^3.0.4"
- },
- "engines": {
- "node": "^10.12.0 || >=12.0.0"
- }
- },
- "node_modules/fill-range": {
- "version": "7.0.1",
- "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz",
- "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==",
- "dev": true,
- "dependencies": {
- "to-regex-range": "^5.0.1"
- },
- "engines": {
- "node": ">=8"
- }
- },
- "node_modules/find-root": {
- "version": "1.1.0",
- "resolved": "https://registry.npmjs.org/find-root/-/find-root-1.1.0.tgz",
- "integrity": "sha512-NKfW6bec6GfKc0SGx1e07QZY9PE99u0Bft/0rzSD5k3sO/vwkVUpDUKVm5Gpp5Ue3YfShPFTX2070tDs5kB9Ng=="
- },
- "node_modules/find-up": {
- "version": "5.0.0",
- "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz",
- "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==",
- "dev": true,
- "dependencies": {
- "locate-path": "^6.0.0",
- "path-exists": "^4.0.0"
- },
- "engines": {
- "node": ">=10"
- },
- "funding": {
- "url": "https://github.com/sponsors/sindresorhus"
- }
- },
- "node_modules/flat-cache": {
- "version": "3.2.0",
- "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.2.0.tgz",
- "integrity": "sha512-CYcENa+FtcUKLmhhqyctpclsq7QF38pKjZHsGNiSQF5r4FtoKDWabFDl3hzaEQMvT1LHEysw5twgLvpYYb4vbw==",
- "dev": true,
- "dependencies": {
- "flatted": "^3.2.9",
- "keyv": "^4.5.3",
- "rimraf": "^3.0.2"
- },
- "engines": {
- "node": "^10.12.0 || >=12.0.0"
- }
- },
- "node_modules/flatted": {
- "version": "3.3.1",
- "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.1.tgz",
- "integrity": "sha512-X8cqMLLie7KsNUDSdzeN8FYK9rEt4Dt67OsG/DNGnYTSDBG4uFAJFBnUeiV+zCVAvwFy56IjM9sH51jVaEhNxw==",
- "dev": true
- },
- "node_modules/follow-redirects": {
- "version": "1.15.6",
- "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.6.tgz",
- "integrity": "sha512-wWN62YITEaOpSK584EZXJafH1AGpO8RVgElfkuXbTOrPX4fIfOyEpW/CsiNd8JdYrAoOvafRTOEnvsO++qCqFA==",
- "funding": [
- {
- "type": "individual",
- "url": "https://github.com/sponsors/RubenVerborgh"
- }
- ],
- "engines": {
- "node": ">=4.0"
- },
- "peerDependenciesMeta": {
- "debug": {
- "optional": true
- }
- }
- },
- "node_modules/form-data": {
- "version": "4.0.0",
- "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz",
- "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==",
- "dependencies": {
- "asynckit": "^0.4.0",
- "combined-stream": "^1.0.8",
- "mime-types": "^2.1.12"
- },
- "engines": {
- "node": ">= 6"
- }
- },
- "node_modules/fs.realpath": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
- "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==",
- "dev": true
- },
- "node_modules/fsevents": {
- "version": "2.3.3",
- "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz",
- "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==",
- "dev": true,
- "hasInstallScript": true,
- "optional": true,
- "os": [
- "darwin"
- ],
- "engines": {
- "node": "^8.16.0 || ^10.6.0 || >=11.0.0"
- }
- },
- "node_modules/function-bind": {
- "version": "1.1.2",
- "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz",
- "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==",
- "funding": {
- "url": "https://github.com/sponsors/ljharb"
- }
- },
- "node_modules/gensync": {
- "version": "1.0.0-beta.2",
- "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz",
- "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==",
- "dev": true,
- "engines": {
- "node": ">=6.9.0"
- }
- },
- "node_modules/glob": {
- "version": "7.2.3",
- "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz",
- "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==",
- "dev": true,
- "dependencies": {
- "fs.realpath": "^1.0.0",
- "inflight": "^1.0.4",
- "inherits": "2",
- "minimatch": "^3.1.1",
- "once": "^1.3.0",
- "path-is-absolute": "^1.0.0"
- },
- "engines": {
- "node": "*"
- },
- "funding": {
- "url": "https://github.com/sponsors/isaacs"
- }
- },
- "node_modules/glob-parent": {
- "version": "6.0.2",
- "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz",
- "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==",
- "dev": true,
- "dependencies": {
- "is-glob": "^4.0.3"
- },
- "engines": {
- "node": ">=10.13.0"
- }
- },
- "node_modules/glob/node_modules/brace-expansion": {
- "version": "1.1.11",
- "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
- "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
- "dev": true,
- "dependencies": {
- "balanced-match": "^1.0.0",
- "concat-map": "0.0.1"
- }
- },
- "node_modules/glob/node_modules/minimatch": {
- "version": "3.1.2",
- "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
- "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==",
- "dev": true,
- "dependencies": {
- "brace-expansion": "^1.1.7"
- },
- "engines": {
- "node": "*"
- }
- },
- "node_modules/globals": {
- "version": "11.12.0",
- "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz",
- "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==",
- "dev": true,
- "engines": {
- "node": ">=4"
- }
- },
- "node_modules/globby": {
- "version": "11.1.0",
- "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz",
- "integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==",
- "dev": true,
- "dependencies": {
- "array-union": "^2.1.0",
- "dir-glob": "^3.0.1",
- "fast-glob": "^3.2.9",
- "ignore": "^5.2.0",
- "merge2": "^1.4.1",
- "slash": "^3.0.0"
- },
- "engines": {
- "node": ">=10"
- },
- "funding": {
- "url": "https://github.com/sponsors/sindresorhus"
- }
- },
- "node_modules/graphemer": {
- "version": "1.4.0",
- "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz",
- "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==",
- "dev": true
- },
- "node_modules/has-flag": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz",
- "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==",
- "engines": {
- "node": ">=4"
- }
- },
- "node_modules/hasown": {
- "version": "2.0.1",
- "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.1.tgz",
- "integrity": "sha512-1/th4MHjnwncwXsIW6QMzlvYL9kG5e/CpVvLRZe4XPa8TOUNbCELqmvhDmnkNsAjwaG4+I8gJJL0JBvTTLO9qA==",
- "dependencies": {
- "function-bind": "^1.1.2"
- },
- "engines": {
- "node": ">= 0.4"
- }
- },
- "node_modules/hoist-non-react-statics": {
- "version": "3.3.2",
- "resolved": "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz",
- "integrity": "sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw==",
- "dependencies": {
- "react-is": "^16.7.0"
- }
- },
- "node_modules/hoist-non-react-statics/node_modules/react-is": {
- "version": "16.13.1",
- "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz",
- "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ=="
- },
- "node_modules/html-parse-stringify": {
- "version": "3.0.1",
- "resolved": "https://registry.npmjs.org/html-parse-stringify/-/html-parse-stringify-3.0.1.tgz",
- "integrity": "sha512-KknJ50kTInJ7qIScF3jeaFRpMpE8/lfiTdzf/twXyPBLAGrLRTmkz3AdTnKeh40X8k9L2fdYwEp/42WGXIRGcg==",
- "dependencies": {
- "void-elements": "3.1.0"
- }
- },
- "node_modules/i18next": {
- "version": "23.10.0",
- "resolved": "https://registry.npmjs.org/i18next/-/i18next-23.10.0.tgz",
- "integrity": "sha512-/TgHOqsa7/9abUKJjdPeydoyDc0oTi/7u9F8lMSj6ufg4cbC1Oj3f/Jja7zj7WRIhEQKB7Q4eN6y68I9RDxxGQ==",
- "funding": [
- {
- "type": "individual",
- "url": "https://locize.com"
- },
- {
- "type": "individual",
- "url": "https://locize.com/i18next.html"
- },
- {
- "type": "individual",
- "url": "https://www.i18next.com/how-to/faq#i18next-is-awesome.-how-can-i-support-the-project"
- }
- ],
- "dependencies": {
- "@babel/runtime": "^7.23.2"
- }
- },
- "node_modules/i18next-browser-languagedetector": {
- "version": "7.2.0",
- "resolved": "https://registry.npmjs.org/i18next-browser-languagedetector/-/i18next-browser-languagedetector-7.2.0.tgz",
- "integrity": "sha512-U00DbDtFIYD3wkWsr2aVGfXGAj2TgnELzOX9qv8bT0aJtvPV9CRO77h+vgmHFBMe7LAxdwvT/7VkCWGya6L3tA==",
- "dependencies": {
- "@babel/runtime": "^7.23.2"
- }
- },
- "node_modules/i18next-http-backend": {
- "version": "2.5.0",
- "resolved": "https://registry.npmjs.org/i18next-http-backend/-/i18next-http-backend-2.5.0.tgz",
- "integrity": "sha512-Z/aQsGZk1gSxt2/DztXk92DuDD20J+rNudT7ZCdTrNOiK8uQppfvdjq9+DFQfpAnFPn3VZS+KQIr1S/W1KxhpQ==",
- "dependencies": {
- "cross-fetch": "4.0.0"
- }
- },
- "node_modules/ignore": {
- "version": "5.3.1",
- "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.1.tgz",
- "integrity": "sha512-5Fytz/IraMjqpwfd34ke28PTVMjZjJG2MPn5t7OE4eUCUNf8BAa7b5WUS9/Qvr6mwOQS7Mk6vdsMno5he+T8Xw==",
- "dev": true,
- "engines": {
- "node": ">= 4"
- }
- },
- "node_modules/immediate": {
- "version": "3.0.6",
- "resolved": "https://registry.npmjs.org/immediate/-/immediate-3.0.6.tgz",
- "integrity": "sha512-XXOFtyqDjNDAQxVfYxuF7g9Il/IbWmmlQg2MYKOH8ExIT1qg6xc4zyS3HaEEATgs1btfzxq15ciUiY7gjSXRGQ=="
- },
- "node_modules/import-fresh": {
- "version": "3.3.0",
- "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz",
- "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==",
- "dependencies": {
- "parent-module": "^1.0.0",
- "resolve-from": "^4.0.0"
- },
- "engines": {
- "node": ">=6"
- },
- "funding": {
- "url": "https://github.com/sponsors/sindresorhus"
- }
- },
- "node_modules/imurmurhash": {
- "version": "0.1.4",
- "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz",
- "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==",
- "dev": true,
- "engines": {
- "node": ">=0.8.19"
- }
- },
- "node_modules/inflight": {
- "version": "1.0.6",
- "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz",
- "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==",
- "dev": true,
- "dependencies": {
- "once": "^1.3.0",
- "wrappy": "1"
- }
- },
- "node_modules/inherits": {
- "version": "2.0.4",
- "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
- "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==",
- "dev": true
- },
- "node_modules/invariant": {
- "version": "2.2.4",
- "resolved": "https://registry.npmjs.org/invariant/-/invariant-2.2.4.tgz",
- "integrity": "sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA==",
- "dependencies": {
- "loose-envify": "^1.0.0"
- }
- },
- "node_modules/is-arrayish": {
- "version": "0.2.1",
- "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz",
- "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg=="
- },
- "node_modules/is-core-module": {
- "version": "2.13.1",
- "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.13.1.tgz",
- "integrity": "sha512-hHrIjvZsftOsvKSn2TRYl63zvxsgE0K+0mYMoH6gD4omR5IWB2KynivBQczo3+wF1cCkjzvptnI9Q0sPU66ilw==",
- "dependencies": {
- "hasown": "^2.0.0"
- },
- "funding": {
- "url": "https://github.com/sponsors/ljharb"
- }
- },
- "node_modules/is-extglob": {
- "version": "2.1.1",
- "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz",
- "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==",
- "dev": true,
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/is-glob": {
- "version": "4.0.3",
- "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz",
- "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==",
- "dev": true,
- "dependencies": {
- "is-extglob": "^2.1.1"
- },
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/is-number": {
- "version": "7.0.0",
- "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz",
- "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==",
- "dev": true,
- "engines": {
- "node": ">=0.12.0"
- }
- },
- "node_modules/is-path-inside": {
- "version": "3.0.3",
- "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz",
- "integrity": "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==",
- "dev": true,
- "engines": {
- "node": ">=8"
- }
- },
- "node_modules/isexe": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz",
- "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==",
- "dev": true
- },
- "node_modules/js-tokens": {
- "version": "4.0.0",
- "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz",
- "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ=="
- },
- "node_modules/js-yaml": {
- "version": "4.1.0",
- "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz",
- "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==",
- "dev": true,
- "dependencies": {
- "argparse": "^2.0.1"
- },
- "bin": {
- "js-yaml": "bin/js-yaml.js"
- }
- },
- "node_modules/jsesc": {
- "version": "2.5.2",
- "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz",
- "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==",
- "dev": true,
- "bin": {
- "jsesc": "bin/jsesc"
- },
- "engines": {
- "node": ">=4"
- }
- },
- "node_modules/json-buffer": {
- "version": "3.0.1",
- "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz",
- "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==",
- "dev": true
- },
- "node_modules/json-parse-even-better-errors": {
- "version": "2.3.1",
- "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz",
- "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w=="
- },
- "node_modules/json-schema-traverse": {
- "version": "0.4.1",
- "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz",
- "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==",
- "dev": true
- },
- "node_modules/json-stable-stringify-without-jsonify": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz",
- "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==",
- "dev": true
- },
- "node_modules/json5": {
- "version": "2.2.3",
- "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz",
- "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==",
- "dev": true,
- "bin": {
- "json5": "lib/cli.js"
- },
- "engines": {
- "node": ">=6"
- }
- },
- "node_modules/keyv": {
- "version": "4.5.4",
- "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz",
- "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==",
- "dev": true,
- "dependencies": {
- "json-buffer": "3.0.1"
- }
- },
- "node_modules/levn": {
- "version": "0.4.1",
- "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz",
- "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==",
- "dev": true,
- "dependencies": {
- "prelude-ls": "^1.2.1",
- "type-check": "~0.4.0"
- },
- "engines": {
- "node": ">= 0.8.0"
- }
- },
- "node_modules/lie": {
- "version": "3.1.1",
- "resolved": "https://registry.npmjs.org/lie/-/lie-3.1.1.tgz",
- "integrity": "sha512-RiNhHysUjhrDQntfYSfY4MU24coXXdEOgw9WGcKHNeEwffDYbF//u87M1EWaMGzuFoSbqW0C9C6lEEhDOAswfw==",
- "dependencies": {
- "immediate": "~3.0.5"
- }
- },
- "node_modules/lines-and-columns": {
- "version": "1.2.4",
- "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz",
- "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg=="
- },
- "node_modules/localforage": {
- "version": "1.10.0",
- "resolved": "https://registry.npmjs.org/localforage/-/localforage-1.10.0.tgz",
- "integrity": "sha512-14/H1aX7hzBBmmh7sGPd+AOMkkIrHM3Z1PAyGgZigA1H1p5O5ANnMyWzvpAETtG68/dC4pC0ncy3+PPGzXZHPg==",
- "dependencies": {
- "lie": "3.1.1"
- }
- },
- "node_modules/locate-path": {
- "version": "6.0.0",
- "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz",
- "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==",
- "dev": true,
- "dependencies": {
- "p-locate": "^5.0.0"
- },
- "engines": {
- "node": ">=10"
- },
- "funding": {
- "url": "https://github.com/sponsors/sindresorhus"
- }
- },
- "node_modules/lodash.merge": {
- "version": "4.6.2",
- "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz",
- "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==",
- "dev": true
- },
- "node_modules/loose-envify": {
- "version": "1.4.0",
- "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz",
- "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==",
- "dependencies": {
- "js-tokens": "^3.0.0 || ^4.0.0"
- },
- "bin": {
- "loose-envify": "cli.js"
- }
- },
- "node_modules/lru-cache": {
- "version": "5.1.1",
- "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz",
- "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==",
- "dev": true,
- "dependencies": {
- "yallist": "^3.0.2"
- }
- },
- "node_modules/match-sorter": {
- "version": "6.3.4",
- "resolved": "https://registry.npmjs.org/match-sorter/-/match-sorter-6.3.4.tgz",
- "integrity": "sha512-jfZW7cWS5y/1xswZo8VBOdudUiSd9nifYRWphc9M5D/ee4w4AoXLgBEdRbgVaxbMuagBPeUC5y2Hi8DO6o9aDg==",
- "dependencies": {
- "@babel/runtime": "^7.23.8",
- "remove-accents": "0.5.0"
- }
- },
- "node_modules/merge2": {
- "version": "1.4.1",
- "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz",
- "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==",
- "dev": true,
- "engines": {
- "node": ">= 8"
- }
- },
- "node_modules/micromatch": {
- "version": "4.0.5",
- "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz",
- "integrity": "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==",
- "dev": true,
- "dependencies": {
- "braces": "^3.0.2",
- "picomatch": "^2.3.1"
- },
- "engines": {
- "node": ">=8.6"
- }
- },
- "node_modules/mime-db": {
- "version": "1.52.0",
- "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz",
- "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==",
- "engines": {
- "node": ">= 0.6"
- }
- },
- "node_modules/mime-types": {
- "version": "2.1.35",
- "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz",
- "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==",
- "dependencies": {
- "mime-db": "1.52.0"
- },
- "engines": {
- "node": ">= 0.6"
- }
- },
- "node_modules/minimatch": {
- "version": "9.0.3",
- "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.3.tgz",
- "integrity": "sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg==",
- "dev": true,
- "dependencies": {
- "brace-expansion": "^2.0.1"
- },
- "engines": {
- "node": ">=16 || 14 >=14.17"
- },
- "funding": {
- "url": "https://github.com/sponsors/isaacs"
- }
- },
- "node_modules/ms": {
- "version": "2.1.2",
- "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
- "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==",
- "dev": true
- },
- "node_modules/nanoid": {
- "version": "3.3.7",
- "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.7.tgz",
- "integrity": "sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==",
- "dev": true,
- "funding": [
- {
- "type": "github",
- "url": "https://github.com/sponsors/ai"
- }
- ],
- "bin": {
- "nanoid": "bin/nanoid.cjs"
- },
- "engines": {
- "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1"
- }
- },
- "node_modules/natural-compare": {
- "version": "1.4.0",
- "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz",
- "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==",
- "dev": true
- },
- "node_modules/node-fetch": {
- "version": "2.7.0",
- "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz",
- "integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==",
- "dependencies": {
- "whatwg-url": "^5.0.0"
- },
- "engines": {
- "node": "4.x || >=6.0.0"
- },
- "peerDependencies": {
- "encoding": "^0.1.0"
- },
- "peerDependenciesMeta": {
- "encoding": {
- "optional": true
- }
- }
- },
- "node_modules/node-releases": {
- "version": "2.0.14",
- "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.14.tgz",
- "integrity": "sha512-y10wOWt8yZpqXmOgRo77WaHEmhYQYGNA6y421PKsKYWEK8aW+cqAphborZDhqfyKrbZEN92CN1X2KbafY2s7Yw==",
- "dev": true
- },
- "node_modules/object-assign": {
- "version": "4.1.1",
- "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
- "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==",
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/once": {
- "version": "1.4.0",
- "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
- "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==",
- "dev": true,
- "dependencies": {
- "wrappy": "1"
- }
- },
- "node_modules/optionator": {
- "version": "0.9.3",
- "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.3.tgz",
- "integrity": "sha512-JjCoypp+jKn1ttEFExxhetCKeJt9zhAgAve5FXHixTvFDW/5aEktX9bufBKLRRMdU7bNtpLfcGu94B3cdEJgjg==",
- "dev": true,
- "dependencies": {
- "@aashutoshrathi/word-wrap": "^1.2.3",
- "deep-is": "^0.1.3",
- "fast-levenshtein": "^2.0.6",
- "levn": "^0.4.1",
- "prelude-ls": "^1.2.1",
- "type-check": "^0.4.0"
- },
- "engines": {
- "node": ">= 0.8.0"
- }
- },
- "node_modules/p-limit": {
- "version": "3.1.0",
- "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz",
- "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==",
- "dev": true,
- "dependencies": {
- "yocto-queue": "^0.1.0"
- },
- "engines": {
- "node": ">=10"
- },
- "funding": {
- "url": "https://github.com/sponsors/sindresorhus"
- }
- },
- "node_modules/p-locate": {
- "version": "5.0.0",
- "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz",
- "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==",
- "dev": true,
- "dependencies": {
- "p-limit": "^3.0.2"
- },
- "engines": {
- "node": ">=10"
- },
- "funding": {
- "url": "https://github.com/sponsors/sindresorhus"
- }
- },
- "node_modules/parent-module": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz",
- "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==",
- "dependencies": {
- "callsites": "^3.0.0"
- },
- "engines": {
- "node": ">=6"
- }
- },
- "node_modules/parse-json": {
- "version": "5.2.0",
- "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz",
- "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==",
- "dependencies": {
- "@babel/code-frame": "^7.0.0",
- "error-ex": "^1.3.1",
- "json-parse-even-better-errors": "^2.3.0",
- "lines-and-columns": "^1.1.6"
- },
- "engines": {
- "node": ">=8"
- },
- "funding": {
- "url": "https://github.com/sponsors/sindresorhus"
- }
- },
- "node_modules/path-exists": {
- "version": "4.0.0",
- "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz",
- "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==",
- "dev": true,
- "engines": {
- "node": ">=8"
- }
- },
- "node_modules/path-is-absolute": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz",
- "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==",
- "dev": true,
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/path-key": {
- "version": "3.1.1",
- "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz",
- "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==",
- "dev": true,
- "engines": {
- "node": ">=8"
- }
- },
- "node_modules/path-parse": {
- "version": "1.0.7",
- "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz",
- "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw=="
- },
- "node_modules/path-type": {
- "version": "4.0.0",
- "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz",
- "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==",
- "engines": {
- "node": ">=8"
- }
- },
- "node_modules/picocolors": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz",
- "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==",
- "dev": true
- },
- "node_modules/picomatch": {
- "version": "2.3.1",
- "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz",
- "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==",
- "dev": true,
- "engines": {
- "node": ">=8.6"
- },
- "funding": {
- "url": "https://github.com/sponsors/jonschlinkert"
- }
- },
- "node_modules/postcss": {
- "version": "8.4.35",
- "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.35.tgz",
- "integrity": "sha512-u5U8qYpBCpN13BsiEB0CbR1Hhh4Gc0zLFuedrHJKMctHCHAGrMdG0PRM/KErzAL3CU6/eckEtmHNB3x6e3c0vA==",
- "dev": true,
- "funding": [
- {
- "type": "opencollective",
- "url": "https://opencollective.com/postcss/"
- },
- {
- "type": "tidelift",
- "url": "https://tidelift.com/funding/github/npm/postcss"
- },
- {
- "type": "github",
- "url": "https://github.com/sponsors/ai"
- }
- ],
- "dependencies": {
- "nanoid": "^3.3.7",
- "picocolors": "^1.0.0",
- "source-map-js": "^1.0.2"
- },
- "engines": {
- "node": "^10 || ^12 || >=14"
- }
- },
- "node_modules/prelude-ls": {
- "version": "1.2.1",
- "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz",
- "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==",
- "dev": true,
- "engines": {
- "node": ">= 0.8.0"
- }
- },
- "node_modules/prop-types": {
- "version": "15.8.1",
- "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz",
- "integrity": "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==",
- "dependencies": {
- "loose-envify": "^1.4.0",
- "object-assign": "^4.1.1",
- "react-is": "^16.13.1"
- }
- },
- "node_modules/prop-types/node_modules/react-is": {
- "version": "16.13.1",
- "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz",
- "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ=="
- },
- "node_modules/proxy-from-env": {
- "version": "1.1.0",
- "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz",
- "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg=="
- },
- "node_modules/punycode": {
- "version": "2.3.1",
- "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz",
- "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==",
- "dev": true,
- "engines": {
- "node": ">=6"
- }
- },
- "node_modules/queue-microtask": {
- "version": "1.2.3",
- "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz",
- "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==",
- "dev": true,
- "funding": [
- {
- "type": "github",
- "url": "https://github.com/sponsors/feross"
- },
- {
- "type": "patreon",
- "url": "https://www.patreon.com/feross"
- },
- {
- "type": "consulting",
- "url": "https://feross.org/support"
- }
- ]
- },
- "node_modules/react": {
- "version": "18.2.0",
- "resolved": "https://registry.npmjs.org/react/-/react-18.2.0.tgz",
- "integrity": "sha512-/3IjMdb2L9QbBdWiW5e3P2/npwMBaU9mHCSCUzNln0ZCYbcfTsGbTJrU/kGemdH2IWmB2ioZ+zkxtmq6g09fGQ==",
- "dependencies": {
- "loose-envify": "^1.1.0"
- },
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/react-dom": {
- "version": "18.2.0",
- "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.2.0.tgz",
- "integrity": "sha512-6IMTriUmvsjHUjNtEDudZfuDQUoWXVxKHhlEGSk81n4YFS+r/Kl99wXiwlVXtPBtJenozv2P+hxDsw9eA7Xo6g==",
- "dependencies": {
- "loose-envify": "^1.1.0",
- "scheduler": "^0.23.0"
- },
- "peerDependencies": {
- "react": "^18.2.0"
- }
- },
- "node_modules/react-fast-compare": {
- "version": "3.2.2",
- "resolved": "https://registry.npmjs.org/react-fast-compare/-/react-fast-compare-3.2.2.tgz",
- "integrity": "sha512-nsO+KSNgo1SbJqJEYRE9ERzo7YtYbou/OqjSQKxV7jcKox7+usiUVZOAC+XnDOABXggQTno0Y1CpVnuWEc1boQ=="
- },
- "node_modules/react-helmet": {
- "version": "6.1.0",
- "resolved": "https://registry.npmjs.org/react-helmet/-/react-helmet-6.1.0.tgz",
- "integrity": "sha512-4uMzEY9nlDlgxr61NL3XbKRy1hEkXmKNXhjbAIOVw5vcFrsdYbH2FEwcNyWvWinl103nXgzYNlns9ca+8kFiWw==",
- "dependencies": {
- "object-assign": "^4.1.1",
- "prop-types": "^15.7.2",
- "react-fast-compare": "^3.1.1",
- "react-side-effect": "^2.1.0"
- },
- "peerDependencies": {
- "react": ">=16.3.0"
- }
- },
- "node_modules/react-helmet-async": {
- "version": "2.0.4",
- "resolved": "https://registry.npmjs.org/react-helmet-async/-/react-helmet-async-2.0.4.tgz",
- "integrity": "sha512-yxjQMWposw+akRfvpl5+8xejl4JtUlHnEBcji6u8/e6oc7ozT+P9PNTWMhCbz2y9tc5zPegw2BvKjQA+NwdEjQ==",
- "dependencies": {
- "invariant": "^2.2.4",
- "react-fast-compare": "^3.2.2",
- "shallowequal": "^1.1.0"
- },
- "peerDependencies": {
- "react": "^16.6.0 || ^17.0.0 || ^18.0.0",
- "react-dom": "^16.6.0 || ^17.0.0 || ^18.0.0"
- }
- },
- "node_modules/react-i18next": {
- "version": "14.0.7",
- "resolved": "https://registry.npmjs.org/react-i18next/-/react-i18next-14.0.7.tgz",
- "integrity": "sha512-8VN7IUaTB5t6ut/1LZtdHstQl1KSFStZRw3UGAERfkToVKLF4yvQVMz/Tq/YG3VR2zaWHEU8WrvIbVGhCqv90Q==",
- "dependencies": {
- "@babel/runtime": "^7.23.9",
- "html-parse-stringify": "^3.0.1"
- },
- "peerDependencies": {
- "i18next": ">= 23.2.3",
- "react": ">= 16.8.0"
- },
- "peerDependenciesMeta": {
- "react-dom": {
- "optional": true
- },
- "react-native": {
- "optional": true
- }
- }
- },
- "node_modules/react-is": {
- "version": "18.2.0",
- "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.2.0.tgz",
- "integrity": "sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w=="
- },
- "node_modules/react-refresh": {
- "version": "0.14.0",
- "resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.14.0.tgz",
- "integrity": "sha512-wViHqhAd8OHeLS/IRMJjTSDHF3U9eWi62F/MledQGPdJGDhodXJ9PBLNGr6WWL7qlH12Mt3TyTpbS+hGXMjCzQ==",
- "dev": true,
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/react-router": {
- "version": "6.22.2",
- "resolved": "https://registry.npmjs.org/react-router/-/react-router-6.22.2.tgz",
- "integrity": "sha512-YD3Dzprzpcq+tBMHBS822tCjnWD3iIZbTeSXMY9LPSG541EfoBGyZ3bS25KEnaZjLcmQpw2AVLkFyfgXY8uvcw==",
- "dependencies": {
- "@remix-run/router": "1.15.2"
- },
- "engines": {
- "node": ">=14.0.0"
- },
- "peerDependencies": {
- "react": ">=16.8"
- }
- },
- "node_modules/react-router-dom": {
- "version": "6.22.2",
- "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-6.22.2.tgz",
- "integrity": "sha512-WgqxD2qySEIBPZ3w0sHH+PUAiamDeszls9tzqMPBDA1YYVucTBXLU7+gtRfcSnhe92A3glPnvSxK2dhNoAVOIQ==",
- "dependencies": {
- "@remix-run/router": "1.15.2",
- "react-router": "6.22.2"
- },
- "engines": {
- "node": ">=14.0.0"
- },
- "peerDependencies": {
- "react": ">=16.8",
- "react-dom": ">=16.8"
- }
- },
- "node_modules/react-side-effect": {
- "version": "2.1.2",
- "resolved": "https://registry.npmjs.org/react-side-effect/-/react-side-effect-2.1.2.tgz",
- "integrity": "sha512-PVjOcvVOyIILrYoyGEpDN3vmYNLdy1CajSFNt4TDsVQC5KpTijDvWVoR+/7Rz2xT978D8/ZtFceXxzsPwZEDvw==",
- "peerDependencies": {
- "react": "^16.3.0 || ^17.0.0 || ^18.0.0"
- }
- },
- "node_modules/react-transition-group": {
- "version": "4.4.5",
- "resolved": "https://registry.npmjs.org/react-transition-group/-/react-transition-group-4.4.5.tgz",
- "integrity": "sha512-pZcd1MCJoiKiBR2NRxeCRg13uCXbydPnmB4EOeRrY7480qNWO8IIgQG6zlDkm6uRMsURXPuKq0GWtiM59a5Q6g==",
- "dependencies": {
- "@babel/runtime": "^7.5.5",
- "dom-helpers": "^5.0.1",
- "loose-envify": "^1.4.0",
- "prop-types": "^15.6.2"
- },
- "peerDependencies": {
- "react": ">=16.6.0",
- "react-dom": ">=16.6.0"
- }
- },
- "node_modules/regenerator-runtime": {
- "version": "0.14.1",
- "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.14.1.tgz",
- "integrity": "sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw=="
- },
- "node_modules/remove-accents": {
- "version": "0.5.0",
- "resolved": "https://registry.npmjs.org/remove-accents/-/remove-accents-0.5.0.tgz",
- "integrity": "sha512-8g3/Otx1eJaVD12e31UbJj1YzdtVvzH85HV7t+9MJYk/u3XmkOUJ5Ys9wQrf9PCPK8+xn4ymzqYCiZl6QWKn+A=="
- },
- "node_modules/resolve": {
- "version": "1.22.8",
- "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.8.tgz",
- "integrity": "sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==",
- "dependencies": {
- "is-core-module": "^2.13.0",
- "path-parse": "^1.0.7",
- "supports-preserve-symlinks-flag": "^1.0.0"
- },
- "bin": {
- "resolve": "bin/resolve"
- },
- "funding": {
- "url": "https://github.com/sponsors/ljharb"
- }
- },
- "node_modules/resolve-from": {
- "version": "4.0.0",
- "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz",
- "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==",
- "engines": {
- "node": ">=4"
- }
- },
- "node_modules/reusify": {
- "version": "1.0.4",
- "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz",
- "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==",
- "dev": true,
- "engines": {
- "iojs": ">=1.0.0",
- "node": ">=0.10.0"
- }
- },
- "node_modules/rimraf": {
- "version": "3.0.2",
- "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz",
- "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==",
- "dev": true,
- "dependencies": {
- "glob": "^7.1.3"
- },
- "bin": {
- "rimraf": "bin.js"
- },
- "funding": {
- "url": "https://github.com/sponsors/isaacs"
- }
- },
- "node_modules/rollup": {
- "version": "4.12.1",
- "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.12.1.tgz",
- "integrity": "sha512-ggqQKvx/PsB0FaWXhIvVkSWh7a/PCLQAsMjBc+nA2M8Rv2/HG0X6zvixAB7KyZBRtifBUhy5k8voQX/mRnABPg==",
- "dev": true,
- "dependencies": {
- "@types/estree": "1.0.5"
- },
- "bin": {
- "rollup": "dist/bin/rollup"
- },
- "engines": {
- "node": ">=18.0.0",
- "npm": ">=8.0.0"
- },
- "optionalDependencies": {
- "@rollup/rollup-android-arm-eabi": "4.12.1",
- "@rollup/rollup-android-arm64": "4.12.1",
- "@rollup/rollup-darwin-arm64": "4.12.1",
- "@rollup/rollup-darwin-x64": "4.12.1",
- "@rollup/rollup-linux-arm-gnueabihf": "4.12.1",
- "@rollup/rollup-linux-arm64-gnu": "4.12.1",
- "@rollup/rollup-linux-arm64-musl": "4.12.1",
- "@rollup/rollup-linux-riscv64-gnu": "4.12.1",
- "@rollup/rollup-linux-x64-gnu": "4.12.1",
- "@rollup/rollup-linux-x64-musl": "4.12.1",
- "@rollup/rollup-win32-arm64-msvc": "4.12.1",
- "@rollup/rollup-win32-ia32-msvc": "4.12.1",
- "@rollup/rollup-win32-x64-msvc": "4.12.1",
- "fsevents": "~2.3.2"
- }
- },
- "node_modules/run-parallel": {
- "version": "1.2.0",
- "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz",
- "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==",
- "dev": true,
- "funding": [
- {
- "type": "github",
- "url": "https://github.com/sponsors/feross"
- },
- {
- "type": "patreon",
- "url": "https://www.patreon.com/feross"
- },
- {
- "type": "consulting",
- "url": "https://feross.org/support"
- }
- ],
- "dependencies": {
- "queue-microtask": "^1.2.2"
- }
- },
- "node_modules/scheduler": {
- "version": "0.23.0",
- "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.23.0.tgz",
- "integrity": "sha512-CtuThmgHNg7zIZWAXi3AsyIzA3n4xx7aNyjwC2VJldO2LMVDhFK+63xGqq6CsJH4rTAt6/M+N4GhZiDYPx9eUw==",
- "dependencies": {
- "loose-envify": "^1.1.0"
- }
- },
- "node_modules/semver": {
- "version": "7.6.0",
- "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.0.tgz",
- "integrity": "sha512-EnwXhrlwXMk9gKu5/flx5sv/an57AkRplG3hTK68W7FRDN+k+OWBj65M7719OkA82XLBxrcX0KSHj+X5COhOVg==",
- "dev": true,
- "dependencies": {
- "lru-cache": "^6.0.0"
- },
- "bin": {
- "semver": "bin/semver.js"
- },
- "engines": {
- "node": ">=10"
- }
- },
- "node_modules/semver/node_modules/lru-cache": {
- "version": "6.0.0",
- "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz",
- "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==",
- "dev": true,
- "dependencies": {
- "yallist": "^4.0.0"
- },
- "engines": {
- "node": ">=10"
- }
- },
- "node_modules/semver/node_modules/yallist": {
- "version": "4.0.0",
- "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz",
- "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==",
- "dev": true
- },
- "node_modules/shallowequal": {
- "version": "1.1.0",
- "resolved": "https://registry.npmjs.org/shallowequal/-/shallowequal-1.1.0.tgz",
- "integrity": "sha512-y0m1JoUZSlPAjXVtPPW70aZWfIL/dSP7AFkRnniLCrK/8MDKog3TySTBmckD+RObVxH0v4Tox67+F14PdED2oQ=="
- },
- "node_modules/shebang-command": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz",
- "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==",
- "dev": true,
- "dependencies": {
- "shebang-regex": "^3.0.0"
- },
- "engines": {
- "node": ">=8"
- }
- },
- "node_modules/shebang-regex": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz",
- "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==",
- "dev": true,
- "engines": {
- "node": ">=8"
- }
- },
- "node_modules/slash": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz",
- "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==",
- "dev": true,
- "engines": {
- "node": ">=8"
- }
- },
- "node_modules/source-map": {
- "version": "0.5.7",
- "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz",
- "integrity": "sha512-LbrmJOMUSdEVxIKvdcJzQC+nQhe8FUZQTXQy6+I75skNgn3OoQ0DZA8YnFa7gp8tqtL3KPf1kmo0R5DoApeSGQ==",
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/source-map-js": {
- "version": "1.0.2",
- "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.0.2.tgz",
- "integrity": "sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==",
- "dev": true,
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/strip-ansi": {
- "version": "6.0.1",
- "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
- "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
- "dev": true,
- "dependencies": {
- "ansi-regex": "^5.0.1"
- },
- "engines": {
- "node": ">=8"
- }
- },
- "node_modules/strip-json-comments": {
- "version": "3.1.1",
- "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz",
- "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==",
- "dev": true,
- "engines": {
- "node": ">=8"
- },
- "funding": {
- "url": "https://github.com/sponsors/sindresorhus"
- }
- },
- "node_modules/stylis": {
- "version": "4.2.0",
- "resolved": "https://registry.npmjs.org/stylis/-/stylis-4.2.0.tgz",
- "integrity": "sha512-Orov6g6BB1sDfYgzWfTHDOxamtX1bE/zo104Dh9e6fqJ3PooipYyfJ0pUmrZO2wAvO8YbEyeFrkV91XTsGMSrw=="
- },
- "node_modules/supports-color": {
- "version": "5.5.0",
- "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz",
- "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==",
- "dependencies": {
- "has-flag": "^3.0.0"
- },
- "engines": {
- "node": ">=4"
- }
- },
- "node_modules/supports-preserve-symlinks-flag": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz",
- "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==",
- "engines": {
- "node": ">= 0.4"
- },
- "funding": {
- "url": "https://github.com/sponsors/ljharb"
- }
- },
- "node_modules/text-table": {
- "version": "0.2.0",
- "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz",
- "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==",
- "dev": true
- },
- "node_modules/to-fast-properties": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz",
- "integrity": "sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==",
- "engines": {
- "node": ">=4"
- }
- },
- "node_modules/to-regex-range": {
- "version": "5.0.1",
- "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz",
- "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==",
- "dev": true,
- "dependencies": {
- "is-number": "^7.0.0"
- },
- "engines": {
- "node": ">=8.0"
- }
- },
- "node_modules/tr46": {
- "version": "0.0.3",
- "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz",
- "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw=="
- },
- "node_modules/ts-api-utils": {
- "version": "1.2.1",
- "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-1.2.1.tgz",
- "integrity": "sha512-RIYA36cJn2WiH9Hy77hdF9r7oEwxAtB/TS9/S4Qd90Ap4z5FSiin5zEiTL44OII1Y3IIlEvxwxFUVgrHSZ/UpA==",
- "dev": true,
- "engines": {
- "node": ">=16"
- },
- "peerDependencies": {
- "typescript": ">=4.2.0"
- }
- },
- "node_modules/type-check": {
- "version": "0.4.0",
- "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz",
- "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==",
- "dev": true,
- "dependencies": {
- "prelude-ls": "^1.2.1"
- },
- "engines": {
- "node": ">= 0.8.0"
- }
- },
- "node_modules/type-fest": {
- "version": "0.20.2",
- "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz",
- "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==",
- "dev": true,
- "engines": {
- "node": ">=10"
- },
- "funding": {
- "url": "https://github.com/sponsors/sindresorhus"
- }
- },
- "node_modules/typescript": {
- "version": "5.3.3",
- "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.3.3.tgz",
- "integrity": "sha512-pXWcraxM0uxAS+tN0AG/BF2TyqmHO014Z070UsJ+pFvYuRSq8KH8DmWpnbXe0pEPDHXZV3FcAbJkijJ5oNEnWw==",
- "dev": true,
- "bin": {
- "tsc": "bin/tsc",
- "tsserver": "bin/tsserver"
- },
- "engines": {
- "node": ">=14.17"
- }
- },
- "node_modules/update-browserslist-db": {
- "version": "1.0.13",
- "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.13.tgz",
- "integrity": "sha512-xebP81SNcPuNpPP3uzeW1NYXxI3rxyJzF3pD6sH4jE7o/IX+WtSpwnVU+qIsDPyk0d3hmFQ7mjqc6AtV604hbg==",
- "dev": true,
- "funding": [
- {
- "type": "opencollective",
- "url": "https://opencollective.com/browserslist"
- },
- {
- "type": "tidelift",
- "url": "https://tidelift.com/funding/github/npm/browserslist"
- },
- {
- "type": "github",
- "url": "https://github.com/sponsors/ai"
- }
- ],
- "dependencies": {
- "escalade": "^3.1.1",
- "picocolors": "^1.0.0"
- },
- "bin": {
- "update-browserslist-db": "cli.js"
- },
- "peerDependencies": {
- "browserslist": ">= 4.21.0"
- }
- },
- "node_modules/uri-js": {
- "version": "4.4.1",
- "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz",
- "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==",
- "dev": true,
- "dependencies": {
- "punycode": "^2.1.0"
- }
- },
- "node_modules/vite": {
- "version": "5.1.7",
- "resolved": "https://registry.npmjs.org/vite/-/vite-5.1.7.tgz",
- "integrity": "sha512-sgnEEFTZYMui/sTlH1/XEnVNHMujOahPLGMxn1+5sIT45Xjng1Ec1K78jRP15dSmVgg5WBin9yO81j3o9OxofA==",
- "dev": true,
- "dependencies": {
- "esbuild": "^0.19.3",
- "postcss": "^8.4.35",
- "rollup": "^4.2.0"
- },
- "bin": {
- "vite": "bin/vite.js"
- },
- "engines": {
- "node": "^18.0.0 || >=20.0.0"
- },
- "funding": {
- "url": "https://github.com/vitejs/vite?sponsor=1"
- },
- "optionalDependencies": {
- "fsevents": "~2.3.3"
- },
- "peerDependencies": {
- "@types/node": "^18.0.0 || >=20.0.0",
- "less": "*",
- "lightningcss": "^1.21.0",
- "sass": "*",
- "stylus": "*",
- "sugarss": "*",
- "terser": "^5.4.0"
- },
- "peerDependenciesMeta": {
- "@types/node": {
- "optional": true
- },
- "less": {
- "optional": true
- },
- "lightningcss": {
- "optional": true
- },
- "sass": {
- "optional": true
- },
- "stylus": {
- "optional": true
- },
- "sugarss": {
- "optional": true
- },
- "terser": {
- "optional": true
- }
- }
- },
- "node_modules/void-elements": {
- "version": "3.1.0",
- "resolved": "https://registry.npmjs.org/void-elements/-/void-elements-3.1.0.tgz",
- "integrity": "sha512-Dhxzh5HZuiHQhbvTW9AMetFfBHDMYpo23Uo9btPXgdYP+3T5S+p+jgNy7spra+veYhBP2dCSgxR/i2Y02h5/6w==",
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/webidl-conversions": {
- "version": "3.0.1",
- "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz",
- "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ=="
- },
- "node_modules/whatwg-url": {
- "version": "5.0.0",
- "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz",
- "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==",
- "dependencies": {
- "tr46": "~0.0.3",
- "webidl-conversions": "^3.0.0"
- }
- },
- "node_modules/which": {
- "version": "2.0.2",
- "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz",
- "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==",
- "dev": true,
- "dependencies": {
- "isexe": "^2.0.0"
- },
- "bin": {
- "node-which": "bin/node-which"
- },
- "engines": {
- "node": ">= 8"
- }
- },
- "node_modules/wrappy": {
- "version": "1.0.2",
- "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
- "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==",
- "dev": true
- },
- "node_modules/yallist": {
- "version": "3.1.1",
- "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz",
- "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==",
- "dev": true
- },
- "node_modules/yaml": {
- "version": "1.10.2",
- "resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.2.tgz",
- "integrity": "sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==",
- "engines": {
- "node": ">= 6"
- }
- },
- "node_modules/yocto-queue": {
- "version": "0.1.0",
- "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz",
- "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==",
- "dev": true,
- "engines": {
- "node": ">=10"
- },
- "funding": {
- "url": "https://github.com/sponsors/sindresorhus"
- }
- }
- }
-}
diff --git a/frontend/frontend/package.json b/frontend/frontend/package.json
index 0f01a29d5..79c8455e5 100644
--- a/frontend/frontend/package.json
+++ b/frontend/frontend/package.json
@@ -9,7 +9,10 @@
"lint": "eslint . --ext ts,tsx --report-unused-disable-directives --max-warnings 0",
"preview": "vite preview",
"format": "prettier --write .",
- "prepare": "cd ../.. && husky frontend/frontend/.husky"
+ "prepare": "cd ../.. && husky frontend/frontend/.husky",
+ "cypress-open": "node_modules/.bin/cypress open",
+ "cypress-e2e": "node_modules/.bin/cypress run",
+ "cypress-component": "node_modules/.bin/cypress run --component"
},
"dependencies": {
"@azure/msal-browser": "^3.11.0",
@@ -45,6 +48,7 @@
"@typescript-eslint/eslint-plugin": "^7.0.2",
"@typescript-eslint/parser": "^7.0.2",
"@vitejs/plugin-react": "^4.2.1",
+ "cypress": "^13.9.0",
"eslint": "^8.56.0",
"eslint-config-prettier": "^9.1.0",
"eslint-plugin-prettier": "^5.1.3",
@@ -53,6 +57,6 @@
"husky": "^7.0.0",
"prettier": "^3.2.5",
"typescript": "^5.2.2",
- "vite": "^5.1.7"
+ "vite": "^5.2.11"
}
}
diff --git a/frontend/frontend/public/assets/logo_duif.png b/frontend/frontend/public/assets/logo_duif.png
new file mode 100644
index 000000000..5e8da0b78
Binary files /dev/null and b/frontend/frontend/public/assets/logo_duif.png differ
diff --git a/frontend/frontend/public/assets/logo_duif_top.png b/frontend/frontend/public/assets/logo_duif_top.png
new file mode 100644
index 000000000..45a19151f
Binary files /dev/null and b/frontend/frontend/public/assets/logo_duif_top.png differ
diff --git a/frontend/frontend/public/assets/logo_duif_top.svg b/frontend/frontend/public/assets/logo_duif_top.svg
new file mode 100644
index 000000000..21966a30e
--- /dev/null
+++ b/frontend/frontend/public/assets/logo_duif_top.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/frontend/frontend/public/assets/logo_duif_top_wit.png b/frontend/frontend/public/assets/logo_duif_top_wit.png
new file mode 100644
index 000000000..c0f1471e3
Binary files /dev/null and b/frontend/frontend/public/assets/logo_duif_top_wit.png differ
diff --git a/frontend/frontend/src/Theme.ts b/frontend/frontend/src/Theme.ts
index e170c57a7..b2a6b70be 100644
--- a/frontend/frontend/src/Theme.ts
+++ b/frontend/frontend/src/Theme.ts
@@ -5,7 +5,7 @@ const theme = createTheme({
primary: {
main: '#1E64C8',
light: '#D0E4FF',
- dark: '#1E64C8',
+ dark: '#194F9C',
contrastText: '#FCF8FD',
},
secondary: {
@@ -14,6 +14,7 @@ const theme = createTheme({
},
background: {
default: '#FCF8FD',
+ paper: '#FCF8FD',
},
text: {
primary: '#47464A',
@@ -25,6 +26,9 @@ const theme = createTheme({
success: {
main: '#81A476',
},
+ warning: {
+ main: '#FFC107',
+ },
info: {
main: '#47464A',
},
diff --git a/frontend/frontend/src/axiosConfig.ts b/frontend/frontend/src/axiosConfig.ts
index d75204a94..f1374580a 100644
--- a/frontend/frontend/src/axiosConfig.ts
+++ b/frontend/frontend/src/axiosConfig.ts
@@ -1,8 +1,8 @@
import axios from 'axios'
const instance = axios.create({
- baseURL: 'https://sel2-4.ugent.be/api/',
- //baseURL: 'http://localhost:8000/api/',
+ //baseURL: 'https://sel2-4.ugent.be/api/',
+ baseURL: 'http://localhost:8000/api/',
headers: { 'Content-Type': 'application/json' },
})
diff --git a/frontend/frontend/src/components/AssignmentListItem.tsx b/frontend/frontend/src/components/AssignmentListItem.tsx
index cc55d83a5..0f49f130f 100644
--- a/frontend/frontend/src/components/AssignmentListItem.tsx
+++ b/frontend/frontend/src/components/AssignmentListItem.tsx
@@ -3,18 +3,21 @@ import {
ListItemButton,
ListItemIcon,
ListItemText,
+ Typography,
} from '@mui/material'
import CheckCircleOutlineIcon from '@mui/icons-material/CheckCircleOutline'
import HighlightOffIcon from '@mui/icons-material/HighlightOff'
+import AccessAlarmIcon from '@mui/icons-material/AccessAlarm'
import { useNavigate } from 'react-router-dom'
import { t } from 'i18next'
+import { SubmissionStatus } from '../pages/submissionPage/SubmissionPage.tsx'
interface AssignmentListItemProps {
id: string
courseId: string
projectName: string
- dueDate?: Date
- status: boolean
+ dueDate?: string
+ status: SubmissionStatus
isStudent: boolean
}
@@ -46,6 +49,7 @@ export function AssignmentListItem({
return (
<>
{/* Project Name */}
-
+
+
+ {projectName}
+
+
{/* Due Date */}
{/* Status Icon (for students only) */}
{isStudent && (
- {status ? (
+ {status === SubmissionStatus.PASSED ? (
- ) : (
+ ) : status === SubmissionStatus.FAIL ? (
+ ) : (
+
)}
)}
@@ -95,4 +109,4 @@ export function AssignmentListItem({
>
)
-}
+}
\ No newline at end of file
diff --git a/frontend/frontend/src/components/CopyToClipboard.tsx b/frontend/frontend/src/components/CopyToClipboard.tsx
new file mode 100644
index 000000000..74cab02f4
--- /dev/null
+++ b/frontend/frontend/src/components/CopyToClipboard.tsx
@@ -0,0 +1,105 @@
+import { useState } from 'react'
+import { darken } from '@mui/system'
+import { Box, Collapse, Tooltip, Typography } from '@mui/material'
+import { Button } from './CustomComponents.tsx'
+import { AssignmentTurnedIn, ContentPaste } from '@mui/icons-material'
+import { t } from 'i18next'
+import theme from '../Theme.ts'
+
+interface CopyToClipboardProps {
+ invitationLink: string
+}
+
+export const CopyToClipboard = ({ invitationLink }: CopyToClipboardProps) => {
+ //variable for invitation link clipboard copy and variable
+ const [isCopied, setIsCopied] = useState(false)
+
+ async function copyToClipboard() {
+ await navigator.clipboard.writeText(invitationLink)
+ setIsCopied(true)
+ setTimeout(() => {
+ setIsCopied(false)
+ }, 2000)
+ }
+
+ return (
+ <>
+
+
+
+
+
+ {isCopied ? (
+
+ ) : (
+
+ )}
+
+
+ {t('copy_invite')}
+
+
+
+
+
+ >
+ )
+}
diff --git a/frontend/frontend/src/components/CourseCard.tsx b/frontend/frontend/src/components/CourseCard.tsx
index 7cf616f87..c59b055b1 100644
--- a/frontend/frontend/src/components/CourseCard.tsx
+++ b/frontend/frontend/src/components/CourseCard.tsx
@@ -1,11 +1,9 @@
+import { Card, Divider } from './CustomComponents.tsx'
import {
Box,
- Card,
CardActionArea,
CardContent,
- Divider,
IconButton,
- Skeleton,
Typography,
} from '@mui/material'
import { t } from 'i18next'
@@ -15,7 +13,18 @@ import instance from '../axiosConfig.ts'
import { AssignmentListItem } from './AssignmentListItem.tsx'
import List from '@mui/material/List'
import ArchiveOutlinedIcon from '@mui/icons-material/ArchiveOutlined'
-import course, { project } from '../pages/mainPage/MainPage.tsx'
+import PushPinIcon from '@mui/icons-material/PushPin'
+import PushPinOutlinedIcon from '@mui/icons-material/PushPinOutlined'
+import { Course, project } from '../pages/mainPage/MainPage.tsx'
+import dayjs from 'dayjs'
+
+import { CourseCardSkeleton } from './CourseCardSkeleton.tsx'
+import {
+ Submission,
+ SubmissionStatus,
+} from '../pages/submissionPage/SubmissionPage.tsx'
+import { Group } from '../pages/groupsPage/GroupsPage.tsx'
+import axios from 'axios'
/*
* CourseCard component displays a card with course information and a list of assignments
* @param courseId: string, the id of the course
@@ -26,30 +35,50 @@ import course, { project } from '../pages/mainPage/MainPage.tsx'
//TODO: fix archived with state so that the card moves to ArchivedView when archived
interface CourseCardProps {
+ userid: number
courseId: string
archived: boolean
isStudent: boolean
+ archiveEvent?: () => void
+ pinned: boolean
+ pinEvent: () => void
}
-export function CourseCard({ courseId, archived, isStudent }: CourseCardProps) {
+export function CourseCard({
+ userid,
+ courseId,
+ archived,
+ isStudent,
+ archiveEvent,
+ pinned,
+ pinEvent,
+}: CourseCardProps) {
// State variables
- const [course, setCourse] = useState({
+ const [course, setCourse] = useState({
vak_id: 0,
naam: '',
+ jaartal: 0,
studenten: [],
lesgevers: [],
+ gearchiveerd: false,
})
const [assignments, setAssignments] = useState([])
+ const [submissions, setSubmissions] = useState([])
+ const [groups, setGroups] = useState([])
const [teachers, setTeachers] = useState<
{ first_name: string; last_name: string }[]
>([])
+ const [loading, setLoading] = useState(true)
+
const navigate = useNavigate()
// Get all necessary data from backend
useEffect(() => {
async function fetchData() {
+ // Set loading to true every time the data is requested
+ setLoading(true)
try {
- const courseResponse = await instance.get(
+ const courseResponse = await instance.get(
`/vakken/${courseId}/`
)
const assignmentsResponse = await instance.get(
@@ -59,6 +88,29 @@ export function CourseCard({ courseId, archived, isStudent }: CourseCardProps) {
setCourse(courseResponse.data)
setAssignments(assignmentsResponse.data)
+ //fetch submissions as well if user is student
+ if (isStudent) {
+ const groups: Group[] = await instance
+ .get(`/groepen/?student=${userid}`)
+ .then((response) => response.data)
+
+ setGroups(groups)
+
+ const submissionPromises = groups.map(async (group) => {
+ const response = await instance.get(
+ `/indieningen/?project=${group.project}&groep=${group.groep_id}`
+ )
+ return response.data.sort(
+ (a: Submission, b: Submission) => {
+ return dayjs(b.tijdstip).diff(dayjs(a.tijdstip))
+ }
+ )[0]
+ })
+ const submissions = await axios.all(submissionPromises)
+
+ setSubmissions(submissions)
+ }
+
if (courseResponse.data && courseResponse.data.lesgevers) {
const lesgevers = []
for (const teacherId of courseResponse.data.lesgevers) {
@@ -76,6 +128,8 @@ export function CourseCard({ courseId, archived, isStudent }: CourseCardProps) {
} catch (error) {
console.error('Error fetching data:', error)
}
+ // Set loading to false after data is fetched
+ setLoading(false)
}
fetchData().catch((error) =>
@@ -89,37 +143,47 @@ export function CourseCard({ courseId, archived, isStudent }: CourseCardProps) {
navigate(`/course/${courseId}`)
}
- const archive = () => {
- console.log('Archive clicked')
- //update db
+ const pinCourse = (e: React.MouseEvent) => {
+ e.stopPropagation()
+ console.log('Course pinned/unpinned')
+ pinEvent()
+ }
+
+ function getmyStatus(assignment: project) {
+ const myGroup = groups.find(
+ (group) => group.project === assignment.project_id
+ )
+
+ if (!myGroup) {
+ return SubmissionStatus.FAIL
+ }
+
+ const mySubmission = submissions.find(
+ (submission) =>
+ (submission ? submission.groep : 0) === myGroup.groep_id
+ )
+
+ if (!mySubmission) {
+ return SubmissionStatus.FAIL
+ }
+ console.log('myStatus', mySubmission.status)
+ return mySubmission.status
}
return (
<>
- {!course ? (
+ {loading ? (
// If course is not available, show a skeleton loading component
-
+
) : (
// If course is available, show course details inside a card component
- {teachers.map((teacher) => (
+ {teachers.map((teacher, index) => (
{/* Number of students enrolled */}
+
+
+ {pinned ? (
+
+ ) : (
+
+ )}
+
+
{t('students') + ': '}
{course.studenten.length || 0}
@@ -198,7 +289,6 @@ export function CourseCard({ courseId, archived, isStudent }: CourseCardProps) {
- Project
- Deadline
- Status
+
+ Project
+
+
+ Deadline
+
+
+ Status
+
) : (
// Display assignments for teachers
<>
{archived ? (
-
+
Project
-
+
Deadline
) : (
-
+
Project
-
+
Deadline
)}
>
)}
-
+
{isStudent ? (
// Render assignment list for students
@@ -266,10 +381,30 @@ export function CourseCard({ courseId, archived, isStudent }: CourseCardProps) {
sx={{
width: '100%',
height: 130,
- overflow: 'auto',
+ overflowY: 'auto',
}}
>
-
+
+ {assignments.length === 0 && (
+
+
+ {t('no_projects')}
+
+
+ )}
{assignments
.filter(
(assignment) =>
@@ -277,23 +412,24 @@ export function CourseCard({ courseId, archived, isStudent }: CourseCardProps) {
)
.map((assignment) => (
))}
@@ -309,17 +445,17 @@ export function CourseCard({ courseId, archived, isStudent }: CourseCardProps) {
height: 130,
}}
>
-
- {assignments
- .filter(
- (assignment) =>
- assignment.zichtbaar
- )
- .map((assignment) => (
+
+ {assignments.map(
+ (assignment) => (
- ))}
+ )
+ )}
) : (
@@ -359,9 +501,7 @@ export function CourseCard({ courseId, archived, isStudent }: CourseCardProps) {
)
.map((assignment) => (
{
+ return (
+ <>
+
+
+
+
+ >
+ )
+}
diff --git a/frontend/frontend/src/components/CustomComponents.tsx b/frontend/frontend/src/components/CustomComponents.tsx
new file mode 100644
index 000000000..f5bf46103
--- /dev/null
+++ b/frontend/frontend/src/components/CustomComponents.tsx
@@ -0,0 +1,140 @@
+import { darken } from '@mui/system'
+import {
+ Button as BaseButton,
+ Card as BaseCard,
+ Divider as BaseDivider,
+ Box,
+ ButtonProps,
+ CardProps,
+ DividerProps,
+} from '@mui/material'
+import theme from '../Theme.ts'
+import { ReactNode } from 'react'
+
+interface PrimaryButtonProps extends ButtonProps {
+ children: ReactNode
+}
+
+export const Button = ({ children, ...props }: PrimaryButtonProps) => {
+ return (
+
+ {children}
+
+ )
+}
+
+interface SecondaryButtonProps extends ButtonProps {
+ children: ReactNode
+}
+
+export const SecondaryButton = ({
+ children,
+ ...props
+}: SecondaryButtonProps) => {
+ return (
+
+ {children}
+
+ )
+}
+
+interface CustomCardProps extends CardProps {
+ children: ReactNode
+}
+
+export const Card = ({ children, ...props }: CustomCardProps) => {
+ return (
+
+ {children}
+
+ )
+}
+
+interface CustomDividerProps extends DividerProps {
+ children?: ReactNode
+}
+
+export const Divider = ({ children, ...props }: CustomDividerProps) => {
+ return (
+
+ {children}
+
+ )
+}
+
+interface EvenlySpacedRowProps {
+ items: ReactNode[]
+}
+
+export const EvenlySpacedRow = ({ items }: EvenlySpacedRowProps) => {
+ return (
+
+ {items.map((item, index) => (
+
+ {item}
+
+ ))}
+
+ )
+}
+
+export default { Button, Card, Divider, EvenlySpacedRow }
diff --git a/frontend/frontend/src/components/DeadlineCalendar.tsx b/frontend/frontend/src/components/DeadlineCalendar.tsx
index 48d3662a3..4bb319404 100644
--- a/frontend/frontend/src/components/DeadlineCalendar.tsx
+++ b/frontend/frontend/src/components/DeadlineCalendar.tsx
@@ -5,9 +5,21 @@ import {
PickersDayProps,
} from '@mui/x-date-pickers'
import dayjs, { Dayjs } from 'dayjs'
-import { Badge, SxProps } from '@mui/material'
+import {
+ Badge,
+ List,
+ ListItem,
+ ListItemButton,
+ ListItemText,
+ Stack,
+ SxProps,
+ Typography,
+} from '@mui/material'
import { useEffect, useRef, useState } from 'react'
import AssignmentIcon from '@mui/icons-material/Assignment'
+import { useNavigate } from 'react-router-dom'
+import { t } from 'i18next'
+import { project } from '../pages/mainPage/MainPage.tsx'
/*
* This component is a calendar that displays deadlines.
@@ -53,9 +65,15 @@ function ServerDay(
key={props.day.toString()}
overlap="circular"
badgeContent={
- isSelected ? (
-
- ) : undefined
+ isSelected
+ ? props.day.isBefore(dayjs())
+ ? (
+
+ )
+ : (
+
+ )
+ : undefined
}
>
{
+ console.log('Project clicked')
+ navigate(`/course/${courseId}/assignment/${projectId}`)
+ }
+
+ return (
+
+
+ {t('deadlines_on')}: {selectedDay?.format('DD/MM/YYYY')}
+
+
+ {assignments.filter((assignment: project) => {
+ return dayjs(assignment.deadline).isSame(selectedDay, 'day')
+ }).length === 0 && (
+
+
+
+ {t('no_deadline') + 's'}
+
+
+
+ )}
+ {assignments
+ .filter((assignment: project) =>
+ dayjs(assignment.deadline).isSame(selectedDay, 'day')
+ )
+ .map((assignment: project) => (
+
+
+ handleProjectClick(
+ assignment.vak,
+ assignment.project_id
+ )
+ }
+ >
+
+
+ {assignment.titel}
+
+
+
+
+ ))}
+
+
+ )
+}
+
interface DeadlineCalendarProps {
deadlines: Dayjs[]
+ assignments: project[]
}
-export function DeadlineCalendar({ deadlines }: DeadlineCalendarProps) {
+export function DeadlineCalendar({
+ deadlines,
+ assignments,
+}: DeadlineCalendarProps) {
const requestAbortController = useRef(null)
const [isLoading, setIsLoading] = useState(false)
- const [highlightedDays, setHighlightedDays] = useState([1, 2, 15])
+ const [highlightedDays, setHighlightedDays] = useState([])
const [value, setValue] = useState(dayjs())
const fetchHighlightedDays = (date: Dayjs) => {
@@ -121,36 +221,51 @@ export function DeadlineCalendar({ deadlines }: DeadlineCalendarProps) {
fetchHighlightedDays(date)
}
+ const handleYearChange = (date: Dayjs) => {
+ setIsLoading(true)
+ setHighlightedDays([])
+ fetchHighlightedDays(date)
+ }
+
return (
<>
- {/*Calendar*/}
- setValue(newValue)}
- onMonthChange={(newValue) => handleMonthChange(newValue)}
- renderLoading={() => }
- loading={isLoading}
- sx={dateStyle}
- slots={{
- day: ServerDay,
- }}
- slotProps={{
- day: {
- highlightedDays,
- } as never,
- }}
- />
+
+ {/*Calendar*/}
+ setValue(newValue)}
+ onMonthChange={(newValue) => handleMonthChange(newValue)}
+ onYearChange={(newValue) => handleYearChange(newValue)}
+ renderLoading={() => }
+ loading={isLoading}
+ sx={dateStyle}
+ slots={{
+ day: ServerDay,
+ }}
+ slotProps={{
+ day: {
+ highlightedDays,
+ } as never,
+ }}
+ />
+
+
>
)
}
const dateStyle: SxProps = {
'& .MuiPickersDay-root.Mui-selected': {
- color: 'text.primary',
+ color: 'secondary.contrastText',
backgroundColor: 'secondary.main',
},
'& .MuiPickersDay-root.Mui-selected:hover': {
+ color: 'secondary.contrastText',
+ backgroundColor: 'secondary.main',
+ },
+ '& .MuiPickersDay-root.Mui-selected:not(hover)': {
+ color: 'secondary.contrastText',
backgroundColor: 'secondary.main',
},
}
diff --git a/frontend/frontend/src/components/FileUploadButton.tsx b/frontend/frontend/src/components/FileUploadButton.tsx
index c093d8652..9e9621176 100644
--- a/frontend/frontend/src/components/FileUploadButton.tsx
+++ b/frontend/frontend/src/components/FileUploadButton.tsx
@@ -1,5 +1,5 @@
import { styled } from '@mui/material/styles'
-import Button from '@mui/material/Button'
+import { Button } from './CustomComponents'
import UploadFileIcon from '@mui/icons-material/UploadFile'
import { ChangeEvent } from 'react'
import { Box, IconButton, Tooltip, Typography } from '@mui/material'
@@ -63,21 +63,15 @@ export default function InputFileUpload({
{/* Tooltip for the upload button */}
}
+ id='uploadButton'
variant={'contained'}
color={'secondary'}
size={'small'}
- startIcon={ }
disableElevation
component="label"
role={undefined}
tabIndex={-1}
- sx={{
- padding: 1,
- '& .MuiButton-startIcon': {
- margin: 0,
- marginRight: 1,
- },
- }}
>
{/* Hidden input for file selection */}
{
navigate(`/course/${courseid}/assignment/${assignmentid}/groups`)
}
- useEffect(() => {
- //set max group size to 1 if groups are not allowed and register all students to a group of their own
- }, [allowGroups])
-
return (
<>
- {/* Button to navigate to groups page */}
- {allowGroups ? (
-
- {t('groups')}
-
- ) : (
- // Show text indicating groups are not allowed
-
- {t('groups')}
-
- )}
- {/* Switch to toggle group access */}
- setAllowGroups(!allowGroups)}
- color={'primary'}
- />
+ {/* Button to navigate to groups page */}
+ {t('groups')}
>
)
diff --git a/frontend/frontend/src/components/Header.tsx b/frontend/frontend/src/components/Header.tsx
index 4071189c3..799e130ad 100644
--- a/frontend/frontend/src/components/Header.tsx
+++ b/frontend/frontend/src/components/Header.tsx
@@ -2,8 +2,6 @@ import {
AppBar,
Box,
IconButton,
- Menu,
- MenuItem,
Toolbar,
Tooltip,
Typography,
@@ -11,12 +9,11 @@ import {
import { useTranslation } from 'react-i18next'
import ArrowBackIcon from '@mui/icons-material/ArrowBack'
import EditIcon from '@mui/icons-material/Edit'
-import React from 'react'
-import { AccountCircle } from '@mui/icons-material'
import { useLocation, useNavigate } from 'react-router-dom'
import { LanguageSwitcher } from './LanguageSwitcher.tsx'
import { useMsal } from '@azure/msal-react'
import axios from 'axios'
+import Button from '@mui/material/Button'
/**
* Header component
@@ -31,7 +28,7 @@ import axios from 'axios'
* Interface for Header props
*/
interface Props {
- variant: 'not_main' | 'editable' | 'default'
+ variant: 'not_main' | 'editable' | 'default' | 'main'
title: string
}
@@ -41,15 +38,7 @@ interface Props {
*/
export const Header = ({ variant, title }: Props) => {
const { t } = useTranslation()
- const [anchorEl, setAnchorEl] = React.useState(null)
const { instance } = useMsal()
- /**
- * Function to handle menu opening
- * @param {React.MouseEvent} event - The event object
- */
- const handleMenu = (event: React.MouseEvent) => {
- setAnchorEl(event.currentTarget)
- }
/**
* Function to handle edit action
@@ -71,13 +60,6 @@ export const Header = ({ variant, title }: Props) => {
navigate(path || '/')
}
- /**
- * Function to handle menu closing
- */
- const handleClose = () => {
- setAnchorEl(null)
- }
-
const navigate = useNavigate()
const location = useLocation()
@@ -99,6 +81,8 @@ export const Header = ({ variant, title }: Props) => {
return (
<>
{
}}
>
- {/* Logo and Home Button */}
-
-
- navigate('/')}
- sx={{ padding: 0, borderRadius: 5 }}
- >
+
+ {/* Logo and Home Button */}
+
+
+ navigate('/')}
+ sx={{ padding: 0, borderRadius: 5 }}
+ >
+
+
+
+ {/* Back Button (if variant is not default) */}
+ {(variant === 'not_main' ||
+ variant === 'editable') && (
+
+
+
+
+
+ )}
+
+ {/* Title */}
+
+ {variant === 'main' && (
-
-
- {/* Back Button (if variant is not default) */}
- {variant !== 'default' && (
-
-
-
-
-
- )}
-
- {/* Title */}
-
- {title}
- {variant === 'editable' && (
-
-
-
- )}
-
- {/* User Menu */}
-
+
+ Logout
+
+
+
>
diff --git a/frontend/frontend/src/components/ItemList.tsx b/frontend/frontend/src/components/ItemList.tsx
index 8ac17c1a6..0af0e9643 100644
--- a/frontend/frontend/src/components/ItemList.tsx
+++ b/frontend/frontend/src/components/ItemList.tsx
@@ -30,9 +30,10 @@ export const ItemList: FC = ({ itemList }) => {
return (
-
+
= ({ itemList }) => {
Opdracht
= ({ itemList }) => {
Deadline
= ({ itemList }) => {
Status
= ({ itemList }) => {
-
+
{/* Map through itemList and render a TableRow for each item */}
{itemList.map((item, index) => (
-
- {item.opdracht}
- {item.deadline}
- {item.status}
- {item.score}
+
+ {item.opdracht}
+ {item.deadline}
+ {item.status}
+ {item.score}
))}
diff --git a/frontend/frontend/src/components/LanguageSwitcher.tsx b/frontend/frontend/src/components/LanguageSwitcher.tsx
index 02efb5395..8c678f5dc 100644
--- a/frontend/frontend/src/components/LanguageSwitcher.tsx
+++ b/frontend/frontend/src/components/LanguageSwitcher.tsx
@@ -1,5 +1,7 @@
import { useTranslation } from 'react-i18next'
-import { MenuItem } from '@mui/material'
+import { Box, Divider } from '@mui/material'
+import Button from '@mui/material/Button'
+import LanguageIcon from '@mui/icons-material/Language'
// Define language options with their corresponding titles
const locales = {
@@ -7,7 +9,6 @@ const locales = {
nl: { title: 'Nederlands' },
}
-
/**
* Component to switch between different language options.
* This component renders a list of menu items, each representing a language option.
@@ -17,18 +18,54 @@ export function LanguageSwitcher() {
return (
<>
{/* Map through language options and render a MenuItem for each */}
- {Object.keys(locales).map((locale) => (
-
+ {
+ i18n.changeLanguage('en').then(() =>
+ window.location.reload()
+ )
+ }}
+ >
+ {'En'}
+
+
+ {
- i18n.changeLanguage(locale).then(() =>
+ i18n.changeLanguage('nl').then(() =>
window.location.reload()
)
}}
>
- {locale}
-
- ))}
+ {'Nl'}
+
+
+
>
)
}
diff --git a/frontend/frontend/src/components/RestrictionCard.tsx b/frontend/frontend/src/components/RestrictionCard.tsx
index 763b51057..504238c6d 100644
--- a/frontend/frontend/src/components/RestrictionCard.tsx
+++ b/frontend/frontend/src/components/RestrictionCard.tsx
@@ -1,6 +1,8 @@
import { restriction } from '../pages/addChangeAssignmentPage/AddChangeAssignmentPage.tsx'
-import { Box, IconButton, Typography } from '@mui/material'
+import { IconButton, Switch, Typography } from '@mui/material'
import CloseIcon from '@mui/icons-material/Close'
+import React from 'react'
+import { EvenlySpacedRow } from './CustomComponents.tsx'
interface RestrictionCardProps {
restriction: restriction
@@ -13,26 +15,36 @@ export function RestrictionCard({
restrictions,
setRestrictions,
}: RestrictionCardProps) {
+ const [mustPass, setMustPass] = React.useState(restriction.moet_slagen)
+
//handle the removal of the restriction from the list
const handleRemove = () => {
setRestrictions(restrictions.filter((r) => r !== restriction))
}
+ const handleMustPassChange = () => {
+ setMustPass(!mustPass)
+ restriction.moet_slagen = !restriction.moet_slagen
+ }
+
return (
<>
-
-
- {restriction.script.replace(/^.*[\\/]/, '')}
-
-
-
-
-
+
+ {restriction.script.replace(/^.*[\\/]/, '')}
+ ,
+ handleMustPassChange()}
+ />,
+
+
+ ,
+ ]}
+ />
>
)
}
diff --git a/frontend/frontend/src/components/SubmissionListItemStudentPage.tsx b/frontend/frontend/src/components/SubmissionListItemStudentPage.tsx
index 1ca1b0744..b2c07f9cf 100644
--- a/frontend/frontend/src/components/SubmissionListItemStudentPage.tsx
+++ b/frontend/frontend/src/components/SubmissionListItemStudentPage.tsx
@@ -1,8 +1,10 @@
+import { EvenlySpacedRow } from './CustomComponents.tsx'
import {
ListItem,
+ ListItemButton,
ListItemIcon,
ListItemText,
- ListItemButton,
+ Box,
} from '@mui/material'
import CheckCircleOutlineIcon from '@mui/icons-material/CheckCircleOutline'
import HighlightOffIcon from '@mui/icons-material/HighlightOff'
@@ -10,8 +12,9 @@ import { useNavigate } from 'react-router-dom'
import { t } from 'i18next'
interface SubmissionListItemStudentPageProps {
- id: string
- timestamp?: Date
+ realId: string
+ visualId: string
+ timestamp?: string
status: boolean
assignment_id: string
course_id: string
@@ -27,7 +30,8 @@ interface SubmissionListItemStudentPageProps {
*/
export function SubmissionListItemStudentPage({
- id,
+ realId,
+ visualId,
timestamp,
status,
assignment_id,
@@ -38,59 +42,53 @@ export function SubmissionListItemStudentPage({
// Function to handle submission click event
const handleSubmissionClick = () => {
console.log('Submission clicked')
- if (id) {
+ if (realId) {
navigate(
- `/course/${course_id}/assignment/${assignment_id}/submission/${id}`
+ `/course/${course_id}/assignment/${assignment_id}/submission/${realId}`
)
}
}
return (
<>
-
+
- {/* Display submission id */}
-
- {/* Display submission timestamp */}
- ,
+ ,
+
+
+ {status ? (
+
+ ) : (
+
+ )}
+
+ ,
+ ]}
/>
- {/* Display submission status icon */}
-
- {status ? (
-
- ) : (
-
- )}
-
>
diff --git a/frontend/frontend/src/components/SubmissionListItemTeacherPage.tsx b/frontend/frontend/src/components/SubmissionListItemTeacherPage.tsx
index 935457857..7f887a3cc 100644
--- a/frontend/frontend/src/components/SubmissionListItemTeacherPage.tsx
+++ b/frontend/frontend/src/components/SubmissionListItemTeacherPage.tsx
@@ -1,8 +1,9 @@
import {
+ Box,
ListItem,
- ListItemText,
- ListItemIcon,
ListItemButton,
+ ListItemIcon,
+ ListItemText,
} from '@mui/material'
import { useNavigate } from 'react-router-dom'
import DownloadIcon from '@mui/icons-material/Download'
@@ -11,9 +12,13 @@ import HighlightOffIcon from '@mui/icons-material/HighlightOff'
import { useEffect, useState } from 'react'
import instance from '../axiosConfig'
import { Submission } from '../pages/submissionPage/SubmissionPage.tsx'
+import { t } from 'i18next'
import dayjs from 'dayjs'
+import { EvenlySpacedRow } from './CustomComponents.tsx'
+import { Project } from '../pages/scoresPage/ProjectScoresPage.tsx'
interface SubmissionListItemTeacherPageProps {
+ group_name: string
group_id: string
assignment_id: string
course_id: string
@@ -30,6 +35,7 @@ export interface Score {
* @param {SubmissionListItemTeacherPageProps} props - Props for SubmissionListItemTeacherPage component
*/
export function SubmissionListItemTeacherPage({
+ group_name,
group_id,
assignment_id,
course_id,
@@ -48,17 +54,54 @@ export function SubmissionListItemTeacherPage({
// State for submitted data and score
const [submitted, setSubmitted] = useState()
const [score, setScore] = useState()
+ const [assignment, setAssignment] = useState()
useEffect(() => {
async function fetchData() {
try {
const submissionsResponse = await instance.get(
`/indieningen/?groep=${group_id}`
)
- const lastSubmission =
- submissionsResponse.data[
- submissionsResponse.data.length - 1
- ]
- setSubmitted(lastSubmission)
+ const lastSubmission = submissionsResponse.data.sort(
+ (a: Submission, b: Submission) => {
+ return dayjs(a.tijdstip).isBefore(dayjs(b.tijdstip))
+ ? -1
+ : 1
+ }
+ )[submissionsResponse.data.length - 1]
+ if (lastSubmission) {
+ const lastSubmissionResponse = await instance.get(
+ `indieningen/${lastSubmission.indiening_id}/`
+ )
+ //Get the submission file
+ const newSubmission: Submission =
+ lastSubmissionResponse.data
+ newSubmission.filename =
+ lastSubmissionResponse.data.bestand.replace(
+ /^.*[\\/]/,
+ ''
+ )
+ newSubmission.bestand = await instance
+ .get(
+ `/indieningen/${lastSubmission.indiening_id}/indiening_bestand/`,
+ {
+ responseType: 'blob',
+ }
+ )
+ .then((res) => {
+ let filename = 'indiening.zip'
+ if (newSubmission.filename) {
+ filename = newSubmission.filename
+ }
+ const blob = new Blob([res.data], {
+ type: res.headers['content-type'],
+ })
+ const file: File = new File([blob], filename, {
+ type: res.headers['content-type'],
+ })
+ return file
+ })
+ setSubmitted(newSubmission)
+ }
if (lastSubmission) {
const scoreResponse = await instance.get(
`/scores/?indiening=${lastSubmission.indiening_id}`
@@ -66,6 +109,11 @@ export function SubmissionListItemTeacherPage({
setScore(scoreResponse.data[scoreResponse.data.length - 1])
console.log(score?.score)
}
+
+ const assignmentResponse = await instance.get(
+ `/projecten/${assignment_id}/`
+ )
+ setAssignment(assignmentResponse.data)
} catch (error) {
console.error('Error fetching data:', error)
}
@@ -75,38 +123,14 @@ export function SubmissionListItemTeacherPage({
// Function to download the submission
const downloadSubmission = () => {
- if (submitted) {
- instance
- .get(
- `/indieningen/${submitted.indiening_id}/indiening_bestanden/`,
- { responseType: 'blob' }
- )
- .then((res) => {
- let filename = 'lege_indiening.zip'
- if (submitted.indiening_bestanden.length > 0) {
- filename =
- submitted.indiening_bestanden[0].bestand.replace(
- /^.*[\\/]/,
- ''
- )
- }
- const blob = new Blob([res.data], {
- type: res.headers['content-type'],
- })
- const file: File = new File([blob], filename, {
- type: res.headers['content-type'],
- })
- const url = window.URL.createObjectURL(file)
- const a = document.createElement('a')
- a.href = url
- a.download = filename
- document.body.appendChild(a)
- a.click()
- a.remove()
- })
- .catch((err) => {
- console.error(err)
- })
+ if (submitted?.bestand) {
+ const url = window.URL.createObjectURL(submitted?.bestand)
+ const a = document.createElement('a')
+ a.href = url
+ a.download = submitted.filename ? submitted.filename : 'opgave.zip'
+ document.body.appendChild(a)
+ a.click()
+ a.remove()
}
}
@@ -122,72 +146,90 @@ export function SubmissionListItemTeacherPage({
return (
<>
-
+
- {/* Display group id */}
-
- {/* Display submission timestamp */}
-
- {/* Display score */}
-
- {/* Display submission status icon */}
-
- {submitted?.status ? (
-
- ) : (
- submitted !== undefined && (
-
- )
- )}
-
- {/* Display download icon */}
-
-
- {submitted ? (
-
- ) : (
-
- )}
-
-
+ ,
+ ,
+ ,
+
+
+ {!submitted?.status ? (
+
+ ) : submitted.status > 0 ? (
+
+ ) : (
+
+ )}
+
+ ,
+
+
+
+ {submitted ? (
+
+ ) : (
+
+ )}
+
+
+ ,
+ ]}
+ >
>
diff --git a/frontend/frontend/src/components/TabSwitcher.tsx b/frontend/frontend/src/components/TabSwitcher.tsx
index 032ec6ba8..c66333805 100644
--- a/frontend/frontend/src/components/TabSwitcher.tsx
+++ b/frontend/frontend/src/components/TabSwitcher.tsx
@@ -8,8 +8,11 @@ import { ReactNode } from 'react'
import { t } from 'i18next'
import theme from '../Theme.ts'
import { grey } from '@mui/material/colors'
+import { Box, MenuItem, Select } from '@mui/material'
interface TabSwitcherProps {
+ selectedYear?: number
+ setSelectedYear: (year: number) => void
titles: string[]
nodes: ReactNode[]
}
@@ -22,25 +25,77 @@ interface TabSwitcherProps {
* the number of titles and nodes should be the same
*/
-export default function TabSwitcher({ titles, nodes }: TabSwitcherProps) {
+export default function TabSwitcher({
+ selectedYear,
+ setSelectedYear,
+ titles,
+ nodes,
+}: TabSwitcherProps) {
titles.length !== nodes.length &&
console.error('The number of titles and nodes should be the same')
return (
-
-
- {titles.map((title, index) => (
-
- {t(title)}
-
+ <>
+
+
+
+ {titles.map((title, index) => (
+
+ {t(title)}
+
+ ))}
+
+ {selectedYear && (
+
+
+ setSelectedYear(e.target.value as number)
+ }
+ label="Select Academic Year"
+ sx={{
+ minWidth: 150,
+ '& .MuiSelect-outlined': {
+ border: 1.5,
+ borderColor: 'primary.main',
+ },
+ }}
+ >
+ {[2022, 2023, 2024, 2025].map((year) => (
+
+ {year}
+
+ ))}
+
+
+ )}
+
+ {nodes.map((node, index) => (
+
+ {node}
+
))}
-
- {nodes.map((node, index) => (
-
- {node}
-
- ))}
-
+
+ >
)
}
@@ -82,7 +137,7 @@ const Tab = styled(BaseTab)`
const TabPanel = styled(BaseTabPanel)(
({ theme }) => `
- width: "15%";
+ width: "100%";
font-family: 'IBM Plex Sans', sans-serif;
font-size: 0.875rem;
padding: 20px 12px;
diff --git a/frontend/frontend/src/components/WarningPopup.tsx b/frontend/frontend/src/components/WarningPopup.tsx
index ecbef41cc..37bc00d80 100644
--- a/frontend/frontend/src/components/WarningPopup.tsx
+++ b/frontend/frontend/src/components/WarningPopup.tsx
@@ -1,5 +1,5 @@
+import { Button, SecondaryButton } from './CustomComponents'
import {
- Button,
DialogActions,
DialogContent,
DialogContentText,
@@ -43,25 +43,20 @@ export default function WarningPopup({
<>
{/* Warning popup dialog */}
- {title}
+
- {content}
+
-
+
{t('cancel')}
-
+
{/* Action button */}
-
+
{buttonName}
diff --git a/frontend/frontend/src/i18n/en.ts b/frontend/frontend/src/i18n/en.ts
index 75dabe2c4..a5e689d04 100644
--- a/frontend/frontend/src/i18n/en.ts
+++ b/frontend/frontend/src/i18n/en.ts
@@ -11,9 +11,9 @@ const english = {
submissions: 'Submissions',
no_submission: 'no submission',
error: 'Something went wrong.',
- assignment: 'Assignment:',
- filename: 'Submitted file:',
- restrictions: 'Restrictions:',
+ assignment: 'Assignment',
+ filename: 'Submitted file',
+ restrictions: 'Restrictions',
current_projects: 'Current projects',
time: 'Time',
upload: 'Upload',
@@ -27,10 +27,12 @@ const english = {
students_choose: 'Students Can Choose',
random: 'Random',
members: 'Members',
+ members_loading: 'Members loading...',
group_members: 'Members Of The Group',
- assignmentName: 'Name assignment:',
+ no_members_yet: 'No members yet',
+ assignmentName: 'Name assignment',
upload_assignment: 'Upload assignment',
- description: 'Description assignment:',
+ description: 'Description assignment',
uploadToolTip: 'upload a .zip or .pdf file',
noFile: 'No file chosen',
remove: 'Remove',
@@ -68,7 +70,7 @@ const english = {
no_submissions: 'No submissions',
edit: 'Edit',
upload_scores: 'Upload Scores',
- last_submission: 'Last Submission:',
+ last_submission: 'Last Submission',
delete_project_warning: 'Delete Project?',
archive_project_warning: 'Archive Project?',
cant_be_undone: 'This cannot be undone.',
@@ -78,7 +80,7 @@ const english = {
edit_scores_warning: 'Edit Scores?',
undo_changes_warning: 'Undo Changes?',
optional: 'Optional',
- deadlineCheck: 'extra deadline must be after the first deadline',
+ deadlineCheck: 'Extra deadline must be after the first deadline',
not_before_now: 'Deadline must be in the future',
noStudentsYet: 'No students yet',
change_groups: 'Change groups?',
@@ -90,6 +92,55 @@ const english = {
failed: 'Failed',
result: 'Result',
must_pass: 'Must pass',
+ loading: 'Loading',
+ file: 'File',
+ archive_course_warning: 'Archive Course?',
+ will_archive_projects: 'This will archive all projects.',
+ first_name: 'First name',
+ last_name: 'Last name',
+ copied: 'Copied!',
+ copy: 'Copy to clipboard',
+ join_course: 'Join course?',
+ join: 'Join',
+ join_group: 'Join',
+ group_full: 'Group is full',
+ leave: 'Leave',
+ acces: 'This gives you access to the course.',
+ copy_invite: 'Copy invitation link',
+ deadlines_on: 'Deadlines on',
+ no_assignmentfile: 'No assignment file',
+ email: 'Email',
+ n_of_members: 'Max Amount Of Members',
+ existing_submissions:
+ 'This could impact existing submissions. Are you sure?',
+ divide_groups: 'Divide Groups',
+ noGroup: "You don't seem to be in a group yet.",
+ contactTeacher: 'Please contact your teacher.',
+ chooseGroup: 'Join a group before submitting.',
+ group_number: 'Group Number',
+ open_with_ui: 'Open with UI',
+ choose_existing: 'Choose existing',
+ make_new_script: 'Make new script',
+ cant_add_teachers_to_student_list:
+ 'You cannot add teachers to the student list',
+ cant_add_students_to_teacher_list:
+ 'You cannot add students to the teacher list',
+ cant_add_users_twice: 'You cannot add users to a subject twice',
+ this_user_doesnt_exist: 'This user does not exist',
+ status: 'Status',
+ visibility: 'Change Visibility',
+ template_saved_successfully: 'Template saved successfully',
+ save_course_warning: 'Save Course?',
+ try_again: 'Try again',
+ openinUi: 'Open in UI',
+ openInEditor: 'Open in editor',
+ no_params: 'No parameters where found in the template.',
+ save_scores: 'Save Scores',
+ add_course: 'Add course',
+ add_project: 'Add project',
+ no_projects: 'No projects',
+ upload_project: 'Upload your project files.',
+ may_fail: 'May fail',
}
export default english
diff --git a/frontend/frontend/src/i18n/nl.ts b/frontend/frontend/src/i18n/nl.ts
index 9b377fbcb..e72d9181b 100644
--- a/frontend/frontend/src/i18n/nl.ts
+++ b/frontend/frontend/src/i18n/nl.ts
@@ -11,9 +11,9 @@ const dutch = {
submissions: 'Indieningen',
no_submission: 'niet ingediend',
error: 'Er is iets misgegaan.',
- assignment: 'Opgave:',
- filename: 'Ingediend bestand:',
- restrictions: 'Restricties:',
+ assignment: 'Opgave',
+ filename: 'Ingediend bestand',
+ restrictions: 'Restricties',
current_projects: 'Huidige projecten',
time: 'Tijdstip',
upload: 'Uploaden',
@@ -28,10 +28,12 @@ const dutch = {
random: 'Willekeurige',
members: 'Leden',
group_members: 'Groepsleden',
- assignmentName: 'Naam opdracht:',
+ no_members_yet: 'Nog geen leden',
+ members_loading: 'Leden ophalen...',
+ assignmentName: 'Naam opdracht',
upload_assignment: 'Upload opgavebestand',
- description: 'Beschrijving opgave:',
- uploadToolTip: 'upload een .zip of .pdf bestand',
+ description: 'Beschrijving opgave',
+ uploadToolTip: 'Upload een .zip of .pdf bestand',
noFile: 'Geen bestand gekozen',
remove: 'Verwijder',
submit: 'Bevestig',
@@ -68,7 +70,7 @@ const dutch = {
no_submissions: 'Geen indieningen',
edit: 'Aanpassen',
upload_scores: 'Upload Scores',
- last_submission: 'Laatste Indiening:',
+ last_submission: 'Laatste Indiening',
delete_project_warning: 'Opdracht Verwijderen?',
archive_project_warning: 'Opdracht Archiveren?',
cant_be_undone: 'Dit kan niet ongedaan gemaakt worden.',
@@ -78,7 +80,7 @@ const dutch = {
edit_scores_warning: 'Scores Aanpassen?',
undo_changes_warning: 'Aanpassingen Verwijderen?',
optional: 'Optioneel',
- deadlineCheck: 'extra deadline moet na de eerste deadline liggen',
+ deadlineCheck: 'Extra deadline moet na de eerste deadline liggen',
not_before_now: 'Datum moet in de toekomst liggen',
noStudentsYet: 'Nog geen studenten',
change_groups: 'Groepen aanpassen?',
@@ -90,6 +92,55 @@ const dutch = {
failed: 'Gefaald',
result: 'Resultaat',
must_pass: 'Moet slagen',
+ archive_course_warning: 'Vak Archiveren?',
+ will_archive_projects: 'Alle opdrachten worden gearchiveerd.',
+ first_name: 'Voornaam',
+ last_name: 'Familienaam',
+ loading: 'Laden',
+ file: 'Bestand',
+ copied: 'Gekopieerd!',
+ copy: 'Kopieer naar klembord',
+ join_course: 'Voeg vak toe?',
+ join: 'Voeg toe',
+ join_group: 'Wordt lid',
+ group_full: 'Groep zit vol',
+ leave: 'Verlaat',
+ acces: 'Dit geeft je toegang tot het vak.',
+ copy_invite: 'Kopieer uitnodigingslink',
+ no_assignmentfile: 'Geen opdrachtbestand',
+ deadlines_on: 'Deadlines op',
+ email: 'Email',
+ n_of_members: 'Max Aantal Leden',
+ existing_submissions:
+ 'Dit kan invloed hebben op bestaande indieningen. Weet je het zeker?',
+ divide_groups: 'Verdeel Groepen',
+ noGroup: 'Je lijkt nog geen groep te hebben.',
+ chooseGroup: 'Kies een groep voor je indient',
+ contactTeacher: 'Gelieve contact op te nemen met je lesgever.',
+ group_number: 'Groepnummer',
+ open_with_ui: 'Open met UI',
+ choose_existing: 'Kies een bestaand script',
+ make_new_script: 'Maak nieuw script aan',
+ cant_add_teachers_to_student_list:
+ 'Je kan geen leerkrachten aan de studenten lijst toevoegen',
+ cant_add_students_to_teacher_list:
+ 'Je kan geen studenten aan de leerkrachten lijst toevoegen',
+ cant_add_users_twice: 'Je kan gebruikers geen twee keer toevoegen',
+ this_user_doesnt_exist: 'Deze gebruiker bestaat niet',
+ status: 'Status',
+ visibility: 'Verander Zichtbaarheid',
+ template_saved_successfully: 'Template succesvol opgeslagen',
+ save_course_warning: 'Vak Opslaan?',
+ try_again: 'Probeer opnieuw',
+ openinUi: 'Open in UI',
+ openInEditor: 'Open in editor',
+ no_params: 'Geen parameters gevonden in de template.',
+ save_scores: 'Scores Opslaan',
+ add_course: 'Voeg vak toe',
+ add_project: 'Voeg project toe',
+ no_projects: 'Geen projecten',
+ upload_project: 'Upload je projectbestanden.',
+ may_fail: 'Mag falen',
}
export default dutch
diff --git a/frontend/frontend/src/main_original.tsx b/frontend/frontend/src/main_original.tsx
new file mode 100644
index 000000000..fcfd62dd1
--- /dev/null
+++ b/frontend/frontend/src/main_original.tsx
@@ -0,0 +1,129 @@
+import React from 'react'
+import ReactDOM from 'react-dom/client'
+import { ThemeProvider } from '@mui/material'
+import theme from './Theme.ts'
+import './i18n/config.ts'
+import { RouterProvider } from 'react-router-dom'
+import {
+ AuthenticationResult,
+ EventMessage,
+ EventType,
+ InteractionRequiredAuthError,
+ PublicClientApplication,
+} from '@azure/msal-browser'
+import { msalConfig } from './authConfig/authConfig.ts'
+
+import { Helmet, HelmetProvider } from 'react-helmet-async'
+import { LocalizationProvider } from '@mui/x-date-pickers'
+import { AdapterDayjs } from '@mui/x-date-pickers/AdapterDayjs/AdapterDayjs'
+import { LoginPage } from './pages/loginPage/LoginPage.tsx'
+
+import {
+ AuthenticatedTemplate,
+ MsalProvider,
+ UnauthenticatedTemplate,
+} from '@azure/msal-react'
+import router from './routes.tsx'
+import instance from './axiosConfig.ts'
+
+/**
+ * MSAL should be instantiated outside the component tree to prevent it from being re-instantiated on re-renders.
+ * For more, visit: https://github.com/AzureAD/microsoft-authentication-library-for-js/blob/dev/lib/msal-react/docs/getting-started.md
+ */
+const msalInstance = new PublicClientApplication(msalConfig)
+
+// Default to using the first account if no account is active on page load
+if (
+ !msalInstance.getActiveAccount() &&
+ msalInstance.getAllAccounts().length > 0
+) {
+ // Account selection logic is app dependent. Adjust as needed for different use cases.
+ msalInstance.setActiveAccount(
+ msalInstance.getActiveAccount() || msalInstance.getAllAccounts()[0]
+ )
+}
+
+// Function to acquire access token
+const acquireAccessToken = async () => {
+ const accounts = msalInstance.getAllAccounts()
+ if (accounts.length === 0) {
+ // User is not signed in. Redirect to login page or throw an error.
+ throw new Error('User is not signed in')
+ }
+
+ const request = {
+ scopes: ['user.read'],
+ account: accounts[0],
+ }
+
+ try {
+ const response = await msalInstance.acquireTokenSilent(request)
+ return response.accessToken
+ } catch (error) {
+ if (error instanceof InteractionRequiredAuthError) {
+ // Fallback to interaction when silent call fails
+ // This could be a redirect or a popup, depending on your application's flow
+ await msalInstance.acquireTokenRedirect(request)
+ } else {
+ console.error(error)
+ }
+ }
+}
+
+// Acquire token on app load
+instance.interceptors.request.use(
+ async (config) => {
+ try {
+ const token = await acquireAccessToken()
+ if (token) {
+ config.headers.Authorization = `Bearer ${token}`
+ console.log('auth token set')
+ }
+ return config
+ } catch (error) {
+ console.error(error)
+ throw error
+ }
+ },
+ (error) => {
+ return Promise.reject(error)
+ }
+)
+
+// Listen for sign-in event and set active account
+msalInstance.addEventCallback((event: EventMessage) => {
+ if (event.eventType === EventType.LOGIN_SUCCESS) {
+ // Cast event.payload to AuthenticationResult to access the account property
+ console.log('login success')
+ const authResult = event.payload as AuthenticationResult
+ if (authResult.account) {
+ const account = authResult.account
+ msalInstance.setActiveAccount(account)
+ }
+ }
+})
+
+ReactDOM.createRoot(document.getElementById('root')!).render(
+
+
+
+
+
+
+ Loading...}>
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+)
diff --git a/frontend/frontend/src/main_test.tsx b/frontend/frontend/src/main_test.tsx
new file mode 100644
index 000000000..f7340c833
--- /dev/null
+++ b/frontend/frontend/src/main_test.tsx
@@ -0,0 +1,70 @@
+import React from 'react'
+import ReactDOM from 'react-dom/client'
+import { ThemeProvider } from '@mui/material'
+import theme from './Theme.ts'
+import './i18n/config.ts'
+import { RouterProvider } from 'react-router-dom'
+import {
+ AuthenticationResult,
+ EventMessage,
+ EventType,
+ InteractionRequiredAuthError,
+ PublicClientApplication,
+} from '@azure/msal-browser'
+import { msalConfig } from './authConfig/authConfig.ts'
+
+import { Helmet, HelmetProvider } from 'react-helmet-async'
+import { LocalizationProvider } from '@mui/x-date-pickers'
+import { AdapterDayjs } from '@mui/x-date-pickers/AdapterDayjs/AdapterDayjs'
+
+import router from './routes.tsx'
+import instance from './axiosConfig.ts'
+
+/**
+ * MSAL should be instantiated outside the component tree to prevent it from being re-instantiated on re-renders.
+ * For more, visit: https://github.com/AzureAD/microsoft-authentication-library-for-js/blob/dev/lib/msal-react/docs/getting-started.md
+ */
+const msalInstance = new PublicClientApplication(msalConfig)
+
+// Default to using the first account if no account is active on page load
+if (
+ !msalInstance.getActiveAccount() &&
+ msalInstance.getAllAccounts().length > 0
+) {
+ // Account selection logic is app dependent. Adjust as needed for different use cases.
+ msalInstance.setActiveAccount(
+ msalInstance.getActiveAccount() || msalInstance.getAllAccounts()[0]
+ )
+}
+
+
+// Listen for sign-in event and set active account
+msalInstance.addEventCallback((event: EventMessage) => {
+ if (event.eventType === EventType.LOGIN_SUCCESS) {
+ // Cast event.payload to AuthenticationResult to access the account property
+ console.log('login success')
+ const authResult = event.payload as AuthenticationResult
+ if (authResult.account) {
+ const account = authResult.account
+ msalInstance.setActiveAccount(account)
+ }
+ }
+})
+
+ReactDOM.createRoot(document.getElementById('root')!).render(
+
+
+
+
+
+
+ Loading...}>
+
+
+
+
+
+
+
+
+)
diff --git a/frontend/frontend/src/pages/FourOFourPage.tsx b/frontend/frontend/src/pages/FourOFourPage.tsx
new file mode 100644
index 000000000..b4e2098d4
--- /dev/null
+++ b/frontend/frontend/src/pages/FourOFourPage.tsx
@@ -0,0 +1,106 @@
+import { Box, Typography } from '@mui/material'
+import { t } from 'i18next'
+
+export default function FourOFourPage() {
+ return (
+ <>
+
+
+
+
+
+
+
+ {"404"}
+
+
+ {"Error: Page not found"}
+
+
+
+
+
+ >
+ )
+}
\ No newline at end of file
diff --git a/frontend/frontend/src/pages/addChangeAssignmentPage/AddChangeAssignmentPage.tsx b/frontend/frontend/src/pages/addChangeAssignmentPage/AddChangeAssignmentPage.tsx
index 20e0ccda4..617465b19 100644
--- a/frontend/frontend/src/pages/addChangeAssignmentPage/AddChangeAssignmentPage.tsx
+++ b/frontend/frontend/src/pages/addChangeAssignmentPage/AddChangeAssignmentPage.tsx
@@ -1,8 +1,14 @@
import {
- Box,
Card,
+ Divider,
+ EvenlySpacedRow,
+} from '../../components/CustomComponents.tsx'
+import {
+ Box,
+ CircularProgress,
IconButton,
ListItem,
+ Skeleton,
Stack,
TextField,
Tooltip,
@@ -32,8 +38,8 @@ import instance from '../../axiosConfig.ts'
import WarningPopup from '../../components/WarningPopup.tsx'
import AddRestrictionButton from './AddRestrictionButton.tsx'
import { RestrictionCard } from '../../components/RestrictionCard.tsx'
+import { User } from '../subjectsPage/AddChangeSubjectPage.tsx'
-//TODO: add restriction functionality
/**
* This page is used to add or change an assignment.
* The page should only be accessible to teachers of the course.
@@ -84,18 +90,25 @@ interface errorChecks {
export function AddChangeAssignmentPage() {
const navigate = useNavigate()
+ //State for loading the data or showing skeletons
+ const [loading, setLoading] = useState(false)
+ const [userLoading, setUserLoading] = useState(true)
+
// State for the different fields of the assignment
const [title, setTitle] = useState('')
const [description, setDescription] = useState('')
const [dueDate, setDueDate] = useState(null)
const [extraDueDate, setExtraDueDate] = useState(null)
+ const [oldRestrictions, setOldRestrictions] = useState([])
const [restrictions, setRestrictions] = useState([])
- const [visible, setVisible] = useState(false)
+ const [visible, setVisible] = useState(true)
const [assignmentFile, setAssignmentFile] = useState()
const [maxScore, SetMaxScore] = useState(20)
const [cleared, setCleared] = useState(false)
- const [filename, setFilename] = useState('indiening.zip')
+ const [filename, setFilename] = useState('')
+ const [groupSize, setGroupSize] = useState(1)
+ const [user, setUser] = useState()
// State for the error checks of the assignment
const [assignmentErrors, setAssignmentErrors] = useState({
title: false,
@@ -108,11 +121,16 @@ export function AddChangeAssignmentPage() {
//confirmation dialogs
const [deleteConfirmation, setDeleteConfirmation] = useState(false)
const [saveConfirmation, setSaveConfirmation] = useState(false)
+ const [cancelConfirmation, setCancelConfirmation] = useState(false)
const closeSaveConfirmation = () => {
setSaveConfirmation(false)
}
+ const closeCancel = () => {
+ setCancelConfirmation(false)
+ }
+
//open the delete confirmation dialog
const openDeleteConfirmation = () => {
setDeleteConfirmation(true)
@@ -148,8 +166,18 @@ export function AddChangeAssignmentPage() {
//set the initial values of the assignment if it is an edit
useEffect(() => {
- if (assignmentId !== undefined) {
- instance
+ //get the data
+ const fetchUser = async () => {
+ setUserLoading(true)
+ const userResponse = await instance.get('/gebruikers/me/')
+ setUser(userResponse.data)
+ setUserLoading(false)
+ }
+ const fetchData = async () => {
+ //begin loading -> set loading to true
+ setLoading(true)
+ //get the assignment
+ await instance
.get(`/projecten/${assignmentId}`)
.then((response) => {
const assignment = response.data
@@ -163,24 +191,19 @@ export function AddChangeAssignmentPage() {
setDescription(assignment.beschrijving)
console.log('bestand' + assignment.opgave_bestand)
- setFilename(() => assignment.opgave_bestand)
+ if (assignment.opgave_bestand !== null) {
+ setFilename(() => assignment.opgave_bestand)
+ }
SetMaxScore(assignment.max_score)
console.log('max score' + assignment.max_score)
setVisible(assignment.zichtbaar)
if (assignment.deadline !== null) {
- setDueDate(
- dayjs(assignment.deadline, 'YYYY-MM-DDTHH:mm:ss')
- )
+ setDueDate(dayjs(assignment.deadline))
console.log('deadline' + assignment.deadline)
}
if (assignment.extra_deadline !== null) {
- setExtraDueDate(
- dayjs(
- assignment.extra_deadline,
- 'YYYY-MM-DDTHH:mm:ss'
- )
- )
+ setExtraDueDate(dayjs(assignment.extra_deadline))
console.log(
'extra deadline' + assignment.extra_deadline
)
@@ -191,7 +214,7 @@ export function AddChangeAssignmentPage() {
})
//get the restrictions
- instance
+ await instance
.get(`/restricties/?project=${assignmentId}`)
.then(async (response) => {
const restrictions = response.data
@@ -200,7 +223,9 @@ export function AddChangeAssignmentPage() {
await instance
.get(
`/restricties/${restr.restrictie_id}/script/`,
- { responseType: 'blob' }
+ {
+ responseType: 'blob',
+ }
)
.then((response) => {
const blob = new Blob([response.data], {
@@ -214,6 +239,7 @@ export function AddChangeAssignmentPage() {
console.error(error)
})
}
+ setOldRestrictions(restrictions)
setRestrictions(restrictions)
})
.catch((error) => {
@@ -221,23 +247,38 @@ export function AddChangeAssignmentPage() {
})
//get the assignment file
- instance
- .get(`/projecten/${assignmentId}/opgave_bestand/`, {
- responseType: 'blob',
- })
- .then((response) => {
- const blob = new Blob([response.data], {
- type: response.headers['content-type'],
+ if (filename !== '') {
+ await instance
+ .get(`/projecten/${assignmentId}/opgave_bestand/`, {
+ responseType: 'blob',
})
- const file: File = new File([blob], filename, {
- type: response.headers['content-type'],
+ .then((response) => {
+ const blob = new Blob([response.data], {
+ type: response.headers['content-type'],
+ })
+ const file: File = new File([blob], filename, {
+ type: response.headers['content-type'],
+ })
+
+ setAssignmentFile(file)
})
+ .catch((error) => {
+ console.error(error)
+ })
+ }
- setAssignmentFile(file)
- })
- .catch((error) => {
- console.error(error)
- })
+ //end loading -> set loading to false
+ setLoading(false)
+ }
+
+ //if there is an assignmentId, get the data else use the default values
+ fetchUser().catch((error) => {
+ console.error(error)
+ })
+ if (assignmentId !== undefined) {
+ fetchData().catch((error) => {
+ console.error(error)
+ })
}
}, [assignmentId, filename])
@@ -270,7 +311,11 @@ export function AddChangeAssignmentPage() {
if (dueDate === null && extraDueDate === null) {
setDeadlineCheck(false)
} else if (dueDate !== null && extraDueDate !== null) {
- setDeadlineCheck(extraDueDate.diff(dueDate) < 0)
+ if (dayjs(extraDueDate).isSame(dayjs(dueDate))) {
+ setDeadlineCheck(true)
+ } else {
+ setDeadlineCheck(dayjs(extraDueDate).isBefore(dayjs(dueDate)))
+ }
} else if (dueDate !== null && extraDueDate === null) {
setDeadlineCheck(false)
} else {
@@ -301,12 +346,23 @@ export function AddChangeAssignmentPage() {
},
}
+ //delete removed restrictions
+ oldRestrictions.forEach((oldRestriction) => {
+ if (!restrictions.includes(oldRestriction)) {
+ instance
+ .delete(
+ '/restricties/' + oldRestriction.restrictie_id + '/'
+ )
+ .catch((error) => {
+ console.error(error)
+ })
+ }
+ })
+
restrictions.forEach((restriction) => {
const formData = new FormData()
formData.append('project', projectId)
- if (restriction.file !== undefined) {
- formData.append('script', restriction.file)
- }
+
formData.append('moet_slagen', restriction.moet_slagen.toString())
if (restriction.restrictie_id !== undefined) {
formData.append(
@@ -323,6 +379,9 @@ export function AddChangeAssignmentPage() {
console.error(error)
})
} else {
+ if (restriction.file !== undefined) {
+ formData.append('script', restriction.file)
+ }
instance
.post('/restricties/', formData, config)
.catch((error) => {
@@ -332,80 +391,87 @@ export function AddChangeAssignmentPage() {
})
}
- // Upload the assignment to the API. patch if it is an edit, post if it is a new assignment.
+ // Upload the assignment to the API. put if it is an edit, post if it is a new assignment.
const uploadAssignment = async () => {
- let optionalFile: File | null = null
- if (assignmentFile !== undefined) {
- optionalFile = assignmentFile
- }
- const formData = new FormData()
- formData.append('titel', title)
- formData.append('beschrijving', description)
- formData.append('vak', parseInt(courseId as string).toString())
- if (optionalFile) {
- formData.append('opgave_bestand', optionalFile)
- }
- formData.append('zichtbaar', visible.toString())
+ try {
+ const config = {
+ headers: {
+ 'Content-Type': 'multipart/form-data',
+ },
+ }
+ // editing an existing assignment: need to put
+ if (assignmentId) {
+ const formData = new FormData()
+ formData.append('project_id', assignmentId)
+ if (assignmentFile) {
+ formData.append('opgave_bestand', assignmentFile)
+ }
- // Add optional fields
- if (maxScore !== 20) {
- formData.append('max_score', maxScore.toString())
- }
- if (dueDate !== null) {
- formData.append('deadline', dueDate.format())
- }
- if (extraDueDate !== null) {
- formData.append('extra_deadline', extraDueDate.format())
- }
+ formData.append('titel', title)
+ formData.append('beschrijving', description)
+ if (courseId) {
+ formData.append('vak', courseId)
+ }
+ if (maxScore !== 20) {
+ formData.append('max_score', maxScore.toString())
+ }
+ if (dueDate) {
+ formData.append('deadline', dueDate.format())
+ }
+ if (extraDueDate) {
+ formData.append('extra_deadline', extraDueDate.format())
+ }
+ formData.append('zichtbaar', visible.toString())
+ formData.append('max_groep_grootte', groupSize.toString())
- const config = {
- headers: {
- 'Content-Type': 'multipart/form-data',
- },
- }
- if (assignmentId !== undefined) {
- formData.append('project_id', assignmentId)
- await instance
- .patch(
- '/projecten/' + parseInt(assignmentId) + '/',
+ // Send the data to the API
+ await instance.put(
+ `/projecten/${assignmentId}/`,
formData,
config
)
- .catch((error) => {
- console.error(error)
- })
-
- //upload the restrictions
- handleRestrictionUpload(assignmentId.toString())
- } else {
- //if there is no assignmentId, it is a new assignment
- let project_id: number = 0
-
- await instance
- .post('/projecten/', formData, config)
- .then((response) => (project_id = response.data.project_id))
- .catch((error) => {
- console.error(error)
- })
+ handleRestrictionUpload(assignmentId.toString())
+ } else {
+ // Creating a new assignment: need to post
+ const formData = new FormData()
+ if (assignmentFile) {
+ formData.append('opgave_bestand', assignmentFile)
+ }
+ formData.append('titel', title)
+ formData.append('beschrijving', description)
+ if (courseId) {
+ formData.append('vak', courseId)
+ }
+ if (maxScore !== 20) {
+ formData.append('max_score', maxScore.toString())
+ }
+ if (dueDate) {
+ formData.append('deadline', dueDate.format())
+ }
+ if (extraDueDate) {
+ formData.append('extra_deadline', extraDueDate.format())
+ }
+ formData.append('zichtbaar', visible.toString())
+ formData.append('max_groep_grootte', groupSize.toString())
- //upload the restrictions
- handleRestrictionUpload(project_id.toString())
- }
+ // Send the data to the API
+ const project = await instance.post(
+ '/projecten/',
+ formData,
+ config
+ )
+ handleRestrictionUpload(project.data.id)
+ }
- console.info(
- 'Form submitted',
- title,
- description,
- dueDate,
- restrictions,
- visible,
- assignmentFile
- )
- setSaveConfirmation(false)
- if (assignmentId !== undefined) {
- navigate('/course/' + courseId + '/assignment/' + assignmentId)
- } else {
- navigate('/course/' + courseId)
+ if (assignmentId !== undefined) {
+ navigate('/course/' + courseId + '/assignment/' + assignmentId)
+ } else {
+ navigate('/course/' + courseId)
+ }
+ } catch (error) {
+ console.error(error)
+ } finally {
+ setSaveConfirmation(false)
}
}
@@ -424,432 +490,750 @@ export function AddChangeAssignmentPage() {
return (
<>
- {/* Stack container for layout */}
-
-
-
- {/* Form for submitting assignment */}
-
-
-
-
- {t('assignmentName')}
-
-
- setTitle(event.target.value)
- }
- />
-
- {/* File Upload button */}
-
-
-
-
- {/* Deadline section */}
-
-
-
- Deadline:
-
-
-
- SetDeadlineError(newError)
- }
- slotProps={{
- field: {
- clearable: true,
- onClear: () => setCleared(true),
- },
- textField: {
- helperText: errorMessage,
- },
- }}
- onChange={(newValue) =>
- setDueDate(newValue)
- }
- />
-
-
-
-
- Extra Deadline:
-
-
- setCleared(true),
- },
- textField: {
- error: deadlineCheckError,
- helperText: deadlineCheckError
- ? t('deadlineCheck')
- : '',
- },
- }}
- onChange={(newValue) =>
- setExtraDueDate(newValue)
- }
- />
-
-
-
- {/* Description section */}
-
-
-
- {t('description')}
-
-
- setDescription(event.target.value)
- }
- fullWidth
- error={assignmentErrors.description}
- helperText={
- assignmentErrors.description
- ? t('descriptionName') +
- ' ' +
- t('is_required')
- : ''
- }
- sx={{ overflowY: 'auto', maxHeight: '25svh' }}
- />
-
-
- {/* Restrictions section */}
-
-
-
- {t('restrictions')}
-
-
-
+
+
+ ) : (
+ <>
+ {user?.is_lesgever ? (
+ // Rendering UI for teacher
+ <>
+ {/* Stack container for layout */}
+
+ {/*very ugly but it works functionally*/}
+ {loading ? (
+
+ ) : (
+
+ )}
+ {/* Form for submitting assignment */}
+
- {restrictions.map((restriction, index) => {
- return (
-
-
+
+ {/* Here the user gets to specify the assignment name */}
+
+ {t('assignmentName')}
+
+ {loading ? (
+
+ ) : (
+
+ setTitle(
+ event.target.value
+ )
+ }
+ />
+ )}
+
+ {/* File Upload button */}
+
+ {loading ? (
+ {
+ console.log('loading')
+ }}
+ fileTypes={['.pdf', '.zip']}
+ path={
+ new File(
+ [],
+ 'loading...'
+ )
}
/>
-
- )
- })}
-
-
-
-
- {/* */}
-
- setRestrictions(newRestrictions)
- }
- >
- {/*Show restrictions */}
-
-
-
-
- {/* Main actions section */}
-
-
-
- {visible ? (
- setVisible(!visible)}
+ ) : (
+
+ )}
+
+
+ {/* Deadline section.
+ There is both the normal deadline,
+ and an extra deadline in case people need more time. */}
+
-
-
- ) : (
- setVisible(!visible)}
+
+ {/* This section renders the normal deadline. */}
+
+ Deadline:
+
+ {loading ? (
+
+ ) : (
+
+
+ SetDeadlineError(
+ newError
+ )
+ }
+ slotProps={{
+ field: {
+ clearable: true,
+ onClear: () =>
+ setCleared(
+ true
+ ),
+ },
+ textField: {
+ helperText:
+ errorMessage,
+ },
+ }}
+ onChange={(newValue) =>
+ setDueDate(newValue)
+ }
+ />
+
+ )}
+
+
+ {/* This section renders the extra deadline. */}
+
+ Extra Deadline:
+
+ {loading ? (
+
+ ) : (
+
+
+ setCleared(
+ true
+ ),
+ },
+ textField: {
+ error: deadlineCheckError,
+ helperText:
+ deadlineCheckError
+ ? t(
+ 'deadlineCheck'
+ )
+ : '',
+ },
+ }}
+ onChange={(newValue) =>
+ setExtraDueDate(
+ newValue
+ )
+ }
+ />
+
+ )}
+
+
+ {/* Description section */}
+
+
+
+ {t('description')}
+
+ {loading ? (
+
+ ) : (
+
+ setDescription(
+ event.target.value
+ )
+ }
+ fullWidth
+ error={
+ assignmentErrors.description
+ }
+ // Show an error message if the description is not filled in.
+ helperText={
+ assignmentErrors.description
+ ? t(
+ 'descriptionName'
+ ) +
+ ' ' +
+ t('is_required')
+ : ''
+ }
+ sx={{
+ overflowY: 'auto',
+ maxHeight: '23svh',
+ }}
+ />
+ )}
+
+
+ {/* Restrictions section */}
+
-
-
- )}
-
-
+
+
+ {t('restrictions')}
+
+
+ {t('name')}
+ ,
+
+ {t('must_pass')}
+ ,
+
+ {t('remove')}
+ ,
+ ]}
+ />
+
+
+ {/*This list will render the restrictions that are added to the assignment.*/}
+
+
+ {loading ? (
+
+ ) : (
+ <>
+ {restrictions.map(
+ (
+ restriction,
+ index
+ ) => {
+ return (
+ <>
+
+
+
+
+ >
+ )
+ }
+ )}
+ >
+ )}
+
+
+
+
+
+ setRestrictions(
+ newRestrictions
+ )
+ }
+ userid={user.user}
+ >
+
+
+ {/* Main actions section */}
+
-
-
-
-
-
-
- Max Score
-
- {
- if (event.target.value !== '') {
- const newScore = Math.max(
- parseInt(event.target.value),
- 0
- )
- SetMaxScore
- ? SetMaxScore(newScore)
- : undefined
- } else {
- SetMaxScore
- ? SetMaxScore(
- parseInt(
- event.target.value
- )
- )
- : undefined
- }
- }}
+
+
+
+ {visible ? (
+
+ setVisible(
+ !visible
+ )
+ }
+ >
+
+
+ ) : (
+
+ setVisible(
+ !visible
+ )
+ }
+ >
+
+
+ )}
+
+ {assignmentId && !loading && (
+
+
+
+
+
+ )}
+
+ {/* change group size allowed, no need for extra group switch*/}
+ {!assignmentId && (
+
+
+ {t('n_of_members')}
+
+ {loading ? (
+
+ ) : (
+
+ setGroupSize(
+ parseInt(
+ event
+ .target
+ .value
+ )
+ )
+ }
+ />
+ )}
+
+ )}
+ {/* This section allows the teacher to set the maximum score for the assignment.*/}
+
+
+ Max Score
+
+ {loading ? (
+
+ ) : (
+ {
+ if (
+ event.target
+ .value !==
+ ''
+ ) {
+ const newScore =
+ Math.max(
+ parseInt(
+ event
+ .target
+ .value
+ ),
+ 0
+ )
+ SetMaxScore
+ ? SetMaxScore(
+ newScore
+ )
+ : undefined
+ } else {
+ SetMaxScore
+ ? SetMaxScore(
+ parseInt(
+ event
+ .target
+ .value
+ )
+ )
+ : undefined
+ }
+ }}
+ />
+ )}
+
+
+ {/* Submit and Cancel buttons */}
+
+
+
+ setCancelConfirmation(
+ true
+ )
+ }
+ sx={{
+ backgroundColor:
+ 'secondary.main',
+ borderRadius: 2,
+ }}
+ >
+
+
+
+
+
+
+
+
+
+
+
+
+ {/* Popup for adding restrictions */}
+
+ {/* Confirmation popup for deleting project */}
+
+
-
-
- {/* Submit and Cancel buttons */}
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- {/* Popup for adding restrictions */}
-
- {/* Confirmation popup for deleting project */}
-
-
- {/* Confirmation popup for saving project */}
-
-
+ {/* Confirmation popup for saving project */}
+
+ {/* Confirmation popup for canceling changes*/}
+
+
+ >
+ ) : (
+ navigate('*')
+ )}
+ >
+ )}
>
)
}
diff --git a/frontend/frontend/src/pages/addChangeAssignmentPage/AddRestrictionButton.tsx b/frontend/frontend/src/pages/addChangeAssignmentPage/AddRestrictionButton.tsx
index 843db2e8a..0886c4954 100644
--- a/frontend/frontend/src/pages/addChangeAssignmentPage/AddRestrictionButton.tsx
+++ b/frontend/frontend/src/pages/addChangeAssignmentPage/AddRestrictionButton.tsx
@@ -1,5 +1,5 @@
import * as React from 'react'
-import Button from '@mui/material/Button'
+import { SecondaryButton } from '../../components/CustomComponents.tsx'
import Dialog, { DialogProps } from '@mui/material/Dialog'
import DialogActions from '@mui/material/DialogActions'
import DialogContent from '@mui/material/DialogContent'
@@ -7,21 +7,22 @@ import DialogTitle from '@mui/material/DialogTitle'
import AddIcon from '@mui/icons-material/Add'
import RestrictionsDialog from './RestrictionsDialog'
import { t } from 'i18next'
-import { IconButton } from '@mui/material'
+import { IconButton, Tooltip } from '@mui/material'
import { restriction } from './AddChangeAssignmentPage.tsx'
-
/**
* Component for an "Add Restriction" button that opens a dialog for adding restrictions.
* @returns {React.ReactElement} - The rendered component.
*/
interface AddRestrictionButtonProps {
+ userid: number
restrictions: restriction[]
setRestrictions: (restriction: restriction[]) => void
}
export default function AddRestrictionButton({
+ userid,
restrictions,
setRestrictions,
}: AddRestrictionButtonProps) {
@@ -35,19 +36,21 @@ export default function AddRestrictionButton({
return (
<>
{/* Add Restriction Button */}
- {
- setOpen(true)
- setScroll('paper')
- }}
- >
-
-
+
+ {
+ setOpen(true)
+ setScroll('paper')
+ }}
+ >
+
+
+
{/* Add Restriction Dialog */}
{/* Cancel Button */}
- {t('cancel')}
+
+ {t('cancel')}
+
>
diff --git a/frontend/frontend/src/pages/addChangeAssignmentPage/RestrictionTemplateUI.tsx b/frontend/frontend/src/pages/addChangeAssignmentPage/RestrictionTemplateUI.tsx
new file mode 100644
index 000000000..a7246e991
--- /dev/null
+++ b/frontend/frontend/src/pages/addChangeAssignmentPage/RestrictionTemplateUI.tsx
@@ -0,0 +1,454 @@
+import { useState } from 'react'
+import {
+ Box,
+ Button,
+ Checkbox,
+ IconButton,
+ List,
+ ListItem,
+ ListItemText,
+ TextField,
+} from '@mui/material'
+import AddCircleOutlineIcon from '@mui/icons-material/AddCircleOutline'
+import DeleteOutlineIcon from '@mui/icons-material/DeleteOutline'
+import Typography from '@mui/material/Typography'
+import AppBar from '@mui/material/AppBar'
+import Toolbar from '@mui/material/Toolbar'
+import CloseIcon from '@mui/icons-material/Close'
+import { restriction } from './AddChangeAssignmentPage.tsx'
+import { t } from 'i18next'
+
+// Define a type for the parameters
+type ParamValue = string | number | boolean | string[]
+
+const saveRestrictionTemplate = (
+ restrictionCode: string,
+ params: { [key: string]: ParamValue },
+ templateFileName: string
+) => {
+ // This function will save the restriction template with the given parameters.
+ // It finds the #@param tags in the restriction code and replaces the values with the given parameters.
+ // It does this with javascript regexp
+
+ // get the extension after the dot of the filename
+ const extension = templateFileName.split('.').pop()
+
+ let newRestrictionCode = restrictionCode
+ Object.keys(params).forEach((key) => {
+ // match on the #@param tag and the variable name
+ // between the #@param tag there can be any number of newline comments and whitespace
+ // between the value and the variable name is an equals sign
+ // There may be any number of spaces around the equals sign
+ let valueString = params[key]
+
+ // If the value is an array, we need to convert it to a string
+
+ if (Array.isArray(valueString)) {
+ if (extension === 'sh') {
+ valueString = '(' + valueString.join(' ') + ')'
+ } else if (extension === 'py') {
+ valueString = '[' + valueString.join(', ') + ']'
+ }
+ } else if (typeof valueString === 'string') {
+ valueString = '"' + valueString + '"'
+ } else if (typeof valueString === 'boolean') {
+ if (extension === 'sh') {
+ valueString = valueString ? 'true' : 'false'
+ } else if (extension === 'py') {
+ valueString = valueString ? 'True' : 'False'
+ }
+ } else if (typeof valueString === 'number') {
+ // This case already works without intervention.
+ }
+
+ // write this value next to the equals sign in the variable line, keep all the lines surrounding it, such as the comments
+ // It may be that comments come after the value, those should be kept as well
+ newRestrictionCode = newRestrictionCode.replace(
+ new RegExp(`(#@param[\\s\\S]*?${key}[\\s\\S]*?=)[^\\n]*`),
+ `$1${valueString}`
+ )
+ })
+
+ console.log(newRestrictionCode)
+
+ return newRestrictionCode
+}
+
+interface RestrictionTemplateUIProps {
+ restrictionCode: string
+ handleCloseTemplateInterface: () => void
+ templateFileName: string
+ restrictions: restriction[]
+ setRestrictions: (restriction: restriction[]) => void
+}
+
+/**
+ * This UI will show different fields for parameters regarding the restrictions template.
+ * @returns {React.ReactElement} - The rendered component.
+ */
+export default function RestrictionTemplateUI({
+ restrictionCode,
+ handleCloseTemplateInterface,
+ templateFileName,
+ restrictions,
+ setRestrictions,
+}: RestrictionTemplateUIProps) {
+ // De onderste twee rijen code zouden bijvoorbeeld het volgende doen
+ // voor de templateFileName 'template.sh':
+ // templateExtension = 'sh'
+ // templateName = 'template'
+ const templateName = templateFileName.split('.').shift()
+
+ // There are four types of variables that can be used in a restriction template:
+ // 1. Integer
+ // 2. String
+ // 3. Boolean
+ // 4. List
+
+ const params = parseParams(restrictionCode)
+
+ // change this paramsState to be an array of the following type of objects:
+ // {name: string, value: string | number | boolean | string[]}
+ // it mus have a [Symbol.iterator] method
+ const [paramsState, setParamsState] = useState<{
+ [key: string]: number | string | boolean | string[]
+ }>({})
+
+ // Initialize paramsState with default values
+ useState(() => {
+ const initialState: { [key: string]: ParamValue } = {}
+ params.forEach((param) => {
+ initialState[param.variable] = param.value
+ })
+ setParamsState(initialState)
+ })
+
+ //handle the submission of the form
+ const handleSubmit = (code: string) => {
+ /*if (restrictionName === '') {
+ setNameError(true)
+ return
+ }*/
+
+ const newRestriction: restriction = {
+ script: templateFileName,
+ file: new File([code], templateFileName, { type: 'text/plain' }),
+ moet_slagen: false,
+ }
+ console.log('code:')
+ console.log(code)
+ setRestrictions([...restrictions, newRestriction])
+ handleCloseTemplateInterface()
+ }
+
+ // All functions beneath are for handling the array type of parameters.
+ const handleArrayChange = (
+ variable: string,
+ newValue: number | string | boolean | string[]
+ ) => {
+ setParamsState((prevState) => ({
+ ...prevState,
+ [variable]: newValue,
+ }))
+ }
+
+ // This function is specifically for handling the array type of parameters.
+ // It will change the row at the given index to the new value.
+ const handleChangeRow = (
+ variable: string,
+ newValue: string,
+ index: number
+ ) => {
+ const currentValue = paramsState[variable]
+ if (Array.isArray(currentValue)) {
+ const newArrayValues = [...currentValue] // TypeScript now knows currentValue is a string[]
+ newArrayValues[index] = newValue
+ setParamsState((prevState) => ({
+ ...prevState,
+ [variable]: newArrayValues,
+ }))
+ } else {
+ console.error(
+ `Expected an array for variable ${variable}, but got:`,
+ currentValue
+ )
+ }
+ }
+
+ const handleDeleteRow = (variable: string, index: number) => {
+ const currentValue = paramsState[variable]
+ if (Array.isArray(currentValue)) {
+ const newArrayValues = [...currentValue] // TypeScript now knows currentValue is a string[]
+ newArrayValues.splice(index, 1)
+ setParamsState((prevState) => ({
+ ...prevState,
+ [variable]: newArrayValues,
+ }))
+ } else {
+ console.error(
+ `Expected an array for variable ${variable}, but got:`,
+ currentValue
+ )
+ }
+ }
+
+ const handleAddRow = (variable: string) => {
+ setParamsState((prevState) => {
+ const currentArray = prevState[variable]
+ if (Array.isArray(currentArray)) {
+ return {
+ ...prevState,
+ [variable]: [...currentArray, ''], // Initialize with an empty string
+ }
+ } else {
+ console.error(
+ `Expected an array for variable ${variable}, but got:`,
+ currentArray
+ )
+ return prevState // Return previous state without modification
+ }
+ })
+ }
+
+ return (
+
+
+
+
+
+
+
+ {templateName}
+
+
+ handleSubmit(
+ saveRestrictionTemplate(
+ restrictionCode,
+ paramsState,
+ templateFileName
+ )
+ )
+ }
+ >
+ save
+
+
+
+
+ {params.length === 0 && (
+
+ {t('no_params')}
+
+ )}
+ {params.map((param, index) => (
+
+ {param.type === 'number' && (
+ // In this case we have a number
+ // It needs the appropriate input field
+ <>
+
+ {param.description}
+
+
+ handleArrayChange(
+ param.variable,
+ Number(e.target.value)
+ )
+ }
+ />
+ >
+ )}
+ {param.type === 'string' && (
+ // In this case we have a string
+ // It needs the appropriate input field
+ <>
+
+ {param.description}
+
+
+ handleArrayChange(
+ param.variable,
+ e.target.value
+ )
+ }
+ />
+ >
+ )}
+ {param.type === 'boolean' && (
+ // In this case we have a boolean
+ // It needs the appropriate input field
+ <>
+
+ {param.description}
+
+
+ handleArrayChange(
+ param.variable,
+ e.target.checked
+ )
+ }
+ />
+ >
+ )}
+ {param.type === 'array' && (
+ // In this case we have a list
+ // It needs to have a listing of the present elements,
+ // as well as a button to add entries to the list.
+
+
+ {param.description}
+
+
+ {(() => {
+ const paramArray = paramsState[
+ param.variable
+ ] as string[] | undefined
+ return (
+ Array.isArray(paramArray) &&
+ paramArray.map(
+ (item: string, idx: number) => (
+
+
+
+ handleChangeRow(
+ param.variable,
+ e.target
+ .value,
+ idx
+ )
+ }
+ />
+
+
+ handleDeleteRow(
+ param.variable,
+ idx
+ )
+ }
+ >
+
+
+
+ )
+ )
+ )
+ })()}
+
+
+
handleAddRow(param.variable)}
+ startIcon={ }
+ >
+ Add Row
+
+
+ )}
+
+ ))}
+
+
+ )
+}
+
+// Helper function for parsing a value in either python or bash code.
+function parseValue(value: string): string | number | boolean | string[] {
+ if (value.startsWith('"') && value.endsWith('"')) {
+ return value.slice(1, -1)
+ } else if (value === 'True' || value === 'true') {
+ // capital T for python or lowercase t for bash
+ return true
+ } else if (value === 'False' || value === 'false') {
+ // capital F for python or lowercase f for bash
+ return false
+ } else if (!isNaN(parseInt(value))) {
+ return parseInt(value)
+ } else if (value.startsWith('[') && value.endsWith(']')) {
+ // square brackets for python lists
+ // Python-style list
+ return value
+ .slice(1, -1)
+ .split(',')
+ .map((item) => String(parseValue(item.trim()))) // in python the values are separated by commas
+ } else if (value.startsWith('(') && value.endsWith(')')) {
+ // round brackets for bash lists
+ // Bash-style list
+ return value
+ .slice(1, -1)
+ .split(' ')
+ .map((item) => String(parseValue(item.trim()))) // in bash the values are separated by whitespace
+ } else {
+ return value
+ }
+}
+
+function parseParams(code: string) {
+ // Parse the restriction code into different fields
+ const params = []
+ const lines = code.split('\n')
+ let description = ''
+
+ for (let i = 0; i < lines.length; i++) {
+ const line = lines[i].trim()
+
+ // Look for the #@param tag
+ if (line.startsWith('#@param')) {
+ description = lines[i + 1].trim().replace(/^# ?/, '')
+ const variableLine = lines[i + 2].trim()
+ const [variable, value] = variableLine
+ .split('=')
+ .map((str) => str.trim())
+
+ const parsedValue = parseValue(value)
+
+ params.push({
+ type: Array.isArray(parsedValue) ? 'array' : typeof parsedValue,
+ description,
+ variable,
+ value: parsedValue,
+ })
+ }
+ }
+
+ return params
+}
diff --git a/frontend/frontend/src/pages/addChangeAssignmentPage/RestrictionsDialog.tsx b/frontend/frontend/src/pages/addChangeAssignmentPage/RestrictionsDialog.tsx
index 00d8e0037..8f42c7333 100644
--- a/frontend/frontend/src/pages/addChangeAssignmentPage/RestrictionsDialog.tsx
+++ b/frontend/frontend/src/pages/addChangeAssignmentPage/RestrictionsDialog.tsx
@@ -1,26 +1,37 @@
import * as React from 'react'
import { ChangeEvent, useState } from 'react'
-import Button from '@mui/material/Button'
+import {
+ Button,
+ Card,
+ SecondaryButton,
+} from '../../components/CustomComponents.tsx'
import Dialog from '@mui/material/Dialog'
import AppBar from '@mui/material/AppBar'
import Toolbar from '@mui/material/Toolbar'
import IconButton from '@mui/material/IconButton'
import Typography from '@mui/material/Typography'
import CloseIcon from '@mui/icons-material/Close'
+import CodeIcon from '@mui/icons-material/Code'
import {
+ Alert,
Box,
- ButtonGroup,
+ Collapse,
FormControl,
MenuItem,
Select,
+ Stack,
TextField,
+ Tooltip,
} from '@mui/material'
import { t } from 'i18next'
import { restriction } from './AddChangeAssignmentPage.tsx'
-import Switch from '@mui/material/Switch'
import WarningPopup from '../../components/WarningPopup.tsx'
+import RestrictionsTemplateUI from './RestrictionTemplateUI.tsx'
+import instance from '../../axiosConfig.ts'
+import BorderColorIcon from '@mui/icons-material/BorderColor'
interface RestrictionsDialogProps {
+ userid: number
restrictions: restriction[]
setRestrictions: (restriction: restriction[]) => void
closeParentDialog: () => void
@@ -31,6 +42,35 @@ enum restrictionExtension {
Python = '.py',
}
+export interface Template {
+ template_id: number
+ user: number
+ bestand: string
+}
+
+/*const code = `
+
+#@param
+# Schrijf in dit veld je voornaam.
+naam=""
+
+#@param
+# Schrijf in dit veld je leeftijd.
+leeftijd=0
+
+#@param
+# Ben je lesgever?
+lesgever=False
+
+#@param
+# Welke bestanden moeten aanwezig zijn?
+bestanden=["main.py", "test.py"]
+
+# de constante pi, nodig voor wanneer we de omtrek berekenen van het object dat de student indient.
+pi=3.14159
+
+#code...`*/
+
/**
* Dialog component for managing restrictions related to file uploads.
* @param {Object} props - Props object.
@@ -38,19 +78,24 @@ enum restrictionExtension {
* @returns {React.ReactElement} - Dialog component.
*/
export default function RestrictionsDialog({
+ userid,
restrictions,
setRestrictions,
closeParentDialog,
}: RestrictionsDialogProps) {
- const [open, setOpen] = useState(false)
- const [mustPass, setMustPass] = useState(false)
+ const [openTextEditor, setOpenTextEditor] = useState(false)
+ const [openTemplateInterface, setOpenTemplateInterface] = useState(false)
+ const [code, setCode] = useState('' /*code*/) // code is currently not used
const [textFieldContent, setTextFieldContent] = useState('')
const [restrictionName, setRestrictionName] = useState('')
const [restrictionType, setRestrictionType] =
useState(restrictionExtension.Shell)
const [nameError, setNameError] = useState(false)
const [popupOpen, setPopupOpen] = useState(false)
+ const [myTemplates, setMyTemplates] = useState([])
+ const [saveAlert, setSaveAlert] = useState(false)
+ const [saveErrorAlert, setSaveErrorAlert] = useState(false)
// function to handle the uploaded files and send them to the parent component
const handleUploadedFiles = (e: ChangeEvent) => {
const files = e.target.files
@@ -63,13 +108,13 @@ export default function RestrictionsDialog({
const newRestriction: restriction = {
script: file.name,
file: file,
- moet_slagen: mustPass,
+ moet_slagen: false,
}
newRestrictions.push(newRestriction)
}
}
setRestrictions([...restrictions, ...newRestrictions])
- handleClose()
+ closeParentDialog()
}
//handle the submission of the form
@@ -86,50 +131,102 @@ export default function RestrictionsDialog({
restrictionName + restrictionType,
{ type: 'text/plain' }
),
- moet_slagen: mustPass,
+ moet_slagen: false,
}
setRestrictions([...restrictions, newRestriction])
- handleClose()
+ handleCloseTextEditor()
+ }
+
+ const handleClickOpenTextEditor = () => {
+ setOpenTextEditor(true)
}
- const handleClickOpen = () => {
- setOpen(true)
+ const handleClickOpenTemplateInterface = () => {
+ setOpenTemplateInterface(true)
+ }
+
+ //upload the test script as a template for later use
+ const handleSaveTemplate = async (
+ filename: string,
+ fileExtension: restrictionExtension,
+ code: string
+ ) => {
+ // note how we don't write a dot between the filename and the file extension
+ // this is because the file extension already starts with a dot
+ const file = new File([code], `${filename}${fileExtension}`, {
+ type: 'text/plain',
+ })
+ try {
+ const config = {
+ headers: {
+ 'Content-Type': 'multipart/form-data',
+ },
+ }
+ const formdata = new FormData()
+
+ formdata.append('user', userid.toString())
+ formdata.append('bestand', file)
+
+ const result = await instance.post(`/templates/`, formdata, config)
+ if (result.status === 201) {
+ setSaveAlert(true) // Show the alert
+ setTimeout(() => {
+ setSaveAlert(false) // Hide the alert after 5 seconds
+ }, 4000)
+ }
+ } catch (error) {
+ setSaveErrorAlert(true)
+ setTimeout(() => {
+ setSaveErrorAlert(false)
+ }, 4000)
+ console.error('Error updating data:', error)
+ }
}
// Buttons array for the vertical button group
const buttons = [
-
- Upload
+
+ Upload script
,
{
- handleClickOpen()
+ handleClickOpenTextEditor()
}}
>
{t('new_script')}
,
- {
- handleClickOpen()
- }}
- >
- File Extension Check
- ,
- {
- handleClickOpen()
- }}
- >
- Files Present Check
- ,
]
- const handleClose = () => {
- setOpen(false)
+ React.useEffect(() => {
+ async function fetchData() {
+ try {
+ const response = await instance.get(
+ `/templates/?lesgever_id=${userid}`
+ )
+ setMyTemplates(response.data)
+ console.log('templates:')
+ console.log(response.data)
+ console.log('----------')
+ } catch (error) {
+ console.error('Error fetching data:', error)
+ }
+ }
+
+ fetchData().catch((e) => {
+ console.error(e)
+ })
+ }, [userid])
+
+ const handleCloseTextEditor = () => {
+ setOpenTextEditor(false)
+ closeParentDialog()
+ }
+
+ const handleCloseTemplateInterface = () => {
+ setOpenTemplateInterface(false)
closeParentDialog()
}
@@ -137,6 +234,7 @@ export default function RestrictionsDialog({
{/* File input for uploading files */}
{
@@ -145,27 +243,191 @@ export default function RestrictionsDialog({
multiple
/>
{/* Vertical button group */}
-
+ {t('make_new_script') + ':'}
+
+
+
{buttons}
-
-
-
- {t('must_pass') + ':'}
-
- setMustPass(!mustPass)}
- />
- {/* Dialog component */}
-
+
+
+ {t('choose_existing') + ':'}
+
+
+
+ {/* This button groups shows the templates the teacher has made */}
+
+ {myTemplates.map((template) => (
+
+
+ {
+ const response = await instance.get(
+ `/templates/${template.template_id}/template/?content=true`
+ )
+ // This textFieldContent will be used when a template is opened in textmode
+ setTextFieldContent(
+ response.data.content
+ )
+
+ // The code will be used when a template is opened in the UI
+ setCode(response.data.content)
+
+ const templatename =
+ template.bestand
+ .replace(/^.*[\\/]/, '')
+ .split('.')[0]
+ const templateextension =
+ '.' +
+ template.bestand.split('.')[1]
+ setRestrictionName(templatename)
+ setRestrictionType(
+ templateextension as restrictionExtension
+ )
+ handleSubmit()
+ }}
+ >
+
+ {template.bestand.replace(
+ /^.*[\\/]/,
+ ''
+ )}
+
+
+
+
+ {
+ const response = await instance.get(
+ `/templates/${template.template_id}/template/?content=true`
+ )
+ // This textFieldContent will be used when a template is opened in textmode
+ setTextFieldContent(
+ response.data.content
+ )
+
+ // The code will be used when a template is opened in the UI
+ setCode(response.data.content)
+
+ const templatename =
+ template.bestand
+ .replace(/^.*[\\/]/, '')
+ .split('.')[0]
+ const templateextension =
+ '.' +
+ template.bestand.split('.')[1]
+ setRestrictionName(templatename)
+ setRestrictionType(
+ templateextension as restrictionExtension
+ )
+ handleClickOpenTemplateInterface()
+ }}
+ >
+
+
+
+
+ {
+ const response = await instance.get(
+ `/templates/${template.template_id}/template/?content=true`
+ )
+ // This textFieldContent will be used when a template is opened in textmode
+ setTextFieldContent(
+ response.data.content
+ )
+
+ // The code will be used when a template is opened in the UI
+ setCode(response.data.content)
+
+ const templatename =
+ template.bestand
+ .replace(/^.*[\\/]/, '')
+ .split('.')[0]
+ const templateextension =
+ '.' +
+ template.bestand.split('.')[1]
+ setRestrictionName(templatename)
+ setRestrictionType(
+ templateextension as restrictionExtension
+ )
+ handleClickOpenTextEditor()
+ }}
+ >
+
+
+
+
+ ))}
+
+
+
+ {/* This is the template interface. */}
+
+
+
+ {/* This is the code editor. */}
+
-
+
@@ -197,6 +460,8 @@ export default function RestrictionsDialog({
gap={1}
>
- setPopupOpen(true)}
+
- save
-
+
+ handleSaveTemplate(
+ restrictionName,
+ restrictionType,
+ textFieldContent
+ )
+ } // bestand if maakt niet uit, wordt toch niet gebruikt
+ >
+ save as template
+
+
+ setPopupOpen(true)}
+ >
+ save
+
+
{/* TextField for entering test code */}
-
-
- setTextFieldContent(e.target.value)
- }
- id="filled-textarea"
- multiline
- label={'Test-Content'}
- variant="standard"
- sx={{
- overflowY: 'auto',
- maxHeight: '100%',
- }}
- />
+
+
+
+
+ setTextFieldContent(e.target.value)
+ }
+ multiline
+ label={'Test-Content'}
+ variant="standard"
+ sx={{
+ overflowY: 'auto',
+ maxHeight: '100%',
+ }}
+ />
+
+
+
+ {saveAlert && (
+
+
+ {t('template_saved_successfully')}
+
+
+ )}
+
+ {saveErrorAlert && (
+
+
+ {t('error') + ' ' + t('try_again')}
+
+
+ )}
+
{
console.log('go to scores')
- navigate(`/course/${courseId}/assignment/${assignmentId}/groups`)
+ navigate(`/course/${courseId}/assignment/${assignmentId}/groups/choose`)
}
// State variables
@@ -49,96 +76,254 @@ export function AssignmentPage() {
const [assignment, setAssignment] = useState()
const [submissions, setSubmissions] = useState([])
const [groups, setGroups] = useState([])
+ const [singleStudents, setSingleStudents] = useState([])
const [submissionFile, setSubmissionFile] = useState()
+ const [submit, setSubmit] = useState(false)
+ const [students, setStudents] = useState([])
+ const [lastSubmission, setLastSubmission] = useState()
+ const [score, setScore] = useState()
+
+ //state for loading the page
+ const [loading, setLoading] = useState(true)
+ const [userLoading, setUserLoading] = useState(true)
+ const [studentsLoading, setStudentsLoading] = useState(true)
+
+ // state for the warning popup
+ const [openNoGroup, setOpenNoGroup] = useState(false)
+
+ // Function to handle the error when the user has no group
+ function handleNoGroupError() {
+ if (assignment?.student_groep) {
+ navigate(
+ `/course/${courseId}/assignment/${assignmentId}/groups/choose`
+ )
+ } else {
+ setOpenNoGroup(false)
+ }
+ }
useEffect(() => {
+ async function fetchUser() {
+ setUserLoading(true)
+ const userResponse = await instance.get('/gebruikers/me/')
+ setUser(userResponse.data)
+ setUserLoading(false)
+ }
+
async function fetchData() {
try {
- const userResponse = await instance.get('/gebruikers/me/')
- setUser(userResponse.data)
+ setLoading(true)
const assignmentResponse = await instance.get(
`/projecten/${assignmentId}/`
)
- setAssignment(assignmentResponse.data)
- if (userResponse.data) {
+ const newAssignment: Project = assignmentResponse.data
+ if (assignmentResponse.data.opgave_bestand) {
+ newAssignment.filename =
+ assignmentResponse.data.opgave_bestand.replace(
+ /^.*[\\/]/,
+ ''
+ )
+ newAssignment.opgave_bestand = await instance
+ .get(`/projecten/${assignmentId}/opgave_bestand/`, {
+ responseType: 'blob',
+ })
+ .then((res) => {
+ let filename = 'indiening.zip'
+ if (newAssignment.filename) {
+ filename = newAssignment.filename
+ }
+ const blob = new Blob([res.data], {
+ type: res.headers['content-type'],
+ })
+ const file: File = new File([blob], filename, {
+ type: res.headers['content-type'],
+ })
+ return file
+ })
+ }
+ setAssignment(newAssignment)
+ if (user) {
if (user.is_lesgever) {
const groupsResponse = await instance.get(
`/groepen/?project=${assignmentId}`
)
setGroups(groupsResponse.data)
+ if (newAssignment.max_groep_grootte == 1) {
+ const userPromises: Promise>[] =
+ groupsResponse.data.map((group: Group) =>
+ instance.get(
+ '/gebruikers/' + group.studenten[0]
+ )
+ )
+
+ const temp_students = await axios.all(userPromises)
+
+ setSingleStudents(
+ temp_students.map((res) => res.data)
+ )
+ }
} else {
- const submissionsResponse = await instance.get(
- `/indieningen/?vak=${courseId}`
+ const groupResponse = await instance.get<[Group]>(
+ `/groepen/?student=${user.user}&project=${assignmentId}`
)
- setSubmissions(submissionsResponse.data)
+ if (groupResponse.data.length > 0) {
+ const submissionsResponse = await instance.get(
+ `/indieningen/?project=${assignmentId}&groep=${groupResponse.data[0].groep_id}`
+ )
+ setSubmissions(submissionsResponse.data)
+ }
}
}
} catch (error) {
console.error('Error fetching data:', error)
+ navigate('/error')
+ } finally {
+ setLoading(false)
}
}
- fetchData().catch((err) => console.error(err))
- }, [assignmentId, courseId, user.is_lesgever])
+ // Ensure fetchUser completes before fetchData starts
+ ;(async () => {
+ if (user.user === 0) {
+ await fetchUser()
+ }
+ await fetchData() // Use await here to ensure fetchData waits for fetchUser to complete
+ })()
+ }, [assignmentId, courseId, user.is_lesgever, submit, user])
+
+ useEffect(() => {
+ async function fetchStudents() {
+ setStudentsLoading(true)
+ const groupResponse = await instance.get(
+ `groepen/?student=${user.user}&project=${assignmentId}`
+ )
+
+ if (groupResponse.data.length > 0) {
+ const group: Group = groupResponse.data[0]
+
+ const studentPromises: Promise>[] =
+ group.studenten.map((id: number) =>
+ instance.get('/gebruikers/' + id)
+ )
+
+ const temp_students = await axios.all(studentPromises)
+ setStudents(temp_students.map((res) => res.data))
+ }
+
+ setStudentsLoading(false)
+ }
+ // Fetch students
+ if (!user.is_lesgever && user.user !== 0) {
+ fetchStudents().catch((error) =>
+ console.error('Error fetching students data:', error)
+ )
+ }
+ }, [user, assignment, groups, assignmentId])
+
+ useEffect(() => {
+ async function fetchScore() {
+ if (submissions) {
+ setLastSubmission(submissions[submissions.length - 1])
+ if (lastSubmission) {
+ const scoreResponse = await instance.get(
+ `/scores/?indiening=${lastSubmission.indiening_id}`
+ )
+ setScore(scoreResponse.data[scoreResponse.data.length - 1])
+ console.log('scoreresponse', scoreResponse.data)
+ console.log('score', score?.score)
+ }
+ }
+ }
+ fetchScore().catch((error) =>
+ console.error('Error fetching score', error)
+ )
+ }, [submissions])
// Function to download all submissions as a zip file
const downloadAllSubmissions = () => {
const zip = new JSZip()
- const downloadPromises: Promise[] = []
- submissions.forEach((submission, index) => {
- // Iterating over submissions and creating promises for each download
- downloadPromises.push(
- new Promise((resolve, reject) => {
- instance
- .get(
- `/indieningen/${submission.indiening_id}/indiening_bestanden/`,
- { responseType: 'blob' }
- )
- .then((res) => {
- let filename = 'lege_indiening_zip.zip'
- if (submission.indiening_bestanden.length > 0) {
- filename =
- submission.indiening_bestanden[0].bestand.replace(
- /^.*[\\/]/,
- ''
- )
- }
- if (filename !== 'lege_indiening_zip.zip') {
- zip.file(filename, res.data)
- }
- resolve()
- })
- .catch((err) => {
- console.error(
- `Error downloading submission ${index + 1}:`,
- err
- )
- reject(err)
- })
+ const downloadPromises = submissions.map(async (submission) => {
+ try {
+ // Get the submission details
+ const submissionResponse = await instance.get(
+ `/indieningen/${submission.indiening_id}/`
+ )
+ const newSubmission = submissionResponse.data
+
+ // Get the submission file
+ const fileResponse = await instance.get(
+ `/indieningen/${submission.indiening_id}/indiening_bestand/`,
+ { responseType: 'blob' }
+ )
+
+ let filename = 'indiening.zip'
+ if (newSubmission.bestand) {
+ filename = newSubmission.bestand.replace(/^.*[\\/]/, '')
+ }
+
+ const blob = new Blob([fileResponse.data], {
+ type: fileResponse.headers['content-type'],
})
- )
+ newSubmission.bestand = new File([blob], filename, {
+ type: fileResponse.headers['content-type'],
+ })
+ newSubmission.filename = filename
+
+ // Add the file to the zip
+ zip.file(filename, fileResponse.data)
+
+ return newSubmission
+ } catch (err) {
+ console.error(
+ `Error downloading submission for id ${submission.indiening_id}:`,
+ err
+ )
+ throw err
+ }
})
- Promise.all(downloadPromises)
- .then(() => {
- zip.generateAsync({ type: 'blob' })
- .then((blob) => {
- const url = window.URL.createObjectURL(blob)
- const a = document.createElement('a')
- a.href = url
- a.download = 'all_submissions.zip'
- document.body.appendChild(a)
- a.click()
- a.remove()
- })
- .catch((err) => {
- console.error('Error generating zip file:', err)
- })
+
+ Promise.allSettled(downloadPromises)
+ .then((results) => {
+ const errors = results.filter(
+ (result) => result.status === 'rejected'
+ )
+ if (errors.length > 0) {
+ console.error(
+ 'Some submissions failed to download:',
+ errors
+ )
+ }
+ return zip.generateAsync({ type: 'blob' })
+ })
+ .then((blob) => {
+ const url = window.URL.createObjectURL(blob)
+ const a = document.createElement('a')
+ a.href = url
+ a.download = `all_submissions_${assignment?.titel || 'assignment'}.zip`
+ document.body.appendChild(a)
+ a.click()
+ a.remove()
})
.catch((err) => {
- console.error('Error downloading submissions:', err)
+ console.error('Error generating zip file:', err)
})
}
+ // Function to download the assignment file
+ const downloadAssignment = () => {
+ if (assignment?.opgave_bestand) {
+ const url = window.URL.createObjectURL(assignment?.opgave_bestand)
+ const a = document.createElement('a')
+ a.href = url
+ a.download = assignment.filename
+ ? assignment.filename
+ : 'opgave.zip'
+ document.body.appendChild(a)
+ a.click()
+ a.remove()
+ }
+ }
+
// Function to handle file change event
const handleFileChange = (event: ChangeEvent) => {
if (event.target.files) {
@@ -148,6 +333,7 @@ export function AssignmentPage() {
}
// Function to upload submission file
+ // check for failures and open popup if no group
const uploadIndiening = async () => {
if (submissionFile) {
const config = {
@@ -156,361 +342,996 @@ export function AssignmentPage() {
},
}
const groupResponse = await instance.get(
- `/groepen/?project=${assignmentId}`
+ `/groepen/?student=${user.user}&project=${assignmentId}`
)
if (groupResponse.data) {
const group = groupResponse.data[0]
- const formData = new FormData()
- formData.append('groep', group.groep_id)
- formData.append('indiening_bestanden', submissionFile)
-
- await instance
- .post('/indieningen/', formData, config)
- .catch((error) => {
- console.error(error)
- })
- setSubmissionFile(undefined)
+ if (group) {
+ const formData = new FormData()
+ formData.append('groep', group.groep_id)
+ formData.append('bestand', submissionFile)
+
+ await instance
+ .post('/indieningen/', formData, config)
+ .catch((error) => {
+ console.error(error)
+ })
+ setSubmissionFile(undefined)
+ } else {
+ setOpenNoGroup(true)
+ console.error(
+ 'Group not found for assingmentId: ',
+ assignmentId
+ )
+ }
}
+ setSubmit(!submit)
}
}
return (
<>
{/* Rendering different UI based on user role */}
- {user.is_lesgever ? (
- // Rendering UI for teacher
- <>
-
+
-
- {/*deadline and groep button */}
-
-
- Deadline
- {assignment
- ? dayjs(assignment.deadline).format(
- 'DD/MM/YYYY-HH:MM'
- )
- : 'no deadline'}
-
-
+ ) : (
+ <>
+ {user.is_lesgever ? (
+ // Rendering UI for teacher
+ <>
+
-
-
- {/*Opgave*/}
-
-
-
+ {/*deadline and group button */}
+
- {t('assignment')}
-
-
- {assignment ? assignment.beschrijving : ''}
-
-
-
-
- {/*Indieningen*/}
-
-
-
- {t('group')}
-
-
- {t('time')}
-
-
- Score
-
-
- Status
-
-
- {t('download')}
-
-
-
-
-
- {groups.map((group) => (
-
-
-
+
+ Deadline:
+
+ {loading ? (
+
+ ) : (
+
-
+ )}
+
+ {loading ? (
+
+ ) : (
+ <>
+ {(assignment?.max_groep_grootte
+ ? assignment.max_groep_grootte
+ : 1) > 1 && (
+
-
-
- ))}
-
-
-
-
- {/* */}
-
- {/*
-
- */}
-
- {/*Export- en Aanpasknop*/}
-
-
- {submissions.length > 0 && (
-
+ )}
+
+ {/*extra deadline*/}
+ {assignment?.extra_deadline && (
+
-
- {t('export')} {t('submissions')}
+
+ Extra Deadline:
-
+ {loading ? (
+
+ ) : (
+
+ {assignment
+ ? dayjs(
+ assignment.extra_deadline
+ ).format(
+ 'DD/MM/YYYY HH:mm'
+ )
+ : 'no deadline'}
+
+ )}
+
)}
-
-
-
- {t('adjust_scores')}
-
-
-
-
-
- >
- ) : (
- <>
-
-
- {/*deadline and groep button */}
-
-
-
- Deadline
- {assignment
- ? assignment.deadline?.toString()
- : 'no deadline'}
-
-
-
+
+
+ {t('assignment')}
+
+ {loading ? (
+
+ ) : (
+
+ {assignment
+ ? assignment.beschrijving
+ : ''}
+
+ )}
+
+
+
+ {/* This renders a list of submissions.
+ It shows metadata about the submissions and allows the teacher to download them.
+ The metadata includes group number, submission time, score, and status.
+ */}
+
+
+
+ {t('group')}
+ ,
+
+ {t('time')}
+ ,
+
+ Score
+ ,
+
+ Status
+ ,
+
+ {t('download')}
+ ,
+ ]}
+ />
+
+
+
+
+
+ {loading ? (
+ [...Array(3)].map(
+ (_, index) => (
+
+ )
+ )
+ ) : (
+ <>
+ {groups.map(
+ (group, index) => (
+ <>
+ {index !=
+ 0 ? (
+
+ ) : (
+ <>>
+ )}
+
+ 1
+ ? t(
+ 'group'
+ ) +
+ ' ' +
+ (
+ index +
+ 1
+ ).toString()
+ : singleStudents[
+ index
+ ]
+ ? singleStudents[
+ index
+ ]
+ .first_name +
+ ' ' +
+ singleStudents[
+ index
+ ]
+ .last_name
+ : ''
+ : ''
+ }
+ group_id={group.groep_id.toString()}
+ assignment_id={
+ assignmentId
+ ? assignmentId
+ : ''
+ }
+ course_id={
+ courseId
+ ? courseId
+ : ''
+ }
+ />
+ >
+ )
+ )}
+ >
+ )}
+
+
+
+
+
+ {/*Export- and edit-button*/}
+
-
- {t('group')}
-
-
+
+ {submissions.length > 0 && (
+
+
+ {t('export')}{' '}
+ {t('submissions')}
+
+
+ )}
+
+ {loading ? (
+
+ ) : (
+
+ {t('adjust_scores')}
+
+ )}
+
+
-
-
- {/*Opgave*/}
-
-
-
+ ) : (
+ // Rendering UI for student
+ <>
+
+
+ {/*deadline and groep button */}
+
- {t('assignment')}
-
-
- {assignment ? assignment.beschrijving : ''}
-
-
-
-
- {/*Indieningen*/}
-
-
-
- {t('submission')}
-
-
- {t('time')}
-
-
- Status
-
-
-
-
-
- {submissions.map((submission) => (
-
-
-
+
+
-
+ {loading ? (
+
-
+ ) : (
+
+ {assignment &&
+ assignment.deadline
+ ? dayjs(
+ assignment.deadline
+ ).format(
+ 'DD/MM/YYYY HH:mm'
+ )
+ : t('no_deadline')}
+
+ )}
- ))}
-
-
-
-
- {/*Upload knop*/}
-
-
- {
-
- }
-
+ {loading ? (
+
+ ) : (
+ <>
+ {assignment?.student_groep ? (
+
+
+ {t('group')}
+
+
+ ) : (
+
+ {assignment?.max_groep_grootte ===
+ 1 ? (
+
+ {user.first_name +
+ ' ' +
+ user.last_name}
+
+ ) : (
+ <>
+
+ >
+ )}
+
+ )}
+ >
+ )}
+
+
+ {/*extra deadline*/}
+ {assignment?.extra_deadline && (
+
+
+ Extra Deadline:
+
+ {loading ? (
+
+ ) : (
+
+ {assignment
+ ? dayjs(
+ assignment.extra_deadline
+ ).format(
+ 'DD/MM/YYYY HH:mm'
+ )
+ : 'no deadline'}
+
+ )}
+
+ )}
+ {/*download opgave*/}
+
+
+ {t('assignment') +
+ ' ' +
+ t('file').toLowerCase() +
+ ':'}
+
+ }
+ onClick={downloadAssignment}
+ disabled={
+ assignment === undefined ||
+ assignment.filename === undefined
+ }
+ >
+ {loading ? (
+
+ ) : (
+ <>
+ {assignment
+ ? assignment.filename
+ ? assignment.filename
+ : t('no_assignmentfile')
+ : t('no_assignmentfile')}
+ >
+ )}
+
+
+
-
- Confirm Upload
+
+ Score:
-
-
+ {submissions.length > 0 ? (
+
+ {score
+ ? `${score.score}/${assignment?.max_score} (${Math.round((100 * score.score) / Number(assignment?.max_score))}%)`
+ : t('no_score_yet')}
+
+ ) : (
+
+ {t('no_score_yet')}
+
+ )}
+
+
+ {/* Assignment */}
+
+
+
+ {t('assignment') + ':'}
+
+
+ {loading ? (
+
+ ) : (
+ <>
+ {assignment
+ ? assignment.beschrijving
+ : ''}
+ >
+ )}
+
+
+
+
+ {/* Submissions */}
+
+
+
+ {t('submission')}
+ ,
+
+ {t('time')}
+ ,
+
+ Status
+ ,
+ ]}
+ />
+
+
+
+
+ {loading ? (
+ [...Array(3)].map(
+ (_, index) => (
+
+ )
+ )
+ ) : (
+ <>
+ {submissions.length > 0 ? (
+ submissions
+ .sort((a, b) =>
+ a.tijdstip <
+ b.tijdstip
+ ? 1
+ : -1
+ )
+ .map(
+ (
+ submission,
+ index
+ ) => (
+
+ {index !=
+ 0 ? (
+
+ ) : (
+ <>
+
+ >
+ )}
+
+ 0
+ }
+ assignment_id={
+ assignmentId
+ ? assignmentId
+ : ''
+ }
+ course_id={
+ courseId
+ ? courseId
+ : ''
+ }
+ />
+
+ )
+ )
+ ) : (
+
+
+ {t(
+ 'no_submissions'
+ )}
+
+
+ )}
+ >
+ )}
+
+
+
+
+ {/*Upload button, this is what the student will see. */}
+
+ {loading ||
+ !assignment?.deadline ||
+ (!dayjs().isAfter(
+ dayjs(assignment.deadline)
+ ) &&
+ !assignment.extra_deadline) ||
+ (assignment.extra_deadline &&
+ !dayjs().isAfter(
+ dayjs(assignment.extra_deadline)
+ )) ? (
+ <>
+
+
+
+
+
+
+
+
+ {t('submit')}
+
+
+
+
+
+ >
+ ) : null}
+
-
-
+ {assignment?.student_groep ? (
+ setOpenNoGroup(false)}
+ doAction={handleNoGroupError}
+ />
+ ) : (
+ setOpenNoGroup(false)}
+ >
+ {t('error')}
+
+
+ {t('noGroup') +
+ ' ' +
+ t('contactTeacher')}
+
+
+
+
+ setOpenNoGroup(false)
+ }
+ color="primary"
+ autoFocus
+ >
+ {t('ok')}
+
+
+
+ )}
+ >
+ )}
>
)}
>
diff --git a/frontend/frontend/src/pages/groupsPage/ChooseGroup.tsx b/frontend/frontend/src/pages/groupsPage/ChooseGroup.tsx
new file mode 100644
index 000000000..f59696512
--- /dev/null
+++ b/frontend/frontend/src/pages/groupsPage/ChooseGroup.tsx
@@ -0,0 +1,718 @@
+import {
+ Box,
+ CircularProgress,
+ Dialog,
+ ListItem,
+ ListItemText,
+ Skeleton,
+ Stack,
+ Typography,
+} from '@mui/material'
+import List from '@mui/material/List'
+import { useEffect, useState } from 'react'
+import { Header } from '../../components/Header.tsx'
+import { t } from 'i18next'
+import {
+ Button,
+ Card,
+ Divider,
+ EvenlySpacedRow,
+} from '../../components/CustomComponents.tsx'
+import { useNavigate, useParams } from 'react-router-dom'
+import instance from '../../axiosConfig.ts'
+import { User } from '../subjectsPage/AddChangeSubjectPage.tsx'
+
+export interface Group {
+ groep_id: number
+ studenten: number[]
+ project: number
+}
+
+export interface Assignment {
+ project_id: number
+ titel: string
+ beschrijving: string
+ opgave_bestand: string
+ vak: number
+ max_score: number
+ max_groep_grootte: number
+ deadline: string | null
+ extra_deadline: string | null
+ zichtbaar: boolean
+ gearchiveerd: boolean
+ file?: File
+}
+
+function joinLeaveButton(
+ isin: boolean,
+ disabled: boolean,
+ handleJoin: () => void,
+ handleLeave: () => void
+) {
+ if (isin) {
+ return (
+ <>
+
+ {t('leave')}
+
+ >
+ )
+ }
+ if (disabled) {
+ return (
+ <>
+ {t('group_full')}
+ >
+ )
+ }
+ return (
+ <>
+
+ {t('join_group')}
+
+ >
+ )
+}
+
+export function ChooseGroup() {
+ const params = useParams()
+ const navigate = useNavigate()
+
+ const [studenten, setStudenten] = useState>({})
+ const [groups, setGroups] = useState([])
+ const [open, setOpen] = useState(false)
+ const [user, setUser] = useState()
+ const [assignment, setAssignment] = useState()
+
+ const [loading, setLoading] = useState(true)
+ const [userLoading, setUserLoading] = useState(true)
+
+ const assignmentId = params.assignmentId
+ const courseid = params.courseId
+
+ const handleClose = () => {
+ setOpen(false)
+ }
+
+ useEffect(() => {
+ async function fetchData() {
+ setLoading(true)
+ setUserLoading(true)
+ try {
+ const user = await instance.get('/gebruikers/me/')
+ setUser(user.data)
+ setUserLoading(false)
+
+ const assignment = await instance.get(
+ '/projecten/' + assignmentId + '/'
+ )
+ setAssignment(assignment.data)
+
+ const course = await instance.get('vakken/' + courseid)
+ for (let i = 0; i < course.data.studenten.length; i++) {
+ const studentid = course.data.studenten[i]
+ const student = await instance.get(
+ 'gebruikers/' + studentid
+ )
+ setStudenten((oldstudenten) => {
+ return {
+ ...oldstudenten,
+ [student.data.user]: student.data,
+ }
+ })
+ }
+ const groups = await instance.get(
+ 'groepen/?project=' + assignmentId
+ )
+ const sortedGroups = groups.data.sort((a: Group, b: Group) => {
+ return a.groep_id - b.groep_id
+ })
+
+ setGroups(sortedGroups)
+ } catch (err) {
+ console.error(err)
+ } finally {
+ setLoading(false)
+ }
+ }
+ fetchData().catch((err) => console.error(err))
+ }, [assignmentId, courseid])
+
+ return (
+ <>
+ {/* Rendering different UI based on user role */}
+ {userLoading ? (
+
+
+
+
+ ) : (
+ <>
+ {!user?.is_lesgever ? (
+ // Rendering UI for teacher
+ <>
+
+
+
+
+
+
+ {t('group_number')}
+ ,
+
+ {t('members')}
+ ,
+ ,
+ ]}
+ />
+
+ :not(style)': {
+ marginBottom: '8px',
+ },
+ }}
+ >
+ {loading ? (
+ [...Array(3)].map(() => (
+
+ ))
+ ) : (
+ <>
+ {groups.map(
+ (group: Group) => {
+ //const group=getGroup(id)
+
+ const handleJoin =
+ () => {
+ setGroups(
+ (
+ oldGroups
+ ): Group[] => {
+ if (
+ user ==
+ undefined
+ ) {
+ return oldGroups
+ }
+ if (
+ assignment ==
+ undefined
+ ) {
+ return oldGroups
+ }
+
+ let j = 0
+
+ let edittedgroup =
+ undefined
+
+ for (
+ let i = 0;
+ i <
+ oldGroups.length;
+ i++
+ ) {
+ if (
+ oldGroups[
+ i
+ ].studenten.includes(
+ user.user
+ )
+ ) {
+ const newgroup1 =
+ {
+ groep_id:
+ oldGroups[
+ i
+ ]
+ .groep_id,
+ project:
+ oldGroups[
+ i
+ ]
+ .project,
+ studenten:
+ oldGroups[
+ i
+ ].studenten.filter(
+ (
+ student
+ ) =>
+ student !=
+ user.user
+ ),
+ }
+
+ instance
+ .patch(
+ 'groepen/' +
+ oldGroups[
+ i
+ ]
+ .groep_id +
+ '/',
+ {
+ studenten:
+ oldGroups[
+ i
+ ].studenten.filter(
+ (
+ student
+ ) =>
+ student !=
+ user.user
+ ),
+ }
+ )
+ .catch(
+ (
+ err
+ ) => {
+ console.log(
+ err
+ )
+ }
+ )
+ j =
+ i
+ edittedgroup =
+ newgroup1
+ }
+ }
+
+ for (
+ let i = 0;
+ i <
+ oldGroups.length;
+ i++
+ ) {
+ if (
+ oldGroups[
+ i
+ ]
+ .groep_id ==
+ group.groep_id
+ ) {
+ if (
+ group
+ .studenten
+ .length >=
+ assignment.max_groep_grootte
+ ) {
+ return oldGroups
+ }
+ const newgroup =
+ {
+ groep_id:
+ group.groep_id,
+ project:
+ group.project,
+ studenten:
+ [
+ ...group.studenten,
+ user.user,
+ ],
+ }
+ instance
+ .patch(
+ 'groepen/' +
+ group.groep_id +
+ '/',
+ {
+ studenten:
+ [
+ ...group.studenten,
+ user.user,
+ ],
+ }
+ )
+ .catch(
+ (
+ err
+ ) => {
+ console.log(
+ err
+ )
+ }
+ )
+
+ if (
+ edittedgroup !=
+ undefined
+ ) {
+ if (
+ i <
+ j
+ ) {
+ return [
+ ...oldGroups.slice(
+ 0,
+ i
+ ),
+ newgroup,
+ ...oldGroups.slice(
+ i +
+ 1,
+ j
+ ),
+ edittedgroup,
+ ...oldGroups.slice(
+ j +
+ 1
+ ),
+ ].sort(
+ (
+ a,
+ b
+ ) => {
+ return (
+ a.groep_id -
+ b.groep_id
+ )
+ }
+ )
+ }
+ return [
+ ...oldGroups.slice(
+ 0,
+ j
+ ),
+ edittedgroup,
+ ...oldGroups.slice(
+ j +
+ 1,
+ i
+ ),
+ newgroup,
+ ...oldGroups.slice(
+ i +
+ 1
+ ),
+ ].sort(
+ (
+ a,
+ b
+ ) => {
+ return (
+ a.groep_id -
+ b.groep_id
+ )
+ }
+ )
+ }
+
+ return [
+ ...oldGroups.slice(
+ 0,
+ i
+ ),
+ newgroup,
+ ...oldGroups.slice(
+ i +
+ 1
+ ),
+ ].sort(
+ (
+ a,
+ b
+ ) => {
+ return (
+ a.groep_id -
+ b.groep_id
+ )
+ }
+ )
+ }
+ }
+ return oldGroups
+ }
+ )
+ }
+
+ const handleLeave =
+ () => {
+ setGroups(
+ (
+ oldGroups
+ ) => {
+ if (
+ user ==
+ undefined
+ ) {
+ return oldGroups
+ }
+ for (
+ let i = 0;
+ i <
+ oldGroups.length;
+ i++
+ ) {
+ if (
+ oldGroups[
+ i
+ ]
+ .groep_id ==
+ group.groep_id
+ ) {
+ const newgroup =
+ {
+ groep_id:
+ group.groep_id,
+ project:
+ group.project,
+ studenten:
+ group.studenten.filter(
+ (
+ student
+ ) =>
+ student !=
+ user.user
+ ),
+ }
+ instance
+ .patch(
+ 'groepen/' +
+ group.groep_id +
+ '/',
+ {
+ studenten:
+ group.studenten.filter(
+ (
+ student
+ ) =>
+ student !=
+ user.user
+ ),
+ }
+ )
+ .catch(
+ (
+ err
+ ) => {
+ console.log(
+ err
+ )
+ }
+ )
+
+ return [
+ ...oldGroups.slice(
+ 0,
+ i
+ ),
+ newgroup,
+ ...oldGroups.slice(
+ i +
+ 1
+ ),
+ ].sort(
+ (
+ a,
+ b
+ ) => {
+ return (
+ a.groep_id -
+ b.groep_id
+ )
+ }
+ )
+ }
+ }
+ return oldGroups
+ }
+ )
+ }
+
+ return (
+ <>
+
+
+ ,
+
+ {loading ? (
+
+ {t(
+ 'members_loading'
+ )}
+
+ ) : (
+ <>
+ {group
+ .studenten
+ .length >
+ 0 ? (
+ group.studenten.map(
+ (
+ studentid
+ ) => {
+ const student =
+ studenten[
+ studentid
+ ]
+ if (
+ student
+ ) {
+ console.log(
+ 'Student:',
+ student
+ )
+ return (
+
+ {student.first_name +
+ ' ' +
+ student.last_name}
+
+ )
+ }
+ return null
+ }
+ )
+ ) : (
+
+ {t(
+ 'no_members_yet'
+ )}
+
+ )}
+ >
+ )}
+ ,
+ <>
+ {joinLeaveButton(
+ user !=
+ undefined
+ ? group.studenten.includes(
+ user.user
+ )
+ : false,
+ group
+ .studenten
+ .length ===
+ assignment?.max_groep_grootte &&
+ !(user !=
+ undefined
+ ? group.studenten.includes(
+ user.user
+ )
+ : false),
+ handleJoin,
+ handleLeave
+ )}
+ >,
+ ]}
+ />
+
+ >
+ )
+ }
+ )}
+ >
+ )}
+
+
+
+
+
+ {' '}
+ {'placeholder'}{' '}
+
+
+
+
+
+
+ >
+ ) : (
+ navigate('*')
+ )}
+ >
+ )}
+ >
+ )
+}
diff --git a/frontend/frontend/src/pages/groupsPage/GroupsPage.tsx b/frontend/frontend/src/pages/groupsPage/GroupsPage.tsx
index bce9457bb..e59fe533c 100644
--- a/frontend/frontend/src/pages/groupsPage/GroupsPage.tsx
+++ b/frontend/frontend/src/pages/groupsPage/GroupsPage.tsx
@@ -1,17 +1,20 @@
import { Header } from '../../components/Header.tsx'
+import { Button, Card } from '../../components/CustomComponents.tsx'
import {
+ Autocomplete,
Box,
- Button,
+ CircularProgress,
Grid,
IconButton,
MenuItem,
Select,
SelectChangeEvent,
+ Skeleton,
Stack,
Table,
TableBody,
TableCell,
- TableHead,
+ TableContainer,
TableRow,
TextField,
Tooltip,
@@ -24,19 +27,22 @@ import { useNavigate, useParams } from 'react-router-dom'
import instance from '../../axiosConfig.ts'
import CancelIcon from '@mui/icons-material/Cancel'
import DialogActions from '@mui/material/DialogActions'
-import DialogContent from '@mui/material/DialogContent'
import { Add } from '@mui/icons-material'
import ClearIcon from '@mui/icons-material/Clear'
import SaveIcon from '@mui/icons-material/Save'
import WarningPopup from '../../components/WarningPopup.tsx'
+import axios, { AxiosResponse } from 'axios'
+import { User } from '../subjectsPage/AddChangeSubjectPage.tsx'
-// goup interface
+// group interface
export interface Group {
groep_id?: number
studenten: number[]
project: number
}
+//FIXME: groupsize should not be signed after creation of project, refactor!
+
// Typescript typing for hashmap
type Hashmap = Map
@@ -54,38 +60,57 @@ export function GroupsPage() {
)
//state for new groups and new groupSize, don't change the old groups and groupSize until the user clicks save
const [newGroups, setNewGroups] = useState([])
- const [newGroupSize, setNewGroupSize] = useState(1)
const [currentGroup, setCurrentGroup] = useState('')
const [availableStudents, setAvailableStudents] = useState([])
+ const [projectName, setProjectName] = useState('')
+ const [user, setUser] = useState()
+ const [max_group_size, setMaxGroupSize] = useState(0)
+ const [studentsCanChoose, setStudentsCanChoose] = useState(false)
// confirmation dialog state
const [confirmOpen, setConfirmOpen] = useState(false)
+ //random groups dialog state
+ const [randomOpen, setRandomOpen] = useState(false)
+
+ // state for correct loading of the page
+ const [loading, setLoading] = useState(true)
+ const [userLoading, setUserLoading] = useState(true)
+
// handle confirmation dialog
- const confirmSave = () => {
+ const confirmSave = async () => {
+ await instance.patch('/projecten/' + assignmentId + '/', {
+ student_groep: studentsCanChoose,
+ })
+
if (newGroups[0].groep_id === undefined) {
// delete the old groups and replace them with the new groups
- instance
+ await instance
.get('/groepen/?project=' + assignmentId)
- .then((response) => {
+ .then(async (response) => {
for (const group of response.data) {
- instance
+ await instance
.delete('/groepen/' + group.groep_id + '/')
.catch((error) => {
console.log(error)
})
}
})
+ .catch((error) => {
+ console.log(error)
+ })
for (const group of newGroups) {
- instance
- .post('/groepen', {
- studenten: group.studenten,
- vak: courseId,
- })
- .then((response) => {
- console.log(response)
- })
+ if (group.studenten.length !== 0) {
+ instance
+ .post('/groepen', {
+ studenten: group.studenten,
+ project: parseInt(assignmentId),
+ })
+ .catch((error) => {
+ console.log(error)
+ })
+ }
}
} else {
// update the old groups with the new groups
@@ -94,10 +119,10 @@ export function GroupsPage() {
.put('/groepen/' + group.groep_id + '/', {
groep_id: group.groep_id,
studenten: group.studenten,
- vak: courseId,
+ project: parseInt(assignmentId),
})
- .then((response) => {
- console.log(response)
+ .catch((error) => {
+ console.log(error)
})
}
}
@@ -120,110 +145,89 @@ export function GroupsPage() {
navigate('/course/' + courseId + '/assignment/' + assignmentId)
}
- // Change max group size
- const handleGroupSizeChange = (newValue: number) => {
- setNewGroupSize(newValue)
- setAvailableStudents(() => Array.from(studentNames.keys()))
- setCurrentGroup('0')
- setNewGroups(() => {
- const newGroups = []
- console.log(
- 'new amount of groups' +
- Math.ceil(availableStudents.length / newValue)
- )
- for (
- let i = 0;
- i < Math.ceil(availableStudents.length / newValue);
- i++
- ) {
- console.log('new group' + i)
- newGroups.push({
- studenten: [],
- project: parseInt(assignmentId),
- })
- }
- return newGroups
- })
-
- instance.get('/vakken/' + courseId).then((response) => {
- setAvailableStudents(response.data.studenten)
- })
- }
-
//get the current groups and group size from the backend
useEffect(() => {
- instance.get('/vakken/' + courseId).then(async (response) => {
- const newStudentNames = new Map()
+ async function fetchData() {
+ setUserLoading(true)
+ setLoading(true)
+ const userResponse = await instance.get('/gebruikers/me/')
+ setUser(userResponse.data)
+ setUserLoading(false)
+ // Get the student names
+ await instance
+ .get('/vakken/' + courseId)
+ .then(async (response) => {
+ // This function fetches the names of the students in parallel
+ const newStudentNames = new Map()
- for (const student of response.data.studenten) {
- await instance
- .get('/gebruikers/' + student)
- .then((response) => {
- newStudentNames.set(
- student,
- response.data.first_name +
- ' ' +
- response.data.last_name
+ const studentPromises: Promise>[] =
+ response.data.studenten.map((id: number) =>
+ instance.get('/gebruikers/' + id)
)
- console.log(
- 'available names:' +
- Array.from(newStudentNames.entries())
+ const studentResponses = await axios.all(studentPromises)
+
+ studentResponses.forEach((response) => {
+ const student: User = response.data
+ newStudentNames.set(
+ student.user,
+ student.first_name + ' ' + student.last_name
)
})
- }
- setStudentNames(() => newStudentNames)
- })
+ setStudentNames(() => newStudentNames)
+ })
+ .catch((error) => {
+ console.log('error fetching students')
+ console.error(error)
+ })
- instance.get('/projecten/' + assignmentId).then((response) => {
- setNewGroupSize(response.data.max_groep_grootte)
- })
+ // Get the max group size, project name and students can choose
+ await instance
+ .get('/projecten/' + assignmentId)
+ .then((response) => {
+ setProjectName(response.data.titel)
+ setStudentsCanChoose(response.data.student_groep)
- instance
- .get(`/groepen/?project=${assignmentId}`)
- .then((response) => {
- setNewGroups(response.data)
- })
+ setMaxGroupSize(response.data.max_groep_grootte)
+ })
+ .catch((error) => {
+ console.log(error)
+ })
+ .catch((error) => {
+ console.log('error fetching project')
+ console.error(error)
+ })
+
+ // Get the existing groups
+ await instance
+ .get(`/groepen/?project=${assignmentId}`)
+ .then((response) => {
+ const newgroups: Group[] = response.data
+ setNewGroups(newgroups)
+ })
+ .catch((error) => {
+ console.log('error fetching groups')
+ console.error(error)
+ })
+
+ setLoading(false)
+ }
+
+ fetchData().catch((error) => {
+ console.error(error)
+ })
+ setCurrentGroup('0')
}, [assignmentId, courseId])
useEffect(() => {
- setAvailableStudents(() =>
- Array.from(studentNames.keys()).filter(
- (student) =>
- !newGroups.some((group) =>
- group.studenten.includes(student)
- )
- )
+ const filteredStudents = Array.from(studentNames.keys()).filter(
+ (student) =>
+ !newGroups.some((group) => group.studenten.includes(student))
)
- }, [newGroups, studentNames])
- // Create new groups when the group size changes
- useEffect(() => {
- if (newGroups.length === 0) {
- setAvailableStudents(() => Array.from(studentNames.keys()))
- setCurrentGroup('0')
- setNewGroups(() => {
- const newGroups = []
- for (
- let i = 0;
- i < Math.ceil(availableStudents.length / newGroupSize);
- i++
- ) {
- newGroups.push({
- studenten: [],
- project: parseInt(assignmentId),
- })
- }
- return newGroups
- })
- }
- }, [
- assignmentId,
- availableStudents.length,
- newGroupSize,
- newGroups.length,
- studentNames,
- ])
+ setAvailableStudents(filteredStudents)
+ setFilteredStudents(filteredStudents)
+ }, [newGroups, studentNames])
//Handle current group change
const handleCurrentGroupChange = (event: SelectChangeEvent) => {
@@ -233,22 +237,18 @@ export function GroupsPage() {
// Randomise groups
const randomGroups = () => {
const students = Array.from(studentNames.keys())
- const newGroups: Group[] = []
- for (let i = 0; i < Math.ceil(students.length / newGroupSize); i++) {
- newGroups.push({
- studenten: [],
- project: parseInt(assignmentId),
+ const shuffledStudents = students.sort(() => Math.random() - 0.5)
+ const randomisedGroups: Group[] = []
+ for (const group of newGroups) {
+ const sliceSize = Math.min(max_group_size, shuffledStudents.length)
+ randomisedGroups.push({
+ ...group,
+ studenten: shuffledStudents.slice(0, sliceSize),
})
+ shuffledStudents.splice(0, sliceSize)
+ if (shuffledStudents.length === 0) break
}
- for (let i = 0; i < students.length; i++) {
- let randomGroup = Math.floor(Math.random() * newGroups.length)
- while (newGroups[randomGroup].studenten.length >= newGroupSize) {
- randomGroup = Math.floor(Math.random() * newGroups.length)
- }
- newGroups[randomGroup].studenten.push(students[i])
- }
-
- setNewGroups(newGroups)
+ setNewGroups(randomisedGroups)
}
// assign a student to a group
@@ -258,12 +258,11 @@ export function GroupsPage() {
(student) => student !== studentId
)
setAvailableStudents(updatedAvailableStudents)
- console.log('group id: ' + groupId)
+ setFilteredStudents(availableStudents)
// Then, create a new copy of the newGroups array with the updated group
const updatedNewGroups = newGroups.map((group, index) => {
if (index === groupId) {
// Create a new copy of the group with the updated studenten array
- console.log('group.studenten: ' + group.studenten)
return {
...group,
studenten: [...group.studenten, studentId],
@@ -300,318 +299,754 @@ export function GroupsPage() {
setNewGroups(updatedNewGroups)
}
+ const [filteredStudents, setFilteredStudents] = useState(availableStudents)
+
+ // for filtering students
+ const handleAutocompleteChange = (_: unknown, value: number | null) => {
+ if (value) {
+ setFilteredStudents([value])
+ }
+ }
+
+ const resetAutocompleteChange = () => {
+ setFilteredStudents(availableStudents)
+ }
+
+ const filterOptions = (
+ _: unknown,
+ { inputValue }: { inputValue: string }
+ ) => {
+ return availableStudents.filter((option) => {
+ const label = studentNames.get(option)
+ return label?.toLowerCase().startsWith(inputValue.toLowerCase())
+ })
+ }
return (
<>
-
-
-
-
-
-
- {t('groups')}
-
-
-
-
-
- {t('amount')} {t('members')}/
- {t('group')}
-
-
-
- {
- if (
- parseInt(
- newValue.target.value
- ) < 1
- )
- return
- handleGroupSizeChange(
- parseInt(
- newValue.target.value
- )
- )
- }}
- variant="outlined"
- sx={{ width: 80 }}
- />
-
-
-
-
+
+
+ ) : (
+ <>
+ {user?.is_lesgever ? (
+ // Rendering UI for teacher
+ <>
+
-
+
-
- {t('random')} {t('groups')}
-
-
-
-
- {t('students_choose')}
-
-
-
-
-
-
-
-
-
-
-
-
-
- {t('studenten')}
-
-
-
-
-
- {availableStudents.map((student) => (
-
-
- {studentNames.get(
- student
- ) === ''
- ? student
- : studentNames.get(
- student
- )}
- =
- newGroupSize
- : true
- }
- onClick={() => {
- assignStudent(
- student,
- parseInt(
- currentGroup
- )
- )
+
+
+
+ {t('groups')}
+
+
+
+
+
+ {t('amount')}{' '}
+ {t('members')}/
+ {t('group') + ':'}
+
+
+ {max_group_size}
+
+
+
+
+
+ {loading ? (
+
+ ) : (
+
+ setRandomOpen(true)
+ }
>
-
-
-
-
- ))}
-
-
-
-
-
-
-
- {t('group')}
-
-
+ )}
+
- {newGroups.map(
- (_, index) => (
-
- {t('group') +
- (index + 1)}
-
- )
- )}
-
-
-
-
-
- {newGroups[parseInt(currentGroup)] &&
- newGroups[
- parseInt(currentGroup)
- ].studenten.map((student) => (
-
-
- {studentNames.get(
- student
- )}
- {
- removeStudent(
- student,
- parseInt(
- currentGroup
- )
+
+ {t('students_choose')}
+
+ {loading ? (
+
+ ) : (
+
+ setStudentsCanChoose(
+ !studentsCanChoose
)
+ }
+ />
+ )}
+
+
+
+
+ *:not(:last-child)': {
+ marginRight: '40px', // Add right margin of 20 pixels
+ },
+ }}
+ >
+
+
+
+
+
+
+ {t(
+ 'group'
+ )}
+
+
+
+ {loading ? (
+
+ ) : (
+
+ {newGroups.map(
+ (
+ _,
+ index
+ ) => (
+
+ {t(
+ 'group'
+ ) +
+ (index +
+ 1)}
+
+ )
+ )}
+
+ )}
+
+
+
+
+
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+ {loading ? (
+ [
+ ...Array(
+ 3
+ ),
+ ].map(
+ (
+ _,
+ index
+ ) => (
+
+ )
+ )
+ ) : (
+ <>
+ {newGroups[
+ parseInt(
+ currentGroup
+ )
+ ] &&
+ newGroups[
+ parseInt(
+ currentGroup
+ )
+ ].studenten.map(
+ (
+ student
+ ) => (
+
+
+ {studentNames.get(
+ student
+ )}
+ {
+ removeStudent(
+ student,
+ parseInt(
+ currentGroup
+ )
+ )
+ }}
+ >
+
+
+
+
+ )
+ )}
+ >
+ )}
+
+
+
+
+
+
+
+
+
+
+ {t(
+ 'students'
+ )}
+
+
+
+
+
+ {loading ? (
+
+ ) : (
+ {
+ const name =
+ studentNames.get(
+ student
+ )
+ return name !=
+ null
+ ? name
+ : '' // This checks for both null and undefined
+ }}
+ onChange={
+ handleAutocompleteChange
+ }
+ filterOptions={
+ filterOptions
+ }
+ renderInput={(
+ student
+ ) => (
+
+ )}
+ />
+ )}
+
+
+
+
+
+
+
+
+ {loading ? (
+ [
+ ...Array(
+ 3
+ ),
+ ].map(
+ (
+ _,
+ index
+ ) => (
+
+ )
+ )
+ ) : (
+ <>
+
+ {chunkArray(
+ filteredStudents,
+ Math.ceil(
+ Math.sqrt(
+ filteredStudents.length
+ )
+ )
+ ).map(
+ (
+ students
+ ) => (
+
+ {students.map(
+ (
+ student
+ ) => (
+
+
+ {studentNames.get(
+ student
+ ) ===
+ ''
+ ? student
+ : studentNames.get(
+ student
+ )}
+ =
+ max_group_size
+ : true
+ }
+ onClick={() => {
+ assignStudent(
+ student,
+ parseInt(
+ currentGroup
+ )
+ )
+ resetAutocompleteChange()
+ }}
+ >
+
+
+
+
+ )
+ )}
+
+ )
+ )}
+
+ >
+ )}
+
+
+
+
+
+
+
+
+
-
-
-
-
-
-
-
- {/* Warning popup for when the user wants to confirm the group changes */}
-
+
+
+
+
+
+
+
+
+ {loading ? (
+
+ ) : (
+ <>
+
+
+
+
+
+ >
+ )}
+
+
+
+
+
+ {/* Warning popup for when the user wants to confirm the group changes */}
+
+ setRandomOpen(false)}
+ doAction={randomGroups}
+ />
+ >
+ ) : (
+ navigate(
+ '/course/' +
+ courseId +
+ '/assignment/' +
+ assignmentId +
+ '/groups/choose'
+ )
+ )}
+ >
+ )}
>
)
}
+
+function chunkArray(arr: T[], chunkSize: number): T[][] {
+ const result: T[][] = []
+ for (let i = 0; i < arr.length; i += chunkSize) {
+ const chunk: T[] = arr.slice(i, i + chunkSize)
+ result.push(chunk)
+ }
+ return result
+}
diff --git a/frontend/frontend/src/pages/loginPage/LoginPage.tsx b/frontend/frontend/src/pages/loginPage/LoginPage.tsx
index 0eede4144..fc30cd012 100644
--- a/frontend/frontend/src/pages/loginPage/LoginPage.tsx
+++ b/frontend/frontend/src/pages/loginPage/LoginPage.tsx
@@ -1,4 +1,5 @@
-import { Box, Button, Typography } from '@mui/material'
+import { Button } from '../../components/CustomComponents.tsx'
+import { Box } from '@mui/material'
import { t } from 'i18next'
import { useMsal } from '@azure/msal-react'
import { loginRequest } from '../../authConfig/authConfig.ts'
@@ -76,27 +77,32 @@ export function LoginPage() {
flexDirection="row"
alignItems="center"
justifyContent={'center'}
+ gap={0}
alignSelf={'center'}
>
-
- Pigeonhole
-
+ />
{/* Lower container with login button */}
-
+
{t('login')}
diff --git a/frontend/frontend/src/pages/mainPage/ArchivedView.tsx b/frontend/frontend/src/pages/mainPage/ArchivedView.tsx
index f7947bce5..0f374b9de 100644
--- a/frontend/frontend/src/pages/mainPage/ArchivedView.tsx
+++ b/frontend/frontend/src/pages/mainPage/ArchivedView.tsx
@@ -1,31 +1,39 @@
import { Stack } from '@mui/material'
import { CourseCard } from '../../components/CourseCard.tsx'
-import course from './MainPage.tsx'
+import { Course } from './MainPage.tsx'
interface CourseCardProps {
+ userid: number
isStudent: boolean
- archivedCourses: course[]
+ archivedCourses: Course[]
+ pinnedCourses: number[]
+ pinCourse: (courseId: number) => void
}
-export function ArchivedView({ isStudent, archivedCourses }: CourseCardProps) {
+export function ArchivedView({
+ userid,
+ isStudent,
+ archivedCourses,
+ pinnedCourses,
+ pinCourse,
+}: CourseCardProps) {
return (
<>
{
return (
pinCourse(course.vak_id)}
/>
)
})}
diff --git a/frontend/frontend/src/pages/mainPage/CoursesView.tsx b/frontend/frontend/src/pages/mainPage/CoursesView.tsx
index 51eecb057..84d52470a 100644
--- a/frontend/frontend/src/pages/mainPage/CoursesView.tsx
+++ b/frontend/frontend/src/pages/mainPage/CoursesView.tsx
@@ -1,47 +1,63 @@
-import { IconButton, Stack } from '@mui/material'
+import { IconButton, Stack, Tooltip } from '@mui/material'
import { CourseCard } from '../../components/CourseCard.tsx'
import AddIcon from '@mui/icons-material/Add'
import { useNavigate } from 'react-router-dom'
import { Course } from './MainPage.tsx'
interface CourseCardProps {
+ userid: number
isStudent: boolean
activecourses: Course[]
+ pinnedCourses: number[]
+ archiveCourse: (courseId: number) => void
+ pinCourse: (courseId: number) => void
}
-export function CoursesView({ isStudent, activecourses }: CourseCardProps) {
+export function CoursesView({
+ userid,
+ isStudent,
+ activecourses,
+ pinnedCourses,
+ archiveCourse,
+ pinCourse,
+}: CourseCardProps) {
const navigate = useNavigate()
return (
<>
- {/* Map the list of the cirrent courses to CourseCards.
+ {/* Map the list of the current courses to CourseCards.
A CourseCard displays brief information about the course such as the title, deadlines, ...*/}
{activecourses.map((course: Course) => (
+ archiveCourse(course.vak_id)
+ }
+ pinned={pinnedCourses.includes(course.vak_id)}
+ pinEvent={() => pinCourse(course.vak_id)}
/>
))}
@@ -53,13 +69,16 @@ export function CoursesView({ isStudent, activecourses }: CourseCardProps) {
padding={0}
>
{/* Teachers get an extra button to add courses. */}
- navigate('/course/edit')}
- >
-
-
+
+ navigate('/course/new')}
+ >
+
+
+
)}
diff --git a/frontend/frontend/src/pages/mainPage/MainPage.tsx b/frontend/frontend/src/pages/mainPage/MainPage.tsx
index 55acd1d75..cb8ef6791 100644
--- a/frontend/frontend/src/pages/mainPage/MainPage.tsx
+++ b/frontend/frontend/src/pages/mainPage/MainPage.tsx
@@ -1,5 +1,6 @@
import { Header } from '../../components/Header.tsx'
-import { Box, Button, Stack } from '@mui/material'
+import { Button } from '../../components/CustomComponents.tsx'
+import { Box, Stack } from '@mui/material'
import TabSwitcher from '../../components/TabSwitcher.tsx'
import { ArchivedView } from './ArchivedView.tsx'
import { CoursesView } from './CoursesView.tsx'
@@ -10,12 +11,16 @@ import { useEffect, useState } from 'react'
import instance from '../../axiosConfig.ts'
import { AxiosError, AxiosResponse } from 'axios'
import { useNavigate } from 'react-router-dom'
+import { CourseCardSkeleton } from '../../components/CourseCardSkeleton.tsx'
+import WarningPopup from '../../components/WarningPopup.tsx'
export interface Course {
vak_id: number
naam: string
+ jaartal: number
studenten: number[]
lesgevers: number[]
+ gearchiveerd: boolean
}
export interface project {
@@ -23,7 +28,7 @@ export interface project {
titel: string
beschrijving: string
opgave_bestand: File | null
- vak_id: number
+ vak: number
deadline: string
extra_deadline: string | null
max_score: number
@@ -37,52 +42,70 @@ export interface project {
* This is the main page of the application.
* It contains a header, a tab switcher for current and archived courses, a deadline calendar, and an admin button.
*/
-export function MainPage() {
+export default function MainPage() {
// State for role
- const [role, setRole] = useState('')
+ const [user, setUser] = useState(0)
+ const [role, setRole] = useState('student')
const [courses, setCourses] = useState([])
+ const [pinnedCourses, setPinnedCourses] = useState([])
+ const [courseOrder, setCourseOrder] = useState([])
const [deadlines, setDeadlines] = useState([])
+ const [loading, setLoading] = useState(true)
+ const [selectedYear, setSelectedYear] = useState(dayjs().year())
+
+ //navigator for routing
+ const [assignments, setAssignments] = useState([])
const navigator = useNavigate()
useEffect(() => {
- // Get the role of the person that has logged in
- console.log('requesting api')
- instance
- .get('/gebruikers/me/')
- .then((response: AxiosResponse) => {
- console.log(response.data)
- setRole(response.data.is_lesgever ? 'teacher' : 'student')
- })
- .catch((e: AxiosError) => {
- console.error(e)
- })
-
- // Get the courses, their projects, and their respective deadlines
+ // Get the courses, their projects, and their respective deadlines + the role of the user
async function fetchData() {
+ //set loading to true every time the data is requested
+ setLoading(true)
+ await instance
+ .get('/gebruikers/me/')
+ .then((response: AxiosResponse) => {
+ console.log(response.data)
+ setUser(response.data.user)
+ setRole(response.data.is_lesgever ? 'teacher' : 'student')
+ setPinnedCourses(response.data.gepinde_vakken)
+ })
+ .catch((e: AxiosError) => {
+ console.error(e)
+ })
+
+ let courses: Course[] = []
try {
- const response = await instance.get('/vakken/')
+ const response =
+ await instance.get('/vakken/?in=true')
+ courses = response.data
setCourses(response.data)
} catch (error) {
console.error('Error fetching courses:', error)
}
- instance
- .get('/projecten/')
- .then((response: AxiosResponse) => {
- const deadlines: Dayjs[] = []
- response.data.forEach((project: project) => {
- if (project.zichtbaar && !project.gearchiveerd) {
- deadlines.push(
- dayjs(project.deadline, 'YYYY-MM-DD-HH:mm:ss')
- )
- }
+ const deadlines: Dayjs[] = []
+ const assignments: project[] = []
+ for (const course of courses) {
+ await instance
+ .get(`/projecten/?vak=${course.vak_id}`)
+ .then((response: AxiosResponse) => {
+ response.data.forEach((project: project) => {
+ if (project.zichtbaar && !project.gearchiveerd) {
+ deadlines.push(dayjs(project.deadline))
+ assignments.push(project)
+ }
+ })
})
- console.log(deadlines)
- setDeadlines(deadlines)
- })
- .catch((e: AxiosError) => {
- console.error(e)
- })
+ .catch((e: AxiosError) => {
+ console.error(e)
+ })
+ }
+ console.log(deadlines)
+ setDeadlines(deadlines)
+ setAssignments(assignments)
+
+ setLoading(false)
}
fetchData().catch((e) => {
@@ -90,8 +113,64 @@ export function MainPage() {
})
}, [])
+ // Logging order of courses
+ // This only changes on page reload
+ useEffect(() => {
+ instance
+ .get('/gebruikers/me/')
+ .then((response: AxiosResponse) => {
+ setCourseOrder(response.data.gepinde_vakken)
+ })
+ .catch((e: AxiosError) => {
+ console.error(e)
+ })
+ }, [])
+
useEffect(() => {}, [courses])
+ const [openArchivePopup, setOpenArchivePopup] = useState(false)
+ const [archiveCourseId, setArchiveCourseId] = useState(0)
+
+ const archiveCourse = (courseId: number) => {
+ setArchiveCourseId(courseId)
+ setOpenArchivePopup(true)
+ }
+ const doArchive = async () => {
+ console.log('Archive clicked')
+ const newCourses = courses.map((course) =>
+ course.vak_id == archiveCourseId
+ ? archiveSingleCourse(course)
+ : course
+ )
+ setCourses(newCourses)
+ try {
+ await instance.patch(`/vakken/${archiveCourseId}/`, {
+ gearchiveerd: true,
+ })
+ } catch (error) {
+ console.error('Error updating data:', error)
+ }
+ }
+
+ const pinCourse = async (courseId: number) => {
+ let newPinnedCourses: number[]
+ if (pinnedCourses.includes(courseId)) {
+ newPinnedCourses = pinnedCourses.filter(
+ (pinnedId) => pinnedId !== courseId
+ )
+ } else {
+ newPinnedCourses = [...pinnedCourses, courseId]
+ }
+ setPinnedCourses(newPinnedCourses)
+ try {
+ await instance.patch(`/gebruikers/${user}/`, {
+ gepinde_vakken: newPinnedCourses,
+ })
+ } catch (error) {
+ console.error('Error updating data:', error)
+ }
+ }
+
return (
<>
-
+
{/* Two tabs to select either the current or archived courses,
CoursesView is a scroll-box with the current courses,
ArchivedView is the same but for the archived courses. */}
- ,
- ,
- ]}
- />
+
+
+ {[...Array(3)].map((_, index) => (
+
+ ))}
+ ,
+
+ {[...Array(3)].map((_, index) => (
+
+ ))}
+ ,
+ ]
+ : [
+
+ !course.gearchiveerd &&
+ course.jaartal ===
+ selectedYear
+ )
+ .sort(
+ (
+ a: Course,
+ b: Course
+ ) => {
+ if (
+ courseOrder.includes(
+ a.vak_id
+ )
+ ) {
+ if (
+ courseOrder.includes(
+ b.vak_id
+ )
+ ) {
+ return (
+ courseOrder.indexOf(
+ a.vak_id
+ ) -
+ courseOrder.indexOf(
+ b.vak_id
+ )
+ )
+ } else {
+ return -1
+ }
+ } else {
+ if (
+ courseOrder.includes(
+ b.vak_id
+ )
+ ) {
+ return 1
+ } else {
+ return 0
+ }
+ }
+ }
+ )}
+ pinnedCourses={pinnedCourses}
+ archiveCourse={archiveCourse}
+ pinCourse={pinCourse}
+ />,
+
+ course.gearchiveerd &&
+ course.jaartal ===
+ selectedYear
+ )
+ .sort(
+ (
+ a: Course,
+ b: Course
+ ) => {
+ if (
+ courseOrder.includes(
+ a.vak_id
+ )
+ ) {
+ if (
+ courseOrder.includes(
+ b.vak_id
+ )
+ ) {
+ return (
+ courseOrder.indexOf(
+ a.vak_id
+ ) -
+ courseOrder.indexOf(
+ b.vak_id
+ )
+ )
+ } else {
+ return -1
+ }
+ } else {
+ if (
+ courseOrder.includes(
+ b.vak_id
+ )
+ ) {
+ return 1
+ } else {
+ return 0
+ }
+ }
+ }
+ )}
+ pinnedCourses={pinnedCourses}
+ pinCourse={pinCourse}
+ />,
+ ]
+ }
+ />
+
+
{/* Add a calendar to the right of the mainpage. */}
-
+
{role === 'admin' && (
@@ -153,8 +369,6 @@ export function MainPage() {
}}
>
navigator('/addTeacher')}
aria-label={'admin-button'}
sx={{ margin: 5 }}
@@ -163,7 +377,22 @@ export function MainPage() {
)}
+ setOpenArchivePopup(false)}
+ doAction={doArchive}
+ />
>
)
}
+
+function archiveSingleCourse(course: Course): Course {
+ return {
+ ...course,
+ gearchiveerd: true,
+ }
+}
diff --git a/frontend/frontend/src/pages/scoresPage/ProjectScoresPage.tsx b/frontend/frontend/src/pages/scoresPage/ProjectScoresPage.tsx
index bee29c0a6..6f293a351 100644
--- a/frontend/frontend/src/pages/scoresPage/ProjectScoresPage.tsx
+++ b/frontend/frontend/src/pages/scoresPage/ProjectScoresPage.tsx
@@ -1,5 +1,14 @@
import { Header } from '../../components/Header'
-import { Box, Button, Stack, styled } from '@mui/material'
+import { Card, SecondaryButton } from '../../components/CustomComponents.tsx'
+import {
+ Box,
+ CircularProgress,
+ IconButton,
+ Skeleton,
+ Stack,
+ styled,
+ Tooltip,
+} from '@mui/material'
import { useNavigate, useParams } from 'react-router-dom'
import { StudentsView } from './StudentsView.tsx'
import { t } from 'i18next'
@@ -11,6 +20,8 @@ import WarningPopup from '../../components/WarningPopup.tsx'
import ErrorPage from '../ErrorPage.tsx'
import JSZip from 'jszip'
import Papa from 'papaparse'
+import { User } from '../subjectsPage/AddChangeSubjectPage.tsx'
+import { Submission } from '../submissionPage/SubmissionPage.tsx'
const VisuallyHiddenInput = styled('input')({
clipPath: 'inset(50%)',
@@ -31,10 +42,12 @@ export interface Project {
vak: number
max_score: number
max_groep_grootte: number
+ student_groep?: boolean
deadline: Date | null
extra_deadline: Date | null
zichtbaar: boolean
gearchiveerd: boolean
+ filename?: string
}
interface Groep {
@@ -46,16 +59,10 @@ interface Groep {
export interface Indiening {
indiening_id: number
groep: number
+ bestand: File
tijdstip: Date
status: number
result: string
- indiening_bestanden: IndieningBestand[]
-}
-
-interface IndieningBestand {
- indiening_bestand_id: number
- indiening: number
- bestand: string
}
interface Score {
@@ -85,6 +92,11 @@ export function ProjectScoresPage() {
const [project, setProject] = useState()
const [groepen, setGroepen] = useState([])
const [fetchError, setFetchError] = useState(false)
+ const [user, setUser] = useState()
+
+ // State for loading the page properly
+ const [loading, setLoading] = useState(true)
+ const [userLoading, setUserLoading] = useState(true)
const navigate = useNavigate()
@@ -131,6 +143,11 @@ export function ProjectScoresPage() {
useEffect(() => {
async function fetchData() {
try {
+ setUserLoading(true)
+ setLoading(true)
+ const userResponse = await instance.get('/gebruikers/me/')
+ setUser(userResponse.data)
+ setUserLoading(false)
const assignmentResponse = await instance.get(
`/projecten/${assignmentId}/`
)
@@ -138,6 +155,8 @@ export function ProjectScoresPage() {
} catch (error) {
console.log('Error fetching data:', error)
setFetchError(true)
+ } finally {
+ setLoading(false)
}
}
@@ -146,43 +165,50 @@ export function ProjectScoresPage() {
const downloadAllSubmissions = () => {
const zip = new JSZip()
- const downloadPromises: Promise[] = []
+ const downloadPromises: Promise[] = []
+
groepen
.filter((groep) => groep.lastSubmission !== undefined)
.map((groep) => groep.lastSubmission)
- .forEach((submission, index) => {
+ .forEach((submission) => {
downloadPromises.push(
- new Promise((resolve, reject) => {
- instance
- .get(
- `/indieningen/${submission?.indiening_id}/indiening_bestanden/`,
+ (async () => {
+ try {
+ // Get the submission details
+ const submissionResponse = await instance.get(
+ `/indieningen/${submission?.indiening_id}/`
+ )
+ const newSubmission = submissionResponse.data
+ // Get the submission file
+ const fileResponse = await instance.get(
+ `/indieningen/${submission?.indiening_id}/indiening_bestand/`,
{ responseType: 'blob' }
)
- .then((res) => {
- let filename = 'lege_indiening_zip.zip'
- if (submission === undefined) return
- if (submission.indiening_bestanden.length > 0) {
- filename =
- submission?.indiening_bestanden[0].bestand.replace(
- /^.*[\\/]/,
- ''
- )
- }
- if (filename !== 'lege_indiening_zip.zip') {
- zip.file(filename, res.data)
- }
- resolve()
- })
- .catch((err) => {
- console.error(
- `Error downloading submission ${index + 1}:`,
- err
+ let filename = 'indiening.zip'
+ if (newSubmission.bestand) {
+ filename = newSubmission.bestand.replace(
+ /^.*[\\/]/,
+ ''
)
- reject(err)
+ }
+ const blob = new Blob([fileResponse.data], {
+ type: fileResponse.headers['content-type'],
})
- })
+ newSubmission.bestand = new File([blob], filename, {
+ type: fileResponse.headers['content-type'],
+ })
+ newSubmission.filename = filename
+ // Add the file to the zip
+ zip.file(filename, fileResponse.data)
+ return newSubmission // Return the submission instead of resolving a promise
+ } catch (err) {
+ console.error(`Error downloading submission:`, err)
+ throw err // Throw error instead of rejecting a promise
+ }
+ })() // Immediately invoke the async function
)
})
+
Promise.all(downloadPromises)
.then(() => {
zip.generateAsync({ type: 'blob' })
@@ -190,7 +216,8 @@ export function ProjectScoresPage() {
const url = window.URL.createObjectURL(blob)
const a = document.createElement('a')
a.href = url
- a.download = 'all_submissions.zip'
+ a.download =
+ 'all_submissions_' + project?.titel + '_.zip'
document.body.appendChild(a)
a.click()
a.remove()
@@ -262,110 +289,206 @@ export function ProjectScoresPage() {
return (
<>
-
-
- {/* Main content box */}
+ {/* Rendering different UI based on user role */}
+ {userLoading ? (
- {/* Render StudentsView component if project is defined */}
- {project !== undefined && (
-
- )}
-
- {/* Footer section with action buttons */}
-
-
-
- {t('export_submissions')}
-
-
-
- {t('upload_scores')}
-
-
-
-
- setOpenSaveScoresPopup(true)}
- variant="contained"
- startIcon={ }
- />
- setOpenDeleteScoresPopup(true)}
- variant="contained"
- color="secondary"
- startIcon={ }
- />
-
+
+
- {/* Popup for confirming saving scores */}
- setOpenSaveScoresPopup(false)}
- doAction={saveScores}
- />
- {/* Popup for confirming deletion of scores */}
- setOpenDeleteScoresPopup(false)}
- doAction={deleteScores}
- />
-
+ ) : (
+ <>
+ {user?.is_lesgever ? (
+ // Rendering UI for teacher
+ <>
+
+
+ {/* Main content box */}
+
+
+ {/* Render StudentsView component if project is defined */}
+ {loading ? (
+
+
+
+ ) : (
+ <>
+ {project && (
+
+ )}
+ >
+ )}
+
+ {/* Footer section with action buttons */}
+
+
+
+
+ {t('export_submissions')}
+
+
+
+ {t('upload_scores')}
+
+
+
+
+
+ {loading ? (
+
+ ) : (
+ <>
+
+
+ setOpenSaveScoresPopup(
+ true
+ )
+ }
+ sx={{
+ color: 'background.default',
+ '&:hover': {
+ color: 'text.primary',
+ },
+ backgroundColor:
+ 'primary.main',
+ borderRadius: 2,
+ }}
+ >
+
+
+
+ >
+ )}
+
+
+
+ setOpenDeleteScoresPopup(
+ true
+ )
+ }
+ sx={{
+ backgroundColor:
+ 'secondary.main',
+ borderRadius: 2,
+ }}
+ >
+
+
+
+
+
+ {/* Popup for confirming saving scores */}
+
+ setOpenSaveScoresPopup(false)
+ }
+ doAction={saveScores}
+ />
+ {/* Popup for confirming deletion of scores */}
+
+ setOpenDeleteScoresPopup(false)
+ }
+ doAction={deleteScores}
+ />
+
+ >
+ ) : (
+ navigate('*')
+ )}
+ >
+ )}
>
)
}
diff --git a/frontend/frontend/src/pages/scoresPage/StudentScoreListItem.tsx b/frontend/frontend/src/pages/scoresPage/StudentScoreListItem.tsx
index 960f44d8f..8b829a2ff 100644
--- a/frontend/frontend/src/pages/scoresPage/StudentScoreListItem.tsx
+++ b/frontend/frontend/src/pages/scoresPage/StudentScoreListItem.tsx
@@ -1,15 +1,18 @@
+import { Divider, EvenlySpacedRow } from '../../components/CustomComponents.tsx'
import {
- Divider,
- IconButton,
+ CircularProgress,
+ ListItemIcon,
ListItem,
ListItemText,
TextField,
+ Box,
} from '@mui/material'
import DownloadIcon from '@mui/icons-material/Download'
import { t } from 'i18next'
import { useEffect, useState } from 'react'
import instance from '../../axiosConfig'
import { Indiening } from './ProjectScoresPage.tsx'
+import dayjs from 'dayjs'
interface StudentScoreListItemProps {
key: number
@@ -31,10 +34,13 @@ export function StudentScoreListItem({
changeScore,
}: StudentScoreListItemProps) {
const [name, setName] = useState(t('group') + ' ' + groupNumber)
+ // state for loaders
+ const [loading, setLoading] = useState(true)
// Get all necessary data
useEffect(() => {
async function fetchName() {
+ setLoading(true)
if (studenten.length == 1) {
const studentId = studenten[0]
const studentResponse = await instance.get(
@@ -46,43 +52,42 @@ export function StudentScoreListItem({
studentResponse.data.last_name
)
}
+ setLoading(false)
}
fetchName().catch((e) => console.error(e))
}, [studenten])
// Function to download a single submission
- const downloadSubmission = () => {
+ const downloadSubmission = async () => {
try {
- instance
- .get(
- `/indieningen/${lastSubmission?.indiening_id}/indiening_bestanden/`,
- { responseType: 'blob' }
- )
- .then((res) => {
- let filename = 'lege_indiening.zip'
- if (lastSubmission === undefined) return
- if (lastSubmission.indiening_bestanden.length > 0) {
- filename =
- lastSubmission.indiening_bestanden[0].bestand.replace(
- /^.*[\\/]/,
- ''
- )
- }
- const blob = new Blob([res.data], {
- type: res.headers['content-type'],
- })
- const file: File = new File([blob], filename, {
- type: res.headers['content-type'],
- })
- const url = window.URL.createObjectURL(file)
- const a = document.createElement('a')
- a.href = url
- a.download = filename
- document.body.appendChild(a)
- a.click()
- a.remove()
- })
+ let filename = 'lege_indiening.zip'
+ // Get the submission details
+ const submissionResponse = await instance.get(
+ `/indieningen/${lastSubmission?.indiening_id}/`
+ )
+ const newSubmission = submissionResponse.data
+ // Get the submission file
+ const fileResponse = await instance.get(
+ `/indieningen/${lastSubmission?.indiening_id}/indiening_bestand/`,
+ { responseType: 'blob' }
+ )
+ if (newSubmission.bestand) {
+ filename = newSubmission.bestand.replace(/^.*[\\/]/, '')
+ }
+ const blob = new Blob([fileResponse.data], {
+ type: fileResponse.headers['content-type'],
+ })
+ const file = new File([blob], filename, {
+ type: fileResponse.headers['content-type'],
+ })
+ const url = window.URL.createObjectURL(file)
+ const a = document.createElement('a')
+ a.href = url
+ a.download = filename
+ document.body.appendChild(a)
+ a.click()
+ a.remove()
} catch (error) {
console.error(error)
}
@@ -90,73 +95,91 @@ export function StudentScoreListItem({
return (
<>
-
+
{/* Inner list item for displaying submission details */}
-
+
{/* Content section */}
<>
-
-
- {/* Score section */}
-
- {lastSubmission ? (
- <>
-
- changeScore(
- parseInt(event.target.value)
- )
- }
- variant="filled"
- size="small"
- />
+ {loading ? (
+
+ ) : (
+
- >
- ) : (
-
- )}
-
- {/* Button to download submission */}
-
-
-
-
-
+ sx={{ maxWidth: 200 }}
+ primary={name}
+ />,
+ ,
+
+ {lastSubmission ? (
+ <>
+
+
+ changeScore(
+ parseInt(
+ event.target
+ .value
+ )
+ )
+ }
+ variant="standard"
+ size="small"
+ />
+
+
+ >
+ ) : (
+
+ )}
+ ,
+
+
+
+ {lastSubmission ? (
+
+ ) : (
+
+ )}
+
+
+ ,
+ ]}
+ />
+ )}
>
diff --git a/frontend/frontend/src/pages/scoresPage/StudentsView.tsx b/frontend/frontend/src/pages/scoresPage/StudentsView.tsx
index 134cccf65..c81dfe716 100644
--- a/frontend/frontend/src/pages/scoresPage/StudentsView.tsx
+++ b/frontend/frontend/src/pages/scoresPage/StudentsView.tsx
@@ -1,8 +1,9 @@
-import { Box, Divider, Typography } from '@mui/material'
+import { Divider, EvenlySpacedRow } from '../../components/CustomComponents.tsx'
+import { Box, Skeleton, Typography } from '@mui/material'
import List from '@mui/material/List'
import { StudentScoreListItem } from './StudentScoreListItem.tsx'
import { t } from 'i18next'
-import { useEffect } from 'react'
+import { useEffect, useState } from 'react'
import instance from '../../axiosConfig.ts'
import { Project, ScoreGroep } from './ProjectScoresPage.tsx'
@@ -19,6 +20,9 @@ export function StudentsView({
setGroepen,
changeScore,
}: StudentsViewProps) {
+ //state for loading the page
+ const [loading, setLoading] = useState(true)
+
// useEffect hook to periodically fetch all data
useEffect(() => {
async function fetchGroups(assignment: Project): Promise {
@@ -82,6 +86,7 @@ export function StudentsView({
async function fetchData() {
try {
+ setLoading(true)
const groupArray = await fetchGroups(project)
const submissionPromises = groupArray.map((scoregroep) =>
@@ -97,6 +102,8 @@ export function StudentsView({
setGroepen(scoreArray)
} catch (error) {
console.error('Error fetching all data:', error)
+ } finally {
+ setLoading(false)
}
}
@@ -109,21 +116,43 @@ export function StudentsView({
- <>
- {t('group')}
- {t('time')}
- Score
- Download
- >
+
+ {t('group')}
+ ,
+
+ {t('time')}
+ ,
+
+ Score
+ ,
+
+ Download
+ ,
+ ]}
+ />
{/* Scrollable list of students */}
@@ -143,19 +169,38 @@ export function StudentsView({
{/* Mapping through groups to render StudentScoreListItem */}
- {groepen.map((groep, index) => (
-
- changeScore(index, score)
- }
- />
- ))}
+ {loading ? (
+ [...Array(3)].map((_, index) => (
+
+ ))
+ ) : (
+ <>
+ {groepen.map((groep, index) => (
+
+ changeScore(index, score)
+ }
+ />
+ ))}
+ >
+ )}
diff --git a/frontend/frontend/src/pages/subjectsPage/AddChangeSubjectPage.tsx b/frontend/frontend/src/pages/subjectsPage/AddChangeSubjectPage.tsx
index 8491245fe..43647a326 100644
--- a/frontend/frontend/src/pages/subjectsPage/AddChangeSubjectPage.tsx
+++ b/frontend/frontend/src/pages/subjectsPage/AddChangeSubjectPage.tsx
@@ -1,28 +1,35 @@
import {
- Box,
+ Button,
+ Card,
Divider,
+ EvenlySpacedRow,
+ SecondaryButton,
+} from '../../components/CustomComponents.tsx'
+
+import {
+ Box,
+ CircularProgress,
IconButton,
- ListItemButton,
+ ListItem,
ListItemText,
+ Skeleton,
Stack,
TextField,
+ Tooltip,
Typography,
} from '@mui/material'
-import Button from '@mui/material/Button'
-import { Header } from '../../components/Header.tsx'
-import { useEffect, useState } from 'react'
-import { useParams } from 'react-router-dom'
+import { Header } from '../../components/Header'
+import React, { ChangeEvent, useEffect, useRef, useState } from 'react'
+import { useNavigate, useParams } from 'react-router-dom'
import List from '@mui/material/List'
import { t } from 'i18next'
import 'dayjs/locale/nl'
import FileUploadButton from '../../components/FileUploadButton'
import ClearIcon from '@mui/icons-material/Clear'
-
import Dialog from '@mui/material/Dialog'
-
import instance from '../../axiosConfig.ts'
-
-import Papa from 'papaparse'
+import Papa, { ParseResult } from 'papaparse'
+import WarningPopup from '../../components/WarningPopup.tsx'
export interface User {
user: number
@@ -32,67 +39,271 @@ export interface User {
email: string
}
+interface errorChecks {
+ title: boolean
+}
+
+// This function takes a list of users and will render it.
+// It can be used for both the teachers and the students.
+function UserList(
+ loading: boolean,
+ users: User[],
+ setSelected: React.Dispatch>,
+ setOpen: React.Dispatch>,
+ handleRemove: () => void
+) {
+ return (
+
+
+ {loading ? (
+ [...Array(5)].map((_, index) => (
+
+
+
+ ))
+ ) : (
+ <>
+ {users.map((user) => {
+ const handleClickOpen = () => {
+ setSelected(user.user)
+
+ if (user.is_lesgever) {
+ setOpen(true)
+ } else {
+ handleRemove()
+ }
+ }
+ {
+ /* The list of users is mapped onto buttons
+ This makes it possible to click through on a person. */
+ }
+ return (
+ <>
+
+
+ ,
+
+
+
+
+
+ ,
+ ]}
+ />
+
+ >
+ )
+ })}
+ >
+ )}
+
+
+ )
+}
+
+// This function will render the UI for adding extra students or teachers.
+// It can either be done by uploading a file or by typing in the email.
+function UploadPart(
+ file: File | undefined,
+ handleFileChange: (e: ChangeEvent) => void,
+ setEmail: React.Dispatch>,
+ handleAdd: () => void,
+ str: string,
+ textFieldRef: React.RefObject
+) {
+ return (
+ <>
+
+
+
+
+ {/* This box allows you to add extra people by their email. */}
+
+ setEmail(event.target.value)
+ }
+ inputRef={textFieldRef}
+ />
+
+
+
+ {t('add')}
+
+
+
+
+
+
+ >
+ )
+}
+
+// When you try to delete someone from the list, a pop-up will appear.
+// This pop-up will ask you if you are sure you want to delete the person.
+// This function will render the pop-up.
+function DialogWindow(
+ handleClose: () => void,
+ open: boolean,
+ handleRemove: () => void,
+ str: string
+) {
+ return (
+ <>
+
+
+ {str + '?'}
+
+
+ {t('cancel')}
+
+
+
+ {t('delete')}
+
+
+
+
+ >
+ )
+}
+
export function AddChangeSubjectPage() {
const params = useParams()
+ const navigate = useNavigate()
+
// State for the different fields of the subject
- const [title, setTitle] = useState('')
- const [emailStudent, setEmailStudent] = useState('')
- const [emailTeacher, setEmailTeacher] = useState('')
+ const [title, setTitle] = useState('')
+ const [emailStudent, setEmailStudent] = useState('')
+ const [emailTeacher, setEmailTeacher] = useState('')
const [students, setStudents] = useState([])
const [teachers, setTeachers] = useState([])
- const [selectedStudent, setSelectedStudent] = useState(0)
- const [openStudent, setOpenStudent] = useState(false)
- const [selectedTeacher, setSelectedTeacher] = useState(0)
- const [openTeacher, setOpenTeacher] = useState(false)
- const [studentFile, setStudentFile] = useState()
- const [teacherFile, setTeacherFile] = useState()
- const vakID = params.courseId
-
- const handleCloseStudent = () => {
+ const [selectedStudent, setSelectedStudent] = useState(0)
+ const [openStudent, setOpenStudent] = useState(false)
+ const [selectedTeacher, setSelectedTeacher] = useState(0)
+ const [openTeacher, setOpenTeacher] = useState(false)
+ const [studentFile, setStudentFile] = useState()
+ const [teacherFile, setTeacherFile] = useState()
+ const [user, setUser] = useState()
+ const studentRef = useRef(null)
+ const teacherRef = useRef(null)
+
+ const [vakID, setVakID] = useState(params.courseId)
+
+ // state for spinners
+ const [loading, setLoading] = useState(false)
+ const [userLoading, setUserLoading] = useState(true)
+
+ const [saveConfirmation, setSaveConfirmation] = useState(false)
+ const [cancelConfirmation, setCancelConfirmation] = useState(false)
+
+ const [assignmentErrors, setAssignmentErrors] = useState({
+ title: false,
+ })
+
+ const handleSubmit = () => {
+ setAssignmentErrors({
+ title: title === '',
+ })
+ if (title === '') {
+ return
+ }
+ setSaveConfirmation(true)
+ }
+
+ const closeSaveConfirmation = () => {
+ setSaveConfirmation(false)
+ }
+
+ const closeCancel = () => {
+ setCancelConfirmation(false)
+ }
+
+ const handleCloseStudent = (): void => {
setOpenStudent(false)
}
- const handleRemoveStudent = () => {
- setStudents((oldstudents) => {
- for (let i = 0; i < oldstudents.length; i++) {
- if (oldstudents[i].user == selectedStudent) {
- oldstudents.splice(i, 1)
- return oldstudents
- }
- }
- return oldstudents
- })
+ const handleRemoveStudent = (): void => {
+ const newstudents = students.filter(
+ (student) => student.user != selectedStudent
+ )
+
+ setStudents(newstudents)
+
setOpenStudent(false)
}
- const handleAddStudent = () => {
+ const handleAddStudent = (): void => {
instance
.get('gebruikers/?email=' + emailStudent)
.then((res) => {
setStudents((oldstudents) => {
if (res.data.length == 0) {
+ alert(t('this_user_doesnt_exist'))
return oldstudents
}
-
- //This is like this to prevent the same user being in the list twice
- let found = false
-
- const data = res.data[0]
-
- const id = data.user
- if (data.is_lesgever) {
- return oldstudents
- }
- for (const student of oldstudents) {
- if (student.user == id) {
- found = true
- }
- }
- if (found) {
- return oldstudents
- } else {
- return [...oldstudents, data]
- }
+ return addUser(false, res.data[0], oldstudents)
})
})
.catch((err) => {
@@ -100,10 +311,19 @@ export function AddChangeSubjectPage() {
})
handleUploadStudent()
+
+ if (studentRef.current) {
+ studentRef.current.value = ''
+ setEmailStudent('')
+ }
}
- const handleStudentFileChange = (e) => {
- if (e.target.files.length) {
+ const handleStudentFileChange = (
+ e: ChangeEvent
+ ): void => {
+ if (e.target.files == null) {
+ setStudentFile(undefined)
+ } else if (e.target.files.length) {
const inputFile = e.target.files[0]
setStudentFile(inputFile)
} else {
@@ -112,14 +332,20 @@ export function AddChangeSubjectPage() {
}
}
- const handleUploadStudent = () => {
+ const handleUploadStudent = (): void => {
const reader = new FileReader()
reader.onload = async ({ target }) => {
- const csv = Papa.parse(target.result, {
+ if (target == null) {
+ return
+ }
+ if (typeof target.result != 'string') {
+ return
+ }
+ const csv: ParseResult = Papa.parse(target.result, {
header: true,
})
-
+ // This will loop through the csv file and add the students to the list.
for (let i = 0; i < csv.data.length; i++) {
if (csv.data[i].email != '') {
instance
@@ -130,25 +356,7 @@ export function AddChangeSubjectPage() {
return oldstudents
}
- //This is like this to prevent the same user being in the list twice
- let found = false
-
- const data = res.data[0]
-
- const id = data.user
- if (data.is_lesgever) {
- return oldstudents
- }
- for (const student of oldstudents) {
- if (student.user == id) {
- found = true
- }
- }
- if (found) {
- return oldstudents
- } else {
- return [...oldstudents, data]
- }
+ return addUser(false, res.data[0], oldstudents)
})
})
.catch((err) => {
@@ -157,65 +365,57 @@ export function AddChangeSubjectPage() {
}
}
}
-
- reader.readAsText(studentFile)
+ if (studentFile != undefined) {
+ reader.readAsText(studentFile)
+ }
}
- const handleCloseTeacher = () => {
+ const handleCloseTeacher = (): void => {
setOpenTeacher(false)
}
+ // This function will remove a teacher from the list.
+ // It does so by looping through the list and removing the teacher with the correct ID.
const handleRemoveTeacher = () => {
- setTeachers((oldteacher) => {
- for (let i = 0; i < oldteacher.length; i++) {
- if (oldteacher[i].user == selectedTeacher) {
- oldteacher.splice(i, 1)
- return oldteacher
- }
- }
- return oldteacher
- })
+ const newTeachers = teachers.filter(
+ (teacher) => teacher.user != selectedTeacher
+ )
+ setTeachers(newTeachers)
setOpenTeacher(false)
}
- const handleAddTeacher = () => {
+ const handleAddTeacher = (): void => {
instance
.get('gebruikers/?email=' + emailTeacher)
.then((res) => {
- setTeachers((oldteacher) => {
+ setTeachers((oldteachers) => {
//This is like this to prevent the same user being in the list twice
if (res.data.length == 0) {
- return oldteacher
+ alert(t('this_user_doesnt_exist'))
+ return oldteachers
}
- const data = res.data[0]
-
- let found = false
- const id = data.user
- if (!data.is_lesgever) {
- return oldteacher
- }
- for (const teacher of oldteacher) {
- if (teacher.user == id) {
- found = true
- }
- }
- if (found) {
- return oldteacher
- } else {
- return [...oldteacher, data]
- }
+ return addUser(true, res.data[0], oldteachers)
})
})
.catch((err) => {
console.log(err)
})
handleUploadTeacher()
+
+ if (teacherRef.current) {
+ teacherRef.current.value = ''
+ setEmailTeacher('')
+ }
}
- const handleTeacherFileChange = (e) => {
- if (e.target.files.length) {
+ const handleTeacherFileChange = (
+ e: ChangeEvent
+ ): void => {
+ if (e.target.files == null) {
+ setTeacherFile(undefined)
+ } else if (e.target.files.length) {
const inputFile = e.target.files[0]
setTeacherFile(inputFile)
} else {
@@ -224,14 +424,22 @@ export function AddChangeSubjectPage() {
}
}
- const handleUploadTeacher = () => {
+ // This function will upload a file with teachers.
+ const handleUploadTeacher = (): void => {
const reader = new FileReader()
reader.onload = async ({ target }) => {
- const csv = Papa.parse(target.result, {
+ if (target == null) {
+ return
+ }
+ if (typeof target.result != 'string') {
+ return
+ }
+ const csv: ParseResult = Papa.parse(target.result, {
header: true,
})
for (let i = 0; i < csv.data.length; i++) {
+ // This will loop through the csv file and add the teachers to the list.
if (csv.data[i].email != '') {
instance
.get('gebruikers/?email=' + csv.data[i].email)
@@ -241,25 +449,7 @@ export function AddChangeSubjectPage() {
return oldteachers
}
- //This is like this to prevent the same user being in the list twice
- let found = false
-
- const data = res.data[0]
-
- const id = data.user
- if (!data.is_lesgever) {
- return oldteachers
- }
- for (const teacher of oldteachers) {
- if (teacher.user == id) {
- found = true
- }
- }
- if (found) {
- return oldteachers
- } else {
- return [...oldteachers, data]
- }
+ return addUser(true, res.data[0], oldteachers)
})
})
.catch((err) => {
@@ -268,385 +458,410 @@ export function AddChangeSubjectPage() {
}
}
}
+ if (teacherFile != undefined) {
+ reader.readAsText(teacherFile)
+ }
+ }
- reader.readAsText(teacherFile)
+ const addUser = (
+ isLesgever: boolean,
+ userData: User,
+ olduser: User[]
+ ): User[] => {
+ //This is like this to prevent the same user being in the list twice
+ let found = false
+ const id = userData.user
+ if (userData.is_lesgever != isLesgever) {
+ if (userData.is_lesgever) {
+ alert(t('cant_add_teachers_to_student_list'))
+ } else {
+ alert(t('cant_add_students_to_teacher_list'))
+ }
+ return olduser
+ }
+ for (const teacher of olduser) {
+ if (teacher.user == id) {
+ found = true
+ }
+ }
+ if (found) {
+ alert(t('cant_add_users_twice'))
+ return olduser
+ } else {
+ return [...olduser, userData]
+ }
}
- const handleSave = () => {
+ const handleSave = (): void => {
const studentIDs = students.map((student) => student.user)
const teacherIDs = teachers.map((teacher) => teacher.user)
- instance
- .put('vakken/' + vakID + '/', {
- naam: title,
- studenten: studentIDs,
- lesgevers: teacherIDs,
- })
- .catch((err) => {
- console.log(err)
- })
+ if (vakID == undefined) {
+ instance
+ .post('vakken/', {
+ naam: title,
+ studenten: studentIDs,
+ lesgevers: teacherIDs,
+ })
+ .then((res) => {
+ setVakID(res.data.vak_id)
+ navigate(`/course/${res.data.vak_id}`)
+ })
+ .catch((err) => {
+ console.log(err)
+ })
+ } else {
+ instance
+ .put('vakken/' + vakID + '/', {
+ naam: title,
+ studenten: studentIDs,
+ lesgevers: teacherIDs,
+ })
+ .catch((err) => {
+ console.log(err)
+ alert(err.response.data)
+ })
+ navigate(`/course/${vakID}`)
+ }
+ }
+
+ const handleCancel = (): void => {
+ navigate('/')
}
useEffect(() => {
- instance
- .get('vakken/' + vakID)
- .then((res) => {
- setTitle(res.data.naam)
- for (const id of res.data.studenten) {
- instance
- .get('gebruikers/' + id)
- .then((res) => {
- setStudents((oldstudents) => {
- //This is like this to prevent the same user being in the list twice
- let found = false
- const id = res.data.user
- for (const student of oldstudents) {
- if (student.user == id) {
- found = true
+ async function fetchUser() {
+ setUserLoading(true)
+ const userResponse = await instance.get('/gebruikers/me/')
+ if (vakID == undefined) {
+ setTeachers([userResponse.data])
+ }
+ setUser(userResponse.data)
+ setUserLoading(false)
+ }
+ async function fetchData() {
+ setLoading(true)
+ await instance
+ .get('vakken/' + vakID)
+ .then(async (res) => {
+ setTitle(res.data.naam)
+ for (const id of res.data.studenten) {
+ await instance
+ .get('gebruikers/' + id)
+ .then((res) => {
+ setStudents((oldstudents) => {
+ //This is like this to prevent the same user being in the list twice
+ let found = false
+ const id = res.data.user
+ for (const student of oldstudents) {
+ if (student.user == id) {
+ found = true
+ }
}
- }
- if (found) {
- return oldstudents
- } else {
- return [...oldstudents, res.data]
- }
+ if (found) {
+ return oldstudents
+ } else {
+ return [...oldstudents, res.data]
+ }
+ })
})
- })
- .catch((err) => {
- console.log(err)
- })
- }
- for (const id of res.data.lesgevers) {
- instance
- .get('gebruikers/' + id)
- .then((res) => {
- setTeachers((oldteachers) => {
- //This is like this to prevent the same user being in the list twice
- let found = false
- const id = res.data.user
- for (const teacher of oldteachers) {
- if (teacher.user == id) {
- found = true
+ .catch((err) => {
+ console.log(err)
+ })
+ }
+ for (const id of res.data.lesgevers) {
+ await instance
+ .get('gebruikers/' + id)
+ .then((res) => {
+ setTeachers((oldteachers) => {
+ //This is like this to prevent the same user being in the list twice
+ let found = false
+ const id = res.data.user
+ for (const teacher of oldteachers) {
+ if (teacher.user == id) {
+ found = true
+ }
}
- }
- if (found) {
- return oldteachers
- } else {
- return [...oldteachers, res.data]
- }
+ if (found) {
+ return oldteachers
+ } else {
+ return [...oldteachers, res.data]
+ }
+ })
})
- })
- .catch((err) => {
- console.log(err)
- })
- }
- })
- .catch((err) => {
+ .catch((err) => {
+ console.log(err)
+ })
+ }
+ })
+ .catch((err) => {
+ console.log(err)
+ })
+ setLoading(false)
+ }
+ fetchUser().catch((err) => {
+ console.log(err)
+ })
+ if (vakID != undefined) {
+ fetchData().catch((err) => {
console.log(err)
})
+ }
}, [vakID])
return (
<>
-
-
-
-
- {t('save')}
-
-
-
-
- {t('subject_name') + ':'}
-
- setTitle(event.target.value)}
- />
-
-
-
- {t('students') + ':'}
-
- :not(style)': {
- marginBottom: '8px',
- width: '75vw',
- },
- }}
- >
- {students.map((student) => {
- const handleClickOpen = () => {
- setSelectedStudent(student.user)
- setOpenStudent(true)
- }
-
- return (
- <>
-
-
-
-
-
-
-
-
-
- >
- )
- })}
-
-
-
+
+
+ ) : (
+ <>
+ {user?.is_lesgever ? (
+ // Rendering UI for teacher
+ <>
+
+
-
-
- setEmailStudent(event.target.value)
- }
- />
-
- {t('add')}
-
-
-
-
-
-
-
-
-
- {' '}
- {t('delete_student') + '?'}{' '}
-
-
-
- {t('cancel')}
-
-
- {t('delete')}
-
-
-
-
-
-
- {t('teachers') + ':'}
-
- :not(style)': {
- marginBottom: '8px',
- width: '75vw',
- },
- }}
- >
- {teachers.map((teacher) => {
- const handleClickOpen = () => {
- setSelectedTeacher(teacher.user)
- setOpenTeacher(true)
- }
-
- return (
- <>
-
+
+
-
+ {loading ? (
+
-
-
+ setTitle(
+ event.target.value
+ )
+ }
+ sx={{ height: 60 }}
/>
-
+
+
+
+ setCancelConfirmation(
+ true
+ )
+ }
>
-
-
-
-
- >
- )
- })}
-
-
-
+
+
+
+
+ {t('save')}
+
+
+
+
+
+
+
+
+
+
+ {t('students')}
+
+
+
+ {UserList(
+ loading,
+ students,
+ setSelectedStudent,
+ setOpenStudent,
+ handleRemoveStudent
+ )}
+
+
+
+ {UploadPart(
+ studentFile,
+ handleStudentFileChange,
+ setEmailStudent,
+ handleAddStudent,
+ t('upload_students'),
+ studentRef
+ )}
+
+ {DialogWindow(
+ handleCloseStudent,
+ openStudent,
+ handleRemoveStudent,
+ t('delete_student')
+ )}
+
+
+
+
+
+
+
+ {t('teachers')}
+
+
+
+ {UserList(
+ loading,
+ teachers,
+ setSelectedTeacher,
+ setOpenTeacher,
+ handleRemoveTeacher
+ )}
+
+
+
+ {UploadPart(
+ teacherFile,
+ handleTeacherFileChange,
+ setEmailTeacher,
+ handleAddTeacher,
+ t('upload_teachers'),
+ teacherRef
+ )}
+
+ {DialogWindow(
+ handleCloseTeacher,
+ openTeacher,
+ handleRemoveTeacher,
+ t('delete_teacher')
+ )}
+
+
+
+
+ {/* Confirmation popup for saving course */}
+
-
-
- setEmailTeacher(event.target.value)
- }
- />
-
- {t('add')}
-
-
-
-
-
-
-
-
-
- {' '}
- {t('delete_teacher') + '?'}{' '}
-
-
-
- {t('cancel')}
-
-
- {t('delete')}
-
-
-
-
-
-
+ {/* Confirmation popup for canceling changes*/}
+
+
+ >
+ ) : (
+ navigate('*')
+ )}
+ >
+ )}
>
)
}
diff --git a/frontend/frontend/src/pages/subjectsPage/AssignmentListItemSubjectsPage.tsx b/frontend/frontend/src/pages/subjectsPage/AssignmentListItemSubjectsPage.tsx
index ea550c8d6..95035eeae 100644
--- a/frontend/frontend/src/pages/subjectsPage/AssignmentListItemSubjectsPage.tsx
+++ b/frontend/frontend/src/pages/subjectsPage/AssignmentListItemSubjectsPage.tsx
@@ -1,9 +1,10 @@
import {
- Divider,
IconButton,
ListItem,
ListItemButton,
ListItemText,
+ Tooltip,
+ Typography,
} from '@mui/material'
import { useNavigate } from 'react-router-dom'
import { t } from 'i18next'
@@ -11,15 +12,18 @@ import VisibilityOutlinedIcon from '@mui/icons-material/VisibilityOutlined'
import VisibilityOffOutlinedIcon from '@mui/icons-material/VisibilityOffOutlined'
import ArchiveOutlinedIcon from '@mui/icons-material/ArchiveOutlined'
import DeleteOutlinedIcon from '@mui/icons-material/DeleteOutlined'
-import { useState } from 'react'
+import React, { useState } from 'react'
import dayjs, { Dayjs } from 'dayjs'
import { Score } from '../../components/SubmissionListItemTeacherPage.tsx'
+import { EvenlySpacedRow } from '../../components/CustomComponents.tsx'
+import { Submission } from '../submissionPage/SubmissionPage.tsx'
/**
* This component is used to display a single assignment in the list of assignments.
* @param projectName: string - the name of the project
* @param dueDate: Date - the due date of the project
* @param submissions: number - number of submissions for the project
+ * @param lastSubmission: Submission - last submission for the project
* @param score: number - assigned score on the project
* @param isStudent: boolean - wether the user is a student or a teacher
* @param archived: boolean - wether the assignment is archived
@@ -31,9 +35,10 @@ import { Score } from '../../components/SubmissionListItemTeacherPage.tsx'
interface AssignmentListItemSubjectsPageProps {
projectName: string
- dueDate: Dayjs
+ dueDate: Dayjs | undefined
submissions: number
- score: Score
+ lastSubmission?: Submission
+ score: Score | undefined
maxScore: number
isStudent: boolean
archived: boolean
@@ -49,6 +54,7 @@ export function AssignmentListItemSubjectsPage({
projectName,
dueDate,
submissions,
+ lastSubmission,
score,
maxScore,
isStudent,
@@ -73,94 +79,121 @@ export function AssignmentListItemSubjectsPage({
return (
<>
{isStudent ? (
- <>
-
-
- 0
- ? submissions > 1
- ? submissions +
- ' ' +
- t('submissions')
- : submissions +
- ' ' +
- t('submission')
- : t('no_submissions')
- }
- />
- {submissions > 0 ? (
+ ,
+
+
+ {dueDate
+ ? dayjs(dueDate).format(
+ 'DD/MM/YYYY HH:mm'
+ )
+ : t('no_deadline')}
+
+ ,
0
+ ? submissions > 1
+ ? submissions +
+ ' ' +
+ t('submissions')
+ : submissions +
+ ' ' +
+ t('submission')
+ : t('no_submissions')
}
- />
- ) : (
-
- )}
- >
+ data-cy="submissions"
+ />,
+ <>
+ {submissions > 0 ? (
+
+ ) : (
+
+ )}
+ >,
+ ]}
+ />
) : (
<>
{/* In case of the user being the teacher: */}
-
-
- ,
+
+
+ {dueDate
+ ? dayjs(dueDate).format(
+ 'DD/MM/YYYY HH:mm'
+ )
+ : t('no_deadline')}
+
+ ,
+ ,
+ ]}
/>
>
)}
-
>
)
}
@@ -182,7 +215,10 @@ function ButtonActions({
}: ButtonActionsProps) {
const [visible, setVisible] = useState(startVisible)
- const handleIconClick = (e, icon: string) => {
+ const handleIconClick = (
+ e: React.MouseEvent,
+ icon: string
+ ) => {
e.stopPropagation()
console.log(icon + ' clicked')
switch (icon) {
@@ -203,39 +239,50 @@ function ButtonActions({
return (
- {visible ? (
- handleIconClick(e, 'visible')}
- edge="end"
- aria-label="visible"
- >
-
-
- ) : (
- handleIconClick(e, 'visible')}
- edge="end"
- aria-label="not-visible"
- >
-
-
- )}
+
+ {visible ? (
+ handleIconClick(e, 'visible')}
+ edge="end"
+ aria-label="visible"
+ >
+
+
+ ) : (
+ handleIconClick(e, 'visible')}
+ edge="end"
+ aria-label="not-visible"
+ >
+
+
+ )}
+
+
{!archived && (
+
+ handleIconClick(e, 'archive')}
+ edge="end"
+ aria-label="archive"
+ >
+
+
+
+ )}
+
handleIconClick(e, 'archive')}
+ id="delete"
+ onClick={(e) => handleIconClick(e, 'delete')}
edge="end"
- aria-label="archive"
+ aria-label="delete"
>
-
+
- )}
- handleIconClick(e, 'delete')}
- edge="end"
- aria-label="delete"
- >
-
-
+
)
}
diff --git a/frontend/frontend/src/pages/subjectsPage/ProjectsView.tsx b/frontend/frontend/src/pages/subjectsPage/ProjectsView.tsx
index 71f1a0842..4675535e7 100644
--- a/frontend/frontend/src/pages/subjectsPage/ProjectsView.tsx
+++ b/frontend/frontend/src/pages/subjectsPage/ProjectsView.tsx
@@ -1,9 +1,14 @@
-import { Box, Typography } from '@mui/material'
+import {
+ Card,
+ Divider,
+ EvenlySpacedRow,
+} from '../../components/CustomComponents.tsx'
+import { Box, Skeleton, Typography } from '@mui/material'
import List from '@mui/material/List'
import { t } from 'i18next'
import { AssignmentListItemSubjectsPage } from './AssignmentListItemSubjectsPage.tsx'
import instance from '../../axiosConfig'
-import { useState, useEffect } from 'react'
+import { useEffect, useState } from 'react'
import { Project } from '../scoresPage/ProjectScoresPage.tsx'
import { Group } from '../groupsPage/GroupsPage.tsx'
import { Submission } from '../submissionPage/SubmissionPage.tsx'
@@ -15,12 +20,13 @@ interface ProjectStudent {
assignment: Project
group?: Group
lastSubmission?: Submission
- submissions?: Submission[]
+ submissions?: number
score?: Score
}
interface ProjectsViewProps {
gebruiker: User
+ showAllAssignments: boolean
archived: boolean
assignments: Project[]
deleteAssignment: (index: number) => void
@@ -36,6 +42,7 @@ interface ProjectsViewProps {
*/
export function ProjectsView({
gebruiker,
+ showAllAssignments,
archived,
assignments,
deleteAssignment,
@@ -44,8 +51,11 @@ export function ProjectsView({
courseId,
}: ProjectsViewProps) {
const [projects, setProjects] = useState([])
+ const [lastSubmission, setLastSubmission] = useState()
+
+ // state to keep track of the loading state
+ const [loading, setLoading] = useState(true)
-
// useEffect hook to periodically fetch all data
useEffect(() => {
async function fetchGroup(
@@ -81,10 +91,11 @@ export function ProjectsView({
const submissionsResponse = await instance.get(
`/indieningen/?groep=${projectstudent.group.groep_id?.toString()}&project=${projectstudent.assignment.project_id.toString()}`
)
- const lastSubmission =
- submissionsResponse.data[
- submissionsResponse.data.length - 1
- ]
+ const lastSubmission = submissionsResponse.data.sort(
+ (a: Submission, b: Submission) =>
+ dayjs(a.tijdstip).isAfter(dayjs(b.tijdstip)) ? 1 : -1
+ )[0]
+ setLastSubmission(lastSubmission)
return {
...projectstudent,
lastSubmission: lastSubmission,
@@ -114,9 +125,10 @@ export function ProjectsView({
return projectstudent
}
}
-
async function fetchData() {
try {
+ //TODO fix correct waiting for all data to be fetched
+ setLoading(true)
const groupPromises = assignments.map((assignment) =>
fetchGroup(assignment)
)
@@ -132,9 +144,34 @@ export function ProjectsView({
)
const scoreArray = await Promise.all(scorePromises)
- setProjects(scoreArray)
+ scoreArray.sort((a, b) => {
+ return dayjs(a.assignment.deadline).isAfter(
+ dayjs(b.assignment.deadline)
+ )
+ ? 1
+ : -1
+ })
+
+ const pastAndTodayDeadlines: ProjectStudent[] = []
+ const futureDeadlines: ProjectStudent[] = []
+ const today = dayjs()
+ // Separate the items based on their deadline
+ scoreArray.forEach((item) => {
+ if (
+ dayjs(item.assignment.deadline).isBefore() ||
+ dayjs(item.assignment.deadline).isSame(today)
+ ) {
+ pastAndTodayDeadlines.push(item)
+ } else {
+ futureDeadlines.push(item)
+ }
+ })
+
+ setProjects(futureDeadlines.concat(pastAndTodayDeadlines))
} catch (e) {
console.error('Error fetching all data:', e)
+ } finally {
+ setLoading(false)
}
}
fetchData().catch((err) => console.error(err))
@@ -142,104 +179,181 @@ export function ProjectsView({
return (
<>
-
- {!gebruiker.is_lesgever ? (
- <>
- {/* Show the UI from the perspective of a student. */}
- Project
- Deadline
-
- {t('submissions')}
-
- Score
- >
- ) : (
- <>
- {/* Show the UI from the perspective of a teacher. */}
- Project
- Deadline
- {t('edit')}
- >
- )}
-
-
-
+
+
+ {!gebruiker.is_lesgever ? (
+ <>
+ {/* Show the UI from the perspective of a student. */}
+
+ Project
+ ,
+
+ Deadline
+ ,
+
+ {t('submissions')}
+ ,
+
+ Score
+ ,
+ ]}
+ />
+ >
+ ) : (
+ <>
+ {/* Show the UI from the perspective of a teacher. */}
+
+ Project
+ ,
+
+ Deadline
+ ,
+
+ {t('edit')}
+ ,
+ ]}
+ />
+ >
+ )}
+
+
{/* The list below will display the projects with their information */}
-
- {projects
- .map((project, index) => ({
- ...project,
- index,
- }))
- .filter(
- (project) =>
- project.assignment.gearchiveerd ==
- archived
- )
- .filter(
- (project) =>
- project.assignment.zichtbaar ||
- gebruiker.is_lesgever
- )
- .map((project) => (
-
- deleteAssignment(project.index)
- }
- archiveEvent={() =>
- archiveAssignment(project.index)
- }
- visibilityEvent={() =>
- changeVisibilityAssignment(
- project.index
- )
- }
- courseId={courseId}
- assignmentId={
- project.assignment.project_id
- }
+
+ {loading ? (
+ [...Array(3).keys()].map((index) => (
+
- ))}
+ ))
+ ) : (
+ <>
+ {projects
+ .map((project, index) => ({
+ ...project,
+ index,
+ }))
+ .filter(
+ (project) =>
+ project.assignment
+ .gearchiveerd == archived ||
+ showAllAssignments
+ )
+ .filter(
+ (project) =>
+ project.assignment.zichtbaar ||
+ gebruiker.is_lesgever
+ )
+ .map((project) => (
+ <>
+
+
+ deleteAssignment(
+ project.assignment
+ .project_id
+ )
+ }
+ archiveEvent={() =>
+ archiveAssignment(
+ project.index
+ )
+ }
+ visibilityEvent={() =>
+ changeVisibilityAssignment(
+ project.index
+ )
+ }
+ courseId={courseId}
+ assignmentId={project.assignment.project_id.toString()}
+ />
+ >
+ ))}
+ >
+ )}
-
+
>
)
}
diff --git a/frontend/frontend/src/pages/subjectsPage/StudentPopUp.tsx b/frontend/frontend/src/pages/subjectsPage/StudentPopUp.tsx
new file mode 100644
index 000000000..71cca1e11
--- /dev/null
+++ b/frontend/frontend/src/pages/subjectsPage/StudentPopUp.tsx
@@ -0,0 +1,104 @@
+import { User } from './AddChangeSubjectPage'
+import * as React from 'react'
+import {
+ IconButton,
+ List,
+ ListItem,
+ ListItemText,
+ Typography,
+} from '@mui/material'
+import { SecondaryButton } from '../../components/CustomComponents'
+import Dialog from '@mui/material/Dialog'
+import DialogTitle from '@mui/material/DialogTitle'
+import CloseIcon from '@mui/icons-material/Close'
+import DialogContent from '@mui/material/DialogContent'
+import { t } from 'i18next'
+
+interface StudentPopUpProps {
+ students: User[]
+ text: string
+ noGroup: boolean
+}
+
+export default function StudentPopUp({
+ students,
+ text,
+ noGroup,
+}: StudentPopUpProps) {
+ const [open, setOpen] = React.useState(false)
+
+ const handleClose = () => {
+ setOpen(false)
+ }
+
+ return (
+ <>
+ {/* Students Button */}
+ {
+ setOpen(true)
+ }}
+ data-cy="secondaryButton"
+ >
+ {t(text)}
+
+ {/* Students Dialog */}
+
+
+ {t(text)}
+ {/* Close Button */}
+
+
+
+
+
+ {noGroup ? (
+ <>
+
+ {t('noGroup')}
+
+
+ {t('contactTeacher')}
+
+ >
+ ) : (
+ <>
+ {/* List of Students */}
+ {students.length > 0 ? (
+
+ {students.map((student) => (
+
+
+
+ ))}
+
+ ) : (
+
+ {t('loading') + ' ' + t('students') + '...'}
+
+ )}
+ >
+ )}
+
+
+ >
+ )
+}
diff --git a/frontend/frontend/src/pages/subjectsPage/SubjectsPage.tsx b/frontend/frontend/src/pages/subjectsPage/SubjectsPage.tsx
index 748e01534..4deb7f5cc 100644
--- a/frontend/frontend/src/pages/subjectsPage/SubjectsPage.tsx
+++ b/frontend/frontend/src/pages/subjectsPage/SubjectsPage.tsx
@@ -1,15 +1,25 @@
import { Header } from '../../components/Header'
-import { Box, IconButton, Stack } from '@mui/material'
+import {
+ Box,
+ CircularProgress,
+ Grid,
+ IconButton,
+ Stack,
+ Tooltip,
+} from '@mui/material'
import TabSwitcher from '../../components/TabSwitcher.tsx'
import { ProjectsView } from './ProjectsView.tsx'
import { useNavigate, useParams } from 'react-router-dom'
-import AddCircleIcon from '@mui/icons-material/AddCircle'
import WarningPopup from '../../components/WarningPopup.tsx'
import { t } from 'i18next'
import instance from '../../axiosConfig.ts'
import { useEffect, useState } from 'react'
import ErrorPage from '../ErrorPage.tsx'
import { Course } from '../mainPage/MainPage.tsx'
+import { CopyToClipboard } from '../../components/CopyToClipboard.tsx'
+import { User } from './AddChangeSubjectPage'
+import StudentPopUp from './StudentPopUp.tsx'
+import AddCircleIcon from '@mui/icons-material/AddCircle'
interface Project {
project_id: number
@@ -32,8 +42,8 @@ interface Project {
* Any other UI on the course's page is also handled in this view
*/
export function SubjectsPage() {
- let { courseId } = useParams()
- courseId = String(courseId)
+ const { courseId, accept_invite } = useParams()
+ const courseID = String(courseId)
const navigate = useNavigate()
@@ -46,52 +56,128 @@ export function SubjectsPage() {
last_name: '',
email: '',
})
+ const [students, setStudents] = useState([])
const [fetchError, setFetchError] = useState(false)
+ //variable for invitation link popup close
+ const [inviteOpen, setInviteOpen] = useState(false)
+
+ async function confirmJoinCourse() {
+ await instance.get(`/vakken/${courseID}/accept_invite/`)
+ setInviteOpen(false)
+ navigate(`/course/${courseID}`)
+ }
+
+ // State for loading the page
+ const [loading, setLoading] = useState(true)
+ const [userLoading, setUserLoading] = useState(true)
+ const [studentsLoading, setStudentsLoading] = useState(true)
+
useEffect(() => {
// Get the data for this course.
+ async function fetchUser() {
+ try {
+ setUserLoading(true)
+ const userResponse = await instance.get('/gebruikers/me/')
+ setUser(userResponse.data)
+ } catch (error) {
+ console.error('Error fetching data:', error)
+ setFetchError(true)
+ } finally {
+ setUserLoading(false)
+ }
+ }
async function fetchData() {
try {
+ setLoading(true)
+ setUserLoading(true)
+ const userResponse = await instance.get('/gebruikers/me/')
+ setUser(userResponse.data)
+ setUserLoading(false)
+
const courseResponse = await instance.get(
- `/vakken/${courseId}/`
+ `/vakken/${courseID}/`
)
const assignmentsResponse = await instance.get(
- `/projecten/?vak=${courseId}`
+ `/projecten/?vak=${courseID}`
)
setCourse(courseResponse.data)
setAssignments(assignmentsResponse.data)
-
- const userResponse = await instance.get('/gebruikers/me/')
- setUser(userResponse.data)
} catch (error) {
console.error('Error fetching data:', error)
setFetchError(true)
+ } finally {
+ setUserLoading(false)
+ setLoading(false)
+ }
+ }
+ // Fetch user first
+ fetchUser().catch((error) =>
+ console.error('Error fetching data:', error)
+ )
+
+ // check if the url is a special url for accepting an invitation
+ if (
+ accept_invite !== undefined &&
+ accept_invite === 'accept_invitation'
+ ) {
+ if (user.is_lesgever) {
+ navigate(`/course/${courseID}`)
+ } else {
+ setInviteOpen(true)
}
}
+
+ // Fetch the course and assignments
fetchData().catch((error) =>
console.error('Error fetching data:', error)
)
- }, [courseId])
+ }, [accept_invite, courseID, navigate, user.is_lesgever])
+
+ useEffect(() => {
+ async function fetchStudents() {
+ setStudentsLoading(true)
+ const temp_students = []
+ for (const s of course?.studenten || []) {
+ try {
+ const userResponse = await instance.get(`/gebruikers/${s}/`)
+ temp_students.push(userResponse.data)
+ } catch (error) {
+ console.error('Error fetching student data:', error)
+ setFetchError(true)
+ }
+ }
+ // Update the state with the fetched data
+ setStudents(temp_students)
+ setStudentsLoading(false)
+ }
+
+ // Fetch students
+ fetchStudents().catch((error) =>
+ console.error('Error fetching students data:', error)
+ )
+ }, [course])
const addProject = () => {
console.log('add project')
- navigate(`/course/${courseId}/assignment/edit/`)
+ navigate(`/course/${courseID}/assignment/edit/`)
}
const [openDeletePopup, setOpenDeletePopup] = useState(false)
- const [deleteIndex, setDeleteIndex] = useState(0)
+ const [deleteId, setDeleteId] = useState(0)
const [openArchivePopup, setOpenArchivePopup] = useState(false)
const [archiveIndex, setArchiveIndex] = useState(0)
- const deleteAssignment = (index: number) => {
- setDeleteIndex(index)
+ const deleteAssignment = (id: number) => {
+ setDeleteId(id)
setOpenDeletePopup(true)
}
const doDelete = async () => {
- setAssignments(assignments.filter((_, i) => i !== deleteIndex))
+ setAssignments(
+ assignments.filter((project) => project.project_id !== deleteId)
+ )
try {
- const deletedAssignment = assignments[deleteIndex]
- await instance.delete(`/projecten/${deletedAssignment.project_id}/`)
+ await instance.delete(`/projecten/${deleteId}/`)
} catch (error) {
console.error('Error deleting data:', error)
}
@@ -139,131 +225,278 @@ export function SubjectsPage() {
return (
<>
- {user.is_lesgever ? (
+ {userLoading ? (
+
+
+
+ ) : (
<>
- {/* The code below shows the page from the perspecitve of the teacher. */}
-
-
-
- {/* Give the student the option to select current or archived projects. */}
- ,
- ,
- ]}
- />
-
-
-
+ {/* The code below shows the page from the perspecitve of the teacher. */}
+
-
-
-
- setOpenDeletePopup(false)}
- doAction={doDelete}
- />
- setOpenArchivePopup(false)}
- doAction={doArchive}
- />
-
- >
- ) : (
- <>
- {/* The colon is the "else" of the ternary operator.
+
+ {/* Give the student the option to select current or archived projects. */}
+ {course.gearchiveerd ? (
+
+ ) : (
+ undefined}
+ titles={[
+ 'current_projects',
+ 'archived',
+ ]}
+ nodes={[
+ ,
+ ,
+ ]}
+ />
+ )}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ setOpenDeletePopup(false)
+ }
+ doAction={doDelete}
+ />
+
+ setOpenArchivePopup(false)
+ }
+ doAction={doArchive}
+ />
+
+ >
+ ) : (
+ <>
+ {/* The colon is the "else" of the ternary operator.
This means that all the code below is applicable to the student's view. */}
-
-
-
- undefined}
- archiveAssignment={() => undefined}
- changeVisibilityAssignment={() =>
- undefined
- }
- courseId={courseId}
- />,
- undefined}
- archiveAssignment={() => undefined}
- changeVisibilityAssignment={() =>
- undefined
+
+
+
+ {course.gearchiveerd ? (
+ undefined}
+ archiveAssignment={() => undefined}
+ changeVisibilityAssignment={() =>
+ undefined
+ }
+ courseId={courseID}
+ />
+ ) : (
+ undefined}
+ titles={[
+ 'current_projects',
+ 'archived',
+ ]}
+ nodes={[
+
+ undefined
+ }
+ archiveAssignment={() =>
+ undefined
+ }
+ changeVisibilityAssignment={() =>
+ undefined
+ }
+ courseId={courseID}
+ />,
+
+ undefined
+ }
+ archiveAssignment={() =>
+ undefined
+ }
+ changeVisibilityAssignment={() =>
+ undefined
+ }
+ courseId={courseID}
+ />,
+ ]}
+ />
+ )}
+ navigate('/')}
+ doAction={confirmJoinCourse}
+ />
+
+
+ ,
- ]}
- />
-
-
+ text="students"
+ noGroup={false}
+ >
+
+
+ >
+ )}
>
)}
>
diff --git a/frontend/frontend/src/pages/submissionPage/SubmissionPage.tsx b/frontend/frontend/src/pages/submissionPage/SubmissionPage.tsx
index 69ab05494..054739d46 100644
--- a/frontend/frontend/src/pages/submissionPage/SubmissionPage.tsx
+++ b/frontend/frontend/src/pages/submissionPage/SubmissionPage.tsx
@@ -2,23 +2,24 @@ import { Header } from '../../components/Header.tsx'
import { useParams } from 'react-router-dom'
import { t } from 'i18next'
import { useEffect, useState } from 'react'
+import { Button, Card, Divider } from '../../components/CustomComponents.tsx'
+import StudentPopUp from '../subjectsPage/StudentPopUp.tsx'
import {
Box,
- Button,
- Card,
CircularProgress,
- Divider,
ListItem,
- Paper,
+ Skeleton,
+ Stack,
Typography,
} from '@mui/material'
import dayjs, { Dayjs } from 'dayjs'
import DownloadIcon from '@mui/icons-material/Download'
import List from '@mui/material/List'
-import Grid2 from '@mui/material/Unstable_Grid2'
+import Grid2 from '@mui/material/Unstable_Grid2/Grid2'
import instance from '../../axiosConfig.ts'
import { getAssignment } from '../addChangeAssignmentPage/AddChangeAssignmentPage.tsx'
import ErrorPage from '../ErrorPage.tsx'
+import { User } from '../subjectsPage/AddChangeSubjectPage.tsx'
/**
* Page for viewing a specific submission
@@ -28,7 +29,7 @@ import ErrorPage from '../ErrorPage.tsx'
*/
// Define an enum for submission status
-enum SubmissionStatus {
+export enum SubmissionStatus {
FAIL = -1,
PENDING = 0,
PASSED = 1,
@@ -38,14 +39,11 @@ enum SubmissionStatus {
export interface Submission {
indiening_id: number
groep: number
+ bestand: File
tijdstip: Dayjs
status: SubmissionStatus
result: string
- indiening_bestanden: {
- indiening_bestand_id: number
- bestand: string
- indiening: number
- }[]
+ filename?: string
}
// Define the structure of a restriction
@@ -68,19 +66,31 @@ export function SubmissionPage() {
const [project, setProject] = useState()
const [restrictions, setRestrictions] = useState([])
const [fetchError, setFetchError] = useState(false)
+ const [students, setStudents] = useState([])
+ const [user, setUser] = useState({
+ user: 0,
+ is_lesgever: false,
+ first_name: '',
+ last_name: '',
+ email: '',
+ })
+
+ //state to manage proper loading
+ const [loading, setLoading] = useState(true)
+ const [studentsLoading, setStudentsLoading] = useState(true)
// Function to download an artifact
- const downloadArtifact = (artifact: number) => {
- //TODO: artifacts are not yet implemented in the backend
+ const downloadArtifacts = () => {
+ //TODO: test when changes are pulled to the backend
instance
- .get(`/api/submissions/${assignmentId}/${artifact}`, {
+ .get(`/indieningen/${submissionId}/artefacten/`, {
responseType: 'blob',
})
.then((res) => {
const url = window.URL.createObjectURL(res.data)
const a = document.createElement('a')
a.href = url
- a.download = artifact.toString()
+ a.download = 'artifacts.zip'
document.body.appendChild(a)
a.click()
a.remove()
@@ -89,63 +99,86 @@ export function SubmissionPage() {
// Function to download the submission
const downloadSubmission = () => {
- instance
- .get(`/indieningen/${submissionId}/indiening_bestanden/`, {
- responseType: 'blob',
- })
- .then((res) => {
- let filename = 'indiening.zip'
- if (submission) {
- filename =
- submission.indiening_bestanden[0].bestand.replace(
- /^.*[\\/]/,
- ''
- )
- }
- const blob = new Blob([res.data], {
- type: res.headers['content-type'],
- })
- const file: File = new File([blob], filename, {
- type: res.headers['content-type'],
- })
- const url = window.URL.createObjectURL(file)
- const a = document.createElement('a')
- a.href = url
- a.download = filename
- document.body.appendChild(a)
- a.click()
- a.remove()
- })
- .catch((err) => {
- console.error(err)
- setFetchError(true)
- })
+ if (submission?.bestand) {
+ const url = window.URL.createObjectURL(submission?.bestand)
+ const a = document.createElement('a')
+ a.href = url
+ a.download = submission.filename
+ ? submission.filename
+ : 'opgave.zip'
+ document.body.appendChild(a)
+ a.click()
+ a.remove()
+ }
}
useEffect(() => {
//get the project data
- instance
- .get(`/projecten/${assignmentId}/`)
- .then((res) => {
+
+ const fetchdata = async () => {
+ setLoading(true)
+ try {
+ const res = await instance.get(
+ `/projecten/${assignmentId}/`
+ )
setProject(res.data)
- })
- .catch((err) => {
- console.error(err)
- setFetchError(true)
- })
- //get the restrictions for the submission
- //TODO: artifacts are not yet implemented in the backend
- instance
- .get(`/restricties/?project=${assignmentId}`)
- .then((res) => {
- setRestrictions(res.data)
- })
- .catch((err) => {
+ //Get the restrictions for the submission
+ const restrictions = await instance.get(
+ `/restricties/?project=${assignmentId}`
+ )
+ setRestrictions(restrictions.data)
+
+ const submissionResponse = await instance.get(
+ `indieningen/${submissionId}/`
+ )
+ //Get the submission file
+ const newSubmission: Submission = submissionResponse.data
+
+ if (
+ newSubmission.status !== SubmissionStatus.PENDING &&
+ newSubmission.result !== 'No tests: OK'
+ ) {
+ const match = newSubmission.result.match(/Testing.*/)
+ newSubmission.result = match
+ ? match[0]
+ : newSubmission.result
+ }
+ newSubmission.filename =
+ submissionResponse.data.bestand.replace(/^.*[\\/]/, '')
+ newSubmission.bestand = await instance
+ .get(`/indieningen/${submissionId}/indiening_bestand/`, {
+ responseType: 'blob',
+ })
+ .then((res) => {
+ let filename = 'indiening.zip'
+ if (newSubmission.filename) {
+ filename = newSubmission.filename
+ }
+ const blob = new Blob([res.data], {
+ type: res.headers['content-type'],
+ })
+ const file: File = new File([blob], filename, {
+ type: res.headers['content-type'],
+ })
+ return file
+ })
+ setSubmission(newSubmission)
+ // Get the current user
+ const userResponse = await instance.get('/gebruikers/me/')
+ setUser(userResponse.data)
+ } catch (err) {
console.error(err)
setFetchError(true)
- })
- }, [assignmentId])
+ } finally {
+ setLoading(false)
+ }
+ }
+ fetchdata().catch((err) => {
+ console.error(err)
+ setFetchError(true)
+ })
+ }, [assignmentId, submissionId])
useEffect(() => {
const intervalId = setInterval(async () => {
@@ -158,6 +191,39 @@ export function SubmissionPage() {
console.log('Data:', response.data)
} else {
console.log('Status is not 0, stopping requests.')
+ const submission = response.data
+ if (
+ submission.status !== SubmissionStatus.PENDING &&
+ submission.result !== 'No tests: OK'
+ ) {
+ const match = submission.result.match(/Testing.*/)
+ submission.result = match ? match[0] : submission.result
+ }
+ submission.filename = submission.bestand.replace(
+ /^.*[\\/]/,
+ ''
+ )
+ submission.bestand = await instance
+ .get(
+ `/indieningen/${submissionId}/indiening_bestand/`,
+ {
+ responseType: 'blob',
+ }
+ )
+ .then((res) => {
+ let filename = 'indiening.zip'
+ if (submission.filename) {
+ filename = submission.filename
+ }
+ const blob = new Blob([res.data], {
+ type: res.headers['content-type'],
+ })
+ const file: File = new File([blob], filename, {
+ type: res.headers['content-type'],
+ })
+ return file
+ })
+ setSubmission(submission)
clearInterval(intervalId)
}
} catch (err) {
@@ -170,6 +236,34 @@ export function SubmissionPage() {
return () => clearInterval(intervalId)
}, [submissionId])
+ useEffect(() => {
+ async function fetchStudents() {
+ setStudentsLoading(true)
+ const groupId = submission?.groep
+ const groupResponse = await instance.get(`groepen/${groupId}`)
+ const temp_students = []
+ for (const s of groupResponse.data.studenten || []) {
+ try {
+ const userResponse = await instance.get(`/gebruikers/${s}/`)
+ temp_students.push(userResponse.data)
+ } catch (error) {
+ console.error('Error fetching student data:', error)
+ setFetchError(true)
+ }
+ }
+ // Update the state with the fetched data
+ setStudents(temp_students)
+ setStudentsLoading(false)
+ }
+
+ // Fetch students
+ if (submission) {
+ fetchStudents().catch((error) =>
+ console.error('Error fetching students data:', error)
+ )
+ }
+ }, [submission])
+
if (fetchError) {
return
}
@@ -180,7 +274,9 @@ export function SubmissionPage() {
-
-
- Deadline:
-
-
- {project?.deadline
- ? dayjs(project.deadline).format(
- 'DD/MM/YYYY HH:MM'
- )
- : 'error'}
-
-
-
+
+ {t('assignment')}
+
+
+ {project?.beschrijving}
+
+
+
+
-
+ {user.first_name + ' ' + user.last_name}
+
+ ) : (
+
+ )}
+
+ {project?.deadline && (
+
- {t('assignment')}
-
+
+ Deadline
+ {project?.deadline
+ ? dayjs(project.deadline).format(
+ 'DD/MM/YYYY HH:mm'
+ )
+ : 'error'}
+
+
+ )}
+ {project?.extra_deadline && (
- {project?.beschrijving}
+
+ Extra Deadline
+ {project?.extra_deadline
+ ? dayjs(project.extra_deadline).format(
+ 'DD/MM/YYYY HH:mm'
+ )
+ : 'error'}
+
-
+ )}
{t('filename')}
}
onClick={downloadSubmission}
+ data-cy="downloadSubmissionButton"
>
- {submission
- ? submission.indiening_bestanden[0].bestand.replace(
- /^.*[\\/]/,
- ''
- )
- : 'error'}
+ {loading ? (
+
+ ) : (
+ <>
+ {submission ? submission.filename : 'error'}
+ >
+ )}
-
+
{t('restrictions')}
- {restrictions.length > 0 ? (
- restrictions.map((restriction, index) => {
- return (
-
-
-
- {restriction.script}
-
-
- {
- restriction.restrictie_id
- }
-
-
- {restriction.moet_slagen
- ? 'Moet slagen'
- : 'Mag falen'}
-
- {restriction.artifact && (
-
- downloadArtifact(
- restriction.artifact
- ? restriction.artifact
- : 0
- )
- }
- startIcon={
-
- }
- >
- Download artifact
-
- )}
-
-
-
- )
- })
+ {loading ? (
+ [...Array(3).keys()].map((index) => (
+
+ ))
) : (
-
-
- {t('no_restrictions')}
-
-
+ <>
+ {restrictions.length > 0 ? (
+ restrictions.map(
+ (restriction, index) => {
+ return (
+
+
+
+ {restriction.script.replace(
+ /^.*[\\/]/,
+ ''
+ )}
+
+
+ {
+ restriction.restrictie_id
+ }
+
+
+ {restriction.moet_slagen
+ ? t(
+ 'must_pass'
+ )
+ : t(
+ 'may_fail'
+ )}
+
+ {restriction.artifact && (
+
+ }
+ >
+ Download
+ artifact
+
+ )}
+
+
+
+ )
+ }
+ )
+ ) : (
+
+
+ {t('no_restrictions')}
+
+
+ )}
+ >
)}
-
+
{t('status') + ':'}
-
- {submission?.status === SubmissionStatus.PENDING
- ? t('pending')
- : submission?.status ===
+ {loading ? (
+
+ ) : (
+
+ ? 'green'
+ : 'red'
+ }
+ >
+ {submission?.status ===
+ SubmissionStatus.PENDING
+ ? t('pending')
+ : submission?.status ===
+ SubmissionStatus.PASSED
+ ? t('passed')
+ : t('failed')}
+
+ )}
-
+
{t('result')}
diff --git a/frontend/frontend/src/routes.tsx b/frontend/frontend/src/routes.tsx
index 1bc1bfb83..675e925be 100644
--- a/frontend/frontend/src/routes.tsx
+++ b/frontend/frontend/src/routes.tsx
@@ -5,9 +5,11 @@ import { ProjectScoresPage } from './pages/scoresPage/ProjectScoresPage.tsx'
import { AddChangeAssignmentPage } from './pages/addChangeAssignmentPage/AddChangeAssignmentPage.tsx'
import { AddChangeSubjectPage } from './pages/subjectsPage/AddChangeSubjectPage.tsx'
import ErrorPage from './pages/ErrorPage.tsx'
-import { MainPage } from './pages/mainPage/MainPage.tsx'
+import FourOFourPage from "./pages/FourOFourPage.tsx";
+import MainPage from './pages/mainPage/MainPage.tsx'
import { GroupsPage } from './pages/groupsPage/GroupsPage.tsx'
import { AssignmentPage } from './pages/assignmentPage/AssignmentPage.tsx'
+import { ChooseGroup } from './pages/groupsPage/ChooseGroup.tsx'
//TODO: add change/add course page when implemented
const router = createBrowserRouter([
@@ -17,7 +19,7 @@ const router = createBrowserRouter([
errorElement: ,
},
{
- path: '/course/:courseId',
+ path: '/course/:courseId/:accept_invite?',
element: ,
errorElement: ,
},
@@ -52,9 +54,23 @@ const router = createBrowserRouter([
errorElement: ,
},
{
- path: '*',
+ path: '/course/:courseId/assignment/:assignmentId/groups/choose',
+ element: ,
+ errorElement: ,
+ },
+ {
+ path: '/course/new',
+ element: ,
+ errorElement: ,
+ },
+ {
+ path: '/error',
element: ,
},
+ {
+ path: '*',
+ element: ,
+ },
])
export default router
diff --git a/frontend/frontend/tsconfig.json b/frontend/frontend/tsconfig.json
index 30d6ff14f..313168cd2 100644
--- a/frontend/frontend/tsconfig.json
+++ b/frontend/frontend/tsconfig.json
@@ -20,6 +20,6 @@
"noUnusedParameters": true,
"noFallthroughCasesInSwitch": true
},
- "include": ["src"],
+ "include": ["src", "authSecrets.ts"],
"references": [{ "path": "./tsconfig.node.json" }]
}
diff --git a/frontend/frontend/vite.config.ts b/frontend/frontend/vite.config.ts
index 7b80c7e3b..1a26ac5a9 100644
--- a/frontend/frontend/vite.config.ts
+++ b/frontend/frontend/vite.config.ts
@@ -8,7 +8,14 @@ export default defineConfig({
'@emotion/react',
'@emotion/styled',
'@mui/material/Tooltip', // Include other MUI components as needed
- '@mui/material/Grid',
+ ],
+ exclude: [
+ 'chunk-LTJERZ23.js?v=fff2b904',
+ 'chunk-OLZKTZWI.js?v=fff2b904',
+ 'chunk-6ZDRAOHK.js?v=fff2b904',
+ 'chunk-BWG3R63Q.js?v=fff2b904',
+ 'chunk-DVKQGDAD.js?v=99d84b8c',
+ '@mui/material/Grid', // Exclude other MUI components as needed
],
},
plugins: [
@@ -19,4 +26,4 @@ export default defineConfig({
},
}),
],
-})
+})
\ No newline at end of file
diff --git a/frontend/frontend/vite.config.ts.timestamp-1712075430286-84211051e338.mjs b/frontend/frontend/vite.config.ts.timestamp-1712075430286-84211051e338.mjs
deleted file mode 100644
index e4a3d6c6e..000000000
--- a/frontend/frontend/vite.config.ts.timestamp-1712075430286-84211051e338.mjs
+++ /dev/null
@@ -1,8 +0,0 @@
-// vite.config.ts
-import { defineConfig } from 'file:///home/ticoucke/Dropbox/TIMON/Informatica3Ba/SEL2/UGent-4/frontend/frontend/node_modules/vite/dist/node/index.js'
-import react from 'file:///home/ticoucke/Dropbox/TIMON/Informatica3Ba/SEL2/UGent-4/frontend/frontend/node_modules/@vitejs/plugin-react/dist/index.mjs'
-var vite_config_default = defineConfig({
- plugins: [react()],
-})
-export { vite_config_default as default }
-//# sourceMappingURL=data:application/json;base64,ewogICJ2ZXJzaW9uIjogMywKICAic291cmNlcyI6IFsidml0ZS5jb25maWcudHMiXSwKICAic291cmNlc0NvbnRlbnQiOiBbImNvbnN0IF9fdml0ZV9pbmplY3RlZF9vcmlnaW5hbF9kaXJuYW1lID0gXCIvaG9tZS90aWNvdWNrZS9Ecm9wYm94L1RJTU9OL0luZm9ybWF0aWNhM0JhL1NFTDIvVUdlbnQtNC9mcm9udGVuZC9mcm9udGVuZFwiO2NvbnN0IF9fdml0ZV9pbmplY3RlZF9vcmlnaW5hbF9maWxlbmFtZSA9IFwiL2hvbWUvdGljb3Vja2UvRHJvcGJveC9USU1PTi9JbmZvcm1hdGljYTNCYS9TRUwyL1VHZW50LTQvZnJvbnRlbmQvZnJvbnRlbmQvdml0ZS5jb25maWcudHNcIjtjb25zdCBfX3ZpdGVfaW5qZWN0ZWRfb3JpZ2luYWxfaW1wb3J0X21ldGFfdXJsID0gXCJmaWxlOi8vL2hvbWUvdGljb3Vja2UvRHJvcGJveC9USU1PTi9JbmZvcm1hdGljYTNCYS9TRUwyL1VHZW50LTQvZnJvbnRlbmQvZnJvbnRlbmQvdml0ZS5jb25maWcudHNcIjtpbXBvcnQgeyBkZWZpbmVDb25maWcgfSBmcm9tICd2aXRlJ1xuaW1wb3J0IHJlYWN0IGZyb20gJ0B2aXRlanMvcGx1Z2luLXJlYWN0J1xuXG4vLyBodHRwczovL3ZpdGVqcy5kZXYvY29uZmlnL1xuZXhwb3J0IGRlZmF1bHQgZGVmaW5lQ29uZmlnKHtcbiAgcGx1Z2luczogW3JlYWN0KCldLFxufSlcbiJdLAogICJtYXBwaW5ncyI6ICI7QUFBZ1osU0FBUyxvQkFBb0I7QUFDN2EsT0FBTyxXQUFXO0FBR2xCLElBQU8sc0JBQVEsYUFBYTtBQUFBLEVBQzFCLFNBQVMsQ0FBQyxNQUFNLENBQUM7QUFDbkIsQ0FBQzsiLAogICJuYW1lcyI6IFtdCn0K
diff --git a/package-lock.json b/package-lock.json
index de269d449..9546c1e6a 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -8,6 +8,7 @@
"@emotion/react": "^11.11.4",
"@emotion/styled": "^11.11.0",
"@mui/material": "^5.15.12",
+ "lodash": "^4.17.21",
"react-datepicker": "^6.2.0"
}
},
@@ -358,12 +359,12 @@
"integrity": "sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w=="
},
"node_modules/@mui/private-theming": {
- "version": "5.15.12",
- "resolved": "https://registry.npmjs.org/@mui/private-theming/-/private-theming-5.15.12.tgz",
- "integrity": "sha512-cqoSo9sgA5HE+8vZClbLrq9EkyOnYysooepi5eKaKvJ41lReT2c5wOZAeDDM1+xknrMDos+0mT2zr3sZmUiRRA==",
+ "version": "5.15.14",
+ "resolved": "https://registry.npmjs.org/@mui/private-theming/-/private-theming-5.15.14.tgz",
+ "integrity": "sha512-UH0EiZckOWcxiXLX3Jbb0K7rC8mxTr9L9l6QhOZxYc4r8FHUkefltV9VDGLrzCaWh30SQiJvAEd7djX3XXY6Xw==",
"dependencies": {
"@babel/runtime": "^7.23.9",
- "@mui/utils": "^5.15.12",
+ "@mui/utils": "^5.15.14",
"prop-types": "^15.8.1"
},
"engines": {
@@ -384,9 +385,9 @@
}
},
"node_modules/@mui/styled-engine": {
- "version": "5.15.11",
- "resolved": "https://registry.npmjs.org/@mui/styled-engine/-/styled-engine-5.15.11.tgz",
- "integrity": "sha512-So21AhAngqo07ces4S/JpX5UaMU2RHXpEA6hNzI6IQjd/1usMPxpgK8wkGgTe3JKmC2KDmH8cvoycq5H3Ii7/w==",
+ "version": "5.15.14",
+ "resolved": "https://registry.npmjs.org/@mui/styled-engine/-/styled-engine-5.15.14.tgz",
+ "integrity": "sha512-RILkuVD8gY6PvjZjqnWhz8fu68dVkqhM5+jYWfB5yhlSQKg+2rHkmEwm75XIeAqI3qwOndK6zELK5H6Zxn4NHw==",
"dependencies": {
"@babel/runtime": "^7.23.9",
"@emotion/cache": "^11.11.0",
@@ -415,15 +416,15 @@
}
},
"node_modules/@mui/system": {
- "version": "5.15.12",
- "resolved": "https://registry.npmjs.org/@mui/system/-/system-5.15.12.tgz",
- "integrity": "sha512-/pq+GO6yN3X7r3hAwFTrzkAh7K1bTF5r8IzS79B9eyKJg7v6B/t4/zZYMR6OT9qEPtwf6rYN2Utg1e6Z7F1OgQ==",
+ "version": "5.15.15",
+ "resolved": "https://registry.npmjs.org/@mui/system/-/system-5.15.15.tgz",
+ "integrity": "sha512-aulox6N1dnu5PABsfxVGOZffDVmlxPOVgj56HrUnJE8MCSh8lOvvkd47cebIVQQYAjpwieXQXiDPj5pwM40jTQ==",
"dependencies": {
"@babel/runtime": "^7.23.9",
- "@mui/private-theming": "^5.15.12",
- "@mui/styled-engine": "^5.15.11",
- "@mui/types": "^7.2.13",
- "@mui/utils": "^5.15.12",
+ "@mui/private-theming": "^5.15.14",
+ "@mui/styled-engine": "^5.15.14",
+ "@mui/types": "^7.2.14",
+ "@mui/utils": "^5.15.14",
"clsx": "^2.1.0",
"csstype": "^3.1.3",
"prop-types": "^15.8.1"
@@ -454,9 +455,9 @@
}
},
"node_modules/@mui/types": {
- "version": "7.2.13",
- "resolved": "https://registry.npmjs.org/@mui/types/-/types-7.2.13.tgz",
- "integrity": "sha512-qP9OgacN62s+l8rdDhSFRe05HWtLLJ5TGclC9I1+tQngbssu0m2dmFZs+Px53AcOs9fD7TbYd4gc9AXzVqO/+g==",
+ "version": "7.2.14",
+ "resolved": "https://registry.npmjs.org/@mui/types/-/types-7.2.14.tgz",
+ "integrity": "sha512-MZsBZ4q4HfzBsywtXgM1Ksj6HDThtiwmOKUXH1pKYISI9gAVXCNHNpo7TlGoGrBaYWZTdNoirIN7JsQcQUjmQQ==",
"peerDependencies": {
"@types/react": "^17.0.0 || ^18.0.0"
},
@@ -467,9 +468,9 @@
}
},
"node_modules/@mui/utils": {
- "version": "5.15.12",
- "resolved": "https://registry.npmjs.org/@mui/utils/-/utils-5.15.12.tgz",
- "integrity": "sha512-8SDGCnO2DY9Yy+5bGzu00NZowSDtuyHP4H8gunhHGQoIlhlY2Z3w64wBzAOLpYw/ZhJNzksDTnS/i8qdJvxuow==",
+ "version": "5.15.14",
+ "resolved": "https://registry.npmjs.org/@mui/utils/-/utils-5.15.14.tgz",
+ "integrity": "sha512-0lF/7Hh/ezDv5X7Pry6enMsbYyGKjADzvHyo3Qrc/SSlTsQ1VkbDMbH0m2t3OR5iIVLwMoxwM7yGd+6FCMtTFA==",
"dependencies": {
"@babel/runtime": "^7.23.9",
"@types/prop-types": "^15.7.11",
@@ -768,6 +769,11 @@
"resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz",
"integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg=="
},
+ "node_modules/lodash": {
+ "version": "4.17.21",
+ "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz",
+ "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg=="
+ },
"node_modules/loose-envify": {
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz",
diff --git a/package.json b/package.json
index dde0d41f1..f4a11f7ae 100644
--- a/package.json
+++ b/package.json
@@ -3,6 +3,7 @@
"@emotion/react": "^11.11.4",
"@emotion/styled": "^11.11.0",
"@mui/material": "^5.15.12",
+ "lodash": "^4.17.21",
"react-datepicker": "^6.2.0"
}
}
diff --git a/switch_test.sh b/switch_test.sh
new file mode 100644
index 000000000..776f31e25
--- /dev/null
+++ b/switch_test.sh
@@ -0,0 +1,25 @@
+#!/bin/sh
+
+if [ -e "testing" ]; then
+ rm api/middleware/middleware.py
+ cp api/middleware/middleware_original.py api/middleware/middleware.py
+
+ rm frontend/frontend/src/main.tsx
+ cp frontend/frontend/src/main_original.tsx frontend/frontend/src/main.tsx
+
+ rm testing
+else
+ rm api/middleware/middleware.py
+ if [ "$1" = "true" ]; then
+ cp api/middleware/middleware_lesgever_test.py api/middleware/middleware.py
+ else
+ cp api/middleware/middleware_student_test.py api/middleware/middleware.py
+ fi
+
+ rm frontend/frontend/src/main.tsx
+ cp frontend/frontend/src/main_test.tsx frontend/frontend/src/main.tsx
+
+ touch testing
+fi
+
+