diff --git a/.gitignore b/.gitignore index 278b124..6a00f43 100644 --- a/.gitignore +++ b/.gitignore @@ -9,7 +9,7 @@ /coverage # production -/build +/frontend/build # misc .DS_Store @@ -22,4 +22,5 @@ npm-debug.log* yarn-debug.log* yarn-error.log* /backend/auth/auth/secrets.py -/.env +/frontend/.env +/.idea diff --git a/Pipfile b/Pipfile index 9dadf7f..4bed272 100644 --- a/Pipfile +++ b/Pipfile @@ -4,6 +4,7 @@ verify_ssl = true name = "pypi" [packages] +config = "*" [dev-packages] diff --git a/Pipfile.lock b/Pipfile.lock index 52ceab7..a2caf4f 100644 --- a/Pipfile.lock +++ b/Pipfile.lock @@ -1,7 +1,7 @@ { "_meta": { "hash": { - "sha256": "5dad4df0acefd6ab931894470861025699c93b6eb981f0cdf8114665e13eb81e" + "sha256": "908f1fc3843d09a4e48db9ef70bc80ca36abde951462d4ed42d52deb6f7e1723" }, "pipfile-spec": 6, "requires": { @@ -16,6 +16,8 @@ } ] }, - "default": {}, + "default": { + + }, "develop": {} } diff --git a/backend/auth/accounts/email.py b/backend/auth/accounts/email.py new file mode 100644 index 0000000..10d1d18 --- /dev/null +++ b/backend/auth/accounts/email.py @@ -0,0 +1,22 @@ +from djoser import email +from djoser import utils +from djoser.conf import settings +from django.contrib.auth.tokens import default_token_generator + + +class ActivationEmail(email.ActivationEmail): + template_name = 'accounts/ActivationEmail.html' + + def get_context_data(self): + # ActivationEmail can be deleted + context = super().get_context_data() + + user = context.get("user") + context["uid"] = utils.encode_uid(user.pk) + context["token"] = default_token_generator.make_token(user) + context["url"] = settings.ACTIVATION_URL.format(**context) + return context + + +class ConfirmationEmail(email.ConfirmationEmail): + template_name = 'accounts/ConfirmationEmail.html' diff --git a/backend/oauth/oauth/__init__.py b/backend/auth/accounts/management/__init__.py similarity index 100% rename from backend/oauth/oauth/__init__.py rename to backend/auth/accounts/management/__init__.py diff --git a/backend/oauth/oauth/views.py b/backend/auth/accounts/management/commands/__init__.py similarity index 100% rename from backend/oauth/oauth/views.py rename to backend/auth/accounts/management/commands/__init__.py diff --git a/backend/auth/accounts/management/commands/init_rbac.py b/backend/auth/accounts/management/commands/init_rbac.py new file mode 100644 index 0000000..2c63591 --- /dev/null +++ b/backend/auth/accounts/management/commands/init_rbac.py @@ -0,0 +1,39 @@ +from django.core.management.base import BaseCommand +from accounts.models import Module, Permission, Role + + +class Command(BaseCommand): + help = 'Initialize RBAC system with modules, permissions, and roles' + + def handle(self, *args, **kwargs): + self.create_modules() + self.create_permissions() + self.create_roles() + self.stdout.write(self.style.SUCCESS('Successfully initialized RBAC system')) + + def create_modules(self): + self.assets_module = Module.objects.get_or_create(name="Assets")[0] + self.comments_module = Module.objects.get_or_create(name="Comments")[0] + self.users_module = Module.objects.get_or_create(name="Users")[0] + + def create_permissions(self): + self.create_assets = Permission.objects.get_or_create(name="Create", module=self.assets_module)[0] + self.read_assets = Permission.objects.get_or_create(name="Read", module=self.assets_module)[0] + self.update_assets = Permission.objects.get_or_create(name="Update", module=self.assets_module)[0] + self.delete_assets = Permission.objects.get_or_create(name="Delete", module=self.assets_module)[0] + + self.read_comments = Permission.objects.get_or_create(name="Read", module=self.comments_module)[0] + self.create_comments = Permission.objects.get_or_create(name="Create", module=self.comments_module)[0] + + self.read_users = Permission.objects.get_or_create(name="Read", module=self.users_module)[0] + + def create_roles(self): + user_role = Role.objects.get_or_create(name="User")[0] + user_role.permissions.add(self.read_assets, self.read_comments, self.create_comments) + + creator_role = Role.objects.get_or_create(name="Creator")[0] + creator_role.permissions.add(self.create_assets, self.read_assets, self.update_assets, self.delete_assets, + self.read_comments, self.create_comments) + + organization_role = Role.objects.get_or_create(name="Organization")[0] + # Assign permissions based on special access features diff --git a/backend/auth/accounts/migrations/0001_initial.py b/backend/auth/accounts/migrations/0001_initial.py index 080e399..8d7756e 100644 --- a/backend/auth/accounts/migrations/0001_initial.py +++ b/backend/auth/accounts/migrations/0001_initial.py @@ -1,40 +1,73 @@ -# Generated by Django 4.1.10 on 2024-01-01 13:26 +# Generated by Django 4.1.10 on 2024-06-10 17:21 from django.db import migrations, models +import uuid class Migration(migrations.Migration): - initial = True - dependencies = [ - ] + dependencies = [] operations = [ migrations.CreateModel( - name='User', + name="User", fields=[ - ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('password', models.CharField(max_length=128, verbose_name='password')), - ('last_login', models.DateTimeField(blank=True, null=True, verbose_name='last login')), - ('email', models.EmailField(max_length=255, unique=True, verbose_name='Email')), - ('first_name', models.CharField(max_length=200)), - ('last_name', models.CharField(max_length=200)), - ('age', models.PositiveIntegerField()), - ('gender', models.CharField(max_length=200)), - ('address', models.CharField(max_length=600)), - ('preferred_lang', models.CharField(max_length=200)), - ('company', models.CharField(max_length=200)), - ('job_title', models.CharField(max_length=200)), - ('industry', models.CharField(max_length=400)), - ('experience', models.IntegerField()), - ('is_active', models.BooleanField(default=True)), - ('is_admin', models.BooleanField(default=False)), - ('created_at', models.DateTimeField(auto_now_add=True)), - ('updated_at', models.DateTimeField(auto_now=True)), + ("password", models.CharField(max_length=128, verbose_name="password")), + ( + "last_login", + models.DateTimeField( + blank=True, null=True, verbose_name="last login" + ), + ), + ( + "id", + models.UUIDField( + default=uuid.uuid4, + editable=False, + primary_key=True, + serialize=False, + unique=True, + ), + ), + ( + "email", + models.EmailField( + max_length=255, unique=True, verbose_name="Email" + ), + ), + ("first_name", models.CharField(max_length=200)), + ("last_name", models.CharField(max_length=200)), + ("date_of_birth", models.DateField()), + ("gender", models.CharField(max_length=200)), + ("address", models.CharField(max_length=600)), + ("preferred_lang", models.CharField(max_length=200)), + ("company", models.CharField(max_length=200)), + ("job_title", models.CharField(max_length=200)), + ("industry", models.CharField(max_length=400)), + ("experience", models.IntegerField()), + ("is_active", models.BooleanField(default=True)), + ("is_admin", models.BooleanField(default=False)), + ("created_at", models.DateTimeField(auto_now_add=True)), + ("updated_at", models.DateTimeField(auto_now=True)), ], options={ - 'abstract': False, + "abstract": False, }, ), + migrations.CreateModel( + name="Module", + fields=[ + ( + "id", + models.BigAutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ("name", models.CharField(max_length=50)), + ], + ), ] diff --git a/backend/auth/accounts/migrations/0002_userpreferences.py b/backend/auth/accounts/migrations/0002_userpreferences.py new file mode 100644 index 0000000..edeb405 --- /dev/null +++ b/backend/auth/accounts/migrations/0002_userpreferences.py @@ -0,0 +1,36 @@ +# Generated by Django 4.1.10 on 2024-06-15 08:21 + +from django.conf import settings +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + dependencies = [ + ("accounts", "0001_initial"), + ] + + operations = [ + migrations.CreateModel( + name="UserPreferences", + fields=[ + ( + "id", + models.BigAutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ("dark_mode", models.BooleanField(default=False)), + ( + "user", + models.OneToOneField( + on_delete=django.db.models.deletion.CASCADE, + to=settings.AUTH_USER_MODEL, + ), + ), + ], + ), + ] diff --git a/backend/auth/accounts/migrations/0003_permission_role_userrole.py b/backend/auth/accounts/migrations/0003_permission_role_userrole.py new file mode 100644 index 0000000..e35dfb7 --- /dev/null +++ b/backend/auth/accounts/migrations/0003_permission_role_userrole.py @@ -0,0 +1,81 @@ +# Generated by Django 4.1.10 on 2024-06-15 12:51 + +from django.conf import settings +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + dependencies = [ + ("accounts", "0002_userpreferences"), + ] + + operations = [ + migrations.CreateModel( + name="Permission", + fields=[ + ( + "id", + models.BigAutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ("name", models.CharField(max_length=50)), + ( + "module", + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + to="accounts.module", + ), + ), + ], + ), + migrations.CreateModel( + name="Role", + fields=[ + ( + "id", + models.BigAutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ("name", models.CharField(max_length=50)), + ("permissions", models.ManyToManyField(to="accounts.permission")), + ], + ), + migrations.CreateModel( + name="UserRole", + fields=[ + ( + "id", + models.BigAutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ( + "role", + models.ForeignKey( + null=True, + on_delete=django.db.models.deletion.SET_NULL, + to="accounts.role", + ), + ), + ( + "user", + models.OneToOneField( + on_delete=django.db.models.deletion.CASCADE, + to=settings.AUTH_USER_MODEL, + ), + ), + ], + ), + ] diff --git a/backend/auth/accounts/models.py b/backend/auth/accounts/models.py index 84c4a4f..00c7a2d 100644 --- a/backend/auth/accounts/models.py +++ b/backend/auth/accounts/models.py @@ -1,23 +1,25 @@ -from django.db import models +import uuid + from django.contrib.auth.models import BaseUserManager, AbstractBaseUser +from django.db import models # Custom User Manager class UserManager(BaseUserManager): def create_user( - self, - email, - first_name, - last_name, - age, - gender, - address, - preferred_lang, - company, - job_title, - industry, - experience, - password=None, + self, + email, + first_name, + last_name, + date_of_birth, + gender, + address, + preferred_lang, + company, + job_title, + industry, + experience, + password=None, ): """ Creates and saves a User with the given email, name, tc and password. @@ -29,7 +31,7 @@ def create_user( email=self.normalize_email(email), first_name=first_name, last_name=last_name, - age=age, + date_of_birth=date_of_birth, gender=gender, address=address, preferred_lang=preferred_lang, @@ -44,19 +46,19 @@ def create_user( return user def create_superuser( - self, - email, - first_name, - last_name, - age, - gender, - address, - preferred_lang, - company, - job_title, - industry, - experience, - password=None, + self, + email, + first_name, + last_name, + date_of_birth, + gender, + address, + preferred_lang, + company, + job_title, + industry, + experience, + password=None, ): """ Creates and saves a superuser with the given email, name, tc and password. @@ -66,7 +68,7 @@ def create_superuser( password=password, first_name=first_name, last_name=last_name, - age=age, + date_of_birth=date_of_birth, gender=gender, address=address, preferred_lang=preferred_lang, @@ -82,6 +84,12 @@ def create_superuser( # Custom User Model class User(AbstractBaseUser): + id = models.UUIDField( + primary_key=True, + default=uuid.uuid4, + editable=False, + unique=True + ) email = models.EmailField( verbose_name="Email", max_length=255, @@ -89,7 +97,7 @@ class User(AbstractBaseUser): ) first_name = models.CharField(max_length=200) last_name = models.CharField(max_length=200) - age = models.PositiveIntegerField() + date_of_birth = models.DateField() gender = models.CharField(max_length=200) address = models.CharField(max_length=600) preferred_lang = models.CharField(max_length=200) @@ -108,7 +116,7 @@ class User(AbstractBaseUser): REQUIRED_FIELDS = [ "first_name", "last_name", - "age", + "date_of_birth", "gender", "address", "preferred_lang", @@ -136,3 +144,30 @@ def is_staff(self): """Is the user a member of staff?""" # Simplest possible answer: All admins are staff return self.is_admin + + +class Module(models.Model): + name = models.CharField(max_length=50) + + +class Permission(models.Model): + name = models.CharField(max_length=50) + module = models.ForeignKey(Module, on_delete=models.CASCADE) + + +class Role(models.Model): + name = models.CharField(max_length=50) + permissions = models.ManyToManyField(Permission) + + +class UserRole(models.Model): + user = models.OneToOneField(User, on_delete=models.CASCADE) + role = models.ForeignKey(Role, on_delete=models.SET_NULL, null=True) + + +class UserPreferences(models.Model): + user = models.OneToOneField(User, on_delete=models.CASCADE) + dark_mode = models.BooleanField(default=False) + + def __str__(self): + return f'{self.user.first_name} {self.user.last_name} Preferences' diff --git a/backend/auth/accounts/serializers.py b/backend/auth/accounts/serializers.py index 4e6515a..1768b12 100644 --- a/backend/auth/accounts/serializers.py +++ b/backend/auth/accounts/serializers.py @@ -1,5 +1,7 @@ from djoser.serializers import UserCreateSerializer from django.contrib.auth import get_user_model +from rest_framework import serializers +from accounts.models import UserPreferences User = get_user_model() @@ -7,10 +9,11 @@ class UserCreateSerializer(UserCreateSerializer): class Meta(UserCreateSerializer.Meta): model = User - fields = ('email', + fields = ( + 'email', 'first_name', 'last_name', - 'age', + 'date_of_birth', 'gender', 'address', 'preferred_lang', @@ -20,3 +23,9 @@ class Meta(UserCreateSerializer.Meta): 'experience', 'password', ) + + +class UserPreferencesSerializer(serializers.ModelSerializer): + class Meta: + model = UserPreferences + fields = ['dark_mode'] diff --git a/backend/auth/accounts/signals.py b/backend/auth/accounts/signals.py new file mode 100644 index 0000000..a1e324d --- /dev/null +++ b/backend/auth/accounts/signals.py @@ -0,0 +1,10 @@ +from django.db.models.signals import post_save +from django.dispatch import receiver +from .models import User, Role, UserRole + + +@receiver(post_save, sender=User) +def assign_default_role(sender, instance, created, **kwargs): + if created: + user_role = Role.objects.get(name='User') + UserRole.objects.create(user=instance, role=user_role) diff --git a/backend/auth/accounts/templates/accounts/ActivationEmail.html b/backend/auth/accounts/templates/accounts/ActivationEmail.html new file mode 100644 index 0000000..5024801 --- /dev/null +++ b/backend/auth/accounts/templates/accounts/ActivationEmail.html @@ -0,0 +1,28 @@ +{% load i18n %} + +{% block subject %} +{% blocktrans %}Account activation on {{ site_name }}{% endblocktrans %} +{% endblock subject %} + +{% block text_body %} +{% blocktrans %}You're receiving this email because you need to finish activation process on {{ site_name }}.{% endblocktrans %} + +{% trans "Please go to the following page to activate account:" %} +{{ protocol }}://{{ domain }}/{{ url|safe }} + +{% trans "Thanks for using our site!" %} + +{% blocktrans %}The {{ site_name }} team{% endblocktrans %} +{% endblock text_body %} + +{% block html_body %} +

{% blocktrans %}You're receiving this email because you need to finish activation process on {{ site_name }}.{% endblocktrans %}

+ +

{% trans "Please go to the following page to activate account:" %}

+

{{ protocol }}://{{ domain }}/{{ url|safe }}

+ +

{% trans "Thanks for using our site!" %}

+ +

{% blocktrans %}The {{ site_name }} team{% endblocktrans %}

+ +{% endblock html_body %} \ No newline at end of file diff --git a/backend/auth/accounts/templates/accounts/ConfirmationEmail.html b/backend/auth/accounts/templates/accounts/ConfirmationEmail.html new file mode 100644 index 0000000..c8fd066 --- /dev/null +++ b/backend/auth/accounts/templates/accounts/ConfirmationEmail.html @@ -0,0 +1,21 @@ +{% load i18n %} + +{% block subject %} +{% blocktrans %}{{ site_name }} - Your account has been successfully created and activated!{% endblocktrans %} +{% endblock %} + +{% block text_body %} +{% trans "Your account has been created and is ready to use!" %} + +{% trans "Thanks for using our site!" %} + +{% blocktrans %}The {{ site_name }} team{% endblocktrans %} +{% endblock text_body %} + +{% block html_body %} +

{% trans "Your account has been created and is ready to use!" %}

+ +

{% trans "Thanks for using our site!" %}

+ +

{% blocktrans %}The {{ site_name }} team{% endblocktrans %}

+{% endblock html_body %} \ No newline at end of file diff --git a/backend/auth/accounts/tests.py b/backend/auth/accounts/tests.py index 7ce503c..8f6f39c 100644 --- a/backend/auth/accounts/tests.py +++ b/backend/auth/accounts/tests.py @@ -1,3 +1,3 @@ from django.test import TestCase -# Create your tests here. +# Create your test here. diff --git a/backend/auth/accounts/urls.py b/backend/auth/accounts/urls.py new file mode 100644 index 0000000..8b50b48 --- /dev/null +++ b/backend/auth/accounts/urls.py @@ -0,0 +1,10 @@ +from django.urls import path, include +from rest_framework.routers import DefaultRouter +from .views import UserPreferencesViewSet + +router = DefaultRouter() +router.register(r'preferences', UserPreferencesViewSet, basename='userpreferences') + +urlpatterns = [ + path('', include(router.urls)), +] diff --git a/backend/auth/accounts/views.py b/backend/auth/accounts/views.py index 91ea44a..a44c2f1 100644 --- a/backend/auth/accounts/views.py +++ b/backend/auth/accounts/views.py @@ -1,3 +1,16 @@ from django.shortcuts import render +from rest_framework import viewsets +from .models import UserPreferences +from .serializers import UserPreferencesSerializer +from rest_framework.permissions import IsAuthenticated -# Create your views here. + +class UserPreferencesViewSet(viewsets.ModelViewSet): + serializer_class = UserPreferencesSerializer + permission_classes = [IsAuthenticated] + + def get_queryset(self): + return UserPreferences.objects.filter(user=self.request.user) + + def perform_create(self, serializer): + serializer.save(user=self.request.user) diff --git a/backend/auth/auth/settings.py b/backend/auth/auth/settings.py index 55e7a51..4734039 100644 --- a/backend/auth/auth/settings.py +++ b/backend/auth/auth/settings.py @@ -12,7 +12,7 @@ import os.path from datetime import timedelta from pathlib import Path -from .secrets import get_secret +from .secrets import get_secrets # Build paths inside the project like this: BASE_DIR / 'subdir'. BASE_DIR = Path(__file__).resolve().parent.parent @@ -21,7 +21,7 @@ # Quick-start development settings - unsuitable for production # See https://docs.djangoproject.com/en/4.1/howto/deployment/checklist/ -get_secret() +get_secrets() # SECURITY WARNING: keep the secret key used in production secret! SECRET_KEY = 'django-insecure-!te*yedg2t@=j72f3xp!nxmu#ns52ubfr+z49q9*d-wdc_a1ow' @@ -41,8 +41,9 @@ 'django.contrib.messages', 'django.contrib.staticfiles', 'rest_framework', + 'corsheaders', 'djoser', - 'accounts', + 'accounts' ] MIDDLEWARE = [ @@ -53,6 +54,36 @@ 'django.contrib.auth.middleware.AuthenticationMiddleware', 'django.contrib.messages.middleware.MessageMiddleware', 'django.middleware.clickjacking.XFrameOptionsMiddleware', + 'corsheaders.middleware.CorsMiddleware', +] + +CORS_ORIGIN_ALLOW_ALL = True + +CORS_ALLOWED_ORIGINS = [ + "http://localhost:3000", + "http://127.0.0.1:3000", +] + +CORS_ALLOW_CREDENTIALS = True + +CORS_ALLOW_METHODS = [ + "DELETE", + "GET", + "OPTIONS", + "PATCH", + "POST", + "PUT", +] +CORS_ALLOW_HEADERS = [ + "accept", + "accept-encoding", + "authorization", + "content-type", + "dnt", + "origin", + "user-agent", + "x-csrftoken", + "x-requested-with", ] ROOT_URLCONF = 'auth.urls' @@ -79,22 +110,33 @@ # Database # https://docs.djangoproject.com/en/4.1/ref/settings/#databases +# DATABASES = { +# "default": { +# "ENGINE": "django.db.backends.postgresql", +# "NAME": os.environ.get('dbname', ''), +# "USER": os.environ.get('username', ''), +# "PASSWORD": os.environ.get('password', ''), +# "HOST": os.environ.get('host', ''), +# "PORT": os.environ.get('port', ''), +# } +# } + DATABASES = { "default": { "ENGINE": "django.db.backends.postgresql", - "NAME": os.environ.get('dbname', ''), - "USER": os.environ.get('username', ''), - "PASSWORD": os.environ.get('password', ''), - "HOST": os.environ.get('host', ''), - "PORT": os.environ.get('port', ''), + "NAME": 'local-rds-schwarz', + "USER": 'postgres', + "PASSWORD": 'root', + "HOST": 'localhost', + "PORT": '5432', } } EMAIL_BACKEND = 'django.core.mail.backends.smtp.EmailBackend' EMAIL_HOST = 'smtp.gmail.com' EMAIL_PORT = 587 -EMAIL_HOST_USER = 'schwarzstudios@gmail.com' -EMAIL_HOST_PASSWORD = 'xlsyomssafccnnxa' +EMAIL_HOST_USER = os.environ.get('EMAIL_HOST_USER', '') +EMAIL_HOST_PASSWORD = os.environ.get('EMAIL_HOST_PASSWORD', '') EMAIL_USE_TLS = True # Password validation @@ -173,6 +215,7 @@ 'USERNAME_RESET_CONFIRM_URL': 'email/reset/confirm/{uid}/{token}', 'ACTIVATION_URL': 'activate/{uid}/{token}', 'SEND_ACTIVATION_EMAIL': True, + 'SEND_ACTIVATION_EMAIL_TIMEOUT': 3600, 'SOCIAL_AUTH_TOKEN_STRATEGY': 'djoser.social.token.jwt.TokenStrategy', 'SOCIAL_AUTH_ALLOWED_REDIRECT_URIS': ['http://localhost:8000/google', 'http://localhost:8000/facebook'], 'SERIALIZERS': { @@ -180,13 +223,17 @@ 'user': 'accounts.serializers.UserCreateSerializer', 'current_user': 'accounts.serializers.UserCreateSerializer', 'user_delete': 'djoser.serializers.UserDeleteSerializer', - } + }, + 'EMAIL': { + 'activation': 'accounts.email.ActivationEmail', + 'confirmation': 'accounts.email.ConfirmationEmail', + }, } SOCIAL_AUTH_GOOGLE_OAUTH2_KEY = '[YOUR GOOGLE OAUTH2 API KEY]' SOCIAL_AUTH_GOOGLE_OAUTH2_SECRET = '[YOUR GOOGLE OAUTH2 API SECRET]' SOCIAL_AUTH_GOOGLE_OAUTH2_SCOPE = ['https://www.googleapis.com/auth/userinfo.email', 'https://www.googleapis.com/auth' - '/userinfo.profile', 'openid'] + '/userinfo.Profile', 'openid'] SOCIAL_AUTH_GOOGLE_OAUTH2_EXTRA_DATA = ['first_name', 'last_name'] SOCIAL_AUTH_FACEBOOK_KEY = '[YOUR FACEBOOK API KEY]' @@ -202,3 +249,14 @@ DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField' AUTH_USER_MODEL = 'accounts.User' + +# AWS S3 settings +AWS_ACCESS_KEY_ID = os.environ.get('AWS_ACCESS_KEY_ID') +AWS_SECRET_ACCESS_KEY = os.environ.get('AWS_SECRET_ACCESS_KEY') +AWS_STORAGE_BUCKET_NAME = os.environ.get('AWS_STORAGE_BUCKET_NAME') +AWS_S3_CUSTOM_DOMAIN = f'{AWS_STORAGE_BUCKET_NAME}.s3.amazonaws.com' +AWS_S3_OBJECT_PARAMETERS = {'CacheControl': 'max-age=86400'} +AWS_LOCATION = 'media' + +DEFAULT_FILE_STORAGE = 'storages.backends.s3boto3.S3Boto3Storage' +MEDIA_URL = f'https://{AWS_S3_CUSTOM_DOMAIN}/{AWS_LOCATION}/' diff --git a/backend/auth/auth/urls.py b/backend/auth/auth/urls.py index 5329e70..93126f7 100644 --- a/backend/auth/auth/urls.py +++ b/backend/auth/auth/urls.py @@ -5,6 +5,7 @@ path('api/', include('djoser.urls')), path('api/', include('djoser.urls.jwt')), path('api/', include('djoser.social.urls')), + path('api/', include('accounts.urls')), ] urlpatterns += [re_path(r'^.*', TemplateView.as_view(template_name='index.html'))] diff --git a/backend/oauth/oauth/settings.py b/backend/oauth/oauth/settings.py deleted file mode 100644 index ee7a92d..0000000 --- a/backend/oauth/oauth/settings.py +++ /dev/null @@ -1,141 +0,0 @@ -""" -Django settings for oauth project. - -Generated by 'django-admin startproject' using Django 4.1.10. - -For more information on this file, see -https://docs.djangoproject.com/en/4.1/topics/settings/ - -For the full list of settings and their values, see -https://docs.djangoproject.com/en/4.1/ref/settings/ -""" - -from pathlib import Path - -# Build paths inside the project like this: BASE_DIR / 'subdir'. -BASE_DIR = Path(__file__).resolve().parent.parent - - -# Quick-start development settings - unsuitable for production -# See https://docs.djangoproject.com/en/4.1/howto/deployment/checklist/ - -# SECURITY WARNING: keep the secret key used in production secret! -SECRET_KEY = 'django-insecure-b12d0vdug42_bnii#pop$)dbe4kv&g%1k5m@csr&m#x11)0igw' - -# SECURITY WARNING: don't run with debug turned on in production! -DEBUG = True - -ALLOWED_HOSTS = [] - - -# Application definition - -INSTALLED_APPS = [ - 'django.contrib.admin', - 'django.contrib.auth', - 'django.contrib.contenttypes', - 'django.contrib.sessions', - 'django.contrib.messages', - 'django.contrib.staticfiles', - 'allauth', - 'allauth.account', - 'allauth.socialaccount', - 'allauth.socialaccount.providers.google', - 'rest_framework', -] - -SOCIALACCOUNT_PROVIDERS = { - 'google': { - 'SCOPE': [ - 'profile', - 'email', - ], - 'AUTH_PARAMS': { - 'access_type': 'online', - } - }, -} - -MIDDLEWARE = [ - 'django.middleware.security.SecurityMiddleware', - 'django.contrib.sessions.middleware.SessionMiddleware', - 'django.middleware.common.CommonMiddleware', - 'django.middleware.csrf.CsrfViewMiddleware', - 'django.contrib.auth.middleware.AuthenticationMiddleware', - 'django.contrib.messages.middleware.MessageMiddleware', - 'django.middleware.clickjacking.XFrameOptionsMiddleware', - 'allauth.account.middleware.AccountMiddleware', -] - -ROOT_URLCONF = 'oauth.urls' - -TEMPLATES = [ - { - 'BACKEND': 'django.template.backends.django.DjangoTemplates', - 'DIRS': [], - 'APP_DIRS': True, - 'OPTIONS': { - 'context_processors': [ - 'django.template.context_processors.debug', - 'django.template.context_processors.request', - 'django.contrib.auth.context_processors.auth', - 'django.contrib.messages.context_processors.messages', - ], - }, - }, -] - -WSGI_APPLICATION = 'oauth.wsgi.application' - - -# Database -# https://docs.djangoproject.com/en/4.1/ref/settings/#databases - -DATABASES = { - 'default': { - 'ENGINE': 'django.db.backends.sqlite3', - 'NAME': BASE_DIR / 'db.sqlite3', - } -} - - -# Password validation -# https://docs.djangoproject.com/en/4.1/ref/settings/#auth-password-validators - -AUTH_PASSWORD_VALIDATORS = [ - { - 'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator', - }, - { - 'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator', - }, - { - 'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator', - }, - { - 'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator', - }, -] - - -# Internationalization -# https://docs.djangoproject.com/en/4.1/topics/i18n/ - -LANGUAGE_CODE = 'en-us' - -TIME_ZONE = 'UTC' - -USE_I18N = True - -USE_TZ = True - - -# Static files (CSS, JavaScript, Images) -# https://docs.djangoproject.com/en/4.1/howto/static-files/ - -STATIC_URL = 'static/' - -# Default primary key field type -# https://docs.djangoproject.com/en/4.1/ref/settings/#default-auto-field - -DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField' diff --git a/backend/oauth/oauth/urls.py b/backend/oauth/oauth/urls.py deleted file mode 100644 index 7b1f304..0000000 --- a/backend/oauth/oauth/urls.py +++ /dev/null @@ -1,13 +0,0 @@ -# urls.py in the oauth_service project - -from django.urls import path -from allauth.socialaccount import views as socialaccount_views - -urlpatterns = [ - # Include OAuth URLs - path('accounts/', socialaccount_views.connections, name='socialaccount_connections'), - path('login/', socialaccount_views.login, name='account_login'), - path('logout/', socialaccount_views.logout, name='account_logout'), - path('signup/', socialaccount_views.signup, name='account_signup'), - path('disconnect//', socialaccount_views.disconnect, name='socialaccount_disconnect'), -] diff --git a/backend/oauth/requirements.txt b/backend/oauth/requirements.txt deleted file mode 100644 index a5378b7..0000000 Binary files a/backend/oauth/requirements.txt and /dev/null differ diff --git a/backend/oauth/manage.py b/backend/user_profiles/manage.py similarity index 84% rename from backend/oauth/manage.py rename to backend/user_profiles/manage.py index c20c5b6..cca61ec 100644 --- a/backend/oauth/manage.py +++ b/backend/user_profiles/manage.py @@ -6,7 +6,7 @@ def main(): """Run administrative tasks.""" - os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'oauth.settings') + os.environ.setdefault("DJANGO_SETTINGS_MODULE", "user_profiles.settings") try: from django.core.management import execute_from_command_line except ImportError as exc: @@ -18,5 +18,5 @@ def main(): execute_from_command_line(sys.argv) -if __name__ == '__main__': +if __name__ == "__main__": main() diff --git a/frontend/src/App.css b/backend/user_profiles/user_profiles/__init__.py similarity index 100% rename from frontend/src/App.css rename to backend/user_profiles/user_profiles/__init__.py diff --git a/backend/oauth/oauth/asgi.py b/backend/user_profiles/user_profiles/asgi.py similarity index 71% rename from backend/oauth/oauth/asgi.py rename to backend/user_profiles/user_profiles/asgi.py index 0aa0925..a597619 100644 --- a/backend/oauth/oauth/asgi.py +++ b/backend/user_profiles/user_profiles/asgi.py @@ -1,5 +1,5 @@ """ -ASGI config for oauth project. +ASGI config for user_profiles project. It exposes the ASGI callable as a module-level variable named ``application``. @@ -11,6 +11,6 @@ from django.core.asgi import get_asgi_application -os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'oauth.settings') +os.environ.setdefault("DJANGO_SETTINGS_MODULE", "user_profiles.settings") application = get_asgi_application() diff --git a/backend/user_profiles/user_profiles/settings.py b/backend/user_profiles/user_profiles/settings.py new file mode 100644 index 0000000..cd9a45f --- /dev/null +++ b/backend/user_profiles/user_profiles/settings.py @@ -0,0 +1,126 @@ +""" +Django settings for user_profiles project. + +Generated by 'django-admin startproject' using Django 4.1.10. + +For more information on this file, see +https://docs.djangoproject.com/en/4.1/topics/settings/ + +For the full list of settings and their values, see +https://docs.djangoproject.com/en/4.1/ref/settings/ +""" + +from pathlib import Path + +# Other Django settings... + + +# Build paths inside the project like this: BASE_DIR / 'subdir'. +BASE_DIR = Path(__file__).resolve().parent.parent + + +# Quick-start development settings - unsuitable for production +# See https://docs.djangoproject.com/en/4.1/howto/deployment/checklist/ + +# SECURITY WARNING: keep the secret key used in production secret! +SECRET_KEY = "django-insecure-+_t0nutf0*l0$9lc7_$!8cra)+v7!+7^y&9vl31rics9o_j&_c" + +# SECURITY WARNING: don't run with debug turned on in production! +DEBUG = True + +ALLOWED_HOSTS = [] + + +# Application definition + +INSTALLED_APPS = [ + "django.contrib.admin", + "django.contrib.auth", + "django.contrib.contenttypes", + "django.contrib.sessions", + "django.contrib.messages", + "django.contrib.staticfiles", +] + +MIDDLEWARE = [ + "django.middleware.security.SecurityMiddleware", + "django.contrib.sessions.middleware.SessionMiddleware", + "django.middleware.common.CommonMiddleware", + "django.middleware.csrf.CsrfViewMiddleware", + "django.contrib.auth.middleware.AuthenticationMiddleware", + "django.contrib.messages.middleware.MessageMiddleware", + "django.middleware.clickjacking.XFrameOptionsMiddleware", +] + +ROOT_URLCONF = "user_profiles.urls" + +TEMPLATES = [ + { + "BACKEND": "django.template.backends.django.DjangoTemplates", + "DIRS": [], + "APP_DIRS": True, + "OPTIONS": { + "context_processors": [ + "django.template.context_processors.debug", + "django.template.context_processors.request", + "django.contrib.auth.context_processors.auth", + "django.contrib.messages.context_processors.messages", + ], + }, + }, +] + +WSGI_APPLICATION = "user_profiles.wsgi.application" + + +# Database +# https://docs.djangoproject.com/en/4.1/ref/settings/#databases + +DATABASES = { + "default": { + "ENGINE": "django.db.backends.sqlite3", + "NAME": BASE_DIR / "db.sqlite3", + } +} + + +# Password validation +# https://docs.djangoproject.com/en/4.1/ref/settings/#auth-password-validators + +AUTH_PASSWORD_VALIDATORS = [ + { + "NAME": "django.contrib.auth.password_validation.UserAttributeSimilarityValidator", + }, + { + "NAME": "django.contrib.auth.password_validation.MinimumLengthValidator", + }, + { + "NAME": "django.contrib.auth.password_validation.CommonPasswordValidator", + }, + { + "NAME": "django.contrib.auth.password_validation.NumericPasswordValidator", + }, +] + + +# Internationalization +# https://docs.djangoproject.com/en/4.1/topics/i18n/ + +LANGUAGE_CODE = "en-us" + +TIME_ZONE = "UTC" + +USE_I18N = True + +USE_TZ = True + + +# Static files (CSS, JavaScript, Images) +# https://docs.djangoproject.com/en/4.1/howto/static-files/ + +STATIC_URL = "static/" + +# Default primary key field type +# https://docs.djangoproject.com/en/4.1/ref/settings/#default-auto-field + +DEFAULT_AUTO_FIELD = "django.db.models.BigAutoField" diff --git a/backend/user_profiles/user_profiles/urls.py b/backend/user_profiles/user_profiles/urls.py new file mode 100644 index 0000000..31876cf --- /dev/null +++ b/backend/user_profiles/user_profiles/urls.py @@ -0,0 +1,21 @@ +"""user_profiles URL Configuration + +The `urlpatterns` list routes URLs to views. For more information please see: + https://docs.djangoproject.com/en/4.1/topics/http/urls/ +Examples: +Function views + 1. Add an import: from my_app import views + 2. Add a URL to urlpatterns: path('', views.home, name='home') +Class-based views + 1. Add an import: from other_app.views import Home + 2. Add a URL to urlpatterns: path('', Home.as_view(), name='home') +Including another URLconf + 1. Import the include() function: from django.urls import include, path + 2. Add a URL to urlpatterns: path('blog/', include('blog.urls')) +""" +from django.contrib import admin +from django.urls import path + +urlpatterns = [ + path("admin/", admin.site.urls), +] diff --git a/backend/oauth/oauth/wsgi.py b/backend/user_profiles/user_profiles/wsgi.py similarity index 71% rename from backend/oauth/oauth/wsgi.py rename to backend/user_profiles/user_profiles/wsgi.py index 76a479a..3d08c9a 100644 --- a/backend/oauth/oauth/wsgi.py +++ b/backend/user_profiles/user_profiles/wsgi.py @@ -1,5 +1,5 @@ """ -WSGI config for oauth project. +WSGI config for user_profiles project. It exposes the WSGI callable as a module-level variable named ``application``. @@ -11,6 +11,6 @@ from django.core.wsgi import get_wsgi_application -os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'oauth.settings') +os.environ.setdefault("DJANGO_SETTINGS_MODULE", "user_profiles.settings") application = get_wsgi_application() diff --git a/frontend/index.html b/frontend/index.html index 837a4f2..cae75c4 100644 --- a/frontend/index.html +++ b/frontend/index.html @@ -27,7 +27,7 @@ - Schwarz | The all-in-one community for 3D Artists + Schwarz | The all-in-one platform for 3D Artists diff --git a/frontend/package-lock.json b/frontend/package-lock.json index e6748b0..15080d8 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -20,6 +20,7 @@ "react": "^18.2.0", "react-dom": "^18.2.0", "react-icons": "^5.0.1", + "react-loading-skeleton": "^3.4.0", "react-redux": "^9.0.4", "react-router-dom": "^6.15.0", "react-select": "^5.8.0", @@ -32,10 +33,12 @@ }, "devDependencies": { "@types/node": "^20.10.5", + "@vitest/ui": "^1.4.0", "autoprefixer": "^10.4.16", "postcss": "^8.4.32", "tailwindcss": "^3.4.0", - "typescript": "^5.3.3" + "typescript": "^5.3.3", + "vitest": "^1.4.0" } }, "node_modules/@adobe/css-tools": { @@ -658,6 +661,182 @@ "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.14.54", "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.14.54.tgz", @@ -667,7 +846,183 @@ ], "optional": true, "os": [ - "linux" + "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" @@ -853,6 +1208,12 @@ "node": ">=14" } }, + "node_modules/@polka/url": { + "version": "1.0.0-next.25", + "resolved": "https://registry.npmjs.org/@polka/url/-/url-1.0.0-next.25.tgz", + "integrity": "sha512-j7P6Rgr3mmtdkeDGTe0E/aYyWEWVtc5yFXtHCRHs28/jptDEWfaVOc5T7cblqy1XKPPfCxJc/8DwQ5YgLOZOVQ==", + "dev": true + }, "node_modules/@redux-devtools/extension": { "version": "3.3.0", "resolved": "https://registry.npmjs.org/@redux-devtools/extension/-/extension-3.3.0.tgz", @@ -896,6 +1257,201 @@ "node": ">=14.0.0" } }, + "node_modules/@rollup/rollup-android-arm-eabi": { + "version": "4.13.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.13.2.tgz", + "integrity": "sha512-3XFIDKWMFZrMnao1mJhnOT1h2g0169Os848NhhmGweEcfJ4rCi+3yMCOLG4zA61rbJdkcrM/DjVZm9Hg5p5w7g==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-android-arm64": { + "version": "4.13.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.13.2.tgz", + "integrity": "sha512-GdxxXbAuM7Y/YQM9/TwwP+L0omeE/lJAR1J+olu36c3LqqZEBdsIWeQ91KBe6nxwOnb06Xh7JS2U5ooWU5/LgQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-darwin-arm64": { + "version": "4.13.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.13.2.tgz", + "integrity": "sha512-mCMlpzlBgOTdaFs83I4XRr8wNPveJiJX1RLfv4hggyIVhfB5mJfN4P8Z6yKh+oE4Luz+qq1P3kVdWrCKcMYrrA==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-darwin-x64": { + "version": "4.13.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.13.2.tgz", + "integrity": "sha512-yUoEvnH0FBef/NbB1u6d3HNGyruAKnN74LrPAfDQL3O32e3k3OSfLrPgSJmgb3PJrBZWfPyt6m4ZhAFa2nZp2A==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-linux-arm-gnueabihf": { + "version": "4.13.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.13.2.tgz", + "integrity": "sha512-GYbLs5ErswU/Xs7aGXqzc3RrdEjKdmoCrgzhJWyFL0r5fL3qd1NPcDKDowDnmcoSiGJeU68/Vy+OMUluRxPiLQ==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-gnu": { + "version": "4.13.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.13.2.tgz", + "integrity": "sha512-L1+D8/wqGnKQIlh4Zre9i4R4b4noxzH5DDciyahX4oOz62CphY7WDWqJoQ66zNR4oScLNOqQJfNSIAe/6TPUmQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-musl": { + "version": "4.13.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.13.2.tgz", + "integrity": "sha512-tK5eoKFkXdz6vjfkSTCupUzCo40xueTOiOO6PeEIadlNBkadH1wNOH8ILCPIl8by/Gmb5AGAeQOFeLev7iZDOA==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-powerpc64le-gnu": { + "version": "4.13.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.13.2.tgz", + "integrity": "sha512-zvXvAUGGEYi6tYhcDmb9wlOckVbuD+7z3mzInCSTACJ4DQrdSLPNUeDIcAQW39M3q6PDquqLWu7pnO39uSMRzQ==", + "cpu": [ + "ppc64le" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-gnu": { + "version": "4.13.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.13.2.tgz", + "integrity": "sha512-C3GSKvMtdudHCN5HdmAMSRYR2kkhgdOfye4w0xzyii7lebVr4riCgmM6lRiSCnJn2w1Xz7ZZzHKuLrjx5620kw==", + "cpu": [ + "riscv64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-s390x-gnu": { + "version": "4.13.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.13.2.tgz", + "integrity": "sha512-l4U0KDFwzD36j7HdfJ5/TveEQ1fUTjFFQP5qIt9gBqBgu1G8/kCaq5Ok05kd5TG9F8Lltf3MoYsUMw3rNlJ0Yg==", + "cpu": [ + "s390x" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-gnu": { + "version": "4.13.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.13.2.tgz", + "integrity": "sha512-xXMLUAMzrtsvh3cZ448vbXqlUa7ZL8z0MwHp63K2IIID2+DeP5iWIT6g1SN7hg1VxPzqx0xZdiDM9l4n9LRU1A==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-musl": { + "version": "4.13.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.13.2.tgz", + "integrity": "sha512-M/JYAWickafUijWPai4ehrjzVPKRCyDb1SLuO+ZyPfoXgeCEAlgPkNXewFZx0zcnoIe3ay4UjXIMdXQXOZXWqA==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-win32-arm64-msvc": { + "version": "4.13.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.13.2.tgz", + "integrity": "sha512-2YWwoVg9KRkIKaXSh0mz3NmfurpmYoBBTAXA9qt7VXk0Xy12PoOP40EFuau+ajgALbbhi4uTj3tSG3tVseCjuA==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-ia32-msvc": { + "version": "4.13.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.13.2.tgz", + "integrity": "sha512-2FSsE9aQ6OWD20E498NYKEQLneShWes0NGMPQwxWOdws35qQXH+FplabOSP5zEe1pVjurSDOGEVCE2agFwSEsw==", + "cpu": [ + "ia32" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-msvc": { + "version": "4.13.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.13.2.tgz", + "integrity": "sha512-7h7J2nokcdPePdKykd8wtc8QqqkqxIrUz7MHj6aNr8waBRU//NLDVnNjQnqQO6fqtjrtCdftpbTuOKAyrAQETQ==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ] + }, "node_modules/@sinclair/typebox": { "version": "0.27.8", "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.27.8.tgz", @@ -1168,6 +1724,12 @@ "resolved": "https://registry.npmjs.org/@types/aria-query/-/aria-query-5.0.2.tgz", "integrity": "sha512-PHKZuMN+K5qgKIWhBodXzQslTo5P+K/6LqeKXS6O/4liIDdZqaX5RXrCK++LAw+y/nptN48YmUMFiQHRSWYwtQ==" }, + "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/istanbul-lib-coverage": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.4.tgz", @@ -1286,66 +1848,241 @@ "resolved": "https://registry.npmjs.org/@types/testing-library__jest-dom/-/testing-library__jest-dom-5.14.9.tgz", "integrity": "sha512-FSYhIjFlfOpGSRyVoMBMuS3ws5ehFQODymf3vlI7U1K8c7PHwWwFY7VREfmsuzHSOnoKs/9/Y983ayOs7eRzqw==", "dependencies": { - "@types/jest": "*" + "@types/jest": "*" + } + }, + "node_modules/@types/use-sync-external-store": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/@types/use-sync-external-store/-/use-sync-external-store-0.0.3.tgz", + "integrity": "sha512-EwmlvuaxPNej9+T4v5AuBPJa2x2UOJVdjCtDHgcDqitUeOtjnJKJ+apYjVcAoBEMjKW1VVFGZLUb5+qqa09XFA==" + }, + "node_modules/@types/yargs": { + "version": "17.0.25", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.25.tgz", + "integrity": "sha512-gy7iPgwnzNvxgAEi2bXOHWCVOG6f7xsprVJH4MjlAWeBmJ7vh/Y1kwMtUrs64ztf24zVIRCpr3n/z6gm9QIkgg==", + "dependencies": { + "@types/yargs-parser": "*" + } + }, + "node_modules/@types/yargs-parser": { + "version": "21.0.1", + "resolved": "https://registry.npmjs.org/@types/yargs-parser/-/yargs-parser-21.0.1.tgz", + "integrity": "sha512-axdPBuLuEJt0c4yI5OZssC19K2Mq1uKdrfZBzuxLvaztgqUtFYZUNw7lETExPYJR9jdEoIg4mb7RQKRQzOkeGQ==" + }, + "node_modules/@vitejs/plugin-react-refresh": { + "version": "1.3.6", + "resolved": "https://registry.npmjs.org/@vitejs/plugin-react-refresh/-/plugin-react-refresh-1.3.6.tgz", + "integrity": "sha512-iNR/UqhUOmFFxiezt0em9CgmiJBdWR+5jGxB2FihaoJfqGt76kiwaKoVOJVU5NYcDWMdN06LbyN2VIGIoYdsEA==", + "deprecated": "This package has been deprecated in favor of @vitejs/plugin-react", + "dependencies": { + "@babel/core": "^7.14.8", + "@babel/plugin-transform-react-jsx-self": "^7.14.5", + "@babel/plugin-transform-react-jsx-source": "^7.14.5", + "@rollup/pluginutils": "^4.1.1", + "react-refresh": "^0.10.0" + }, + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/@vitejs/plugin-react-refresh/node_modules/@rollup/pluginutils": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-4.2.1.tgz", + "integrity": "sha512-iKnFXr7NkdZAIHiIWE+BX5ULi/ucVFYWD6TbAV+rZctiRTY2PL6tsIKhoIOaoskiWAkgu+VsbXgUVDNLHf+InQ==", + "dependencies": { + "estree-walker": "^2.0.1", + "picomatch": "^2.2.2" + }, + "engines": { + "node": ">= 8.0.0" + } + }, + "node_modules/@vitejs/plugin-react-refresh/node_modules/estree-walker": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz", + "integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==" + }, + "node_modules/@vitejs/plugin-react-refresh/node_modules/react-refresh": { + "version": "0.10.0", + "resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.10.0.tgz", + "integrity": "sha512-PgidR3wST3dDYKr6b4pJoqQFpPGNKDSCDx4cZoshjXipw3LzO7mG1My2pwEzz2JVkF+inx3xRpDeQLFQGH/hsQ==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/@vitest/expect": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-1.4.0.tgz", + "integrity": "sha512-Jths0sWCJZ8BxjKe+p+eKsoqev1/T8lYcrjavEaz8auEJ4jAVY0GwW3JKmdVU4mmNPLPHixh4GNXP7GFtAiDHA==", + "dev": true, + "dependencies": { + "@vitest/spy": "1.4.0", + "@vitest/utils": "1.4.0", + "chai": "^4.3.10" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/runner": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-1.4.0.tgz", + "integrity": "sha512-EDYVSmesqlQ4RD2VvWo3hQgTJ7ZrFQ2VSJdfiJiArkCerDAGeyF1i6dHkmySqk573jLp6d/cfqCN+7wUB5tLgg==", + "dev": true, + "dependencies": { + "@vitest/utils": "1.4.0", + "p-limit": "^5.0.0", + "pathe": "^1.1.1" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/snapshot": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-1.4.0.tgz", + "integrity": "sha512-saAFnt5pPIA5qDGxOHxJ/XxhMFKkUSBJmVt5VgDsAqPTX6JP326r5C/c9UuCMPoXNzuudTPsYDZCoJ5ilpqG2A==", + "dev": true, + "dependencies": { + "magic-string": "^0.30.5", + "pathe": "^1.1.1", + "pretty-format": "^29.7.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/snapshot/node_modules/ansi-styles": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", + "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/@vitest/snapshot/node_modules/pretty-format": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.7.0.tgz", + "integrity": "sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==", + "dev": true, + "dependencies": { + "@jest/schemas": "^29.6.3", + "ansi-styles": "^5.0.0", + "react-is": "^18.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@vitest/snapshot/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==", + "dev": true + }, + "node_modules/@vitest/spy": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-1.4.0.tgz", + "integrity": "sha512-Ywau/Qs1DzM/8Uc+yA77CwSegizMlcgTJuYGAi0jujOteJOUf1ujunHThYo243KG9nAyWT3L9ifPYZ5+As/+6Q==", + "dev": true, + "dependencies": { + "tinyspy": "^2.2.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" } }, - "node_modules/@types/use-sync-external-store": { - "version": "0.0.3", - "resolved": "https://registry.npmjs.org/@types/use-sync-external-store/-/use-sync-external-store-0.0.3.tgz", - "integrity": "sha512-EwmlvuaxPNej9+T4v5AuBPJa2x2UOJVdjCtDHgcDqitUeOtjnJKJ+apYjVcAoBEMjKW1VVFGZLUb5+qqa09XFA==" - }, - "node_modules/@types/yargs": { - "version": "17.0.25", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.25.tgz", - "integrity": "sha512-gy7iPgwnzNvxgAEi2bXOHWCVOG6f7xsprVJH4MjlAWeBmJ7vh/Y1kwMtUrs64ztf24zVIRCpr3n/z6gm9QIkgg==", + "node_modules/@vitest/ui": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/@vitest/ui/-/ui-1.4.0.tgz", + "integrity": "sha512-XC6CMhN1gzYcGbpn6/Oanj4Au2EXwQEX6vpcOeLlZv8dy7g11Ukx8zwtYQbwxs9duK2s9j2o5rbQiCP5DPAcmw==", + "dev": true, "dependencies": { - "@types/yargs-parser": "*" + "@vitest/utils": "1.4.0", + "fast-glob": "^3.3.2", + "fflate": "^0.8.1", + "flatted": "^3.2.9", + "pathe": "^1.1.1", + "picocolors": "^1.0.0", + "sirv": "^2.0.4" + }, + "funding": { + "url": "https://opencollective.com/vitest" + }, + "peerDependencies": { + "vitest": "1.4.0" } }, - "node_modules/@types/yargs-parser": { - "version": "21.0.1", - "resolved": "https://registry.npmjs.org/@types/yargs-parser/-/yargs-parser-21.0.1.tgz", - "integrity": "sha512-axdPBuLuEJt0c4yI5OZssC19K2Mq1uKdrfZBzuxLvaztgqUtFYZUNw7lETExPYJR9jdEoIg4mb7RQKRQzOkeGQ==" - }, - "node_modules/@vitejs/plugin-react-refresh": { - "version": "1.3.6", - "resolved": "https://registry.npmjs.org/@vitejs/plugin-react-refresh/-/plugin-react-refresh-1.3.6.tgz", - "integrity": "sha512-iNR/UqhUOmFFxiezt0em9CgmiJBdWR+5jGxB2FihaoJfqGt76kiwaKoVOJVU5NYcDWMdN06LbyN2VIGIoYdsEA==", - "deprecated": "This package has been deprecated in favor of @vitejs/plugin-react", + "node_modules/@vitest/utils": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-1.4.0.tgz", + "integrity": "sha512-mx3Yd1/6e2Vt/PUC98DcqTirtfxUyAZ32uK82r8rZzbtBeBo+nqgnjx/LvqQdWsrvNtm14VmurNgcf4nqY5gJg==", + "dev": true, "dependencies": { - "@babel/core": "^7.14.8", - "@babel/plugin-transform-react-jsx-self": "^7.14.5", - "@babel/plugin-transform-react-jsx-source": "^7.14.5", - "@rollup/pluginutils": "^4.1.1", - "react-refresh": "^0.10.0" + "diff-sequences": "^29.6.3", + "estree-walker": "^3.0.3", + "loupe": "^2.3.7", + "pretty-format": "^29.7.0" }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/utils/node_modules/ansi-styles": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", + "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", + "dev": true, "engines": { - "node": ">=12.0.0" + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, - "node_modules/@vitejs/plugin-react-refresh/node_modules/@rollup/pluginutils": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-4.2.1.tgz", - "integrity": "sha512-iKnFXr7NkdZAIHiIWE+BX5ULi/ucVFYWD6TbAV+rZctiRTY2PL6tsIKhoIOaoskiWAkgu+VsbXgUVDNLHf+InQ==", + "node_modules/@vitest/utils/node_modules/pretty-format": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.7.0.tgz", + "integrity": "sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==", + "dev": true, "dependencies": { - "estree-walker": "^2.0.1", - "picomatch": "^2.2.2" + "@jest/schemas": "^29.6.3", + "ansi-styles": "^5.0.0", + "react-is": "^18.0.0" }, "engines": { - "node": ">= 8.0.0" + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "node_modules/@vitejs/plugin-react-refresh/node_modules/estree-walker": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz", - "integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==" + "node_modules/@vitest/utils/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==", + "dev": true }, - "node_modules/@vitejs/plugin-react-refresh/node_modules/react-refresh": { - "version": "0.10.0", - "resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.10.0.tgz", - "integrity": "sha512-PgidR3wST3dDYKr6b4pJoqQFpPGNKDSCDx4cZoshjXipw3LzO7mG1My2pwEzz2JVkF+inx3xRpDeQLFQGH/hsQ==", + "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.10.0" + "node": ">=0.4.0" + } + }, + "node_modules/acorn-walk": { + "version": "8.3.2", + "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.3.2.tgz", + "integrity": "sha512-cjkyv4OtNCIeqhHrfS81QWXoCBPExR/J62oyEqepVw8WaQeSqpW2uhuLPh1m9eWhDuOo/jUXVTlifvesOWp/4A==", + "dev": true, + "engines": { + "node": ">=0.4.0" } }, "node_modules/ansi-regex": { @@ -1415,6 +2152,15 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/assertion-error": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-1.1.0.tgz", + "integrity": "sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw==", + "dev": true, + "engines": { + "node": "*" + } + }, "node_modules/asynckit": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", @@ -1558,6 +2304,15 @@ "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" } }, + "node_modules/cac": { + "version": "6.7.14", + "resolved": "https://registry.npmjs.org/cac/-/cac-6.7.14.tgz", + "integrity": "sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, "node_modules/call-bind": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz", @@ -1599,9 +2354,9 @@ } }, "node_modules/caniuse-lite": { - "version": "1.0.30001540", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001540.tgz", - "integrity": "sha512-9JL38jscuTJBTcuETxm8QLsFr/F6v0CYYTEU6r5+qSM98P2Q0Hmu0eG1dTG5GBUmywU3UlcVOUSIJYY47rdFSw==", + "version": "1.0.30001632", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001632.tgz", + "integrity": "sha512-udx3o7yHJfUxMLkGohMlVHCvFvWmirKh9JAH/d7WOLPetlH+LTL5cocMZ0t7oZx/mdlOWXti97xLZWc8uURRHg==", "funding": [ { "type": "opencollective", @@ -1617,6 +2372,24 @@ } ] }, + "node_modules/chai": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/chai/-/chai-4.4.1.tgz", + "integrity": "sha512-13sOfMv2+DWduEU+/xbun3LScLoqN17nBeTLUsmDfKdoiC1fr0n9PU4guu4AhRcOVFk/sW8LyZWHuhWtQZiF+g==", + "dev": true, + "dependencies": { + "assertion-error": "^1.1.0", + "check-error": "^1.0.3", + "deep-eql": "^4.1.3", + "get-func-name": "^2.0.2", + "loupe": "^2.3.6", + "pathval": "^1.1.1", + "type-detect": "^4.0.8" + }, + "engines": { + "node": ">=4" + } + }, "node_modules/chalk": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/chalk/-/chalk-3.0.0.tgz", @@ -1629,6 +2402,18 @@ "node": ">=8" } }, + "node_modules/check-error": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/check-error/-/check-error-1.0.3.tgz", + "integrity": "sha512-iKEoDYaRmd1mxM90a2OEfWhjsjPpYPuQ+lMYsoxB126+t8fw7ySEO48nmDg5COTjxDI65/Y2OWpeEHk3ZOe8zg==", + "dev": true, + "dependencies": { + "get-func-name": "^2.0.2" + }, + "engines": { + "node": "*" + } + }, "node_modules/chokidar": { "version": "3.5.3", "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz", @@ -1795,6 +2580,18 @@ } } }, + "node_modules/deep-eql": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-4.1.3.tgz", + "integrity": "sha512-WaEtAOpRA1MQ0eohqZjpGD8zdI0Ovsm8mmFhaDN8dvDZzyoUMcYDnf5Y6iu7HTXxf8JDS23qWa4a+hKCDyOPzw==", + "dev": true, + "dependencies": { + "type-detect": "^4.0.0" + }, + "engines": { + "node": ">=6" + } + }, "node_modules/deep-equal": { "version": "2.2.2", "resolved": "https://registry.npmjs.org/deep-equal/-/deep-equal-2.2.2.tgz", @@ -2300,6 +3097,38 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/estree-walker": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-3.0.3.tgz", + "integrity": "sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==", + "dev": true, + "dependencies": { + "@types/estree": "^1.0.0" + } + }, + "node_modules/execa": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/execa/-/execa-8.0.1.tgz", + "integrity": "sha512-VyhnebXciFV2DESc+p6B+y0LjSm0krU4OgJN44qFAhBY0TJ+1V61tYD2+wHusZ6F9n5K+vl8k0sTy7PEfV4qpg==", + "dev": true, + "dependencies": { + "cross-spawn": "^7.0.3", + "get-stream": "^8.0.1", + "human-signals": "^5.0.0", + "is-stream": "^3.0.0", + "merge-stream": "^2.0.0", + "npm-run-path": "^5.1.0", + "onetime": "^6.0.0", + "signal-exit": "^4.1.0", + "strip-final-newline": "^3.0.0" + }, + "engines": { + "node": ">=16.17" + }, + "funding": { + "url": "https://github.com/sindresorhus/execa?sponsor=1" + } + }, "node_modules/expect": { "version": "29.7.0", "resolved": "https://registry.npmjs.org/expect/-/expect-29.7.0.tgz", @@ -2352,6 +3181,12 @@ "reusify": "^1.0.4" } }, + "node_modules/fflate": { + "version": "0.8.2", + "resolved": "https://registry.npmjs.org/fflate/-/fflate-0.8.2.tgz", + "integrity": "sha512-cPJU47OaAoCbg0pBvzsgpTPhmhqI5eJjh/JIu8tPj5q+T7iLvW/JAYUqmE7KOB4R1ZyEhzBaIQpQpardBF5z8A==", + "dev": true + }, "node_modules/fill-range": { "version": "7.0.1", "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", @@ -2368,6 +3203,12 @@ "resolved": "https://registry.npmjs.org/find-root/-/find-root-1.1.0.tgz", "integrity": "sha512-NKfW6bec6GfKc0SGx1e07QZY9PE99u0Bft/0rzSD5k3sO/vwkVUpDUKVm5Gpp5Ue3YfShPFTX2070tDs5kB9Ng==" }, + "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.4", "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.4.tgz", @@ -2494,6 +3335,15 @@ "node": ">=6.9.0" } }, + "node_modules/get-func-name": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/get-func-name/-/get-func-name-2.0.2.tgz", + "integrity": "sha512-8vXOvuE167CtIc3OyItco7N/dpRtBbYOsPsXCz7X/PMnlGjYjSGuZJgM1Y7mmew7BKf9BqvLX2tnOVy1BBUsxQ==", + "dev": true, + "engines": { + "node": "*" + } + }, "node_modules/get-intrinsic": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.1.tgz", @@ -2508,6 +3358,18 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/get-stream": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-8.0.1.tgz", + "integrity": "sha512-VaUJspBffn/LMCJVoMvSAdmscJyS1auj5Zulnn5UoYcY531UWmdwhRWkcGKnGU93m5HSXP9LP2usOryrBtQowA==", + "dev": true, + "engines": { + "node": ">=16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/glob": { "version": "10.3.10", "resolved": "https://registry.npmjs.org/glob/-/glob-10.3.10.tgz", @@ -2653,6 +3515,15 @@ "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==" }, + "node_modules/human-signals": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-5.0.0.tgz", + "integrity": "sha512-AXcZb6vzzrFAUE61HnN4mpLqd/cSIwNQjtNWR0euPm6y0iqx3G4gOXaIDdtdDwZmhwe82LA6+zinmW4UBWVePQ==", + "dev": true, + "engines": { + "node": ">=16.17.0" + } + }, "node_modules/immer": { "version": "10.0.3", "resolved": "https://registry.npmjs.org/immer/-/immer-10.0.3.tgz", @@ -2912,6 +3783,18 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/is-stream": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-3.0.0.tgz", + "integrity": "sha512-LnQR4bZ9IADDRSkvpqMGvt/tEJWclzklNgSw48V5EAaAeDd6qGvN8ei6k5p0tvxSR171VmGyHuTiAOfxAbr8kA==", + "dev": true, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/is-string": { "version": "1.0.7", "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.0.7.tgz", @@ -3262,6 +4145,12 @@ "node": ">=6" } }, + "node_modules/jsonc-parser": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/jsonc-parser/-/jsonc-parser-3.2.1.tgz", + "integrity": "sha512-AilxAyFOAcK5wA1+LeaySVBrHsGQvUFCDWXKpZjzaL0PqW+xfBOttn8GNtWKFWqneyMZj41MWF9Kl6iPWLwgOA==", + "dev": true + }, "node_modules/lilconfig": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-2.1.0.tgz", @@ -3276,6 +4165,22 @@ "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==" }, + "node_modules/local-pkg": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/local-pkg/-/local-pkg-0.5.0.tgz", + "integrity": "sha512-ok6z3qlYyCDS4ZEU27HaU6x/xZa9Whf8jD4ptH5UZTQYZVYeb9bnZ3ojVhiJNLiXK1Hfc0GNbLXcmZ5plLDDBg==", + "dev": true, + "dependencies": { + "mlly": "^1.4.2", + "pkg-types": "^1.0.3" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/antfu" + } + }, "node_modules/lodash": { "version": "4.17.21", "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", @@ -3292,6 +4197,15 @@ "loose-envify": "cli.js" } }, + "node_modules/loupe": { + "version": "2.3.7", + "resolved": "https://registry.npmjs.org/loupe/-/loupe-2.3.7.tgz", + "integrity": "sha512-zSMINGVYkdpYSOBmLi0D1Uo7JU9nVdQKrHxC8eYlV+9YKK9WePqAlL7lSlorG/U2Fw1w0hTBmaa/jrQ3UbPHtA==", + "dev": true, + "dependencies": { + "get-func-name": "^2.0.1" + } + }, "node_modules/lru-cache": { "version": "5.1.1", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", @@ -3308,11 +4222,29 @@ "lz-string": "bin/bin.js" } }, + "node_modules/magic-string": { + "version": "0.30.8", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.8.tgz", + "integrity": "sha512-ISQTe55T2ao7XtlAStud6qwYPZjE4GK1S/BeVPus4jrq6JuOnQ00YKQC581RWhR122W7msZV263KzVeLoqidyQ==", + "dev": true, + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.4.15" + }, + "engines": { + "node": ">=12" + } + }, "node_modules/memoize-one": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/memoize-one/-/memoize-one-6.0.0.tgz", "integrity": "sha512-rkpe71W0N0c0Xz6QD0eJETuWAJGnJ9afsl1srmwPrI+yBCkge5EycXXbYRyvL29zZVUWQCY7InPRCv3GDXuZNw==" }, + "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", @@ -3353,6 +4285,18 @@ "node": ">= 0.6" } }, + "node_modules/mimic-fn": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-4.0.0.tgz", + "integrity": "sha512-vqiC06CuhBTUdZH+RYl8sFrL096vA45Ok5ISO6sE/Mr1jRbGH4Csnhi8f3wKVl7x8mO4Au7Ir9D3Oyv1VYMFJw==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/min-indent": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/min-indent/-/min-indent-1.0.1.tgz", @@ -3385,6 +4329,27 @@ "node": ">=16 || 14 >=14.17" } }, + "node_modules/mlly": { + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/mlly/-/mlly-1.6.1.tgz", + "integrity": "sha512-vLgaHvaeunuOXHSmEbZ9izxPx3USsk8KCQ8iC+aTlp5sKRSoZvwhHh5L9VbKSaVC6sJDqbyohIS76E2VmHIPAA==", + "dev": true, + "dependencies": { + "acorn": "^8.11.3", + "pathe": "^1.1.2", + "pkg-types": "^1.0.3", + "ufo": "^1.3.2" + } + }, + "node_modules/mrmime": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/mrmime/-/mrmime-2.0.0.tgz", + "integrity": "sha512-eu38+hdgojoyq63s+yTpN4XMBdt5l8HhMhc4VKLO9KM5caLIBvUm4thi7fFaxyTmCKeNnXZ5pAlBwCUnhA09uw==", + "dev": true, + "engines": { + "node": ">=10" + } + }, "node_modules/ms": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", @@ -3441,6 +4406,33 @@ "node": ">=0.10.0" } }, + "node_modules/npm-run-path": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-5.3.0.tgz", + "integrity": "sha512-ppwTtiJZq0O/ai0z7yfudtBpWIoxM8yE6nHi1X47eFR2EWORqfbu6CnPlNsjeN683eT0qG6H/Pyf9fCcvjnnnQ==", + "dev": true, + "dependencies": { + "path-key": "^4.0.0" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/npm-run-path/node_modules/path-key": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-4.0.0.tgz", + "integrity": "sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/object-assign": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", @@ -3506,6 +4498,36 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/onetime": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-6.0.0.tgz", + "integrity": "sha512-1FlR+gjXK7X+AsAHso35MnyN5KqGwJRi/31ft6x0M194ht7S+rWAvd7PHss9xSKMzE0asv1pyIHaJYq+BbacAQ==", + "dev": true, + "dependencies": { + "mimic-fn": "^4.0.0" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-limit": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-5.0.0.tgz", + "integrity": "sha512-/Eaoq+QyLSiXQ4lyYV23f14mZRQcXnxfHrN0vCai+ak9G0pp9iEQukIIZq5NccEvwRB8PUnZT0KsOoDCINS1qQ==", + "dev": true, + "dependencies": { + "yocto-queue": "^1.0.0" + }, + "engines": { + "node": ">=18" + }, + "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", @@ -3581,6 +4603,21 @@ "node": ">=8" } }, + "node_modules/pathe": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/pathe/-/pathe-1.1.2.tgz", + "integrity": "sha512-whLdWMYL2TwI08hn8/ZqAbrVemu0LNaNNJZX73O6qaIdCTfXutsLhMkjdENX0qhsQ9uIimo4/aQOmXkoon2nDQ==", + "dev": true + }, + "node_modules/pathval": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/pathval/-/pathval-1.1.1.tgz", + "integrity": "sha512-Dp6zGqpTdETdR63lehJYPeIOqpiNBNtc7BpWSLrOje7UaIsE5aY92r/AunQA7rsXvet3lrJ3JnZX29UPTKXyKQ==", + "dev": true, + "engines": { + "node": "*" + } + }, "node_modules/picocolors": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", @@ -3615,10 +4652,21 @@ "node": ">= 6" } }, + "node_modules/pkg-types": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/pkg-types/-/pkg-types-1.0.3.tgz", + "integrity": "sha512-nN7pYi0AQqJnoLPC9eHFQ8AcyaixBUOwvqc5TDnIKCMEE6I0y8P7OKA7fPexsXGCGxQDl/cmrLAp26LhcwxZ4A==", + "dev": true, + "dependencies": { + "jsonc-parser": "^3.2.0", + "mlly": "^1.2.0", + "pathe": "^1.1.0" + } + }, "node_modules/postcss": { - "version": "8.4.32", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.32.tgz", - "integrity": "sha512-D/kj5JNu6oo2EIy+XL/26JEDTlIbB8hw85G8StOE6L74RQAVVP5rej6wxCNqyMbR4RkPfqvezVbPw81Ngd6Kcw==", + "version": "8.4.38", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.38.tgz", + "integrity": "sha512-Wglpdk03BSfXkHoQa3b/oulrotAkwrlLDRSOb9D0bN86FdRyE9lppSp33aHNPgBa0JKCoB+drFLZkQoRRYae5A==", "funding": [ { "type": "opencollective", @@ -3636,7 +4684,7 @@ "dependencies": { "nanoid": "^3.3.7", "picocolors": "^1.0.0", - "source-map-js": "^1.0.2" + "source-map-js": "^1.2.0" }, "engines": { "node": "^10 || ^12 || >=14" @@ -3869,6 +4917,14 @@ "resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz", "integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==" }, + "node_modules/react-loading-skeleton": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/react-loading-skeleton/-/react-loading-skeleton-3.4.0.tgz", + "integrity": "sha512-1oJEBc9+wn7BbkQQk7YodlYEIjgeR+GrRjD+QXkVjwZN7LGIcAFHrx4NhT7UHGBxNY1+zax3c+Fo6XQM4R7CgA==", + "peerDependencies": { + "react": ">=16.8.0" + } + }, "node_modules/react-redux": { "version": "9.0.4", "resolved": "https://registry.npmjs.org/react-redux/-/react-redux-9.0.4.tgz", @@ -4071,6 +5127,40 @@ "node": ">=0.10.0" } }, + "node_modules/rollup": { + "version": "4.13.2", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.13.2.tgz", + "integrity": "sha512-MIlLgsdMprDBXC+4hsPgzWUasLO9CE4zOkj/u6j+Z6j5A4zRY+CtiXAdJyPtgCsc42g658Aeh1DlrdVEJhsL2g==", + "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.13.2", + "@rollup/rollup-android-arm64": "4.13.2", + "@rollup/rollup-darwin-arm64": "4.13.2", + "@rollup/rollup-darwin-x64": "4.13.2", + "@rollup/rollup-linux-arm-gnueabihf": "4.13.2", + "@rollup/rollup-linux-arm64-gnu": "4.13.2", + "@rollup/rollup-linux-arm64-musl": "4.13.2", + "@rollup/rollup-linux-powerpc64le-gnu": "4.13.2", + "@rollup/rollup-linux-riscv64-gnu": "4.13.2", + "@rollup/rollup-linux-s390x-gnu": "4.13.2", + "@rollup/rollup-linux-x64-gnu": "4.13.2", + "@rollup/rollup-linux-x64-musl": "4.13.2", + "@rollup/rollup-win32-arm64-msvc": "4.13.2", + "@rollup/rollup-win32-ia32-msvc": "4.13.2", + "@rollup/rollup-win32-x64-msvc": "4.13.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", @@ -4149,6 +5239,12 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/siginfo": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/siginfo/-/siginfo-2.0.0.tgz", + "integrity": "sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==", + "dev": true + }, "node_modules/signal-exit": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", @@ -4161,6 +5257,20 @@ "url": "https://github.com/sponsors/isaacs" } }, + "node_modules/sirv": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/sirv/-/sirv-2.0.4.tgz", + "integrity": "sha512-94Bdh3cC2PKrbgSOUqTiGPWVZeSiXfKOVZNJniWoqrWrRkB1CJzBU3NEbiTsPcYy1lDsANA/THzS+9WBiy5nfQ==", + "dev": true, + "dependencies": { + "@polka/url": "^1.0.0-next.24", + "mrmime": "^2.0.0", + "totalist": "^3.0.0" + }, + "engines": { + "node": ">= 10" + } + }, "node_modules/slash": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", @@ -4178,9 +5288,9 @@ } }, "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==", + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.0.tgz", + "integrity": "sha512-itJW8lvSA0TXEphiRoawsCksnlf8SyvmFzIhltqAHluXd88pkCd+cXJVHTDwdCr0IzwptSm035IHQktUu1QUMg==", "engines": { "node": ">=0.10.0" } @@ -4204,6 +5314,18 @@ "node": ">=8" } }, + "node_modules/stackback": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/stackback/-/stackback-0.0.2.tgz", + "integrity": "sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==", + "dev": true + }, + "node_modules/std-env": { + "version": "3.7.0", + "resolved": "https://registry.npmjs.org/std-env/-/std-env-3.7.0.tgz", + "integrity": "sha512-JPbdCEQLj1w5GilpiHAx3qJvFndqybBysA3qUOnznweH4QbNYUsW/ea8QzSrnh0vNsezMMw5bcVool8lM0gwzg==", + "dev": true + }, "node_modules/stop-iteration-iterator": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/stop-iteration-iterator/-/stop-iteration-iterator-1.0.0.tgz", @@ -4305,6 +5427,18 @@ "url": "https://github.com/chalk/ansi-regex?sponsor=1" } }, + "node_modules/strip-final-newline": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-3.0.0.tgz", + "integrity": "sha512-dOESqjYr96iWYylGObzd39EuNTa5VJxyvVAEm5Jnh7KGo75V43Hk1odPQkNDyXNmUR6k+gEiDVXnjB8HJ3crXw==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/strip-indent": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/strip-indent/-/strip-indent-3.0.0.tgz", @@ -4316,6 +5450,24 @@ "node": ">=8" } }, + "node_modules/strip-literal": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/strip-literal/-/strip-literal-2.1.0.tgz", + "integrity": "sha512-Op+UycaUt/8FbN/Z2TWPBLge3jWrP3xj10f3fnYxf052bKuS3EKs1ZQcVGjnEMdsNVAM+plXRdmjrZ/KgG3Skw==", + "dev": true, + "dependencies": { + "js-tokens": "^9.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/antfu" + } + }, + "node_modules/strip-literal/node_modules/js-tokens": { + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-9.0.0.tgz", + "integrity": "sha512-WriZw1luRMlmV3LGJaR6QOJjWwgLUTf89OwT2lUOyjX2dJGBwgmIkbcz+7WFZjrZM635JOIR517++e/67CP9dQ==", + "dev": true + }, "node_modules/stylis": { "version": "4.2.0", "resolved": "https://registry.npmjs.org/stylis/-/stylis-4.2.0.tgz", @@ -4428,6 +5580,30 @@ "node": ">=0.8" } }, + "node_modules/tinybench": { + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/tinybench/-/tinybench-2.6.0.tgz", + "integrity": "sha512-N8hW3PG/3aOoZAN5V/NSAEDz0ZixDSSt5b/a05iqtpgfLWMSVuCo7w0k2vVvEjdrIoeGqZzweX2WlyioNIHchA==", + "dev": true + }, + "node_modules/tinypool": { + "version": "0.8.3", + "resolved": "https://registry.npmjs.org/tinypool/-/tinypool-0.8.3.tgz", + "integrity": "sha512-Ud7uepAklqRH1bvwy22ynrliC7Dljz7Tm8M/0RBUW+YRa4YHhZ6e4PpgE+fu1zr/WqB1kbeuVrdfeuyIBpy4tw==", + "dev": true, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/tinyspy": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/tinyspy/-/tinyspy-2.2.1.tgz", + "integrity": "sha512-KYad6Vy5VDWV4GH3fjpseMQ/XU2BhIYP7Vzd0LG44qRWm/Yt2WCOTicFdvmgo6gWaqooMQCawTtILVQJupKu7A==", + "dev": true, + "engines": { + "node": ">=14.0.0" + } + }, "node_modules/to-fast-properties": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", @@ -4447,6 +5623,15 @@ "node": ">=8.0" } }, + "node_modules/totalist": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/totalist/-/totalist-3.0.1.tgz", + "integrity": "sha512-sf4i37nQ2LBx4m3wB74y+ubopq6W/dIzXg0FDGjsYnZHVa1Da8FH853wlL2gtUhg+xJXjfk3kUZS3BRoQeoQBQ==", + "dev": true, + "engines": { + "node": ">=6" + } + }, "node_modules/ts-interface-checker": { "version": "0.1.13", "resolved": "https://registry.npmjs.org/ts-interface-checker/-/ts-interface-checker-0.1.13.tgz", @@ -4458,6 +5643,15 @@ "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==" }, + "node_modules/type-detect": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz", + "integrity": "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==", + "dev": true, + "engines": { + "node": ">=4" + } + }, "node_modules/typescript": { "version": "5.3.3", "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.3.3.tgz", @@ -4471,6 +5665,12 @@ "node": ">=14.17" } }, + "node_modules/ufo": { + "version": "1.5.3", + "resolved": "https://registry.npmjs.org/ufo/-/ufo-1.5.3.tgz", + "integrity": "sha512-Y7HYmWaFwPUmkoQCUIAYpKqkOf+SbVj/2fJJZ4RJMCfZp0rTGwRbzQD+HghfnhKOjL9E01okqz+ncJskGYfBNw==", + "dev": true + }, "node_modules/undici-types": { "version": "5.26.5", "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", @@ -4568,6 +5768,137 @@ } } }, + "node_modules/vite-node": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/vite-node/-/vite-node-1.4.0.tgz", + "integrity": "sha512-VZDAseqjrHgNd4Kh8icYHWzTKSCZMhia7GyHfhtzLW33fZlG9SwsB6CEhgyVOWkJfJ2pFLrp/Gj1FSfAiqH9Lw==", + "dev": true, + "dependencies": { + "cac": "^6.7.14", + "debug": "^4.3.4", + "pathe": "^1.1.1", + "picocolors": "^1.0.0", + "vite": "^5.0.0" + }, + "bin": { + "vite-node": "vite-node.mjs" + }, + "engines": { + "node": "^18.0.0 || >=20.0.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/vite-node/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/vite-node/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/vite-node/node_modules/vite": { + "version": "5.2.7", + "resolved": "https://registry.npmjs.org/vite/-/vite-5.2.7.tgz", + "integrity": "sha512-k14PWOKLI6pMaSzAuGtT+Cf0YmIx12z9YGon39onaJNy8DLBfBJrzg9FQEmkAM5lpHBZs9wksWAsyF/HkpEwJA==", + "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/vite-plugin-svgr": { "version": "0.3.0", "resolved": "https://registry.npmjs.org/vite-plugin-svgr/-/vite-plugin-svgr-0.3.0.tgz", @@ -4593,6 +5924,180 @@ "fsevents": "~2.3.2" } }, + "node_modules/vitest": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/vitest/-/vitest-1.4.0.tgz", + "integrity": "sha512-gujzn0g7fmwf83/WzrDTnncZt2UiXP41mHuFYFrdwaLRVQ6JYQEiME2IfEjU3vcFL3VKa75XhI3lFgn+hfVsQw==", + "dev": true, + "dependencies": { + "@vitest/expect": "1.4.0", + "@vitest/runner": "1.4.0", + "@vitest/snapshot": "1.4.0", + "@vitest/spy": "1.4.0", + "@vitest/utils": "1.4.0", + "acorn-walk": "^8.3.2", + "chai": "^4.3.10", + "debug": "^4.3.4", + "execa": "^8.0.1", + "local-pkg": "^0.5.0", + "magic-string": "^0.30.5", + "pathe": "^1.1.1", + "picocolors": "^1.0.0", + "std-env": "^3.5.0", + "strip-literal": "^2.0.0", + "tinybench": "^2.5.1", + "tinypool": "^0.8.2", + "vite": "^5.0.0", + "vite-node": "1.4.0", + "why-is-node-running": "^2.2.2" + }, + "bin": { + "vitest": "vitest.mjs" + }, + "engines": { + "node": "^18.0.0 || >=20.0.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + }, + "peerDependencies": { + "@edge-runtime/vm": "*", + "@types/node": "^18.0.0 || >=20.0.0", + "@vitest/browser": "1.4.0", + "@vitest/ui": "1.4.0", + "happy-dom": "*", + "jsdom": "*" + }, + "peerDependenciesMeta": { + "@edge-runtime/vm": { + "optional": true + }, + "@types/node": { + "optional": true + }, + "@vitest/browser": { + "optional": true + }, + "@vitest/ui": { + "optional": true + }, + "happy-dom": { + "optional": true + }, + "jsdom": { + "optional": true + } + } + }, + "node_modules/vitest/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/vitest/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/vitest/node_modules/vite": { + "version": "5.2.7", + "resolved": "https://registry.npmjs.org/vite/-/vite-5.2.7.tgz", + "integrity": "sha512-k14PWOKLI6pMaSzAuGtT+Cf0YmIx12z9YGon39onaJNy8DLBfBJrzg9FQEmkAM5lpHBZs9wksWAsyF/HkpEwJA==", + "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/web-vitals": { "version": "2.1.4", "resolved": "https://registry.npmjs.org/web-vitals/-/web-vitals-2.1.4.tgz", @@ -4660,6 +6165,22 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/why-is-node-running": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/why-is-node-running/-/why-is-node-running-2.2.2.tgz", + "integrity": "sha512-6tSwToZxTOcotxHeA+qGCq1mVzKR3CwcJGmVcY+QE8SHy6TnpFnh8PAvPNHYr7EcuVeG0QSMxtYCuO1ta/G/oA==", + "dev": true, + "dependencies": { + "siginfo": "^2.0.0", + "stackback": "0.0.2" + }, + "bin": { + "why-is-node-running": "cli.js" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/wrap-ansi": { "version": "8.1.0", "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz", @@ -4751,6 +6272,18 @@ "engines": { "node": ">= 6" } + }, + "node_modules/yocto-queue": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-1.0.0.tgz", + "integrity": "sha512-9bnSc/HEW2uRy67wc+T8UwauLuPJVn28jb+GtJY16iiKWyvmYJRXVT4UamsAEGQfPohgr2q4Tq0sQbQlxTfi1g==", + "dev": true, + "engines": { + "node": ">=12.20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } } } } diff --git a/frontend/package.json b/frontend/package.json index f42f08c..7268397 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -15,6 +15,7 @@ "react": "^18.2.0", "react-dom": "^18.2.0", "react-icons": "^5.0.1", + "react-loading-skeleton": "^3.4.0", "react-redux": "^9.0.4", "react-router-dom": "^6.15.0", "react-select": "^5.8.0", @@ -27,9 +28,9 @@ }, "scripts": { "dev": "vite", - "build": "tsc && vite build", + "build": "rd /s /q ..\\backend\\build && tsc && vite build && xcopy build ..\\backend\\build /e /i /y", "serve": "vite preview", - "test": "react-scripts test", + "test:ui": "vitest --ui --api 1337", "eject": "react-scripts eject" }, "eslintConfig": { @@ -52,9 +53,11 @@ }, "devDependencies": { "@types/node": "^20.10.5", + "@vitest/ui": "^1.4.0", "autoprefixer": "^10.4.16", "postcss": "^8.4.32", "tailwindcss": "^3.4.0", - "typescript": "^5.3.3" + "typescript": "^5.3.3", + "vitest": "^1.4.0" } } diff --git a/frontend/public/manifest.json b/frontend/public/manifest.json index 080d6c7..e485a6d 100644 --- a/frontend/public/manifest.json +++ b/frontend/public/manifest.json @@ -1,6 +1,6 @@ { - "short_name": "React App", - "name": "Create React App Sample", + "short_name": "Schwarz", + "name": "Schwarz - The all-in-one platform for 3D Artists", "icons": [ { "src": "favicon.ico", diff --git a/frontend/src/App.jsx b/frontend/src/App.jsx index d7340ce..4e4f312 100644 --- a/frontend/src/App.jsx +++ b/frontend/src/App.jsx @@ -1,51 +1,59 @@ import React from 'react'; -import './App.css'; import Otpscreen from "./pages/TwoFa/Otpscreen"; import TwofaForm from "./pages/TwoFa/TwofaOptions"; -import { BrowserRouter as Router, Routes, Route } from "react-router-dom"; -import LoginForm from "./pages/Login/Login"; -import SettingsPage from "./pages/profile/settings"; +import {BrowserRouter as Router, Route, Routes} from "react-router-dom"; +import LoginForm from "./pages/Login/LoginScreen"; import SignupScreen from "./pages/Signup/SignupScreen"; -import SignupTab3 from "./pages/Signup/components/SignupTab3"; -import styles from './MyStyle.css'; -import Profile from './pages/profile/Profile'; +import {LandingScreen} from "./pages/LandingScreen"; +import Store from "./pages/Marketplace/MarketLanding"; +import Activate from "./pages/EmailActivation/Activate"; +import Layout from "./hocs/Layout"; +import {Provider} from "react-redux"; +import {store} from "./app/store"; +import UserAccount from "./pages/UserAccount/UserAccount"; function App() { - return ( -
- - - } /> - } /> - } /> - } /> - } /> - } /> - {/**/} - {/**/} - {/**/} - {/**/} - {/*} />*/} - - - -{/*
*/} -{/*
*/} -{/*
*/} -{/*
*/} -{/*
*/} -{/* */} -{/*

Loading...

*/} -{/*
*/} -{/*
*/} - - ); + return ( +
+ + + + + }/> + }/> + }/> + }/> + }/> + }/> + }/> + } /> + {/*} />*/} + {/**/} + {/**/} + {/**/} + {/*} />*/} + + + + + + {/*
*/} + {/*
*/} + {/*
*/} + {/*
*/} + {/*
*/} + {/* */} + {/*

Loading...

*/} + {/*
*/} + {/*
*/} + + ); } diff --git a/frontend/src/App.test.js b/frontend/src/App.test.js deleted file mode 100644 index 1f03afe..0000000 --- a/frontend/src/App.test.js +++ /dev/null @@ -1,8 +0,0 @@ -import { render, screen } from '@testing-library/react'; -import App from './App'; - -test('renders learn react link', () => { - render(); - const linkElement = screen.getByText(/learn react/i); - expect(linkElement).toBeInTheDocument(); -}); diff --git a/frontend/src/Routes.jsx b/frontend/src/Routes.jsx new file mode 100644 index 0000000..4827d99 --- /dev/null +++ b/frontend/src/Routes.jsx @@ -0,0 +1,61 @@ +import React from "react"; +import {BrowserRouter as Router, Route, Routes} from "react-router-dom"; +import LoginForm from "./pages/Login/LoginScreen"; +import SignupScreen from "./pages/Signup/SignupScreen"; +import Otpscreen from "./pages/TwoFa/Otpscreen"; +import TwofaForm from "./pages/TwoFa/TwofaOptions"; + +// import Home from "pages/Home"; +// import NotFound from "pages/NotFound"; +// const MyProfileHistory = React.lazy(() => import("pages/MyProfileHistory")); +// const MyProfileWallet = React.lazy(() => import("pages/MyProfileWallet")); +// const MyProfileCollection = React.lazy( +// () => import("pages/MyProfileCollection"), +// ); +// const MyProfile = React.lazy(() => import("pages/MyProfilePreview")); +// const Saved = React.lazy(() => import("pages/Saved")); +// const ActiveBid = React.lazy(() => import("pages/ActiveBid")); +// const OtherUserProfile = React.lazy(() => import("pages/OtherUserProfile")); +// const MarketDetail = React.lazy(() => import("pages/MarketDetail")); +const Market = React.lazy(() => import("./pages/Marketplace/MarketLanding")); +// const Settings = React.lazy(() => import("pages/Settings")); +// const Message = React.lazy(() => import("pages/Message")); +// const Dashboard = React.lazy(() => import("pages/Dashboard")); + +const ProjectRoutes = () => { + return ( + Loading...}> + + + {/*} />*/} + {/*} />*/} + {/*} />*/} + {/*} />*/} + }/> + {/*} />*/} + {/*} />*/} + {/*} />*/} + {/*} />*/} + {/*} />*/} + {/*}*/} + {/*/>*/} + {/*} />*/} + {/*} />*/} + {/*} />*/} + }/> + }/> + }/> + }/> + {/**/} + {/**/} + {/**/} + {/**/} + {/*} />*/} + + + + ); +}; +export default ProjectRoutes; diff --git a/frontend/src/assets/css/button.css b/frontend/src/assets/css/button.css new file mode 100644 index 0000000..c1aa6c9 --- /dev/null +++ b/frontend/src/assets/css/button.css @@ -0,0 +1,34 @@ +:root { + --btn-shadow: 1px 1px 25px 10px rgba(255, 255, 255, 0.5); + --shine-degree: 120deg; + --shine-color: rgba(255, 255, 255, 0.2); + --shine-effect: linear-gradient(var(--shine-degree), transparent, var(--shine-color), transparent); + --shine-transition: all 0.65s ease-in-out; +} +.btn { + position: relative; + overflow: hidden; + text-decoration: none; +} +.btn:not(:last-child) { + margin-bottom: 3rem; +} +.btn::before { + content: ""; + position: absolute; + top: 0; + width: 100%; + height: 100%; + background: var(--shine-effect); +} +.btn-hover::before { + left: -100%; + transition: var(--shine-transition); +} +.btn-hover:hover { + box-shadow: var(--btn-shadow); + background-color: #232323; +} +.btn-hover:hover::before { + left: 100%; +} \ No newline at end of file diff --git a/frontend/src/assets/css/dialogbox.css b/frontend/src/assets/css/dialogbox.css new file mode 100644 index 0000000..17182b5 --- /dev/null +++ b/frontend/src/assets/css/dialogbox.css @@ -0,0 +1,22 @@ +.speech-bubble { + z-index: 10; + filter: drop-shadow(0px 2px 5px rgba(50, 50, 105, 0.15)) drop-shadow(0px 1px 1px rgba(0, 0, 0, 0.05)); +} + +.speech-bubble:after { + z-index: 9; + content: ''; + position: absolute; + bottom: 0; + left: 20px; + width: 0; + height: 0; + border-left: 0px solid transparent; + border-right: 16px solid transparent; + border-top: 14px solid transparent; + border-bottom: 0; + border-top-color: white; + margin-left: -8px; + margin-bottom: -11px; + filter: drop-shadow(0px 2px 5px rgba(50, 50, 105, 0.02)) drop-shadow(0px 1px 1px rgba(0, 0, 0, 0.02)); +} diff --git a/frontend/src/assets/css/megamenu.css b/frontend/src/assets/css/megamenu.css new file mode 100644 index 0000000..56efbb0 --- /dev/null +++ b/frontend/src/assets/css/megamenu.css @@ -0,0 +1,8 @@ +.menu-items li:hover .mega-menu { + opacity: 1; + visibility: visible; +} + +.menu-items li:hover ~ .mega-container { + box-shadow: none !important; +} \ No newline at end of file diff --git a/frontend/src/assets/css/sidebar.css b/frontend/src/assets/css/sidebar.css new file mode 100644 index 0000000..7732283 --- /dev/null +++ b/frontend/src/assets/css/sidebar.css @@ -0,0 +1,12 @@ +.sidebar.open { + right: 0; +} + +.overlay { + visibility: hidden; +} + +.overlay.open { + opacity: 1; + visibility: visible; +} \ No newline at end of file diff --git a/frontend/src/assets/css/tabsbar.css b/frontend/src/assets/css/tabsbar.css new file mode 100644 index 0000000..a40ae42 --- /dev/null +++ b/frontend/src/assets/css/tabsbar.css @@ -0,0 +1,7 @@ +.scrollbar-hide { + -ms-overflow-style: none; /* IE and Edge */ + scrollbar-width: none; /* Firefox */ +} +.scrollbar-hide::-webkit-scrollbar { + display: none; /* Chrome, Safari, Opera */ +} \ No newline at end of file diff --git a/frontend/src/assets/img/heart.svg b/frontend/src/assets/img/heart.svg new file mode 100644 index 0000000..85c0a55 --- /dev/null +++ b/frontend/src/assets/img/heart.svg @@ -0,0 +1,3 @@ + + + diff --git a/frontend/src/assets/svg/account.jsx b/frontend/src/assets/svg/account.jsx new file mode 100644 index 0000000..2280ded --- /dev/null +++ b/frontend/src/assets/svg/account.jsx @@ -0,0 +1,10 @@ +import React from 'react'; + +export default function Account({width, height, fillColor}) { + return ( + + + + ); +} \ No newline at end of file diff --git a/frontend/src/assets/svg/addon.jsx b/frontend/src/assets/svg/addon.jsx new file mode 100644 index 0000000..e183d2f --- /dev/null +++ b/frontend/src/assets/svg/addon.jsx @@ -0,0 +1,30 @@ +import React from "react"; + +export default function Addon({ width, height }) { + return ( + + + + + + + + + + + + + + + ); +} \ No newline at end of file diff --git a/frontend/src/assets/svg/check.jsx b/frontend/src/assets/svg/check.jsx new file mode 100644 index 0000000..2efc337 --- /dev/null +++ b/frontend/src/assets/svg/check.jsx @@ -0,0 +1,13 @@ +import React from "react" + +export default function Check({ fillColor, height, width }) { + return ( + + + + ); +} + +Check.defaultProps = { + height: "24px", +} \ No newline at end of file diff --git a/frontend/src/assets/svg/cross.jsx b/frontend/src/assets/svg/cross.jsx new file mode 100644 index 0000000..d3bbfa8 --- /dev/null +++ b/frontend/src/assets/svg/cross.jsx @@ -0,0 +1,10 @@ +import React from "react" + +export default function Cross({ fillColor , height, width, onClick}) { + return ( + + + + ); +} \ No newline at end of file diff --git a/frontend/src/assets/svg/downarrow.jsx b/frontend/src/assets/svg/downarrow.jsx new file mode 100644 index 0000000..2f6f647 --- /dev/null +++ b/frontend/src/assets/svg/downarrow.jsx @@ -0,0 +1,9 @@ +import React from "react"; + +export default function DownArrow({ width, height, fillColor }) { + return ( + + + + ); +} \ No newline at end of file diff --git a/frontend/src/assets/svg/filter.jsx b/frontend/src/assets/svg/filter.jsx new file mode 100644 index 0000000..8fe3bc8 --- /dev/null +++ b/frontend/src/assets/svg/filter.jsx @@ -0,0 +1,9 @@ +import React from "react"; + +export default function Filter({width, height, fillColor}) { + return ( + + + + ); +} \ No newline at end of file diff --git a/frontend/src/assets/svg/hdri.jsx b/frontend/src/assets/svg/hdri.jsx new file mode 100644 index 0000000..191c431 --- /dev/null +++ b/frontend/src/assets/svg/hdri.jsx @@ -0,0 +1,28 @@ +import React from "react"; + +export default function HDRI({ width, height }) { + return ( + + ); +} \ No newline at end of file diff --git a/frontend/src/assets/svg/leftarrow.jsx b/frontend/src/assets/svg/leftarrow.jsx new file mode 100644 index 0000000..84bf407 --- /dev/null +++ b/frontend/src/assets/svg/leftarrow.jsx @@ -0,0 +1,9 @@ +import React from 'react'; + +export default function LeftArrow({ width, height, fillColor }) { + return ( + + + + ); +} \ No newline at end of file diff --git a/frontend/src/assets/svg/menu.jsx b/frontend/src/assets/svg/menu.jsx new file mode 100644 index 0000000..a8cce97 --- /dev/null +++ b/frontend/src/assets/svg/menu.jsx @@ -0,0 +1,10 @@ +import React from "react"; + +export default function Menu({ width, height, fillColor }) { + return ( + + + + ); +} \ No newline at end of file diff --git a/frontend/src/assets/svg/model.jsx b/frontend/src/assets/svg/model.jsx new file mode 100644 index 0000000..67de04a --- /dev/null +++ b/frontend/src/assets/svg/model.jsx @@ -0,0 +1,34 @@ +import React from "react"; + +export default function Model({fillColor, width, height}) { + return ( + + + + + {" "} + + {" "} + + + + {" "} + + {" "} + + + ); +} \ No newline at end of file diff --git a/frontend/src/assets/svg/notifications.jsx b/frontend/src/assets/svg/notifications.jsx new file mode 100644 index 0000000..952d250 --- /dev/null +++ b/frontend/src/assets/svg/notifications.jsx @@ -0,0 +1,10 @@ +import React from "react"; + +export default function Notifications({ fillColor, height, width, classname }) { + return ( + + + + ); +} \ No newline at end of file diff --git a/frontend/src/assets/svg/openinnew.jsx b/frontend/src/assets/svg/openinnew.jsx new file mode 100644 index 0000000..67fa43a --- /dev/null +++ b/frontend/src/assets/svg/openinnew.jsx @@ -0,0 +1,10 @@ +import React from "react"; + +export default function OpenInNew({ width, height, fillColor }) { + return ( + + + + ); +} \ No newline at end of file diff --git a/frontend/src/assets/svg/rig.jsx b/frontend/src/assets/svg/rig.jsx new file mode 100644 index 0000000..8e04ab3 --- /dev/null +++ b/frontend/src/assets/svg/rig.jsx @@ -0,0 +1,17 @@ +import React from "react"; + +export default function Rig({ width, height }) { + return ( + + + + ); +} \ No newline at end of file diff --git a/frontend/src/assets/svg/rightarrow.jsx b/frontend/src/assets/svg/rightarrow.jsx new file mode 100644 index 0000000..12e7846 --- /dev/null +++ b/frontend/src/assets/svg/rightarrow.jsx @@ -0,0 +1,9 @@ +import React from "react"; + +export default function RightArrow({ width, height, fillColor }) { + return ( + + + + ); +} \ No newline at end of file diff --git a/frontend/src/assets/svg/texture.jsx b/frontend/src/assets/svg/texture.jsx new file mode 100644 index 0000000..0589c17 --- /dev/null +++ b/frontend/src/assets/svg/texture.jsx @@ -0,0 +1,189 @@ +import React from "react"; + +export default function Texture({ width, height }) { + return ( + + ); +} \ No newline at end of file diff --git a/frontend/src/assets/svg/uparrow.jsx b/frontend/src/assets/svg/uparrow.jsx new file mode 100644 index 0000000..f030789 --- /dev/null +++ b/frontend/src/assets/svg/uparrow.jsx @@ -0,0 +1,9 @@ +import React from "react"; + +export default function UpArrow({ width, height, fillColor }) { + return ( + + + + ); +} \ No newline at end of file diff --git a/frontend/src/components/SubmitButton.jsx b/frontend/src/components/Button.jsx similarity index 65% rename from frontend/src/components/SubmitButton.jsx rename to frontend/src/components/Button.jsx index d32a489..7dbd3e0 100644 --- a/frontend/src/components/SubmitButton.jsx +++ b/frontend/src/components/Button.jsx @@ -1,33 +1,31 @@ import React from "react"; +import '../assets/css/button.css' -const SubmitButton = ({classname, text, onClick, isForwardStep, isPreviousStep}) => { +export const Button = ({classname, text, onClick, isForwardStep, isPreviousStep, type, ...restProps}) => { return ( ); -}; - -export default SubmitButton; \ No newline at end of file +}; \ No newline at end of file diff --git a/frontend/src/components/Button/index.jsx b/frontend/src/components/Button/index.jsx deleted file mode 100644 index e98d9dd..0000000 --- a/frontend/src/components/Button/index.jsx +++ /dev/null @@ -1,74 +0,0 @@ -import React from "react"; -import PropTypes from "prop-types"; - -const shapes = { round: "rounded-[10px]", circle: "rounded-[50%]" }; -const variants = { - outline: { - white_A700: "outline outline-[0.75px] outline-white-A700 text-white-A700", - gray_900: "outline outline-[0.5px] outline-gray-900 text-gray-900", - gray_100: "outline outline-[0.75px] outline-gray-100 text-black-900", - }, - fill: { - gray_900: "bg-gray-900 text-white-A700", - gray_100: "bg-gray-100 text-gray-500", - green_600: "bg-green-600 text-white-A700", - gray_900_67: "bg-gray-900_67", - black_900: "bg-black-900 text-white-A700", - white_A700: "bg-white-A700 text-gray-900", - gray_50: "bg-gray-50 text-black-900", - gray_900_26: "bg-gray-900_26 text-white-A700", - }, -}; -const sizes = { - xs: "p-[7px]", - sm: "pr-[9px] py-[9px]", - md: "p-2.5", - lg: "p-[13px]", - xl: "pr-[15px] py-[15px]", - "2xl": "p-4", -}; - -const Button = ({ - children, - className = "", - leftIcon, - rightIcon, - shape = "", - size = "", - variant = "", - color = "", - ...restProps -}) => { - return ( - - ); -}; - -Button.propTypes = { - className: PropTypes.string, - children: PropTypes.node, - shape: PropTypes.oneOf(["round", "circle"]), - size: PropTypes.oneOf(["xs", "sm", "md", "lg", "xl", "2xl"]), - variant: PropTypes.oneOf(["outline", "fill"]), - color: PropTypes.oneOf([ - "white_A700", - "gray_900", - "gray_100", - "green_600", - "gray_900_67", - "black_900", - "gray_50", - "gray_900_26", - ]), -}; - -export { Button }; diff --git a/frontend/src/components/CodeField.jsx b/frontend/src/components/CodeField.jsx new file mode 100644 index 0000000..311818e --- /dev/null +++ b/frontend/src/components/CodeField.jsx @@ -0,0 +1,83 @@ +import React, { useState, useRef, useEffect } from "react"; + +const CodeField = () => { + const [otp, setOtp] = useState(["", "", "", "", "", ""]); + const inputs = Array.from({ length: 6 }, (_, index) => useRef(null)); + + const handleInputChange = (index, event) => { + const value = event.target.value; + + if (isNaN(value) || value === "") { + return; // Allow only numeric input + } + + const newOtp = [...otp]; + newOtp[index] = value; + + setOtp(newOtp); + + // Move to the next input + if (index < otp.length - 1 && value !== "") { + inputs[index + 1].current.focus(); + } + }; + + const handleKeyDown = (index, event) => { + if (event.key === "Backspace" && index > 0 && otp[index] === "") { + // Move to the previous input on Backspace press + inputs[index - 1].current.focus(); + } else if (event.key === "Backspace" && otp[index] !== "") { + // Clear the current input if it's not empty + const newOtp = [...otp]; + newOtp[index] = ""; + setOtp(newOtp); + } + }; + + useEffect(() => { + // Focus the first input on initial mount + inputs[0].current.focus(); + }, []); + + return ( +
+
+
+
+
+

OTP Verification

+
+ Enter the OTP you received at + +91 ******876 +
+ +
+ {otp.map((value, index) => ( + handleInputChange(index, event)} + onKeyDown={(event) => handleKeyDown(index, event)} + /> + ))} +
+ + +
+
+
+
+
+ ); +}; + +export default CodeField; diff --git a/frontend/src/components/DialogBox.jsx b/frontend/src/components/DialogBox.jsx new file mode 100644 index 0000000..84730c3 --- /dev/null +++ b/frontend/src/components/DialogBox.jsx @@ -0,0 +1,10 @@ +import React from "react" +import "../assets/css/dialogbox.css" + +export const DialogBox = ({classname, content}) => { + return ( +
+
{content}
+
+ ); +} \ No newline at end of file diff --git a/frontend/src/components/ErrorMessage/index.jsx b/frontend/src/components/ErrorMessage/index.jsx deleted file mode 100644 index ac1584e..0000000 --- a/frontend/src/components/ErrorMessage/index.jsx +++ /dev/null @@ -1,13 +0,0 @@ -import React from "react"; - -export const ErrorMessage = ({ errors = [], className = "" }) => { - return ( - errors?.length > 0 && ( -
- {errors.join(", ")} -
- ) - ); -}; diff --git a/frontend/src/components/Heading.jsx b/frontend/src/components/Heading.jsx new file mode 100644 index 0000000..9f05cb5 --- /dev/null +++ b/frontend/src/components/Heading.jsx @@ -0,0 +1,11 @@ +import React from "react"; +import {motion} from "framer-motion" + +export const Heading = ({text, initial, animate, transition, classname, ...restProps}) => { + return ( + +

{text}

+
+ ); +}; \ No newline at end of file diff --git a/frontend/src/components/HorizontalTabsBar.jsx b/frontend/src/components/HorizontalTabsBar.jsx new file mode 100644 index 0000000..75b505b --- /dev/null +++ b/frontend/src/components/HorizontalTabsBar.jsx @@ -0,0 +1,100 @@ +// import React from "react"; +// import {Tab} from "./Tab"; +// +// export const HorizontalTabsBar = ({ classname, tabs, activeTab, setActiveTab }) => { +// return ( +//
+// {tabs.map((tab, index) => ( +// setActiveTab(index)} +// /> +// ))} +//
+// ); +// } + +import React, { useRef, useEffect, useState } from "react"; +import { Tab } from "./Tab"; +import LeftArrow from "../assets/svg/leftarrow"; +import RightArrow from "../assets/svg/rightarrow"; + +export const HorizontalTabsBar = ({ classname, tabs, activeTab, setActiveTab }) => { + const containerRef = useRef(null); + const [showLeftArrow, setShowLeftArrow] = useState(false); + const [showRightArrow, setShowRightArrow] = useState(false); + + useEffect(() => { + const updateArrows = () => { + if (containerRef.current) { + const { scrollLeft, scrollWidth, clientWidth } = containerRef.current; + setShowLeftArrow(scrollLeft > 0); + setShowRightArrow(scrollLeft + clientWidth < scrollWidth); + } + }; + + updateArrows(); + + window.addEventListener("resize", updateArrows); + return () => window.removeEventListener("resize", updateArrows); + }, []); + + const scrollLeft = () => { + containerRef.current.scrollBy({ left: -200, behavior: "smooth" }); + }; + + const scrollRight = () => { + containerRef.current.scrollBy({ left: 200, behavior: "smooth" }); + }; + + const updateTab = () => { + document.title = `${tabs[activeTab].text} | Schwarz` + } + + return ( +
+ {showLeftArrow && ( + + )} +
{ + const { scrollLeft, scrollWidth, clientWidth } = containerRef.current; + setShowLeftArrow(scrollLeft > 0); + setShowRightArrow(scrollLeft + clientWidth < scrollWidth); + }} + > + {tabs.map((tab, index) => ( + { + setActiveTab(index); + }} + /> + ))} +
+ {showRightArrow && ( + + )} +
+ ); +}; diff --git a/frontend/src/components/Image.jsx b/frontend/src/components/Image.jsx new file mode 100644 index 0000000..ba90b92 --- /dev/null +++ b/frontend/src/components/Image.jsx @@ -0,0 +1,19 @@ +import React from "react"; + +const Img = ({ + className, + src = "defaultNoData.png", + alt = "testImg", + ...restProps + }) => { + return ( + {alt} + ); +}; +export {Img}; diff --git a/frontend/src/components/Img/index.jsx b/frontend/src/components/Img/index.jsx deleted file mode 100644 index 74a4db4..0000000 --- a/frontend/src/components/Img/index.jsx +++ /dev/null @@ -1,19 +0,0 @@ -import React from "react"; - -const Img = ({ - className, - src = "defaultNoData.png", - alt = "testImg", - ...restProps -}) => { - return ( - {alt} - ); -}; -export { Img }; diff --git a/frontend/src/components/Input/index.jsx b/frontend/src/components/Input/index.jsx deleted file mode 100644 index cc4d4b7..0000000 --- a/frontend/src/components/Input/index.jsx +++ /dev/null @@ -1,84 +0,0 @@ -import React from "react"; -import PropTypes from "prop-types"; -import { ErrorMessage } from "../../components/ErrorMessage"; - -const variants = { - outline: { - gray_400: "border border-gray-400 border-solid text-black-900", - gray_300: "border border-gray-300 border-solid", - }, - fill: { gray_50: "bg-gray-50 text-gray-500", white_A700: "bg-white-A700" }, -}; -const shapes = { round: "rounded-[15px]" }; -const sizes = { - xs: "pb-[11px] pr-[11px] pt-[13px]", - md: "pb-[18px] pr-[18px] pt-[19px]", - sm: "pb-[15px] pt-[18px]", -}; - -const Input = React.forwardRef( - ( - { - wrapClassName = "", - className = "", - name = "", - placeholder = "", - type = "text", - children, - errors = [], - label = "", - prefix, - suffix, - onChange, - shape = "", - size = "", - variant = "fill", - color = "white_A700", - ...restProps - }, - ref, - ) => { - const handleChange = (e) => { - if (onChange) onChange(e?.target?.value); - }; - - return ( - <> -
- {!!label && label} - {!!prefix && prefix} - - {!!suffix && suffix} -
- {!!errors && } - - ); - }, -); - -Input.propTypes = { - wrapClassName: PropTypes.string, - className: PropTypes.string, - name: PropTypes.string, - placeholder: PropTypes.string, - type: PropTypes.string, - shape: PropTypes.oneOf(["round"]), - size: PropTypes.oneOf(["xs", "md", "sm"]), - variant: PropTypes.oneOf(["outline", "fill"]), - color: PropTypes.oneOf(["gray_400", "gray_300", "gray_50", "white_A700"]), -}; - -export { Input }; diff --git a/frontend/src/components/InputField.jsx b/frontend/src/components/InputField.jsx index 3242907..e6e506c 100644 --- a/frontend/src/components/InputField.jsx +++ b/frontend/src/components/InputField.jsx @@ -1,20 +1,31 @@ import React from "react"; +import {motion} from "framer-motion" -const InputField = ({ label, type, placeholder, value, onChange, error, showError }) => { - return ( -
- - - {error && showError &&

{error}

} -
- ); +const InputField = ({label, type, placeholder, pattern, value = "", onChange, error, onFocus, onBlur, readonly, classname}) => { + return ( +
+ + + {error && + {error}} +
+ ); }; export default InputField; \ No newline at end of file diff --git a/frontend/src/components/Line/index.jsx b/frontend/src/components/Line/index.jsx deleted file mode 100644 index 24fff00..0000000 --- a/frontend/src/components/Line/index.jsx +++ /dev/null @@ -1,6 +0,0 @@ -import React from "react"; - -const Line = ({ className, ...restProps }) => { - return
; -}; -export { Line }; diff --git a/frontend/src/components/List/index.jsx b/frontend/src/components/List/index.jsx deleted file mode 100644 index 04de0d6..0000000 --- a/frontend/src/components/List/index.jsx +++ /dev/null @@ -1,10 +0,0 @@ -import React from "react"; - -const List = ({ children, className, ...restProps }) => { - return ( -
- {children} -
- ); -}; -export { List }; diff --git a/frontend/src/components/SearchBar.jsx b/frontend/src/components/SearchBar.jsx index 6b97535..80c0572 100644 --- a/frontend/src/components/SearchBar.jsx +++ b/frontend/src/components/SearchBar.jsx @@ -1,7 +1,38 @@ -import React from 'react' +import React, {useState} from 'react' export default function SearchBar() { - return ( -
SearchBar
- ) + const [isClicked, setIsClicked] = useState(false); + return ( +
+
+
+ + + +
+ { + setIsClicked(true) + }} + /> +
+
+ ); } diff --git a/frontend/src/components/SelectBox/index.jsx b/frontend/src/components/SelectBox/index.jsx deleted file mode 100644 index 069edbb..0000000 --- a/frontend/src/components/SelectBox/index.jsx +++ /dev/null @@ -1,145 +0,0 @@ -import React from "react"; -import Select from "react-select"; -import PropTypes from "prop-types"; -import { ErrorMessage } from "../ErrorMessage"; - -const SelectBox = React.forwardRef( - ( - { - children, - placeholder = "Select", - className = "", - options = [], - isSearchable = false, - placeholderClassName = "", - isMulti = false, - onChange, - value = "", - errors = [], - indicator, - shape = "", - size = "", - variant = "", - color = "", - ...restProps - }, - ref, - ) => { - const [selectedVal, setSelectedVal] = React.useState(value); - - const handleChange = (data) => { - setSelectedVal(data); - if (isMulti) { - onChange?.(data?.map((d) => d.value) || []); - } else { - onChange?.(data?.value); - } - }; - - return ( - <> - {options.map((option) => ( - ))} - {error && showError && ( + {error && (

{error}

)}
diff --git a/frontend/src/components/Sidebar.jsx b/frontend/src/components/Sidebar.jsx new file mode 100644 index 0000000..ad72df7 --- /dev/null +++ b/frontend/src/components/Sidebar.jsx @@ -0,0 +1,30 @@ +import React from 'react'; +import '../assets/css/sidebar.css'; +import {SubHeading} from "./SubHeading"; +import Cross from "../assets/svg/cross"; + +const Sidebar = ({isOpen, closeSidebar}) => { + return ( + <> +
+
+ +
+ +
+
+
+ {/* Add your filter options here */} +
+
+
+ + ); +}; + +export default Sidebar; diff --git a/frontend/src/components/SubHeading.jsx b/frontend/src/components/SubHeading.jsx new file mode 100644 index 0000000..9d6d337 --- /dev/null +++ b/frontend/src/components/SubHeading.jsx @@ -0,0 +1,15 @@ +import React from "react"; +import {motion} from "framer-motion" +import {Link} from "react-router-dom"; + +export const SubHeading = ({text, classname, initial, animate, transition, ...restProps}) => { + return ( + +

+ {text} +

+
+ ); +}; \ No newline at end of file diff --git a/frontend/src/components/Tab.jsx b/frontend/src/components/Tab.jsx new file mode 100644 index 0000000..4609e25 --- /dev/null +++ b/frontend/src/components/Tab.jsx @@ -0,0 +1,12 @@ +import React from 'react'; + +export const Tab = ( {text, icon, isActive, onClick, classname, ...restProps} ) => { + return ( +
+ {icon} + {text} +
+ ); +}; diff --git a/frontend/src/components/Text.jsx b/frontend/src/components/Text.jsx new file mode 100644 index 0000000..5cc117c --- /dev/null +++ b/frontend/src/components/Text.jsx @@ -0,0 +1,16 @@ +import React from "react"; + +const Text = ({children, className = "", size, as, ...restProps}) => { + const Component = as || "p"; + + return ( + + {children} + + ); +}; + +export {Text}; diff --git a/frontend/src/components/Text/index.jsx b/frontend/src/components/Text/index.jsx deleted file mode 100644 index 884948f..0000000 --- a/frontend/src/components/Text/index.jsx +++ /dev/null @@ -1,61 +0,0 @@ -import React from "react"; - -const sizeClasses = { - txtOutfitMedium14: "font-medium font-outfit", - txtUrbanistRegular14WhiteA700ab: "font-normal font-urbanist", - txtOutfitRegular14Red600: "font-normal font-outfit", - txtOutfitMedium18: "font-medium font-outfit", - txtUrbanistSemiBold32Black900: "font-semibold font-urbanist", - txtUrbanistSemiBold24: "font-semibold font-urbanist", - txtUrbanistSemiBold20: "font-semibold font-urbanist", - txtUrbanistMedium18: "font-medium font-urbanist", - txtUrbanistRegular14Red600: "font-normal font-urbanist", - txtOutfitSemiBold24: "font-outfit font-semibold", - txtUrbanistRegular16: "font-normal font-urbanist", - txtUrbanistRegular14WhiteA700a2: "font-normal font-urbanist", - txtUrbanistMedium12Gray500: "font-medium font-urbanist", - txtUrbanistMedium18Black900: "font-medium font-urbanist", - txtUrbanistRegular18: "font-normal font-urbanist", - txtUrbanistMedium14: "font-medium font-urbanist", - txtUrbanistSemiBold16: "font-semibold font-urbanist", - txtUrbanistSemiBold18: "font-semibold font-urbanist", - txtOutfitMedium14Red600: "font-medium font-outfit", - txtUrbanistMedium16: "font-medium font-urbanist", - txtUrbanistMedium16Gray500ab: "font-medium font-urbanist", - txtUrbanistMedium14Gray500: "font-medium font-urbanist", - txtUrbanistRegular12: "font-normal font-urbanist", - txtUrbanistMedium14Gray900: "font-medium font-urbanist", - txtUrbanistMedium12Gray901: "font-medium font-urbanist", - txtUrbanistRegular14: "font-normal font-urbanist", - txtOutfitRegular14: "font-normal font-outfit", - txtOutfitRegular10: "font-normal font-outfit", - txtUrbanistMedium14Black900: "font-medium font-urbanist", - txtUrbanistSemiBold34: "font-semibold font-urbanist", - txtUrbanistMedium12: "font-medium font-urbanist", - txtUrbanistSemiBold14: "font-semibold font-urbanist", - txtUrbanistMedium14Green600: "font-medium font-urbanist", - txtUrbanistRegular14Green600: "font-normal font-urbanist", - txtUrbanistSemiBold32: "font-semibold font-urbanist", - txtUrbanistRegular12Black900: "font-normal font-urbanist", - txtUrbanistRegular14Gray500: "font-normal font-urbanist", - txtUrbanistMedium12WhiteA700: "font-medium font-urbanist", - txtOutfitSemiBold18: "font-outfit font-semibold", - txtUrbanistSemiBold18Black900: "font-semibold font-urbanist", - txtUrbanistRegular14Gray900ab: "font-normal font-urbanist", - txtUrbanistRegular14WhiteA700: "font-normal font-urbanist", -}; - -const Text = ({ children, className = "", size, as, ...restProps }) => { - const Component = as || "p"; - - return ( - - {children} - - ); -}; - -export { Text }; diff --git a/frontend/src/components/TextArea/index.jsx b/frontend/src/components/TextArea/index.jsx deleted file mode 100644 index 30b8ab5..0000000 --- a/frontend/src/components/TextArea/index.jsx +++ /dev/null @@ -1,39 +0,0 @@ -import React from "react"; -import PropTypes from "prop-types"; -import { ErrorMessage } from "../../components/ErrorMessage"; - -const TextArea = React.forwardRef( - ( - { className, name, placeholder, children, errors, onChange, ...restProps }, - ref, - ) => { - const handleChange = (e) => { - if (onChange) onChange(e?.target?.value); - }; - - return ( - <> - - - -
- - Website - - - } - color="gray_400" - size="md" - variant="outline" - /> -
- - -
-
- - Notification Settings - - -
- -
-
- - Item Sold - - - When someone purchased one of your items - -
- camera -
- -
-
- - Successful Purchase - - - When you successfully buy an item - -
- camera One -
- -
-
- - Auction Expirates - - - When a timed auction you created ends - -
- camera Two -
- -
-
- - Owned Item Updates - - - When a significant update occurs for one of the items - you have purchased on Enefthy - -
- camera Three -
-
-
- -
-
-
- - Your Photo - - -
-
-
- Ellipse1023 -
- - Edit your photo - -
- - Delete - - - Update - -
-
-
-
- -
- - Click to upload or drag and drop - - - <> - PNG, JPG or Gif -
- Max 20Mb - -
-
-
-
-
-
-
- - Social Linked - - -
-
-
- signal - -
-
- - Edit your photo - - - <> - Use Google to sign in to your account. -
- Click here to learn more. - -
-
-
-
-
- - - - - - - - ); -}; - - - - diff --git a/frontend/src/services/actions/auth.js b/frontend/src/services/actions/auth.js index 804515d..afcc0ef 100644 --- a/frontend/src/services/actions/auth.js +++ b/frontend/src/services/actions/auth.js @@ -32,8 +32,7 @@ export const load_user = () => async dispatch => { }; try { - const res = await axios.get(`${process.env.REACT_APP_API_URL}/auth/users/me/`, config); - + const res = await axios.get(`${import.meta.env.VITE_REACT_APP_API_URL}/api/users/me/`, config); dispatch({ type: USER_LOADED_SUCCESS, payload: res.data @@ -66,7 +65,7 @@ export const googleAuthenticate = (state, code) => async dispatch => { const formBody = Object.keys(details).map(key => encodeURIComponent(key) + '=' + encodeURIComponent(details[key])).join('&'); try { - const res = await axios.post(`${process.env.REACT_APP_API_URL}/auth/o/google-oauth2/?${formBody}`, config); + const res = await axios.post(`${import.meta.env.VITE_REACT_APP_API_URL}/api/o/google-oauth2/?${formBody}`, config); dispatch({ type: GOOGLE_AUTH_SUCCESS, @@ -98,7 +97,7 @@ export const facebookAuthenticate = (state, code) => async dispatch => { const formBody = Object.keys(details).map(key => encodeURIComponent(key) + '=' + encodeURIComponent(details[key])).join('&'); try { - const res = await axios.post(`${process.env.REACT_APP_API_URL}/auth/o/facebook/?${formBody}`, config); + const res = await axios.post(`${import.meta.env.VITE_REACT_APP_API_URL}/api/o/facebook/?${formBody}`, config); dispatch({ type: FACEBOOK_AUTH_SUCCESS, @@ -126,7 +125,7 @@ export const checkAuthenticated = () => async dispatch => { const body = JSON.stringify({ token: localStorage.getItem('access') }); try { - const res = await axios.post(`${process.env.REACT_APP_API_URL}/auth/jwt/verify/`, body, config) + const res = await axios.post(`${import.meta.env.VITE_REACT_APP_API_URL}/api/jwt/verify/`, body, config) if (res.data.code !== 'token_not_valid') { dispatch({ @@ -160,7 +159,7 @@ export const login = (email, password) => async dispatch => { const body = JSON.stringify({ email, password }); try { - const res = await axios.post(`${process.env.REACT_APP_API_URL}/auth/jwt/create/`, body, config); + const res = await axios.post(`${import.meta.env.VITE_REACT_APP_API_URL}/api/jwt/create/`, body, config); dispatch({ type: LOGIN_SUCCESS, @@ -175,17 +174,17 @@ export const login = (email, password) => async dispatch => { } }; -export const signup = (first_name, last_name, email, password, re_password) => async dispatch => { +export const signup = (email, first_name, last_name, date_of_birth, gender, address, preferred_lang, company, job_title, industry, experience, password, re_password) => async dispatch => { const config = { headers: { 'Content-Type': 'application/json' } }; - const body = JSON.stringify({ first_name, last_name, email, password, re_password }); + const body = JSON.stringify({ email, first_name, last_name, date_of_birth, gender, address, preferred_lang, company, job_title, industry, experience, password, re_password }); try { - const res = await axios.post(`${process.env.REACT_APP_API_URL}/auth/users/`, body, config); + const res = await axios.post(`${import.meta.env.VITE_REACT_APP_API_URL}/api/users/`, body, config); dispatch({ type: SIGNUP_SUCCESS, @@ -208,7 +207,7 @@ export const verify = (uid, token) => async dispatch => { const body = JSON.stringify({ uid, token }); try { - await axios.post(`${process.env.REACT_APP_API_URL}/auth/users/activation/`, body, config); + await axios.post(`${import.meta.env.VITE_REACT_APP_API_URL}/api/users/activation/`, body, config); dispatch({ type: ACTIVATION_SUCCESS, @@ -230,7 +229,7 @@ export const reset_password = (email) => async dispatch => { const body = JSON.stringify({ email }); try { - await axios.post(`${process.env.REACT_APP_API_URL}/auth/users/reset_password/`, body, config); + await axios.post(`${import.meta.env.VITE_REACT_APP_API_URL}/api/users/reset_password/`, body, config); dispatch({ type: PASSWORD_RESET_SUCCESS @@ -252,7 +251,7 @@ export const reset_password_confirm = (uid, token, new_password, re_new_password const body = JSON.stringify({ uid, token, new_password, re_new_password }); try { - await axios.post(`${process.env.REACT_APP_API_URL}/auth/users/reset_password_confirm/`, body, config); + await axios.post(`${import.meta.env.VITE_REACT_APP_API_URL}/api/users/reset_password_confirm/`, body, config); dispatch({ type: PASSWORD_RESET_CONFIRM_SUCCESS diff --git a/frontend/tailwind.config.js b/frontend/tailwind.config.js index 9f14cb5..38125b4 100644 --- a/frontend/tailwind.config.js +++ b/frontend/tailwind.config.js @@ -1,43 +1,39 @@ /** @type {import('tailwindcss').Config} */ -module.exports = { - content: [ - "./index.html", - "./src/**/*.{js,ts,jsx,tsx}", - ], - theme: { - extend: { - screens :{ - 'max-md' : {'max': '1024px'}, - }, - colors: { - 'blue': '#0085FF', - 'red' : '#FF1818', - 'green' : '#00B612', - 'yellow' : '#FD9902', - 'blue_bg' : '#B9DDFF', - 'red_bg' : '#FFE3E3', - 'green_bg' : '#C3FFBE', - 'yellow_bg' : '#FFEDD2', - - // Light theme colors - 'l_grey' : '#868686', - 'l_light_grey' : '#E3E1E3', +const plugin = require("tailwindcss/plugin"); - // Dark theme colors - 'd_black' : '#0E0E0E', - 'd_grey_prim' : '#202020', - 'd_white' : '#EAEAEA', - 'd_light_grey' : '#D3D3D3', - 'd_grey_border' : '#363636', - 'd_white_button' : '#EFEFEF', - 'd_grey_sec' : '#363636', - 'd_light_grey_ph' : '#9F9F9F' - }, - fontFamily: { - sans: ['Poppins', 'sans-serif'], - } +module.exports = { + mode: 'jit', + darkMode: 'selector', + content: [ + "./index.html", + "./src/**/*.{js,ts,jsx,tsx}", + ], + theme: { + extend: { + screens :{ + 'max-md' : {'max': '1024px'}, + }, + fontFamily: { + sans: ['Poppins', 'sans-serif'], + } + }, }, - }, - plugins: [], + plugins: [ + plugin(({ addVariant, e, postcss }) => { + addVariant('firefox', ({ container, separator }) => { + const isFirefoxRule = postcss.atRule({ + name: '-moz-document', + params: 'url-prefix()', + }); + isFirefoxRule.append(container.nodes); + container.append(isFirefoxRule); + isFirefoxRule.walkRules((rule) => { + rule.selector = `.${e( + `firefox${separator}${rule.selector.slice(1)}` + )}`; + }); + }); + }), + ], } \ No newline at end of file diff --git a/frontend/test/Button.test.js b/frontend/test/Button.test.js new file mode 100644 index 0000000..c785f17 --- /dev/null +++ b/frontend/test/Button.test.js @@ -0,0 +1,37 @@ +import { expect, test } from 'vitest'; +import {Button} from '../src/components/Button' + +describe('Button components', () => { + test('renders button with text', () => { + const buttonText = 'Click me'; + const { getByText } = render(