From 7e34c7101f21b495992bc6d9e10c4a8ffbd84f0c Mon Sep 17 00:00:00 2001 From: Muaz <6muazx@gmail.com> Date: Mon, 7 Apr 2025 18:55:08 +0530 Subject: [PATCH 01/14] project setup --- backend/api/__init__.py | 0 .../api/__pycache__/__init__.cpython-312.pyc | Bin 0 -> 168 bytes backend/api/__pycache__/admin.cpython-312.pyc | Bin 0 -> 212 bytes backend/api/__pycache__/apps.cpython-312.pyc | Bin 0 -> 468 bytes .../api/__pycache__/models.cpython-312.pyc | Bin 0 -> 209 bytes backend/api/admin.py | 3 + backend/api/apps.py | 6 + backend/api/migrations/__init__.py | 0 .../__pycache__/__init__.cpython-312.pyc | Bin 0 -> 179 bytes backend/api/models.py | 3 + backend/api/tests.py | 3 + backend/api/views.py | 3 + backend/backend/__init__.py | 0 .../__pycache__/__init__.cpython-312.pyc | Bin 0 -> 172 bytes .../__pycache__/settings.cpython-312.pyc | Bin 0 -> 3632 bytes .../backend/__pycache__/urls.cpython-312.pyc | Bin 0 -> 1039 bytes .../backend/__pycache__/wsgi.cpython-312.pyc | Bin 0 -> 660 bytes backend/backend/asgi.py | 16 + backend/backend/settings.py | 153 + backend/backend/urls.py | 22 + backend/backend/wsgi.py | 16 + backend/manage.py | 22 + backend/requirements.txt | 17 + frontend/.gitignore | 24 + frontend/README.md | 12 + frontend/eslint.config.js | 33 + frontend/index.html | 13 + frontend/package-lock.json | 2746 +++++++++++++++++ frontend/package.json | 27 + frontend/public/vite.svg | 1 + frontend/src/App.css | 42 + frontend/src/App.jsx | 35 + frontend/src/assets/react.svg | 1 + frontend/src/index.css | 68 + frontend/src/main.jsx | 10 + frontend/vite.config.js | 7 + 36 files changed, 3283 insertions(+) create mode 100644 backend/api/__init__.py create mode 100644 backend/api/__pycache__/__init__.cpython-312.pyc create mode 100644 backend/api/__pycache__/admin.cpython-312.pyc create mode 100644 backend/api/__pycache__/apps.cpython-312.pyc create mode 100644 backend/api/__pycache__/models.cpython-312.pyc create mode 100644 backend/api/admin.py create mode 100644 backend/api/apps.py create mode 100644 backend/api/migrations/__init__.py create mode 100644 backend/api/migrations/__pycache__/__init__.cpython-312.pyc create mode 100644 backend/api/models.py create mode 100644 backend/api/tests.py create mode 100644 backend/api/views.py create mode 100644 backend/backend/__init__.py create mode 100644 backend/backend/__pycache__/__init__.cpython-312.pyc create mode 100644 backend/backend/__pycache__/settings.cpython-312.pyc create mode 100644 backend/backend/__pycache__/urls.cpython-312.pyc create mode 100644 backend/backend/__pycache__/wsgi.cpython-312.pyc create mode 100644 backend/backend/asgi.py create mode 100644 backend/backend/settings.py create mode 100644 backend/backend/urls.py create mode 100644 backend/backend/wsgi.py create mode 100644 backend/manage.py create mode 100644 backend/requirements.txt create mode 100644 frontend/.gitignore create mode 100644 frontend/README.md create mode 100644 frontend/eslint.config.js create mode 100644 frontend/index.html create mode 100644 frontend/package-lock.json create mode 100644 frontend/package.json create mode 100644 frontend/public/vite.svg create mode 100644 frontend/src/App.css create mode 100644 frontend/src/App.jsx create mode 100644 frontend/src/assets/react.svg create mode 100644 frontend/src/index.css create mode 100644 frontend/src/main.jsx create mode 100644 frontend/vite.config.js diff --git a/backend/api/__init__.py b/backend/api/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/backend/api/__pycache__/__init__.cpython-312.pyc b/backend/api/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..49ae0e2864a22d97870260d9be20ea0fed3cfba1 GIT binary patch literal 168 zcmX@j%ge<81hX%GP6yGCK?FMZ%mNgd&QQsq$>_I|p@<2{`wUX^%iY;3CbT%Us5quH zFF7MI#wE2lyClCLCOAK>q&%@GHO3>vEylyZ6h!NS=%mEt?9{xJn8bq2nE3e2yv&mL lc)fzkUmP~M`6;D2sdh!IKyw*^xERFv$jr#dSi}ru0RTQsDz*Rs literal 0 HcmV?d00001 diff --git a/backend/api/__pycache__/admin.cpython-312.pyc b/backend/api/__pycache__/admin.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..c3771eaa278820f0072feb12cd4db102e85e4326 GIT binary patch literal 212 zcmX@j%ge<81hX%GPIm&*k3k$5V1hC}3ji6@8B!Qh7;_kM8KW2(87i4HnO}mWH5qTQ zCZ^ASF6MM8QPj#85EZAjj_-Fp*R-v1(JnGBNwy`x0&%2chIENxU1pN5 zB(kc2)GnkCyX=;_mZ!0iHuV^HLhanPI&tLnZSJsCroKH?D`}Mwmrgk$X`K);7(0NQ zgxtl{DLb2tn>2PJLWP`|xn6G~mYI zs&5PqkDwkv9ntZGyUY+vi06lzp9J7po)iJXmPj+S+&O+zwwI0eV=sW8ygyXsv{7CM t;s9JMS=M&SsfP<`nyBGTq`#$x6&i&(}*yDq;pou4MQOQuE8z*(xTqIJKxarZg`(BQeG$wK%&Z zzaSw@T{#N_PMyp)*4g3K6*>3Ri~zc_4w?9!Z6yCM#t Xsf<8e3}Sp>W@Kc%#~@S02IK$$lMyyV literal 0 HcmV?d00001 diff --git a/backend/api/admin.py b/backend/api/admin.py new file mode 100644 index 0000000..8c38f3f --- /dev/null +++ b/backend/api/admin.py @@ -0,0 +1,3 @@ +from django.contrib import admin + +# Register your models here. diff --git a/backend/api/apps.py b/backend/api/apps.py new file mode 100644 index 0000000..66656fd --- /dev/null +++ b/backend/api/apps.py @@ -0,0 +1,6 @@ +from django.apps import AppConfig + + +class ApiConfig(AppConfig): + default_auto_field = 'django.db.models.BigAutoField' + name = 'api' diff --git a/backend/api/migrations/__init__.py b/backend/api/migrations/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/backend/api/migrations/__pycache__/__init__.cpython-312.pyc b/backend/api/migrations/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..00f84b8fab1344409fe1fef6077194e9221e36c6 GIT binary patch literal 179 zcmX@j%ge<81hX%GP6yGCK?FMZ%mNgd&QQsq$>_I|p@<2{`wUX^E6CX@CbT%Us5quH zFF7MI#wE2lyClCLCOAK>q&%@GHO3>vEylyZ6h!NS=%mEt?9{xJn8bq2nB2_tqQsKS w{Ji3r`1s7c%#!$cy@JYL95%W6DWy57c15f}D;R;e7{vI<%*e=C#0+Es0FJ#cS^xk5 literal 0 HcmV?d00001 diff --git a/backend/api/models.py b/backend/api/models.py new file mode 100644 index 0000000..71a8362 --- /dev/null +++ b/backend/api/models.py @@ -0,0 +1,3 @@ +from django.db import models + +# Create your models here. diff --git a/backend/api/tests.py b/backend/api/tests.py new file mode 100644 index 0000000..7ce503c --- /dev/null +++ b/backend/api/tests.py @@ -0,0 +1,3 @@ +from django.test import TestCase + +# Create your tests here. diff --git a/backend/api/views.py b/backend/api/views.py new file mode 100644 index 0000000..91ea44a --- /dev/null +++ b/backend/api/views.py @@ -0,0 +1,3 @@ +from django.shortcuts import render + +# Create your views here. diff --git a/backend/backend/__init__.py b/backend/backend/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/backend/backend/__pycache__/__init__.cpython-312.pyc b/backend/backend/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..43cc01476e63b6d3cadb1a698ff44639a63ccb9f GIT binary patch literal 172 zcmX@j%ge<81Pd;FP6yGCK?FMZ%mNgd&QQsq$>_I|p@<2{`wUX^%iGy1CbT%Us5quH zFF7MI#wE2lyClCLCOAK>q&%@GHO3>vEylyZ6h!NS=%mEt?9{vzI2|9KnU`4-AFo$X h`HRCQH$SB`C)KWq6=*gi5Ep|OADI~$8H<>KEC39qEKC3Z literal 0 HcmV?d00001 diff --git a/backend/backend/__pycache__/settings.cpython-312.pyc b/backend/backend/__pycache__/settings.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..be46941733d978e0bc6390a9d9844aac1272d40b GIT binary patch literal 3632 zcmbUjO>^7E6`=S{k)~xzqGd~gVT0jp< zK^G2aAt~geu>aeE3Yr4Uhr{2}{e#^ro{{t2=P&|T|qIxLTDV;_&2mlqr^jhgO*b0k~E2u=<;h= zobW8ZhRl^WkV(CP%;X!$OnEYB`e9I|r74s~S0O@aG~=$<-1WLjH~i8xnw8RMPPz)Q zo`K&r^cG^!jfXzzI@CcBz5U%yPn(P1`tBVMhv$@CEGUam zI??>D(rg%vg>74HHY{e_Fqti-zK5HLX_>|@uG?%hx`Lb7RBVixtpjErxiX6i($ywo z*@|foIOmzR1zMPa(hu1W*roSDyZ}md!^Di*1pT^Vt45Q7uf3y!MODK;1qX0+$F^J6 z$Js10>K5BK;<->a^ek>>L1Wf7T58?OdLC!{>g^rOY_~Oy(NxQ3jP3IqDR2{@swQ(^ z(c0L09a|>e9y0XQDgF#i{8h!?fjy=f3X+jwPwYP=}lMafL?%O0*H%w~>D+rqw8B=mA{UGBR=d$zcm+-qe*v9mcYZS9J?jG}@GsHqB&74R5$3aCzeK>J zW&9+x%;oO#R5?M|sMPZA>1ss~U7%X2iJ2e?^5vCc znJ0c%&hH9^&m{aVyWin8r{R_9td3mXEzy=@Sy0B$a} z;o(^qa~ZnxtN-^*u}+ZA`qjAiVIRFx$eWuJ#{MwrCBpW0dicxjbLs)=Q$y zt&6LCSuEx_r-bBO2~y1qWX7Xc`Py1h5Qb<^Kj{yP5DZDkjP=y{qvi|`q#RhV2bVEC znPw(HCb?XW7X(?XK-tQrVu2TnP+C(pzEIb_JIz}bSx z*JPIs93qz;=jBzN%fqSoS(O($A(-0myk&>$TsL5LS?sdffG8LR6>BKNkdO=mt}e?Y z)PkX>saxh4+=B%BQ_u7}NEk{HJHyWmIH=FCNLZGg8RN)7zzyv_Cczfmw`r5`GAHm( zN0JcCP1Q7-WUPU0Gw8?$^8E7p3W;;2Qe}hB%T8MeBvC92A{@a1bhALBYsGxN#BXpl zo{ZNj6;WQVm2#DGfkZ@pty+Tc2qd{7tP~yo(jaRjlIKLuu@2F4W8$@+e2*&?fmng} zB3|OkE9=|}FXt+G9uz_@WT^t38-|{hi%Y*Olb{2M5=lUQM7dCdmjL?yE=>I*Sm$LZ zJb)of#hl|AnefU4tXAYgkuT-{Mm>l7Qa{|yeZ1+ku(jE4)_0W6Jht}WAI_#=Y}?LE z-CPw5o2&1C+dB9m{0Stnt>N3|D(H3!&N_s(H%8O+sV_?V|C^@h ztN)_r;QLQ1@^32k$M|WG8c+T?{HNPbVmFRrHy+1sK8YxA3Sbk9c%da1&ew6<-9!i3(h->R@Dst&jQtFNQz<$_8s>8^q!$_@rBr{B+I hk7sWjGq;Yf-a3s12f;~9LyV%-)JI3ugjs+D{SRk=U)cZv literal 0 HcmV?d00001 diff --git a/backend/backend/__pycache__/urls.cpython-312.pyc b/backend/backend/__pycache__/urls.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..773524e1564d2f8bd4c600b4ef9cbfdb12852ef3 GIT binary patch literal 1039 zcmb7Dzi-qq6n1jy4^m3S#=v02aE;U^P|+mLBV+H+Q((aUC1C@)q|xo`YP(JnAubRG~EhlYMmg_uOV5-O1U&>C7F? z1heEd9Qb?Bp5Ro9el{2@VlpXkUn<-q=>ZcI7fOy;YC_-N9WuN>R(U~;VM^$IoM+l# zCC7$o3@92KiC<*wP=|O6Ah=WvX9Cdp1h>c;(F|+GVjsgA8dK;vil|I=NJm5rWYaE{ zJYpic6Rt%@7FntzTO2`q9r=%55)N28_P53&tteM*E702^en2TE0%yFC%ETD=mE@Qo zbxBdwhZCAr{aRgb6<7gN3gJ@_J6vwJS882D$=M1PgtL2X8@B_0GbdWF_COe&Lj4;H zX@*Q$B4Ne2p|cHG|HH=CheUTR$a3%pqaBgvW15Kpm{9ez((HsAe|9QkFi?T<*jlz6 z;C^joJh5vsj?fz3kgRK0DpUTMZ7-LD2n=jGwe3NL+YSQ%ySb=8!0sZ0~L*+qc&3zH0aSCH&ega#$Q)%-`jbj&pWjT>)GN zetH%DPFs%SoViPm_X8v6##eOxY!=O2dVB5N`bm5opKKg&eDQ9cw%jH6&4RM8^e4t5 BIYj^f literal 0 HcmV?d00001 diff --git a/backend/backend/__pycache__/wsgi.cpython-312.pyc b/backend/backend/__pycache__/wsgi.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..ec9c6b32038fed25a9fd09607560f962a22070e7 GIT binary patch literal 660 zcmYjPL2DC16rS0QP1)E`#P&Rhmw?@kqL(6qHf-zMhtcs zjDe9cO{41;r!fSi+oeguBA=>A@NQSnJJ2ezNP$FL3&z(CBDfwNNz#uPJR%an8Yq>> zUB?N8FYRDRc_>z%d=Wdqou~E_XCNj@H~}O~Ok?26naD75tl#;X%?i%$hoHRt&bwtJ z+pfKS*{mOVt^Ibp(X6+;w@0<(H~XiX%k6CmN<}=Bm10)(JZ-`AGDFC0b792xwSPMw zo0?iED*_)!so!Gd%NzjgjBjC?hmM219_rE;&&`DTVK%DY}=kyUwuP+KgtijmLHy#(z}}U4<&oT%m4rY literal 0 HcmV?d00001 diff --git a/backend/backend/asgi.py b/backend/backend/asgi.py new file mode 100644 index 0000000..15b2aa4 --- /dev/null +++ b/backend/backend/asgi.py @@ -0,0 +1,16 @@ +""" +ASGI config for backend project. + +It exposes the ASGI callable as a module-level variable named ``application``. + +For more information on this file, see +https://docs.djangoproject.com/en/4.2/howto/deployment/asgi/ +""" + +import os + +from django.core.asgi import get_asgi_application + +os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'backend.settings') + +application = get_asgi_application() diff --git a/backend/backend/settings.py b/backend/backend/settings.py new file mode 100644 index 0000000..3738572 --- /dev/null +++ b/backend/backend/settings.py @@ -0,0 +1,153 @@ +""" +Django settings for backend project. + +Generated by 'django-admin startproject' using Django 4.2.10. + +For more information on this file, see +https://docs.djangoproject.com/en/4.2/topics/settings/ + +For the full list of settings and their values, see +https://docs.djangoproject.com/en/4.2/ref/settings/ +""" + +from pathlib import Path +from dotenv import load_dotenv +from datetime import timedelta +import os + +load_dotenv() + +# 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.2/howto/deployment/checklist/ + +# SECURITY WARNING: keep the secret key used in production secret! +SECRET_KEY = os.environ.get('SECRET_KEY') + +# 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', + 'rest_framework', + 'rest_framework_simplejwt', + 'corsheaders', + 'api', +] + +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', + 'corsheaders.middleware.CorsMiddleware', +] + +ROOT_URLCONF = 'backend.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 = 'backend.wsgi.application' + + +# Database +# https://docs.djangoproject.com/en/4.2/ref/settings/#databases + +DATABASES = { + 'default': { + 'ENGINE': 'django.db.backends.postgresql', + 'NAME': os.environ.get('DB_NAME'), + 'USER': os.environ.get('DB_USER'), + 'PASSWORD': os.environ.get('DB_PASSWORD'), + 'HOST': os.environ.get('DB_HOST'), + 'PORT': os.environ.get('DB_PORT'), + } +} + + +# Password validation +# https://docs.djangoproject.com/en/4.2/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.2/topics/i18n/ + +LANGUAGE_CODE = 'en-us' + +TIME_ZONE = 'UTC' + +USE_I18N = True + +USE_TZ = True + +REST_FRAMEWORK = { + 'DEFAULT_AUTHENTICATION_CLASSES': ( + 'rest_framework_simplejwt.authentication.JWTAuthentication', + ), + 'DEFAULT_PERMISSION_CLASSES': ( + 'rest_framework.permissions.IsAuthenticated', + ), +} + +SIMPLE_JWT = { + 'ACCESS_TOKEN_LIFETIME': timedelta(minutes=60), + 'REFRESH_TOKEN_LIFETIME': timedelta(days=1), + 'ROTATE_REFRESH_TOKENS': True, + 'BLACKLIST_AFTER_ROTATION': True, + 'AUTH_HEADER_TYPES': ('Bearer',), +} + +# Static files (CSS, JavaScript, Images) +# https://docs.djangoproject.com/en/4.2/howto/static-files/ + +STATIC_URL = 'static/' + +# Default primary key field type +# https://docs.djangoproject.com/en/4.2/ref/settings/#default-auto-field + +DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField' diff --git a/backend/backend/urls.py b/backend/backend/urls.py new file mode 100644 index 0000000..97def4c --- /dev/null +++ b/backend/backend/urls.py @@ -0,0 +1,22 @@ +""" +URL configuration for backend project. + +The `urlpatterns` list routes URLs to views. For more information please see: + https://docs.djangoproject.com/en/4.2/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/backend/wsgi.py b/backend/backend/wsgi.py new file mode 100644 index 0000000..ac58012 --- /dev/null +++ b/backend/backend/wsgi.py @@ -0,0 +1,16 @@ +""" +WSGI config for backend project. + +It exposes the WSGI callable as a module-level variable named ``application``. + +For more information on this file, see +https://docs.djangoproject.com/en/4.2/howto/deployment/wsgi/ +""" + +import os + +from django.core.wsgi import get_wsgi_application + +os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'backend.settings') + +application = get_wsgi_application() diff --git a/backend/manage.py b/backend/manage.py new file mode 100644 index 0000000..eb6431e --- /dev/null +++ b/backend/manage.py @@ -0,0 +1,22 @@ +#!/usr/bin/env python +"""Django's command-line utility for administrative tasks.""" +import os +import sys + + +def main(): + """Run administrative tasks.""" + os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'backend.settings') + try: + from django.core.management import execute_from_command_line + except ImportError as exc: + raise ImportError( + "Couldn't import Django. Are you sure it's installed and " + "available on your PYTHONPATH environment variable? Did you " + "forget to activate a virtual environment?" + ) from exc + execute_from_command_line(sys.argv) + + +if __name__ == '__main__': + main() diff --git a/backend/requirements.txt b/backend/requirements.txt new file mode 100644 index 0000000..bee3a32 --- /dev/null +++ b/backend/requirements.txt @@ -0,0 +1,17 @@ +Django==4.2.10 +djangorestframework==3.14.0 +djangorestframework-simplejwt==5.3.1 +psycopg2-binary==2.9.9 +python-dotenv==1.0.1 +celery==5.4.0 +redis==5.0.1 +pytesseract==0.3.10 +opencv-python==4.9.0.80 +cryptography==42.0.5 +scikit-learn==1.4.1 +tensorflow==2.15.0 +web3==6.15.1 +gunicorn==21.2.0 +whitenoise==6.6.0 +django-cors-headers==4.3.1 +Pillow==10.2.0 \ No newline at end of file diff --git a/frontend/.gitignore b/frontend/.gitignore new file mode 100644 index 0000000..a547bf3 --- /dev/null +++ b/frontend/.gitignore @@ -0,0 +1,24 @@ +# Logs +logs +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* +pnpm-debug.log* +lerna-debug.log* + +node_modules +dist +dist-ssr +*.local + +# Editor directories and files +.vscode/* +!.vscode/extensions.json +.idea +.DS_Store +*.suo +*.ntvs* +*.njsproj +*.sln +*.sw? diff --git a/frontend/README.md b/frontend/README.md new file mode 100644 index 0000000..fd3b758 --- /dev/null +++ b/frontend/README.md @@ -0,0 +1,12 @@ +# React + Vite + +This template provides a minimal setup to get React working in Vite with HMR and some ESLint rules. + +Currently, two official plugins are available: + +- [@vitejs/plugin-react](https://github.com/vitejs/vite-plugin-react/blob/main/packages/plugin-react/README.md) uses [Babel](https://babeljs.io/) for Fast Refresh +- [@vitejs/plugin-react-swc](https://github.com/vitejs/vite-plugin-react-swc) uses [SWC](https://swc.rs/) for Fast Refresh + +## Expanding the ESLint configuration + +If you are developing a production application, we recommend using TypeScript and enable type-aware lint rules. Check out the [TS template](https://github.com/vitejs/vite/tree/main/packages/create-vite/template-react-ts) to integrate TypeScript and [`typescript-eslint`](https://typescript-eslint.io) in your project. diff --git a/frontend/eslint.config.js b/frontend/eslint.config.js new file mode 100644 index 0000000..ec2b712 --- /dev/null +++ b/frontend/eslint.config.js @@ -0,0 +1,33 @@ +import js from '@eslint/js' +import globals from 'globals' +import reactHooks from 'eslint-plugin-react-hooks' +import reactRefresh from 'eslint-plugin-react-refresh' + +export default [ + { ignores: ['dist'] }, + { + files: ['**/*.{js,jsx}'], + languageOptions: { + ecmaVersion: 2020, + globals: globals.browser, + parserOptions: { + ecmaVersion: 'latest', + ecmaFeatures: { jsx: true }, + sourceType: 'module', + }, + }, + plugins: { + 'react-hooks': reactHooks, + 'react-refresh': reactRefresh, + }, + rules: { + ...js.configs.recommended.rules, + ...reactHooks.configs.recommended.rules, + 'no-unused-vars': ['error', { varsIgnorePattern: '^[A-Z_]' }], + 'react-refresh/only-export-components': [ + 'warn', + { allowConstantExport: true }, + ], + }, + }, +] diff --git a/frontend/index.html b/frontend/index.html new file mode 100644 index 0000000..0c589ec --- /dev/null +++ b/frontend/index.html @@ -0,0 +1,13 @@ + + + + + + + Vite + React + + +
+ + + diff --git a/frontend/package-lock.json b/frontend/package-lock.json new file mode 100644 index 0000000..0b81453 --- /dev/null +++ b/frontend/package-lock.json @@ -0,0 +1,2746 @@ +{ + "name": "frontend", + "version": "0.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "frontend", + "version": "0.0.0", + "dependencies": { + "react": "^19.0.0", + "react-dom": "^19.0.0" + }, + "devDependencies": { + "@eslint/js": "^9.21.0", + "@types/react": "^19.0.10", + "@types/react-dom": "^19.0.4", + "@vitejs/plugin-react": "^4.3.4", + "eslint": "^9.21.0", + "eslint-plugin-react-hooks": "^5.1.0", + "eslint-plugin-react-refresh": "^0.4.19", + "globals": "^15.15.0", + "vite": "^6.2.0" + } + }, + "node_modules/@ampproject/remapping": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.3.0.tgz", + "integrity": "sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.24" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/code-frame": { + "version": "7.26.2", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.26.2.tgz", + "integrity": "sha512-RJlIHRueQgwWitWgF8OdFYGZX328Ax5BCemNGlqHfplnRT9ESi8JkFlvaVYbS+UubVY6dpv87Fs2u5M29iNFVQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-validator-identifier": "^7.25.9", + "js-tokens": "^4.0.0", + "picocolors": "^1.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/compat-data": { + "version": "7.26.8", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.26.8.tgz", + "integrity": "sha512-oH5UPLMWR3L2wEFLnFJ1TZXqHufiTKAiLfqw5zkhS4dKXLJ10yVztfil/twG8EDTA4F/tvVNw9nOl4ZMslB8rQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/core": { + "version": "7.26.10", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.26.10.tgz", + "integrity": "sha512-vMqyb7XCDMPvJFFOaT9kxtiRh42GwlZEg1/uIgtZshS5a/8OaduUfCi7kynKgc3Tw/6Uo2D+db9qBttghhmxwQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@ampproject/remapping": "^2.2.0", + "@babel/code-frame": "^7.26.2", + "@babel/generator": "^7.26.10", + "@babel/helper-compilation-targets": "^7.26.5", + "@babel/helper-module-transforms": "^7.26.0", + "@babel/helpers": "^7.26.10", + "@babel/parser": "^7.26.10", + "@babel/template": "^7.26.9", + "@babel/traverse": "^7.26.10", + "@babel/types": "^7.26.10", + "convert-source-map": "^2.0.0", + "debug": "^4.1.0", + "gensync": "^1.0.0-beta.2", + "json5": "^2.2.3", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/babel" + } + }, + "node_modules/@babel/generator": { + "version": "7.27.0", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.27.0.tgz", + "integrity": "sha512-VybsKvpiN1gU1sdMZIp7FcqphVVKEwcuj02x73uvcHE0PTihx1nlBcowYWhDwjpoAXRv43+gDzyggGnn1XZhVw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.27.0", + "@babel/types": "^7.27.0", + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.25", + "jsesc": "^3.0.2" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-compilation-targets": { + "version": "7.27.0", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.27.0.tgz", + "integrity": "sha512-LVk7fbXml0H2xH34dFzKQ7TDZ2G4/rVTOrq9V+icbbadjbVxxeFeDsNHv2SrZeWoA+6ZiTyWYWtScEIW07EAcA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/compat-data": "^7.26.8", + "@babel/helper-validator-option": "^7.25.9", + "browserslist": "^4.24.0", + "lru-cache": "^5.1.1", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-imports": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.25.9.tgz", + "integrity": "sha512-tnUA4RsrmflIM6W6RFTLFSXITtl0wKjgpnLgXyowocVPrbYrLUXSBXDgTs8BlbmIzIdlBySRQjINYs2BAkiLtw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/traverse": "^7.25.9", + "@babel/types": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-transforms": { + "version": "7.26.0", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.26.0.tgz", + "integrity": "sha512-xO+xu6B5K2czEnQye6BHA7DolFFmS3LB7stHZFaOLb1pAwO1HWLS8fXA+eh0A2yIvltPVmx3eNNDBJA2SLHXFw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-module-imports": "^7.25.9", + "@babel/helper-validator-identifier": "^7.25.9", + "@babel/traverse": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-plugin-utils": { + "version": "7.26.5", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.26.5.tgz", + "integrity": "sha512-RS+jZcRdZdRFzMyr+wcsaqOmld1/EqTghfaBGQQd/WnRdzdlvSZ//kF7U8VQTxf1ynZ4cjUcYgjVGx13ewNPMg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-string-parser": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.25.9.tgz", + "integrity": "sha512-4A/SCr/2KLd5jrtOMFzaKjVtAei3+2r/NChoBNoZ3EyP/+GlhoaEGoWOZUmFmoITP7zOJyHIMm+DYRd8o3PvHA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-identifier": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.25.9.tgz", + "integrity": "sha512-Ed61U6XJc3CVRfkERJWDz4dJwKe7iLmmJsbOGu9wSloNSFttHV0I8g6UAgb7qnK5ly5bGLPd4oXZlxCdANBOWQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-option": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.25.9.tgz", + "integrity": "sha512-e/zv1co8pp55dNdEcCynfj9X7nyUKUXoUEwfXqaZt0omVOmDe9oOTdKStH4GmAw6zxMFs50ZayuMfHDKlO7Tfw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helpers": { + "version": "7.27.0", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.27.0.tgz", + "integrity": "sha512-U5eyP/CTFPuNE3qk+WZMxFkp/4zUzdceQlfzf7DdGdhp+Fezd7HD+i8Y24ZuTMKX3wQBld449jijbGq6OdGNQg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/template": "^7.27.0", + "@babel/types": "^7.27.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/parser": { + "version": "7.27.0", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.27.0.tgz", + "integrity": "sha512-iaepho73/2Pz7w2eMS0Q5f83+0RKI7i4xmiYeBmDzfRVbQtTOG7Ts0S4HzJVsTMGI9keU8rNfuZr8DKfSt7Yyg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.27.0" + }, + "bin": { + "parser": "bin/babel-parser.js" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/plugin-transform-react-jsx-self": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-self/-/plugin-transform-react-jsx-self-7.25.9.tgz", + "integrity": "sha512-y8quW6p0WHkEhmErnfe58r7x0A70uKphQm8Sp8cV7tjNQwK56sNVK0M73LK3WuYmsuyrftut4xAkjjgU0twaMg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-react-jsx-source": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-source/-/plugin-transform-react-jsx-source-7.25.9.tgz", + "integrity": "sha512-+iqjT8xmXhhYv4/uiYd8FNQsraMFZIfxVSqxxVSZP0WbbSAWvBXAul0m/zu+7Vv4O/3WtApy9pmaTMiumEZgfg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/template": { + "version": "7.27.0", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.27.0.tgz", + "integrity": "sha512-2ncevenBqXI6qRMukPlXwHKHchC7RyMuu4xv5JBXRfOGVcTy1mXCD12qrp7Jsoxll1EV3+9sE4GugBVRjT2jFA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.26.2", + "@babel/parser": "^7.27.0", + "@babel/types": "^7.27.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/traverse": { + "version": "7.27.0", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.27.0.tgz", + "integrity": "sha512-19lYZFzYVQkkHkl4Cy4WrAVcqBkgvV2YM2TU3xG6DIwO7O3ecbDPfW3yM3bjAGcqcQHi+CCtjMR3dIEHxsd6bA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.26.2", + "@babel/generator": "^7.27.0", + "@babel/parser": "^7.27.0", + "@babel/template": "^7.27.0", + "@babel/types": "^7.27.0", + "debug": "^4.3.1", + "globals": "^11.1.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/traverse/node_modules/globals": { + "version": "11.12.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", + "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/types": { + "version": "7.27.0", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.27.0.tgz", + "integrity": "sha512-H45s8fVLYjbhFH62dIJ3WtmJ6RSPt/3DRO0ZcT2SUiYiQyz3BLVb9ADEnLl91m74aQPS3AzzeajZHYOalWe3bg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-string-parser": "^7.25.9", + "@babel/helper-validator-identifier": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@esbuild/aix-ppc64": { + "version": "0.25.2", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.2.tgz", + "integrity": "sha512-wCIboOL2yXZym2cgm6mlA742s9QeJ8DjGVaL39dLN4rRwrOgOyYSnOaFPhKZGLb2ngj4EyfAFjsNJwPXZvseag==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm": { + "version": "0.25.2", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.2.tgz", + "integrity": "sha512-NQhH7jFstVY5x8CKbcfa166GoV0EFkaPkCKBQkdPJFvo5u+nGXLEH/ooniLb3QI8Fk58YAx7nsPLozUWfCBOJA==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm64": { + "version": "0.25.2", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.2.tgz", + "integrity": "sha512-5ZAX5xOmTligeBaeNEPnPaeEuah53Id2tX4c2CVP3JaROTH+j4fnfHCkr1PjXMd78hMst+TlkfKcW/DlTq0i4w==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-x64": { + "version": "0.25.2", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.2.tgz", + "integrity": "sha512-Ffcx+nnma8Sge4jzddPHCZVRvIfQ0kMsUsCMcJRHkGJ1cDmhe4SsrYIjLUKn1xpHZybmOqCWwB0zQvsjdEHtkg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-arm64": { + "version": "0.25.2", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.2.tgz", + "integrity": "sha512-MpM6LUVTXAzOvN4KbjzU/q5smzryuoNjlriAIx+06RpecwCkL9JpenNzpKd2YMzLJFOdPqBpuub6eVRP5IgiSA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-x64": { + "version": "0.25.2", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.2.tgz", + "integrity": "sha512-5eRPrTX7wFyuWe8FqEFPG2cU0+butQQVNcT4sVipqjLYQjjh8a8+vUTfgBKM88ObB85ahsnTwF7PSIt6PG+QkA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-arm64": { + "version": "0.25.2", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.2.tgz", + "integrity": "sha512-mLwm4vXKiQ2UTSX4+ImyiPdiHjiZhIaE9QvC7sw0tZ6HoNMjYAqQpGyui5VRIi5sGd+uWq940gdCbY3VLvsO1w==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-x64": { + "version": "0.25.2", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.2.tgz", + "integrity": "sha512-6qyyn6TjayJSwGpm8J9QYYGQcRgc90nmfdUb0O7pp1s4lTY+9D0H9O02v5JqGApUyiHOtkz6+1hZNvNtEhbwRQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm": { + "version": "0.25.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.2.tgz", + "integrity": "sha512-UHBRgJcmjJv5oeQF8EpTRZs/1knq6loLxTsjc3nxO9eXAPDLcWW55flrMVc97qFPbmZP31ta1AZVUKQzKTzb0g==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm64": { + "version": "0.25.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.2.tgz", + "integrity": "sha512-gq/sjLsOyMT19I8obBISvhoYiZIAaGF8JpeXu1u8yPv8BE5HlWYobmlsfijFIZ9hIVGYkbdFhEqC0NvM4kNO0g==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ia32": { + "version": "0.25.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.2.tgz", + "integrity": "sha512-bBYCv9obgW2cBP+2ZWfjYTU+f5cxRoGGQ5SeDbYdFCAZpYWrfjjfYwvUpP8MlKbP0nwZ5gyOU/0aUzZ5HWPuvQ==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-loong64": { + "version": "0.25.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.2.tgz", + "integrity": "sha512-SHNGiKtvnU2dBlM5D8CXRFdd+6etgZ9dXfaPCeJtz+37PIUlixvlIhI23L5khKXs3DIzAn9V8v+qb1TRKrgT5w==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-mips64el": { + "version": "0.25.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.2.tgz", + "integrity": "sha512-hDDRlzE6rPeoj+5fsADqdUZl1OzqDYow4TB4Y/3PlKBD0ph1e6uPHzIQcv2Z65u2K0kpeByIyAjCmjn1hJgG0Q==", + "cpu": [ + "mips64el" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ppc64": { + "version": "0.25.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.2.tgz", + "integrity": "sha512-tsHu2RRSWzipmUi9UBDEzc0nLc4HtpZEI5Ba+Omms5456x5WaNuiG3u7xh5AO6sipnJ9r4cRWQB2tUjPyIkc6g==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-riscv64": { + "version": "0.25.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.2.tgz", + "integrity": "sha512-k4LtpgV7NJQOml/10uPU0s4SAXGnowi5qBSjaLWMojNCUICNu7TshqHLAEbkBdAszL5TabfvQ48kK84hyFzjnw==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-s390x": { + "version": "0.25.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.2.tgz", + "integrity": "sha512-GRa4IshOdvKY7M/rDpRR3gkiTNp34M0eLTaC1a08gNrh4u488aPhuZOCpkF6+2wl3zAN7L7XIpOFBhnaE3/Q8Q==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-x64": { + "version": "0.25.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.2.tgz", + "integrity": "sha512-QInHERlqpTTZ4FRB0fROQWXcYRD64lAoiegezDunLpalZMjcUcld3YzZmVJ2H/Cp0wJRZ8Xtjtj0cEHhYc/uUg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-arm64": { + "version": "0.25.2", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.2.tgz", + "integrity": "sha512-talAIBoY5M8vHc6EeI2WW9d/CkiO9MQJ0IOWX8hrLhxGbro/vBXJvaQXefW2cP0z0nQVTdQ/eNyGFV1GSKrxfw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-x64": { + "version": "0.25.2", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.2.tgz", + "integrity": "sha512-voZT9Z+tpOxrvfKFyfDYPc4DO4rk06qamv1a/fkuzHpiVBMOhpjK+vBmWM8J1eiB3OLSMFYNaOaBNLXGChf5tg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-arm64": { + "version": "0.25.2", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.2.tgz", + "integrity": "sha512-dcXYOC6NXOqcykeDlwId9kB6OkPUxOEqU+rkrYVqJbK2hagWOMrsTGsMr8+rW02M+d5Op5NNlgMmjzecaRf7Tg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-x64": { + "version": "0.25.2", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.2.tgz", + "integrity": "sha512-t/TkWwahkH0Tsgoq1Ju7QfgGhArkGLkF1uYz8nQS/PPFlXbP5YgRpqQR3ARRiC2iXoLTWFxc6DJMSK10dVXluw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/sunos-x64": { + "version": "0.25.2", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.2.tgz", + "integrity": "sha512-cfZH1co2+imVdWCjd+D1gf9NjkchVhhdpgb1q5y6Hcv9TP6Zi9ZG/beI3ig8TvwT9lH9dlxLq5MQBBgwuj4xvA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-arm64": { + "version": "0.25.2", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.2.tgz", + "integrity": "sha512-7Loyjh+D/Nx/sOTzV8vfbB3GJuHdOQyrOryFdZvPHLf42Tk9ivBU5Aedi7iyX+x6rbn2Mh68T4qq1SDqJBQO5Q==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-ia32": { + "version": "0.25.2", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.2.tgz", + "integrity": "sha512-WRJgsz9un0nqZJ4MfhabxaD9Ft8KioqU3JMinOTvobbX6MOSUigSBlogP8QB3uxpJDsFS6yN+3FDBdqE5lg9kg==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-x64": { + "version": "0.25.2", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.2.tgz", + "integrity": "sha512-kM3HKb16VIXZyIeVrM1ygYmZBKybX8N4p754bw390wGO3Tf2j4L2/WYL+4suWujpgf6GBYs3jv7TyUivdd05JA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@eslint-community/eslint-utils": { + "version": "4.5.1", + "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.5.1.tgz", + "integrity": "sha512-soEIOALTfTK6EjmKMMoLugwaP0rzkad90iIWd1hMO9ARkSAyjfMfkRRhLvD5qH7vvM0Cg72pieUfR6yh6XxC4w==", + "dev": true, + "license": "MIT", + "dependencies": { + "eslint-visitor-keys": "^3.4.3" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + }, + "peerDependencies": { + "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" + } + }, + "node_modules/@eslint-community/eslint-utils/node_modules/eslint-visitor-keys": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", + "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@eslint-community/regexpp": { + "version": "4.12.1", + "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.12.1.tgz", + "integrity": "sha512-CCZCDJuduB9OUkFkY2IgppNZMi2lBQgD2qzwXkEia16cge2pijY/aXi96CJMquDMn3nJdlPV1A5KrJEXwfLNzQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.0.0 || ^14.0.0 || >=16.0.0" + } + }, + "node_modules/@eslint/config-array": { + "version": "0.20.0", + "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.20.0.tgz", + "integrity": "sha512-fxlS1kkIjx8+vy2SjuCB94q3htSNrufYTXubwiBFeaQHbH6Ipi43gFJq2zCMt6PHhImH3Xmr0NksKDvchWlpQQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@eslint/object-schema": "^2.1.6", + "debug": "^4.3.1", + "minimatch": "^3.1.2" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/config-helpers": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.2.1.tgz", + "integrity": "sha512-RI17tsD2frtDu/3dmI7QRrD4bedNKPM08ziRYaC5AhkGrzIAJelm9kJU1TznK+apx6V+cqRz8tfpEeG3oIyjxw==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/core": { + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.12.0.tgz", + "integrity": "sha512-cmrR6pytBuSMTaBweKoGMwu3EiHiEC+DoyupPmlZ0HxBJBtIxwe+j/E4XPIKNx+Q74c8lXKPwYawBf5glsTkHg==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@types/json-schema": "^7.0.15" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/eslintrc": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-3.3.1.tgz", + "integrity": "sha512-gtF186CXhIl1p4pJNGZw8Yc6RlshoePRvE0X91oPGb3vZ8pM3qOS9W9NGPat9LziaBV7XrJWGylNQXkGcnM3IQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ajv": "^6.12.4", + "debug": "^4.3.2", + "espree": "^10.0.1", + "globals": "^14.0.0", + "ignore": "^5.2.0", + "import-fresh": "^3.2.1", + "js-yaml": "^4.1.0", + "minimatch": "^3.1.2", + "strip-json-comments": "^3.1.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@eslint/eslintrc/node_modules/globals": { + "version": "14.0.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-14.0.0.tgz", + "integrity": "sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@eslint/js": { + "version": "9.24.0", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.24.0.tgz", + "integrity": "sha512-uIY/y3z0uvOGX8cp1C2fiC4+ZmBhp6yZWkojtHL1YEMnRt1Y63HB9TM17proGEmeG7HeUY+UP36F0aknKYTpYA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/object-schema": { + "version": "2.1.6", + "resolved": "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-2.1.6.tgz", + "integrity": "sha512-RBMg5FRL0I0gs51M/guSAj5/e14VQ4tpZnQNWwuDT66P14I43ItmPfIZRhO9fUVIPOAQXU47atlywZ/czoqFPA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/plugin-kit": { + "version": "0.2.8", + "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.2.8.tgz", + "integrity": "sha512-ZAoA40rNMPwSm+AeHpCq8STiNAwzWLJuP8Xv4CHIc9wv/PSuExjMrmjfYNj682vW0OOiZ1HKxzvjQr9XZIisQA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@eslint/core": "^0.13.0", + "levn": "^0.4.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/plugin-kit/node_modules/@eslint/core": { + "version": "0.13.0", + "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.13.0.tgz", + "integrity": "sha512-yfkgDw1KR66rkT5A8ci4irzDysN7FRpq3ttJolR88OqQikAWqwA8j5VZyas+vjyBNFIJ7MfybJ9plMILI2UrCw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@types/json-schema": "^7.0.15" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@humanfs/core": { + "version": "0.19.1", + "resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.1.tgz", + "integrity": "sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=18.18.0" + } + }, + "node_modules/@humanfs/node": { + "version": "0.16.6", + "resolved": "https://registry.npmjs.org/@humanfs/node/-/node-0.16.6.tgz", + "integrity": "sha512-YuI2ZHQL78Q5HbhDiBA1X4LmYdXCKCMQIfw0pw7piHJwyREFebJUvrQN4cMssyES6x+vfUbx1CIpaQUKYdQZOw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@humanfs/core": "^0.19.1", + "@humanwhocodes/retry": "^0.3.0" + }, + "engines": { + "node": ">=18.18.0" + } + }, + "node_modules/@humanfs/node/node_modules/@humanwhocodes/retry": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.3.1.tgz", + "integrity": "sha512-JBxkERygn7Bv/GbN5Rv8Ul6LVknS+5Bp6RgDC/O8gEBU/yeH5Ui5C/OlWrTb6qct7LjjfT6Re2NxB0ln0yYybA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=18.18" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, + "node_modules/@humanwhocodes/module-importer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", + "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=12.22" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, + "node_modules/@humanwhocodes/retry": { + "version": "0.4.2", + "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.4.2.tgz", + "integrity": "sha512-xeO57FpIu4p1Ri3Jq/EXq4ClRm86dVF2z/+kvFnyqVYRavTZmaFaUBbWCOuuTh0o/g7DSsk6kc2vrS4Vl5oPOQ==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=18.18" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, + "node_modules/@jridgewell/gen-mapping": { + "version": "0.3.8", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.8.tgz", + "integrity": "sha512-imAbBGkb+ebQyxKgzv5Hu2nmROxoDOXHh80evxdoXNOrvAnVx7zimzc1Oo5h9RlfV4vPXaE2iM5pOFbvOCClWA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/set-array": "^1.2.1", + "@jridgewell/sourcemap-codec": "^1.4.10", + "@jridgewell/trace-mapping": "^0.3.24" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", + "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/set-array": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.2.1.tgz", + "integrity": "sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.0.tgz", + "integrity": "sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.25", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz", + "integrity": "sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" + } + }, + "node_modules/@rollup/rollup-android-arm-eabi": { + "version": "4.39.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.39.0.tgz", + "integrity": "sha512-lGVys55Qb00Wvh8DMAocp5kIcaNzEFTmGhfFd88LfaogYTRKrdxgtlO5H6S49v2Nd8R2C6wLOal0qv6/kCkOwA==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-android-arm64": { + "version": "4.39.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.39.0.tgz", + "integrity": "sha512-It9+M1zE31KWfqh/0cJLrrsCPiF72PoJjIChLX+rEcujVRCb4NLQ5QzFkzIZW8Kn8FTbvGQBY5TkKBau3S8cCQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-darwin-arm64": { + "version": "4.39.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.39.0.tgz", + "integrity": "sha512-lXQnhpFDOKDXiGxsU9/l8UEGGM65comrQuZ+lDcGUx+9YQ9dKpF3rSEGepyeR5AHZ0b5RgiligsBhWZfSSQh8Q==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-darwin-x64": { + "version": "4.39.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.39.0.tgz", + "integrity": "sha512-mKXpNZLvtEbgu6WCkNij7CGycdw9cJi2k9v0noMb++Vab12GZjFgUXD69ilAbBh034Zwn95c2PNSz9xM7KYEAQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-freebsd-arm64": { + "version": "4.39.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.39.0.tgz", + "integrity": "sha512-jivRRlh2Lod/KvDZx2zUR+I4iBfHcu2V/BA2vasUtdtTN2Uk3jfcZczLa81ESHZHPHy4ih3T/W5rPFZ/hX7RtQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-freebsd-x64": { + "version": "4.39.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.39.0.tgz", + "integrity": "sha512-8RXIWvYIRK9nO+bhVz8DwLBepcptw633gv/QT4015CpJ0Ht8punmoHU/DuEd3iw9Hr8UwUV+t+VNNuZIWYeY7Q==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-linux-arm-gnueabihf": { + "version": "4.39.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.39.0.tgz", + "integrity": "sha512-mz5POx5Zu58f2xAG5RaRRhp3IZDK7zXGk5sdEDj4o96HeaXhlUwmLFzNlc4hCQi5sGdR12VDgEUqVSHer0lI9g==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm-musleabihf": { + "version": "4.39.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.39.0.tgz", + "integrity": "sha512-+YDwhM6gUAyakl0CD+bMFpdmwIoRDzZYaTWV3SDRBGkMU/VpIBYXXEvkEcTagw/7VVkL2vA29zU4UVy1mP0/Yw==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-gnu": { + "version": "4.39.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.39.0.tgz", + "integrity": "sha512-EKf7iF7aK36eEChvlgxGnk7pdJfzfQbNvGV/+l98iiMwU23MwvmV0Ty3pJ0p5WQfm3JRHOytSIqD9LB7Bq7xdQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-musl": { + "version": "4.39.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.39.0.tgz", + "integrity": "sha512-vYanR6MtqC7Z2SNr8gzVnzUul09Wi1kZqJaek3KcIlI/wq5Xtq4ZPIZ0Mr/st/sv/NnaPwy/D4yXg5x0B3aUUA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-loongarch64-gnu": { + "version": "4.39.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loongarch64-gnu/-/rollup-linux-loongarch64-gnu-4.39.0.tgz", + "integrity": "sha512-NMRUT40+h0FBa5fb+cpxtZoGAggRem16ocVKIv5gDB5uLDgBIwrIsXlGqYbLwW8YyO3WVTk1FkFDjMETYlDqiw==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-powerpc64le-gnu": { + "version": "4.39.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.39.0.tgz", + "integrity": "sha512-0pCNnmxgduJ3YRt+D+kJ6Ai/r+TaePu9ZLENl+ZDV/CdVczXl95CbIiwwswu4L+K7uOIGf6tMo2vm8uadRaICQ==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-gnu": { + "version": "4.39.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.39.0.tgz", + "integrity": "sha512-t7j5Zhr7S4bBtksT73bO6c3Qa2AV/HqiGlj9+KB3gNF5upcVkx+HLgxTm8DK4OkzsOYqbdqbLKwvGMhylJCPhQ==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-musl": { + "version": "4.39.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.39.0.tgz", + "integrity": "sha512-m6cwI86IvQ7M93MQ2RF5SP8tUjD39Y7rjb1qjHgYh28uAPVU8+k/xYWvxRO3/tBN2pZkSMa5RjnPuUIbrwVxeA==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-s390x-gnu": { + "version": "4.39.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.39.0.tgz", + "integrity": "sha512-iRDJd2ebMunnk2rsSBYlsptCyuINvxUfGwOUldjv5M4tpa93K8tFMeYGpNk2+Nxl+OBJnBzy2/JCscGeO507kA==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-gnu": { + "version": "4.39.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.39.0.tgz", + "integrity": "sha512-t9jqYw27R6Lx0XKfEFe5vUeEJ5pF3SGIM6gTfONSMb7DuG6z6wfj2yjcoZxHg129veTqU7+wOhY6GX8wmf90dA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-musl": { + "version": "4.39.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.39.0.tgz", + "integrity": "sha512-ThFdkrFDP55AIsIZDKSBWEt/JcWlCzydbZHinZ0F/r1h83qbGeenCt/G/wG2O0reuENDD2tawfAj2s8VK7Bugg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-win32-arm64-msvc": { + "version": "4.39.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.39.0.tgz", + "integrity": "sha512-jDrLm6yUtbOg2TYB3sBF3acUnAwsIksEYjLeHL+TJv9jg+TmTwdyjnDex27jqEMakNKf3RwwPahDIt7QXCSqRQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-ia32-msvc": { + "version": "4.39.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.39.0.tgz", + "integrity": "sha512-6w9uMuza+LbLCVoNKL5FSLE7yvYkq9laSd09bwS0tMjkwXrmib/4KmoJcrKhLWHvw19mwU+33ndC69T7weNNjQ==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-msvc": { + "version": "4.39.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.39.0.tgz", + "integrity": "sha512-yAkUOkIKZlK5dl7u6dg897doBgLXmUHhIINM2c+sND3DZwnrdQkkSiDh7N75Ll4mM4dxSkYfXqU9fW3lLkMFug==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@types/babel__core": { + "version": "7.20.5", + "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.5.tgz", + "integrity": "sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.20.7", + "@babel/types": "^7.20.7", + "@types/babel__generator": "*", + "@types/babel__template": "*", + "@types/babel__traverse": "*" + } + }, + "node_modules/@types/babel__generator": { + "version": "7.27.0", + "resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.27.0.tgz", + "integrity": "sha512-ufFd2Xi92OAVPYsy+P4n7/U7e68fex0+Ee8gSG9KX7eo084CWiQ4sdxktvdl0bOPupXtVJPY19zk6EwWqUQ8lg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.0.0" + } + }, + "node_modules/@types/babel__template": { + "version": "7.4.4", + "resolved": "https://registry.npmjs.org/@types/babel__template/-/babel__template-7.4.4.tgz", + "integrity": "sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.1.0", + "@babel/types": "^7.0.0" + } + }, + "node_modules/@types/babel__traverse": { + "version": "7.20.7", + "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.20.7.tgz", + "integrity": "sha512-dkO5fhS7+/oos4ciWxyEyjWe48zmG6wbCheo/G2ZnHx4fs3EU6YC6UM8rk56gAjNJ9P3MTH2jo5jb92/K6wbng==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.20.7" + } + }, + "node_modules/@types/estree": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.7.tgz", + "integrity": "sha512-w28IoSUCJpidD/TGviZwwMJckNESJZXFu7NBZ5YJ4mEUnNraUn9Pm8HSZm/jDF1pDWYKspWE7oVphigUPRakIQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/json-schema": { + "version": "7.0.15", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", + "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/react": { + "version": "19.1.0", + "resolved": "https://registry.npmjs.org/@types/react/-/react-19.1.0.tgz", + "integrity": "sha512-UaicktuQI+9UKyA4njtDOGBD/67t8YEBt2xdfqu8+gP9hqPUPsiXlNPcpS2gVdjmis5GKPG3fCxbQLVgxsQZ8w==", + "dev": true, + "license": "MIT", + "dependencies": { + "csstype": "^3.0.2" + } + }, + "node_modules/@types/react-dom": { + "version": "19.1.1", + "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-19.1.1.tgz", + "integrity": "sha512-jFf/woGTVTjUJsl2O7hcopJ1r0upqoq/vIOoCj0yLh3RIXxWcljlpuZ+vEBRXsymD1jhfeJrlyTy/S1UW+4y1w==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "@types/react": "^19.0.0" + } + }, + "node_modules/@vitejs/plugin-react": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/@vitejs/plugin-react/-/plugin-react-4.3.4.tgz", + "integrity": "sha512-SCCPBJtYLdE8PX/7ZQAs1QAZ8Jqwih+0VBLum1EGqmCCQal+MIUqLCzj3ZUy8ufbC0cAM4LRlSTm7IQJwWT4ug==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/core": "^7.26.0", + "@babel/plugin-transform-react-jsx-self": "^7.25.9", + "@babel/plugin-transform-react-jsx-source": "^7.25.9", + "@types/babel__core": "^7.20.5", + "react-refresh": "^0.14.2" + }, + "engines": { + "node": "^14.18.0 || >=16.0.0" + }, + "peerDependencies": { + "vite": "^4.2.0 || ^5.0.0 || ^6.0.0" + } + }, + "node_modules/acorn": { + "version": "8.14.1", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.14.1.tgz", + "integrity": "sha512-OvQ/2pUDKmgfCg++xsTX1wGxfTaszcHVcTctW4UJB4hibJx2HXxxO5UmVgyjMa+ZDsiaf5wWLXYpRWMmBI0QHg==", + "dev": true, + "license": "MIT", + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/acorn-jsx": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", + "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" + } + }, + "node_modules/ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "dev": true, + "license": "Python-2.0" + }, + "node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true, + "license": "MIT" + }, + "node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/browserslist": { + "version": "4.24.4", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.24.4.tgz", + "integrity": "sha512-KDi1Ny1gSePi1vm0q4oxSF8b4DR44GF4BbmS2YdhPLOEqd8pDviZOGH/GsmRwoWJ2+5Lr085X7naowMwKHDG1A==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "caniuse-lite": "^1.0.30001688", + "electron-to-chromium": "^1.5.73", + "node-releases": "^2.0.19", + "update-browserslist-db": "^1.1.1" + }, + "bin": { + "browserslist": "cli.js" + }, + "engines": { + "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" + } + }, + "node_modules/callsites": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", + "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/caniuse-lite": { + "version": "1.0.30001712", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001712.tgz", + "integrity": "sha512-MBqPpGYYdQ7/hfKiet9SCI+nmN5/hp4ZzveOJubl5DTAMa5oggjAuoi0Z4onBpKPFI2ePGnQuQIzF3VxDjDJig==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/caniuse-lite" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "CC-BY-4.0" + }, + "node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true, + "license": "MIT" + }, + "node_modules/concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", + "dev": true, + "license": "MIT" + }, + "node_modules/convert-source-map": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", + "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", + "dev": true, + "license": "MIT" + }, + "node_modules/cross-spawn": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", + "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", + "dev": true, + "license": "MIT", + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/csstype": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz", + "integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==", + "dev": true, + "license": "MIT" + }, + "node_modules/debug": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz", + "integrity": "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/deep-is": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", + "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/electron-to-chromium": { + "version": "1.5.132", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.132.tgz", + "integrity": "sha512-QgX9EBvWGmvSRa74zqfnG7+Eno0Ak0vftBll0Pt2/z5b3bEGYL6OUXLgKPtvx73dn3dvwrlyVkjPKRRlhLYTEg==", + "dev": true, + "license": "ISC" + }, + "node_modules/esbuild": { + "version": "0.25.2", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.2.tgz", + "integrity": "sha512-16854zccKPnC+toMywC+uKNeYSv+/eXkevRAfwRD/G9Cleq66m8XFIrigkbvauLLlCfDL45Q2cWegSg53gGBnQ==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.25.2", + "@esbuild/android-arm": "0.25.2", + "@esbuild/android-arm64": "0.25.2", + "@esbuild/android-x64": "0.25.2", + "@esbuild/darwin-arm64": "0.25.2", + "@esbuild/darwin-x64": "0.25.2", + "@esbuild/freebsd-arm64": "0.25.2", + "@esbuild/freebsd-x64": "0.25.2", + "@esbuild/linux-arm": "0.25.2", + "@esbuild/linux-arm64": "0.25.2", + "@esbuild/linux-ia32": "0.25.2", + "@esbuild/linux-loong64": "0.25.2", + "@esbuild/linux-mips64el": "0.25.2", + "@esbuild/linux-ppc64": "0.25.2", + "@esbuild/linux-riscv64": "0.25.2", + "@esbuild/linux-s390x": "0.25.2", + "@esbuild/linux-x64": "0.25.2", + "@esbuild/netbsd-arm64": "0.25.2", + "@esbuild/netbsd-x64": "0.25.2", + "@esbuild/openbsd-arm64": "0.25.2", + "@esbuild/openbsd-x64": "0.25.2", + "@esbuild/sunos-x64": "0.25.2", + "@esbuild/win32-arm64": "0.25.2", + "@esbuild/win32-ia32": "0.25.2", + "@esbuild/win32-x64": "0.25.2" + } + }, + "node_modules/escalade": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", + "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/eslint": { + "version": "9.24.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.24.0.tgz", + "integrity": "sha512-eh/jxIEJyZrvbWRe4XuVclLPDYSYYYgLy5zXGGxD6j8zjSAxFEzI2fL/8xNq6O2yKqVt+eF2YhV+hxjV6UKXwQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/eslint-utils": "^4.2.0", + "@eslint-community/regexpp": "^4.12.1", + "@eslint/config-array": "^0.20.0", + "@eslint/config-helpers": "^0.2.0", + "@eslint/core": "^0.12.0", + "@eslint/eslintrc": "^3.3.1", + "@eslint/js": "9.24.0", + "@eslint/plugin-kit": "^0.2.7", + "@humanfs/node": "^0.16.6", + "@humanwhocodes/module-importer": "^1.0.1", + "@humanwhocodes/retry": "^0.4.2", + "@types/estree": "^1.0.6", + "@types/json-schema": "^7.0.15", + "ajv": "^6.12.4", + "chalk": "^4.0.0", + "cross-spawn": "^7.0.6", + "debug": "^4.3.2", + "escape-string-regexp": "^4.0.0", + "eslint-scope": "^8.3.0", + "eslint-visitor-keys": "^4.2.0", + "espree": "^10.3.0", + "esquery": "^1.5.0", + "esutils": "^2.0.2", + "fast-deep-equal": "^3.1.3", + "file-entry-cache": "^8.0.0", + "find-up": "^5.0.0", + "glob-parent": "^6.0.2", + "ignore": "^5.2.0", + "imurmurhash": "^0.1.4", + "is-glob": "^4.0.0", + "json-stable-stringify-without-jsonify": "^1.0.1", + "lodash.merge": "^4.6.2", + "minimatch": "^3.1.2", + "natural-compare": "^1.4.0", + "optionator": "^0.9.3" + }, + "bin": { + "eslint": "bin/eslint.js" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://eslint.org/donate" + }, + "peerDependencies": { + "jiti": "*" + }, + "peerDependenciesMeta": { + "jiti": { + "optional": true + } + } + }, + "node_modules/eslint-plugin-react-hooks": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-5.2.0.tgz", + "integrity": "sha512-+f15FfK64YQwZdJNELETdn5ibXEUQmW1DZL6KXhNnc2heoy/sg9VJJeT7n8TlMWouzWqSWavFkIhHyIbIAEapg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "peerDependencies": { + "eslint": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0 || ^9.0.0" + } + }, + "node_modules/eslint-plugin-react-refresh": { + "version": "0.4.19", + "resolved": "https://registry.npmjs.org/eslint-plugin-react-refresh/-/eslint-plugin-react-refresh-0.4.19.tgz", + "integrity": "sha512-eyy8pcr/YxSYjBoqIFSrlbn9i/xvxUFa8CjzAYo9cFjgGXqq1hyjihcpZvxRLalpaWmueWR81xn7vuKmAFijDQ==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "eslint": ">=8.40" + } + }, + "node_modules/eslint-scope": { + "version": "8.3.0", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-8.3.0.tgz", + "integrity": "sha512-pUNxi75F8MJ/GdeKtVLSbYg4ZI34J6C0C7sbL4YOp2exGwen7ZsuBqKzUhXd0qMQ362yET3z+uPwKeg/0C2XCQ==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "esrecurse": "^4.3.0", + "estraverse": "^5.2.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint-visitor-keys": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.0.tgz", + "integrity": "sha512-UyLnSehNt62FFhSwjZlHmeokpRK59rcz29j+F1/aDgbkbRTk7wIc9XzdoasMUbRNKDM0qQt/+BJ4BrpFeABemw==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/espree": { + "version": "10.3.0", + "resolved": "https://registry.npmjs.org/espree/-/espree-10.3.0.tgz", + "integrity": "sha512-0QYC8b24HWY8zjRnDTL6RiHfDbAWn63qb4LMj1Z4b076A4une81+z03Kg7l7mn/48PUTqoLptSXez8oknU8Clg==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "acorn": "^8.14.0", + "acorn-jsx": "^5.3.2", + "eslint-visitor-keys": "^4.2.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/esquery": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.6.0.tgz", + "integrity": "sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "estraverse": "^5.1.0" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/esrecurse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", + "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "estraverse": "^5.2.0" + }, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=4.0" + } + }, + "node_modules/esutils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/fast-json-stable-stringify": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", + "dev": true, + "license": "MIT" + }, + "node_modules/fast-levenshtein": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", + "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", + "dev": true, + "license": "MIT" + }, + "node_modules/file-entry-cache": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-8.0.0.tgz", + "integrity": "sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "flat-cache": "^4.0.0" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/find-up": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", + "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", + "dev": true, + "license": "MIT", + "dependencies": { + "locate-path": "^6.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/flat-cache": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-4.0.1.tgz", + "integrity": "sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==", + "dev": true, + "license": "MIT", + "dependencies": { + "flatted": "^3.2.9", + "keyv": "^4.5.4" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/flatted": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.3.tgz", + "integrity": "sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg==", + "dev": true, + "license": "ISC" + }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/gensync": { + "version": "1.0.0-beta.2", + "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", + "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/glob-parent": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", + "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", + "dev": true, + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.3" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/globals": { + "version": "15.15.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-15.15.0.tgz", + "integrity": "sha512-7ACyT3wmyp3I61S4fG682L0VA2RGD9otkqGJIwNUMF1SWUombIIk+af1unuDYgMm082aHYwD+mzJvv9Iu8dsgg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/ignore": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", + "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, + "node_modules/import-fresh": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.1.tgz", + "integrity": "sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "parent-module": "^1.0.0", + "resolve-from": "^4.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.8.19" + } + }, + "node_modules/is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "dev": true, + "license": "ISC" + }, + "node_modules/js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/js-yaml": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", + "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "dev": true, + "license": "MIT", + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/jsesc": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz", + "integrity": "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==", + "dev": true, + "license": "MIT", + "bin": { + "jsesc": "bin/jsesc" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/json-buffer": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", + "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true, + "license": "MIT" + }, + "node_modules/json-stable-stringify-without-jsonify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", + "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", + "dev": true, + "license": "MIT" + }, + "node_modules/json5": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", + "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", + "dev": true, + "license": "MIT", + "bin": { + "json5": "lib/cli.js" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/keyv": { + "version": "4.5.4", + "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", + "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", + "dev": true, + "license": "MIT", + "dependencies": { + "json-buffer": "3.0.1" + } + }, + "node_modules/levn": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", + "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "prelude-ls": "^1.2.1", + "type-check": "~0.4.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/locate-path": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", + "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-locate": "^5.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/lodash.merge": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", + "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/lru-cache": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", + "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", + "dev": true, + "license": "ISC", + "dependencies": { + "yallist": "^3.0.2" + } + }, + "node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true, + "license": "MIT" + }, + "node_modules/nanoid": { + "version": "3.3.11", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz", + "integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, + "node_modules/natural-compare": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", + "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", + "dev": true, + "license": "MIT" + }, + "node_modules/node-releases": { + "version": "2.0.19", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.19.tgz", + "integrity": "sha512-xxOWJsBKtzAq7DY0J+DTzuz58K8e7sJbdgwkbMWQe8UYB6ekmsQ45q0M/tJDsGaZmbC+l7n57UV8Hl5tHxO9uw==", + "dev": true, + "license": "MIT" + }, + "node_modules/optionator": { + "version": "0.9.4", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz", + "integrity": "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==", + "dev": true, + "license": "MIT", + "dependencies": { + "deep-is": "^0.1.3", + "fast-levenshtein": "^2.0.6", + "levn": "^0.4.1", + "prelude-ls": "^1.2.1", + "type-check": "^0.4.0", + "word-wrap": "^1.2.5" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/p-limit": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "yocto-queue": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-locate": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", + "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-limit": "^3.0.2" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/parent-module": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", + "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", + "dev": true, + "license": "MIT", + "dependencies": { + "callsites": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/picocolors": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", + "dev": true, + "license": "ISC" + }, + "node_modules/postcss": { + "version": "8.5.3", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.3.tgz", + "integrity": "sha512-dle9A3yYxlBSrt8Fu+IpjGT8SY8hN0mlaA6GY8t0P5PjIOZemULz/E2Bnm/2dcUOena75OTNkHI76uZBNUUq3A==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "nanoid": "^3.3.8", + "picocolors": "^1.1.1", + "source-map-js": "^1.2.1" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, + "node_modules/prelude-ls": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", + "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/punycode": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", + "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/react": { + "version": "19.1.0", + "resolved": "https://registry.npmjs.org/react/-/react-19.1.0.tgz", + "integrity": "sha512-FS+XFBNvn3GTAWq26joslQgWNoFu08F4kl0J4CgdNKADkdSGXQyTCnKteIAJy96Br6YbpEU1LSzV5dYtjMkMDg==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/react-dom": { + "version": "19.1.0", + "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.1.0.tgz", + "integrity": "sha512-Xs1hdnE+DyKgeHJeJznQmYMIBG3TKIHJJT95Q58nHLSrElKlGQqDTR2HQ9fx5CN/Gk6Vh/kupBTDLU11/nDk/g==", + "license": "MIT", + "dependencies": { + "scheduler": "^0.26.0" + }, + "peerDependencies": { + "react": "^19.1.0" + } + }, + "node_modules/react-refresh": { + "version": "0.14.2", + "resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.14.2.tgz", + "integrity": "sha512-jCvmsr+1IUSMUyzOkRcvnVbX3ZYC6g9TDrDbFuFmRDq7PD4yaGbLKNQL6k2jnArV8hjYxh7hVhAZB6s9HDGpZA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/resolve-from": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", + "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/rollup": { + "version": "4.39.0", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.39.0.tgz", + "integrity": "sha512-thI8kNc02yNvnmJp8dr3fNWJ9tCONDhp6TV35X6HkKGGs9E6q7YWCHbe5vKiTa7TAiNcFEmXKj3X/pG2b3ci0g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/estree": "1.0.7" + }, + "bin": { + "rollup": "dist/bin/rollup" + }, + "engines": { + "node": ">=18.0.0", + "npm": ">=8.0.0" + }, + "optionalDependencies": { + "@rollup/rollup-android-arm-eabi": "4.39.0", + "@rollup/rollup-android-arm64": "4.39.0", + "@rollup/rollup-darwin-arm64": "4.39.0", + "@rollup/rollup-darwin-x64": "4.39.0", + "@rollup/rollup-freebsd-arm64": "4.39.0", + "@rollup/rollup-freebsd-x64": "4.39.0", + "@rollup/rollup-linux-arm-gnueabihf": "4.39.0", + "@rollup/rollup-linux-arm-musleabihf": "4.39.0", + "@rollup/rollup-linux-arm64-gnu": "4.39.0", + "@rollup/rollup-linux-arm64-musl": "4.39.0", + "@rollup/rollup-linux-loongarch64-gnu": "4.39.0", + "@rollup/rollup-linux-powerpc64le-gnu": "4.39.0", + "@rollup/rollup-linux-riscv64-gnu": "4.39.0", + "@rollup/rollup-linux-riscv64-musl": "4.39.0", + "@rollup/rollup-linux-s390x-gnu": "4.39.0", + "@rollup/rollup-linux-x64-gnu": "4.39.0", + "@rollup/rollup-linux-x64-musl": "4.39.0", + "@rollup/rollup-win32-arm64-msvc": "4.39.0", + "@rollup/rollup-win32-ia32-msvc": "4.39.0", + "@rollup/rollup-win32-x64-msvc": "4.39.0", + "fsevents": "~2.3.2" + } + }, + "node_modules/scheduler": { + "version": "0.26.0", + "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.26.0.tgz", + "integrity": "sha512-NlHwttCI/l5gCPR3D1nNXtWABUmBwvZpEQiD4IXSbIDq8BzLIK/7Ir5gTFSGZDUu37K5cMNp0hFtzO38sC7gWA==", + "license": "MIT" + }, + "node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dev": true, + "license": "MIT", + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/source-map-js": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", + "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/strip-json-comments": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", + "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/type-check": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", + "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", + "dev": true, + "license": "MIT", + "dependencies": { + "prelude-ls": "^1.2.1" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/update-browserslist-db": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.3.tgz", + "integrity": "sha512-UxhIZQ+QInVdunkDAaiazvvT/+fXL5Osr0JZlJulepYu6Jd7qJtDZjlur0emRlT71EN3ScPoE7gvsuIKKNavKw==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "escalade": "^3.2.0", + "picocolors": "^1.1.1" + }, + "bin": { + "update-browserslist-db": "cli.js" + }, + "peerDependencies": { + "browserslist": ">= 4.21.0" + } + }, + "node_modules/uri-js": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", + "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "punycode": "^2.1.0" + } + }, + "node_modules/vite": { + "version": "6.2.5", + "resolved": "https://registry.npmjs.org/vite/-/vite-6.2.5.tgz", + "integrity": "sha512-j023J/hCAa4pRIUH6J9HemwYfjB5llR2Ps0CWeikOtdR8+pAURAk0DoJC5/mm9kd+UgdnIy7d6HE4EAvlYhPhA==", + "dev": true, + "license": "MIT", + "dependencies": { + "esbuild": "^0.25.0", + "postcss": "^8.5.3", + "rollup": "^4.30.1" + }, + "bin": { + "vite": "bin/vite.js" + }, + "engines": { + "node": "^18.0.0 || ^20.0.0 || >=22.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 || >=22.0.0", + "jiti": ">=1.21.0", + "less": "*", + "lightningcss": "^1.21.0", + "sass": "*", + "sass-embedded": "*", + "stylus": "*", + "sugarss": "*", + "terser": "^5.16.0", + "tsx": "^4.8.1", + "yaml": "^2.4.2" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + }, + "jiti": { + "optional": true + }, + "less": { + "optional": true + }, + "lightningcss": { + "optional": true + }, + "sass": { + "optional": true + }, + "sass-embedded": { + "optional": true + }, + "stylus": { + "optional": true + }, + "sugarss": { + "optional": true + }, + "terser": { + "optional": true + }, + "tsx": { + "optional": true + }, + "yaml": { + "optional": true + } + } + }, + "node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "license": "ISC", + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/word-wrap": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz", + "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/yallist": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", + "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", + "dev": true, + "license": "ISC" + }, + "node_modules/yocto-queue": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", + "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + } + } +} diff --git a/frontend/package.json b/frontend/package.json new file mode 100644 index 0000000..902fd98 --- /dev/null +++ b/frontend/package.json @@ -0,0 +1,27 @@ +{ + "name": "frontend", + "private": true, + "version": "0.0.0", + "type": "module", + "scripts": { + "dev": "vite", + "build": "vite build", + "lint": "eslint .", + "preview": "vite preview" + }, + "dependencies": { + "react": "^19.0.0", + "react-dom": "^19.0.0" + }, + "devDependencies": { + "@eslint/js": "^9.21.0", + "@types/react": "^19.0.10", + "@types/react-dom": "^19.0.4", + "@vitejs/plugin-react": "^4.3.4", + "eslint": "^9.21.0", + "eslint-plugin-react-hooks": "^5.1.0", + "eslint-plugin-react-refresh": "^0.4.19", + "globals": "^15.15.0", + "vite": "^6.2.0" + } +} diff --git a/frontend/public/vite.svg b/frontend/public/vite.svg new file mode 100644 index 0000000..e7b8dfb --- /dev/null +++ b/frontend/public/vite.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/frontend/src/App.css b/frontend/src/App.css new file mode 100644 index 0000000..b9d355d --- /dev/null +++ b/frontend/src/App.css @@ -0,0 +1,42 @@ +#root { + max-width: 1280px; + margin: 0 auto; + padding: 2rem; + text-align: center; +} + +.logo { + height: 6em; + padding: 1.5em; + will-change: filter; + transition: filter 300ms; +} +.logo:hover { + filter: drop-shadow(0 0 2em #646cffaa); +} +.logo.react:hover { + filter: drop-shadow(0 0 2em #61dafbaa); +} + +@keyframes logo-spin { + from { + transform: rotate(0deg); + } + to { + transform: rotate(360deg); + } +} + +@media (prefers-reduced-motion: no-preference) { + a:nth-of-type(2) .logo { + animation: logo-spin infinite 20s linear; + } +} + +.card { + padding: 2em; +} + +.read-the-docs { + color: #888; +} diff --git a/frontend/src/App.jsx b/frontend/src/App.jsx new file mode 100644 index 0000000..f67355a --- /dev/null +++ b/frontend/src/App.jsx @@ -0,0 +1,35 @@ +import { useState } from 'react' +import reactLogo from './assets/react.svg' +import viteLogo from '/vite.svg' +import './App.css' + +function App() { + const [count, setCount] = useState(0) + + return ( + <> + +

Vite + React

+
+ +

+ Edit src/App.jsx and save to test HMR +

+
+

+ Click on the Vite and React logos to learn more +

+ + ) +} + +export default App diff --git a/frontend/src/assets/react.svg b/frontend/src/assets/react.svg new file mode 100644 index 0000000..6c87de9 --- /dev/null +++ b/frontend/src/assets/react.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/frontend/src/index.css b/frontend/src/index.css new file mode 100644 index 0000000..08a3ac9 --- /dev/null +++ b/frontend/src/index.css @@ -0,0 +1,68 @@ +:root { + font-family: system-ui, Avenir, Helvetica, Arial, sans-serif; + line-height: 1.5; + font-weight: 400; + + color-scheme: light dark; + color: rgba(255, 255, 255, 0.87); + background-color: #242424; + + font-synthesis: none; + text-rendering: optimizeLegibility; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; +} + +a { + font-weight: 500; + color: #646cff; + text-decoration: inherit; +} +a:hover { + color: #535bf2; +} + +body { + margin: 0; + display: flex; + place-items: center; + min-width: 320px; + min-height: 100vh; +} + +h1 { + font-size: 3.2em; + line-height: 1.1; +} + +button { + border-radius: 8px; + border: 1px solid transparent; + padding: 0.6em 1.2em; + font-size: 1em; + font-weight: 500; + font-family: inherit; + background-color: #1a1a1a; + cursor: pointer; + transition: border-color 0.25s; +} +button:hover { + border-color: #646cff; +} +button:focus, +button:focus-visible { + outline: 4px auto -webkit-focus-ring-color; +} + +@media (prefers-color-scheme: light) { + :root { + color: #213547; + background-color: #ffffff; + } + a:hover { + color: #747bff; + } + button { + background-color: #f9f9f9; + } +} diff --git a/frontend/src/main.jsx b/frontend/src/main.jsx new file mode 100644 index 0000000..b9a1a6d --- /dev/null +++ b/frontend/src/main.jsx @@ -0,0 +1,10 @@ +import { StrictMode } from 'react' +import { createRoot } from 'react-dom/client' +import './index.css' +import App from './App.jsx' + +createRoot(document.getElementById('root')).render( + + + , +) diff --git a/frontend/vite.config.js b/frontend/vite.config.js new file mode 100644 index 0000000..8b0f57b --- /dev/null +++ b/frontend/vite.config.js @@ -0,0 +1,7 @@ +import { defineConfig } from 'vite' +import react from '@vitejs/plugin-react' + +// https://vite.dev/config/ +export default defineConfig({ + plugins: [react()], +}) From c60b1461be28c00b275d113d52657ef7d3ba909d Mon Sep 17 00:00:00 2001 From: Shman_krishna Date: Tue, 8 Apr 2025 00:38:18 +0530 Subject: [PATCH 02/14] register page --- frontend/package-lock.json | 5200 +++++++++++++---- frontend/package.json | 33 +- frontend/postcss.config.js | 6 + frontend/src/App.css | 41 - frontend/src/App.jsx | 31 +- frontend/src/components/auth/Register.jsx | 470 ++ .../src/components/layouts/auth-layout.jsx | 81 + frontend/src/components/ui/alert.jsx | 62 + frontend/src/components/ui/button.jsx | 55 + frontend/src/components/ui/card.jsx | 65 + frontend/src/components/ui/checkbox.jsx | 32 + frontend/src/components/ui/input.jsx | 22 + frontend/src/components/ui/label.jsx | 27 + frontend/src/hooks/use-mobile.jsx | 23 + frontend/src/hooks/use-toast.js | 154 + frontend/src/index.css | 140 +- frontend/src/lib/util.js | 6 + frontend/tailwind.config.js | 91 + frontend/vite.config.js | 5 +- 19 files changed, 5314 insertions(+), 1230 deletions(-) create mode 100644 frontend/postcss.config.js create mode 100644 frontend/src/components/auth/Register.jsx create mode 100644 frontend/src/components/layouts/auth-layout.jsx create mode 100644 frontend/src/components/ui/alert.jsx create mode 100644 frontend/src/components/ui/button.jsx create mode 100644 frontend/src/components/ui/card.jsx create mode 100644 frontend/src/components/ui/checkbox.jsx create mode 100644 frontend/src/components/ui/input.jsx create mode 100644 frontend/src/components/ui/label.jsx create mode 100644 frontend/src/hooks/use-mobile.jsx create mode 100644 frontend/src/hooks/use-toast.js create mode 100644 frontend/src/lib/util.js create mode 100644 frontend/tailwind.config.js diff --git a/frontend/package-lock.json b/frontend/package-lock.json index 0b81453..bbf59e8 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -8,19 +8,42 @@ "name": "frontend", "version": "0.0.0", "dependencies": { - "react": "^19.0.0", - "react-dom": "^19.0.0" + "@radix-ui/react-checkbox": "^1.1.4", + "@radix-ui/react-label": "^2.1.2", + "@radix-ui/react-slot": "^1.1.2", + "@tailwindcss/forms": "^0.5.7", + "autoprefixer": "^10.4.16", + "class-variance-authority": "^0.7.1", + "framer-motion": "^10.16.4", + "lucide-react": "^0.284.0", + "postcss": "^8.4.31", + "react": "^18.2.0", + "react-dom": "^18.2.0", + "tailwind-merge": "^3.2.0", + "tailwindcss": "^3.3.3", + "tailwindcss-animate": "^1.0.7" }, "devDependencies": { - "@eslint/js": "^9.21.0", - "@types/react": "^19.0.10", - "@types/react-dom": "^19.0.4", - "@vitejs/plugin-react": "^4.3.4", - "eslint": "^9.21.0", - "eslint-plugin-react-hooks": "^5.1.0", - "eslint-plugin-react-refresh": "^0.4.19", - "globals": "^15.15.0", - "vite": "^6.2.0" + "@types/react": "^18.2.15", + "@types/react-dom": "^18.2.7", + "@vitejs/plugin-react": "^4.0.3", + "eslint": "^8.45.0", + "eslint-plugin-react": "^7.32.2", + "eslint-plugin-react-hooks": "^4.6.0", + "eslint-plugin-react-refresh": "^0.4.3", + "vite": "^4.4.5" + } + }, + "node_modules/@alloc/quick-lru": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/@alloc/quick-lru/-/quick-lru-5.2.0.tgz", + "integrity": "sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw==", + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/@ampproject/remapping": { @@ -319,27 +342,27 @@ "node": ">=6.9.0" } }, - "node_modules/@esbuild/aix-ppc64": { - "version": "0.25.2", - "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.2.tgz", - "integrity": "sha512-wCIboOL2yXZym2cgm6mlA742s9QeJ8DjGVaL39dLN4rRwrOgOyYSnOaFPhKZGLb2ngj4EyfAFjsNJwPXZvseag==", - "cpu": [ - "ppc64" - ], - "dev": true, + "node_modules/@emotion/is-prop-valid": { + "version": "0.8.8", + "resolved": "https://registry.npmjs.org/@emotion/is-prop-valid/-/is-prop-valid-0.8.8.tgz", + "integrity": "sha512-u5WtneEAr5IDG2Wv65yhunPSMLIpuKsbuOktRojfrEiEvRyC85LgPMZI63cr7NUqT8ZIGdSVg8ZKGxIug4lXcA==", "license": "MIT", "optional": true, - "os": [ - "aix" - ], - "engines": { - "node": ">=18" + "dependencies": { + "@emotion/memoize": "0.7.4" } }, + "node_modules/@emotion/memoize": { + "version": "0.7.4", + "resolved": "https://registry.npmjs.org/@emotion/memoize/-/memoize-0.7.4.tgz", + "integrity": "sha512-Ja/Vfqe3HpuzRsG1oBtWTHk2PGZ7GR+2Vz5iYGelAw8dx32K0y7PjVuxK6z1nMpZOqAFsRUPCkK1YjJ56qJlgw==", + "license": "MIT", + "optional": true + }, "node_modules/@esbuild/android-arm": { - "version": "0.25.2", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.2.tgz", - "integrity": "sha512-NQhH7jFstVY5x8CKbcfa166GoV0EFkaPkCKBQkdPJFvo5u+nGXLEH/ooniLb3QI8Fk58YAx7nsPLozUWfCBOJA==", + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.18.20.tgz", + "integrity": "sha512-fyi7TDI/ijKKNZTUJAQqiG5T7YjJXgnzkURqmGj13C6dCqckZBLdl4h7bkhHt/t0WP+zO9/zwroDvANaOqO5Sw==", "cpu": [ "arm" ], @@ -350,13 +373,13 @@ "android" ], "engines": { - "node": ">=18" + "node": ">=12" } }, "node_modules/@esbuild/android-arm64": { - "version": "0.25.2", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.2.tgz", - "integrity": "sha512-5ZAX5xOmTligeBaeNEPnPaeEuah53Id2tX4c2CVP3JaROTH+j4fnfHCkr1PjXMd78hMst+TlkfKcW/DlTq0i4w==", + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.18.20.tgz", + "integrity": "sha512-Nz4rJcchGDtENV0eMKUNa6L12zz2zBDXuhj/Vjh18zGqB44Bi7MBMSXjgunJgjRhCmKOjnPuZp4Mb6OKqtMHLQ==", "cpu": [ "arm64" ], @@ -367,13 +390,13 @@ "android" ], "engines": { - "node": ">=18" + "node": ">=12" } }, "node_modules/@esbuild/android-x64": { - "version": "0.25.2", - "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.2.tgz", - "integrity": "sha512-Ffcx+nnma8Sge4jzddPHCZVRvIfQ0kMsUsCMcJRHkGJ1cDmhe4SsrYIjLUKn1xpHZybmOqCWwB0zQvsjdEHtkg==", + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.18.20.tgz", + "integrity": "sha512-8GDdlePJA8D6zlZYJV/jnrRAi6rOiNaCC/JclcXpB+KIuvfBN4owLtgzY2bsxnx666XjJx2kDPUmnTtR8qKQUg==", "cpu": [ "x64" ], @@ -384,13 +407,13 @@ "android" ], "engines": { - "node": ">=18" + "node": ">=12" } }, "node_modules/@esbuild/darwin-arm64": { - "version": "0.25.2", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.2.tgz", - "integrity": "sha512-MpM6LUVTXAzOvN4KbjzU/q5smzryuoNjlriAIx+06RpecwCkL9JpenNzpKd2YMzLJFOdPqBpuub6eVRP5IgiSA==", + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.18.20.tgz", + "integrity": "sha512-bxRHW5kHU38zS2lPTPOyuyTm+S+eobPUnTNkdJEfAddYgEcll4xkT8DB9d2008DtTbl7uJag2HuE5NZAZgnNEA==", "cpu": [ "arm64" ], @@ -401,13 +424,13 @@ "darwin" ], "engines": { - "node": ">=18" + "node": ">=12" } }, "node_modules/@esbuild/darwin-x64": { - "version": "0.25.2", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.2.tgz", - "integrity": "sha512-5eRPrTX7wFyuWe8FqEFPG2cU0+butQQVNcT4sVipqjLYQjjh8a8+vUTfgBKM88ObB85ahsnTwF7PSIt6PG+QkA==", + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.18.20.tgz", + "integrity": "sha512-pc5gxlMDxzm513qPGbCbDukOdsGtKhfxD1zJKXjCCcU7ju50O7MeAZ8c4krSJcOIJGFR+qx21yMMVYwiQvyTyQ==", "cpu": [ "x64" ], @@ -418,13 +441,13 @@ "darwin" ], "engines": { - "node": ">=18" + "node": ">=12" } }, "node_modules/@esbuild/freebsd-arm64": { - "version": "0.25.2", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.2.tgz", - "integrity": "sha512-mLwm4vXKiQ2UTSX4+ImyiPdiHjiZhIaE9QvC7sw0tZ6HoNMjYAqQpGyui5VRIi5sGd+uWq940gdCbY3VLvsO1w==", + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.18.20.tgz", + "integrity": "sha512-yqDQHy4QHevpMAaxhhIwYPMv1NECwOvIpGCZkECn8w2WFHXjEwrBn3CeNIYsibZ/iZEUemj++M26W3cNR5h+Tw==", "cpu": [ "arm64" ], @@ -435,13 +458,13 @@ "freebsd" ], "engines": { - "node": ">=18" + "node": ">=12" } }, "node_modules/@esbuild/freebsd-x64": { - "version": "0.25.2", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.2.tgz", - "integrity": "sha512-6qyyn6TjayJSwGpm8J9QYYGQcRgc90nmfdUb0O7pp1s4lTY+9D0H9O02v5JqGApUyiHOtkz6+1hZNvNtEhbwRQ==", + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.18.20.tgz", + "integrity": "sha512-tgWRPPuQsd3RmBZwarGVHZQvtzfEBOreNuxEMKFcd5DaDn2PbBxfwLcj4+aenoh7ctXcbXmOQIn8HI6mCSw5MQ==", "cpu": [ "x64" ], @@ -452,13 +475,13 @@ "freebsd" ], "engines": { - "node": ">=18" + "node": ">=12" } }, "node_modules/@esbuild/linux-arm": { - "version": "0.25.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.2.tgz", - "integrity": "sha512-UHBRgJcmjJv5oeQF8EpTRZs/1knq6loLxTsjc3nxO9eXAPDLcWW55flrMVc97qFPbmZP31ta1AZVUKQzKTzb0g==", + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.18.20.tgz", + "integrity": "sha512-/5bHkMWnq1EgKr1V+Ybz3s1hWXok7mDFUMQ4cG10AfW3wL02PSZi5kFpYKrptDsgb2WAJIvRcDm+qIvXf/apvg==", "cpu": [ "arm" ], @@ -469,13 +492,13 @@ "linux" ], "engines": { - "node": ">=18" + "node": ">=12" } }, "node_modules/@esbuild/linux-arm64": { - "version": "0.25.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.2.tgz", - "integrity": "sha512-gq/sjLsOyMT19I8obBISvhoYiZIAaGF8JpeXu1u8yPv8BE5HlWYobmlsfijFIZ9hIVGYkbdFhEqC0NvM4kNO0g==", + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.18.20.tgz", + "integrity": "sha512-2YbscF+UL7SQAVIpnWvYwM+3LskyDmPhe31pE7/aoTMFKKzIc9lLbyGUpmmb8a8AixOL61sQ/mFh3jEjHYFvdA==", "cpu": [ "arm64" ], @@ -486,13 +509,13 @@ "linux" ], "engines": { - "node": ">=18" + "node": ">=12" } }, "node_modules/@esbuild/linux-ia32": { - "version": "0.25.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.2.tgz", - "integrity": "sha512-bBYCv9obgW2cBP+2ZWfjYTU+f5cxRoGGQ5SeDbYdFCAZpYWrfjjfYwvUpP8MlKbP0nwZ5gyOU/0aUzZ5HWPuvQ==", + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.18.20.tgz", + "integrity": "sha512-P4etWwq6IsReT0E1KHU40bOnzMHoH73aXp96Fs8TIT6z9Hu8G6+0SHSw9i2isWrD2nbx2qo5yUqACgdfVGx7TA==", "cpu": [ "ia32" ], @@ -503,13 +526,13 @@ "linux" ], "engines": { - "node": ">=18" + "node": ">=12" } }, "node_modules/@esbuild/linux-loong64": { - "version": "0.25.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.2.tgz", - "integrity": "sha512-SHNGiKtvnU2dBlM5D8CXRFdd+6etgZ9dXfaPCeJtz+37PIUlixvlIhI23L5khKXs3DIzAn9V8v+qb1TRKrgT5w==", + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.18.20.tgz", + "integrity": "sha512-nXW8nqBTrOpDLPgPY9uV+/1DjxoQ7DoB2N8eocyq8I9XuqJ7BiAMDMf9n1xZM9TgW0J8zrquIb/A7s3BJv7rjg==", "cpu": [ "loong64" ], @@ -520,13 +543,13 @@ "linux" ], "engines": { - "node": ">=18" + "node": ">=12" } }, "node_modules/@esbuild/linux-mips64el": { - "version": "0.25.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.2.tgz", - "integrity": "sha512-hDDRlzE6rPeoj+5fsADqdUZl1OzqDYow4TB4Y/3PlKBD0ph1e6uPHzIQcv2Z65u2K0kpeByIyAjCmjn1hJgG0Q==", + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.18.20.tgz", + "integrity": "sha512-d5NeaXZcHp8PzYy5VnXV3VSd2D328Zb+9dEq5HE6bw6+N86JVPExrA6O68OPwobntbNJ0pzCpUFZTo3w0GyetQ==", "cpu": [ "mips64el" ], @@ -537,13 +560,13 @@ "linux" ], "engines": { - "node": ">=18" + "node": ">=12" } }, "node_modules/@esbuild/linux-ppc64": { - "version": "0.25.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.2.tgz", - "integrity": "sha512-tsHu2RRSWzipmUi9UBDEzc0nLc4HtpZEI5Ba+Omms5456x5WaNuiG3u7xh5AO6sipnJ9r4cRWQB2tUjPyIkc6g==", + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.18.20.tgz", + "integrity": "sha512-WHPyeScRNcmANnLQkq6AfyXRFr5D6N2sKgkFo2FqguP44Nw2eyDlbTdZwd9GYk98DZG9QItIiTlFLHJHjxP3FA==", "cpu": [ "ppc64" ], @@ -554,13 +577,13 @@ "linux" ], "engines": { - "node": ">=18" + "node": ">=12" } }, "node_modules/@esbuild/linux-riscv64": { - "version": "0.25.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.2.tgz", - "integrity": "sha512-k4LtpgV7NJQOml/10uPU0s4SAXGnowi5qBSjaLWMojNCUICNu7TshqHLAEbkBdAszL5TabfvQ48kK84hyFzjnw==", + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.18.20.tgz", + "integrity": "sha512-WSxo6h5ecI5XH34KC7w5veNnKkju3zBRLEQNY7mv5mtBmrP/MjNBCAlsM2u5hDBlS3NGcTQpoBvRzqBcRtpq1A==", "cpu": [ "riscv64" ], @@ -571,13 +594,13 @@ "linux" ], "engines": { - "node": ">=18" + "node": ">=12" } }, "node_modules/@esbuild/linux-s390x": { - "version": "0.25.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.2.tgz", - "integrity": "sha512-GRa4IshOdvKY7M/rDpRR3gkiTNp34M0eLTaC1a08gNrh4u488aPhuZOCpkF6+2wl3zAN7L7XIpOFBhnaE3/Q8Q==", + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.18.20.tgz", + "integrity": "sha512-+8231GMs3mAEth6Ja1iK0a1sQ3ohfcpzpRLH8uuc5/KVDFneH6jtAJLFGafpzpMRO6DzJ6AvXKze9LfFMrIHVQ==", "cpu": [ "s390x" ], @@ -588,13 +611,13 @@ "linux" ], "engines": { - "node": ">=18" + "node": ">=12" } }, "node_modules/@esbuild/linux-x64": { - "version": "0.25.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.2.tgz", - "integrity": "sha512-QInHERlqpTTZ4FRB0fROQWXcYRD64lAoiegezDunLpalZMjcUcld3YzZmVJ2H/Cp0wJRZ8Xtjtj0cEHhYc/uUg==", + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.18.20.tgz", + "integrity": "sha512-UYqiqemphJcNsFEskc73jQ7B9jgwjWrSayxawS6UVFZGWrAAtkzjxSqnoclCXxWtfwLdzU+vTpcNYhpn43uP1w==", "cpu": [ "x64" ], @@ -605,30 +628,13 @@ "linux" ], "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/netbsd-arm64": { - "version": "0.25.2", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.2.tgz", - "integrity": "sha512-talAIBoY5M8vHc6EeI2WW9d/CkiO9MQJ0IOWX8hrLhxGbro/vBXJvaQXefW2cP0z0nQVTdQ/eNyGFV1GSKrxfw==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "netbsd" - ], - "engines": { - "node": ">=18" + "node": ">=12" } }, "node_modules/@esbuild/netbsd-x64": { - "version": "0.25.2", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.2.tgz", - "integrity": "sha512-voZT9Z+tpOxrvfKFyfDYPc4DO4rk06qamv1a/fkuzHpiVBMOhpjK+vBmWM8J1eiB3OLSMFYNaOaBNLXGChf5tg==", + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.18.20.tgz", + "integrity": "sha512-iO1c++VP6xUBUmltHZoMtCUdPlnPGdBom6IrO4gyKPFFVBKioIImVooR5I83nTew5UOYrk3gIJhbZh8X44y06A==", "cpu": [ "x64" ], @@ -639,30 +645,13 @@ "netbsd" ], "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/openbsd-arm64": { - "version": "0.25.2", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.2.tgz", - "integrity": "sha512-dcXYOC6NXOqcykeDlwId9kB6OkPUxOEqU+rkrYVqJbK2hagWOMrsTGsMr8+rW02M+d5Op5NNlgMmjzecaRf7Tg==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "openbsd" - ], - "engines": { - "node": ">=18" + "node": ">=12" } }, "node_modules/@esbuild/openbsd-x64": { - "version": "0.25.2", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.2.tgz", - "integrity": "sha512-t/TkWwahkH0Tsgoq1Ju7QfgGhArkGLkF1uYz8nQS/PPFlXbP5YgRpqQR3ARRiC2iXoLTWFxc6DJMSK10dVXluw==", + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.18.20.tgz", + "integrity": "sha512-e5e4YSsuQfX4cxcygw/UCPIEP6wbIL+se3sxPdCiMbFLBWu0eiZOJ7WoD+ptCLrmjZBK1Wk7I6D/I3NglUGOxg==", "cpu": [ "x64" ], @@ -673,13 +662,13 @@ "openbsd" ], "engines": { - "node": ">=18" + "node": ">=12" } }, "node_modules/@esbuild/sunos-x64": { - "version": "0.25.2", - "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.2.tgz", - "integrity": "sha512-cfZH1co2+imVdWCjd+D1gf9NjkchVhhdpgb1q5y6Hcv9TP6Zi9ZG/beI3ig8TvwT9lH9dlxLq5MQBBgwuj4xvA==", + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.18.20.tgz", + "integrity": "sha512-kDbFRFp0YpTQVVrqUd5FTYmWo45zGaXe0X8E1G/LKFC0v8x0vWrhOWSLITcCn63lmZIxfOMXtCfti/RxN/0wnQ==", "cpu": [ "x64" ], @@ -690,13 +679,13 @@ "sunos" ], "engines": { - "node": ">=18" + "node": ">=12" } }, "node_modules/@esbuild/win32-arm64": { - "version": "0.25.2", - "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.2.tgz", - "integrity": "sha512-7Loyjh+D/Nx/sOTzV8vfbB3GJuHdOQyrOryFdZvPHLf42Tk9ivBU5Aedi7iyX+x6rbn2Mh68T4qq1SDqJBQO5Q==", + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.18.20.tgz", + "integrity": "sha512-ddYFR6ItYgoaq4v4JmQQaAI5s7npztfV4Ag6NrhiaW0RrnOXqBkgwZLofVTlq1daVTQNhtI5oieTvkRPfZrePg==", "cpu": [ "arm64" ], @@ -707,13 +696,13 @@ "win32" ], "engines": { - "node": ">=18" + "node": ">=12" } }, "node_modules/@esbuild/win32-ia32": { - "version": "0.25.2", - "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.2.tgz", - "integrity": "sha512-WRJgsz9un0nqZJ4MfhabxaD9Ft8KioqU3JMinOTvobbX6MOSUigSBlogP8QB3uxpJDsFS6yN+3FDBdqE5lg9kg==", + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.18.20.tgz", + "integrity": "sha512-Wv7QBi3ID/rROT08SABTS7eV4hX26sVduqDOTe1MvGMjNd3EjOz4b7zeexIR62GTIEKrfJXKL9LFxTYgkyeu7g==", "cpu": [ "ia32" ], @@ -724,13 +713,13 @@ "win32" ], "engines": { - "node": ">=18" + "node": ">=12" } }, "node_modules/@esbuild/win32-x64": { - "version": "0.25.2", - "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.2.tgz", - "integrity": "sha512-kM3HKb16VIXZyIeVrM1ygYmZBKybX8N4p754bw390wGO3Tf2j4L2/WYL+4suWujpgf6GBYs3jv7TyUivdd05JA==", + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.18.20.tgz", + "integrity": "sha512-kTdfRcSiDfQca/y9QIkng02avJ+NCaQvrMejlsB3RRv5sE9rRoeBPISaZpKxHELzRxZyLvNts1P27W3wV+8geQ==", "cpu": [ "x64" ], @@ -741,7 +730,7 @@ "win32" ], "engines": { - "node": ">=18" + "node": ">=12" } }, "node_modules/@eslint-community/eslint-utils": { @@ -763,19 +752,6 @@ "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" } }, - "node_modules/@eslint-community/eslint-utils/node_modules/eslint-visitor-keys": { - "version": "3.4.3", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", - "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", - "dev": true, - "license": "Apache-2.0", - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, "node_modules/@eslint-community/regexpp": { "version": "4.12.1", "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.12.1.tgz", @@ -786,55 +762,17 @@ "node": "^12.0.0 || ^14.0.0 || >=16.0.0" } }, - "node_modules/@eslint/config-array": { - "version": "0.20.0", - "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.20.0.tgz", - "integrity": "sha512-fxlS1kkIjx8+vy2SjuCB94q3htSNrufYTXubwiBFeaQHbH6Ipi43gFJq2zCMt6PHhImH3Xmr0NksKDvchWlpQQ==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@eslint/object-schema": "^2.1.6", - "debug": "^4.3.1", - "minimatch": "^3.1.2" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - } - }, - "node_modules/@eslint/config-helpers": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.2.1.tgz", - "integrity": "sha512-RI17tsD2frtDu/3dmI7QRrD4bedNKPM08ziRYaC5AhkGrzIAJelm9kJU1TznK+apx6V+cqRz8tfpEeG3oIyjxw==", - "dev": true, - "license": "Apache-2.0", - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - } - }, - "node_modules/@eslint/core": { - "version": "0.12.0", - "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.12.0.tgz", - "integrity": "sha512-cmrR6pytBuSMTaBweKoGMwu3EiHiEC+DoyupPmlZ0HxBJBtIxwe+j/E4XPIKNx+Q74c8lXKPwYawBf5glsTkHg==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@types/json-schema": "^7.0.15" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - } - }, "node_modules/@eslint/eslintrc": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-3.3.1.tgz", - "integrity": "sha512-gtF186CXhIl1p4pJNGZw8Yc6RlshoePRvE0X91oPGb3vZ8pM3qOS9W9NGPat9LziaBV7XrJWGylNQXkGcnM3IQ==", + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.1.4.tgz", + "integrity": "sha512-269Z39MS6wVJtsoUl10L60WdkhJVdPG24Q4eZTH3nnF6lpvSShEK3wQjDX9JRWAUPvPh7COouPpU9IrqaZFvtQ==", "dev": true, "license": "MIT", "dependencies": { "ajv": "^6.12.4", "debug": "^4.3.2", - "espree": "^10.0.1", - "globals": "^14.0.0", + "espree": "^9.6.0", + "globals": "^13.19.0", "ignore": "^5.2.0", "import-fresh": "^3.2.1", "js-yaml": "^4.1.0", @@ -842,143 +780,108 @@ "strip-json-comments": "^3.1.1" }, "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" }, "funding": { "url": "https://opencollective.com/eslint" } }, - "node_modules/@eslint/eslintrc/node_modules/globals": { - "version": "14.0.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-14.0.0.tgz", - "integrity": "sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/@eslint/js": { - "version": "9.24.0", - "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.24.0.tgz", - "integrity": "sha512-uIY/y3z0uvOGX8cp1C2fiC4+ZmBhp6yZWkojtHL1YEMnRt1Y63HB9TM17proGEmeG7HeUY+UP36F0aknKYTpYA==", + "version": "8.57.1", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.57.1.tgz", + "integrity": "sha512-d9zaMRSTIKDLhctzH12MtXvJKSSUhaHcjV+2Z+GK+EEY7XKpP5yR4x+N3TAcHTcu963nIr+TMcCb4DBCYX1z6Q==", "dev": true, "license": "MIT", "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - } - }, - "node_modules/@eslint/object-schema": { - "version": "2.1.6", - "resolved": "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-2.1.6.tgz", - "integrity": "sha512-RBMg5FRL0I0gs51M/guSAj5/e14VQ4tpZnQNWwuDT66P14I43ItmPfIZRhO9fUVIPOAQXU47atlywZ/czoqFPA==", - "dev": true, - "license": "Apache-2.0", - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - } - }, - "node_modules/@eslint/plugin-kit": { - "version": "0.2.8", - "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.2.8.tgz", - "integrity": "sha512-ZAoA40rNMPwSm+AeHpCq8STiNAwzWLJuP8Xv4CHIc9wv/PSuExjMrmjfYNj682vW0OOiZ1HKxzvjQr9XZIisQA==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@eslint/core": "^0.13.0", - "levn": "^0.4.1" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" } }, - "node_modules/@eslint/plugin-kit/node_modules/@eslint/core": { + "node_modules/@humanwhocodes/config-array": { "version": "0.13.0", - "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.13.0.tgz", - "integrity": "sha512-yfkgDw1KR66rkT5A8ci4irzDysN7FRpq3ttJolR88OqQikAWqwA8j5VZyas+vjyBNFIJ7MfybJ9plMILI2UrCw==", + "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.13.0.tgz", + "integrity": "sha512-DZLEEqFWQFiyK6h5YIeynKx7JlvCYWL0cImfSRXZ9l4Sg2efkFGTuFf6vzXjK1cq6IYkU+Eg/JizXw+TD2vRNw==", + "deprecated": "Use @eslint/config-array instead", "dev": true, "license": "Apache-2.0", "dependencies": { - "@types/json-schema": "^7.0.15" + "@humanwhocodes/object-schema": "^2.0.3", + "debug": "^4.3.1", + "minimatch": "^3.0.5" }, "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + "node": ">=10.10.0" } }, - "node_modules/@humanfs/core": { - "version": "0.19.1", - "resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.1.tgz", - "integrity": "sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA==", + "node_modules/@humanwhocodes/module-importer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", + "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", "dev": true, "license": "Apache-2.0", "engines": { - "node": ">=18.18.0" + "node": ">=12.22" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" } }, - "node_modules/@humanfs/node": { - "version": "0.16.6", - "resolved": "https://registry.npmjs.org/@humanfs/node/-/node-0.16.6.tgz", - "integrity": "sha512-YuI2ZHQL78Q5HbhDiBA1X4LmYdXCKCMQIfw0pw7piHJwyREFebJUvrQN4cMssyES6x+vfUbx1CIpaQUKYdQZOw==", + "node_modules/@humanwhocodes/object-schema": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-2.0.3.tgz", + "integrity": "sha512-93zYdMES/c1D69yZiKDBj0V24vqNzB/koF26KPaagAfd3P/4gUlh3Dys5ogAK+Exi9QyzlD8x/08Zt7wIKcDcA==", + "deprecated": "Use @eslint/object-schema instead", "dev": true, - "license": "Apache-2.0", + "license": "BSD-3-Clause" + }, + "node_modules/@isaacs/cliui": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", + "integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==", + "license": "ISC", "dependencies": { - "@humanfs/core": "^0.19.1", - "@humanwhocodes/retry": "^0.3.0" + "string-width": "^5.1.2", + "string-width-cjs": "npm:string-width@^4.2.0", + "strip-ansi": "^7.0.1", + "strip-ansi-cjs": "npm:strip-ansi@^6.0.1", + "wrap-ansi": "^8.1.0", + "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0" }, "engines": { - "node": ">=18.18.0" + "node": ">=12" } }, - "node_modules/@humanfs/node/node_modules/@humanwhocodes/retry": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.3.1.tgz", - "integrity": "sha512-JBxkERygn7Bv/GbN5Rv8Ul6LVknS+5Bp6RgDC/O8gEBU/yeH5Ui5C/OlWrTb6qct7LjjfT6Re2NxB0ln0yYybA==", - "dev": true, - "license": "Apache-2.0", + "node_modules/@isaacs/cliui/node_modules/ansi-regex": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.1.0.tgz", + "integrity": "sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA==", + "license": "MIT", "engines": { - "node": ">=18.18" + "node": ">=12" }, "funding": { - "type": "github", - "url": "https://github.com/sponsors/nzakas" + "url": "https://github.com/chalk/ansi-regex?sponsor=1" } }, - "node_modules/@humanwhocodes/module-importer": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", - "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", - "dev": true, - "license": "Apache-2.0", - "engines": { - "node": ">=12.22" + "node_modules/@isaacs/cliui/node_modules/strip-ansi": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", + "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", + "license": "MIT", + "dependencies": { + "ansi-regex": "^6.0.1" }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/nzakas" - } - }, - "node_modules/@humanwhocodes/retry": { - "version": "0.4.2", - "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.4.2.tgz", - "integrity": "sha512-xeO57FpIu4p1Ri3Jq/EXq4ClRm86dVF2z/+kvFnyqVYRavTZmaFaUBbWCOuuTh0o/g7DSsk6kc2vrS4Vl5oPOQ==", - "dev": true, - "license": "Apache-2.0", "engines": { - "node": ">=18.18" + "node": ">=12" }, "funding": { - "type": "github", - "url": "https://github.com/sponsors/nzakas" + "url": "https://github.com/chalk/strip-ansi?sponsor=1" } }, "node_modules/@jridgewell/gen-mapping": { "version": "0.3.8", "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.8.tgz", "integrity": "sha512-imAbBGkb+ebQyxKgzv5Hu2nmROxoDOXHh80evxdoXNOrvAnVx7zimzc1Oo5h9RlfV4vPXaE2iM5pOFbvOCClWA==", - "dev": true, "license": "MIT", "dependencies": { "@jridgewell/set-array": "^1.2.1", @@ -993,7 +896,6 @@ "version": "3.1.2", "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", - "dev": true, "license": "MIT", "engines": { "node": ">=6.0.0" @@ -1003,7 +905,6 @@ "version": "1.2.1", "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.2.1.tgz", "integrity": "sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==", - "dev": true, "license": "MIT", "engines": { "node": ">=6.0.0" @@ -1013,299 +914,309 @@ "version": "1.5.0", "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.0.tgz", "integrity": "sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==", - "dev": true, "license": "MIT" }, "node_modules/@jridgewell/trace-mapping": { "version": "0.3.25", "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz", "integrity": "sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==", - "dev": true, "license": "MIT", "dependencies": { "@jridgewell/resolve-uri": "^3.1.0", "@jridgewell/sourcemap-codec": "^1.4.14" } }, - "node_modules/@rollup/rollup-android-arm-eabi": { - "version": "4.39.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.39.0.tgz", - "integrity": "sha512-lGVys55Qb00Wvh8DMAocp5kIcaNzEFTmGhfFd88LfaogYTRKrdxgtlO5H6S49v2Nd8R2C6wLOal0qv6/kCkOwA==", - "cpu": [ - "arm" - ], - "dev": true, + "node_modules/@nodelib/fs.scandir": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", + "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", "license": "MIT", - "optional": true, - "os": [ - "android" - ] + "dependencies": { + "@nodelib/fs.stat": "2.0.5", + "run-parallel": "^1.1.9" + }, + "engines": { + "node": ">= 8" + } }, - "node_modules/@rollup/rollup-android-arm64": { - "version": "4.39.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.39.0.tgz", - "integrity": "sha512-It9+M1zE31KWfqh/0cJLrrsCPiF72PoJjIChLX+rEcujVRCb4NLQ5QzFkzIZW8Kn8FTbvGQBY5TkKBau3S8cCQ==", - "cpu": [ - "arm64" - ], - "dev": true, + "node_modules/@nodelib/fs.stat": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", + "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", "license": "MIT", - "optional": true, - "os": [ - "android" - ] + "engines": { + "node": ">= 8" + } }, - "node_modules/@rollup/rollup-darwin-arm64": { - "version": "4.39.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.39.0.tgz", - "integrity": "sha512-lXQnhpFDOKDXiGxsU9/l8UEGGM65comrQuZ+lDcGUx+9YQ9dKpF3rSEGepyeR5AHZ0b5RgiligsBhWZfSSQh8Q==", - "cpu": [ - "arm64" - ], - "dev": true, + "node_modules/@nodelib/fs.walk": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", + "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", "license": "MIT", - "optional": true, - "os": [ - "darwin" - ] + "dependencies": { + "@nodelib/fs.scandir": "2.1.5", + "fastq": "^1.6.0" + }, + "engines": { + "node": ">= 8" + } }, - "node_modules/@rollup/rollup-darwin-x64": { - "version": "4.39.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.39.0.tgz", - "integrity": "sha512-mKXpNZLvtEbgu6WCkNij7CGycdw9cJi2k9v0noMb++Vab12GZjFgUXD69ilAbBh034Zwn95c2PNSz9xM7KYEAQ==", - "cpu": [ - "x64" - ], - "dev": true, + "node_modules/@pkgjs/parseargs": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", + "integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==", "license": "MIT", "optional": true, - "os": [ - "darwin" - ] + "engines": { + "node": ">=14" + } }, - "node_modules/@rollup/rollup-freebsd-arm64": { - "version": "4.39.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.39.0.tgz", - "integrity": "sha512-jivRRlh2Lod/KvDZx2zUR+I4iBfHcu2V/BA2vasUtdtTN2Uk3jfcZczLa81ESHZHPHy4ih3T/W5rPFZ/hX7RtQ==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "freebsd" - ] + "node_modules/@radix-ui/primitive": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@radix-ui/primitive/-/primitive-1.1.1.tgz", + "integrity": "sha512-SJ31y+Q/zAyShtXJc8x83i9TYdbAfHZ++tUZnvjJJqFjzsdUnKsxPL6IEtBlxKkU7yzer//GQtZSV4GbldL3YA==", + "license": "MIT" }, - "node_modules/@rollup/rollup-freebsd-x64": { - "version": "4.39.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.39.0.tgz", - "integrity": "sha512-8RXIWvYIRK9nO+bhVz8DwLBepcptw633gv/QT4015CpJ0Ht8punmoHU/DuEd3iw9Hr8UwUV+t+VNNuZIWYeY7Q==", - "cpu": [ - "x64" - ], - "dev": true, + "node_modules/@radix-ui/react-checkbox": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/@radix-ui/react-checkbox/-/react-checkbox-1.1.4.tgz", + "integrity": "sha512-wP0CPAHq+P5I4INKe3hJrIa1WoNqqrejzW+zoU0rOvo1b9gDEJJFl2rYfO1PYJUQCc2H1WZxIJmyv9BS8i5fLw==", "license": "MIT", - "optional": true, - "os": [ - "freebsd" - ] + "dependencies": { + "@radix-ui/primitive": "1.1.1", + "@radix-ui/react-compose-refs": "1.1.1", + "@radix-ui/react-context": "1.1.1", + "@radix-ui/react-presence": "1.1.2", + "@radix-ui/react-primitive": "2.0.2", + "@radix-ui/react-use-controllable-state": "1.1.0", + "@radix-ui/react-use-previous": "1.1.0", + "@radix-ui/react-use-size": "1.1.0" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } }, - "node_modules/@rollup/rollup-linux-arm-gnueabihf": { - "version": "4.39.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.39.0.tgz", - "integrity": "sha512-mz5POx5Zu58f2xAG5RaRRhp3IZDK7zXGk5sdEDj4o96HeaXhlUwmLFzNlc4hCQi5sGdR12VDgEUqVSHer0lI9g==", - "cpu": [ - "arm" - ], - "dev": true, + "node_modules/@radix-ui/react-compose-refs": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-compose-refs/-/react-compose-refs-1.1.1.tgz", + "integrity": "sha512-Y9VzoRDSJtgFMUCoiZBDVo084VQ5hfpXxVE+NgkdNsjiDBByiImMZKKhxMwCbdHvhlENG6a833CbFkOQvTricw==", "license": "MIT", - "optional": true, - "os": [ - "linux" - ] + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } }, - "node_modules/@rollup/rollup-linux-arm-musleabihf": { - "version": "4.39.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.39.0.tgz", - "integrity": "sha512-+YDwhM6gUAyakl0CD+bMFpdmwIoRDzZYaTWV3SDRBGkMU/VpIBYXXEvkEcTagw/7VVkL2vA29zU4UVy1mP0/Yw==", - "cpu": [ - "arm" - ], - "dev": true, + "node_modules/@radix-ui/react-context": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-context/-/react-context-1.1.1.tgz", + "integrity": "sha512-UASk9zi+crv9WteK/NU4PLvOoL3OuE6BWVKNF6hPRBtYBDXQ2u5iu3O59zUlJiTVvkyuycnqrztsHVJwcK9K+Q==", "license": "MIT", - "optional": true, - "os": [ - "linux" - ] + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } }, - "node_modules/@rollup/rollup-linux-arm64-gnu": { - "version": "4.39.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.39.0.tgz", - "integrity": "sha512-EKf7iF7aK36eEChvlgxGnk7pdJfzfQbNvGV/+l98iiMwU23MwvmV0Ty3pJ0p5WQfm3JRHOytSIqD9LB7Bq7xdQ==", - "cpu": [ - "arm64" - ], - "dev": true, + "node_modules/@radix-ui/react-label": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/@radix-ui/react-label/-/react-label-2.1.2.tgz", + "integrity": "sha512-zo1uGMTaNlHehDyFQcDZXRJhUPDuukcnHz0/jnrup0JA6qL+AFpAnty+7VKa9esuU5xTblAZzTGYJKSKaBxBhw==", "license": "MIT", - "optional": true, - "os": [ - "linux" - ] + "dependencies": { + "@radix-ui/react-primitive": "2.0.2" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } }, - "node_modules/@rollup/rollup-linux-arm64-musl": { - "version": "4.39.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.39.0.tgz", - "integrity": "sha512-vYanR6MtqC7Z2SNr8gzVnzUul09Wi1kZqJaek3KcIlI/wq5Xtq4ZPIZ0Mr/st/sv/NnaPwy/D4yXg5x0B3aUUA==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-loongarch64-gnu": { - "version": "4.39.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loongarch64-gnu/-/rollup-linux-loongarch64-gnu-4.39.0.tgz", - "integrity": "sha512-NMRUT40+h0FBa5fb+cpxtZoGAggRem16ocVKIv5gDB5uLDgBIwrIsXlGqYbLwW8YyO3WVTk1FkFDjMETYlDqiw==", - "cpu": [ - "loong64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-powerpc64le-gnu": { - "version": "4.39.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.39.0.tgz", - "integrity": "sha512-0pCNnmxgduJ3YRt+D+kJ6Ai/r+TaePu9ZLENl+ZDV/CdVczXl95CbIiwwswu4L+K7uOIGf6tMo2vm8uadRaICQ==", - "cpu": [ - "ppc64" - ], - "dev": true, + "node_modules/@radix-ui/react-presence": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@radix-ui/react-presence/-/react-presence-1.1.2.tgz", + "integrity": "sha512-18TFr80t5EVgL9x1SwF/YGtfG+l0BS0PRAlCWBDoBEiDQjeKgnNZRVJp/oVBl24sr3Gbfwc/Qpj4OcWTQMsAEg==", "license": "MIT", - "optional": true, - "os": [ - "linux" - ] + "dependencies": { + "@radix-ui/react-compose-refs": "1.1.1", + "@radix-ui/react-use-layout-effect": "1.1.0" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } }, - "node_modules/@rollup/rollup-linux-riscv64-gnu": { - "version": "4.39.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.39.0.tgz", - "integrity": "sha512-t7j5Zhr7S4bBtksT73bO6c3Qa2AV/HqiGlj9+KB3gNF5upcVkx+HLgxTm8DK4OkzsOYqbdqbLKwvGMhylJCPhQ==", - "cpu": [ - "riscv64" - ], - "dev": true, + "node_modules/@radix-ui/react-primitive": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/@radix-ui/react-primitive/-/react-primitive-2.0.2.tgz", + "integrity": "sha512-Ec/0d38EIuvDF+GZjcMU/Ze6MxntVJYO/fRlCPhCaVUyPY9WTalHJw54tp9sXeJo3tlShWpy41vQRgLRGOuz+w==", "license": "MIT", - "optional": true, - "os": [ - "linux" - ] + "dependencies": { + "@radix-ui/react-slot": "1.1.2" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } }, - "node_modules/@rollup/rollup-linux-riscv64-musl": { - "version": "4.39.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.39.0.tgz", - "integrity": "sha512-m6cwI86IvQ7M93MQ2RF5SP8tUjD39Y7rjb1qjHgYh28uAPVU8+k/xYWvxRO3/tBN2pZkSMa5RjnPuUIbrwVxeA==", - "cpu": [ - "riscv64" - ], - "dev": true, + "node_modules/@radix-ui/react-slot": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.1.2.tgz", + "integrity": "sha512-YAKxaiGsSQJ38VzKH86/BPRC4rh+b1Jpa+JneA5LRE7skmLPNAyeG8kPJj/oo4STLvlrs8vkf/iYyc3A5stYCQ==", "license": "MIT", - "optional": true, - "os": [ - "linux" - ] + "dependencies": { + "@radix-ui/react-compose-refs": "1.1.1" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } }, - "node_modules/@rollup/rollup-linux-s390x-gnu": { - "version": "4.39.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.39.0.tgz", - "integrity": "sha512-iRDJd2ebMunnk2rsSBYlsptCyuINvxUfGwOUldjv5M4tpa93K8tFMeYGpNk2+Nxl+OBJnBzy2/JCscGeO507kA==", - "cpu": [ - "s390x" - ], - "dev": true, + "node_modules/@radix-ui/react-use-callback-ref": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-callback-ref/-/react-use-callback-ref-1.1.0.tgz", + "integrity": "sha512-CasTfvsy+frcFkbXtSJ2Zu9JHpN8TYKxkgJGWbjiZhFivxaeW7rMeZt7QELGVLaYVfFMsKHjb7Ak0nMEe+2Vfw==", "license": "MIT", - "optional": true, - "os": [ - "linux" - ] + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } }, - "node_modules/@rollup/rollup-linux-x64-gnu": { - "version": "4.39.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.39.0.tgz", - "integrity": "sha512-t9jqYw27R6Lx0XKfEFe5vUeEJ5pF3SGIM6gTfONSMb7DuG6z6wfj2yjcoZxHg129veTqU7+wOhY6GX8wmf90dA==", - "cpu": [ - "x64" - ], - "dev": true, + "node_modules/@radix-ui/react-use-controllable-state": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-controllable-state/-/react-use-controllable-state-1.1.0.tgz", + "integrity": "sha512-MtfMVJiSr2NjzS0Aa90NPTnvTSg6C/JLCV7ma0W6+OMV78vd8OyRpID+Ng9LxzsPbLeuBnWBA1Nq30AtBIDChw==", "license": "MIT", - "optional": true, - "os": [ - "linux" - ] + "dependencies": { + "@radix-ui/react-use-callback-ref": "1.1.0" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } }, - "node_modules/@rollup/rollup-linux-x64-musl": { - "version": "4.39.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.39.0.tgz", - "integrity": "sha512-ThFdkrFDP55AIsIZDKSBWEt/JcWlCzydbZHinZ0F/r1h83qbGeenCt/G/wG2O0reuENDD2tawfAj2s8VK7Bugg==", - "cpu": [ - "x64" - ], - "dev": true, + "node_modules/@radix-ui/react-use-layout-effect": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-layout-effect/-/react-use-layout-effect-1.1.0.tgz", + "integrity": "sha512-+FPE0rOdziWSrH9athwI1R0HDVbWlEhd+FR+aSDk4uWGmSJ9Z54sdZVDQPZAinJhJXwfT+qnj969mCsT2gfm5w==", "license": "MIT", - "optional": true, - "os": [ - "linux" - ] + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } }, - "node_modules/@rollup/rollup-win32-arm64-msvc": { - "version": "4.39.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.39.0.tgz", - "integrity": "sha512-jDrLm6yUtbOg2TYB3sBF3acUnAwsIksEYjLeHL+TJv9jg+TmTwdyjnDex27jqEMakNKf3RwwPahDIt7QXCSqRQ==", - "cpu": [ - "arm64" - ], - "dev": true, + "node_modules/@radix-ui/react-use-previous": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-previous/-/react-use-previous-1.1.0.tgz", + "integrity": "sha512-Z/e78qg2YFnnXcW88A4JmTtm4ADckLno6F7OXotmkQfeuCVaKuYzqAATPhVzl3delXE7CxIV8shofPn3jPc5Og==", "license": "MIT", - "optional": true, - "os": [ - "win32" - ] + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } }, - "node_modules/@rollup/rollup-win32-ia32-msvc": { - "version": "4.39.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.39.0.tgz", - "integrity": "sha512-6w9uMuza+LbLCVoNKL5FSLE7yvYkq9laSd09bwS0tMjkwXrmib/4KmoJcrKhLWHvw19mwU+33ndC69T7weNNjQ==", - "cpu": [ - "ia32" - ], - "dev": true, + "node_modules/@radix-ui/react-use-size": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-size/-/react-use-size-1.1.0.tgz", + "integrity": "sha512-XW3/vWuIXHa+2Uwcc2ABSfcCledmXhhQPlGbfcRXbiUQI5Icjcg19BGCZVKKInYbvUCut/ufbbLLPFC5cbb1hw==", "license": "MIT", - "optional": true, - "os": [ - "win32" - ] + "dependencies": { + "@radix-ui/react-use-layout-effect": "1.1.0" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } }, - "node_modules/@rollup/rollup-win32-x64-msvc": { - "version": "4.39.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.39.0.tgz", - "integrity": "sha512-yAkUOkIKZlK5dl7u6dg897doBgLXmUHhIINM2c+sND3DZwnrdQkkSiDh7N75Ll4mM4dxSkYfXqU9fW3lLkMFug==", - "cpu": [ - "x64" - ], - "dev": true, + "node_modules/@tailwindcss/forms": { + "version": "0.5.10", + "resolved": "https://registry.npmjs.org/@tailwindcss/forms/-/forms-0.5.10.tgz", + "integrity": "sha512-utI1ONF6uf/pPNO68kmN1b8rEwNXv3czukalo8VtJH8ksIkZXr3Q3VYudZLkCsDd4Wku120uF02hYK25XGPorw==", "license": "MIT", - "optional": true, - "os": [ - "win32" - ] + "dependencies": { + "mini-svg-data-uri": "^1.2.3" + }, + "peerDependencies": { + "tailwindcss": ">=3.0.0 || >= 3.0.0-alpha.1 || >= 4.0.0-alpha.20 || >= 4.0.0-beta.1" + } }, "node_modules/@types/babel__core": { "version": "7.20.5", @@ -1352,40 +1263,41 @@ "@babel/types": "^7.20.7" } }, - "node_modules/@types/estree": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.7.tgz", - "integrity": "sha512-w28IoSUCJpidD/TGviZwwMJckNESJZXFu7NBZ5YJ4mEUnNraUn9Pm8HSZm/jDF1pDWYKspWE7oVphigUPRakIQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/@types/json-schema": { - "version": "7.0.15", - "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", - "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==", - "dev": true, + "node_modules/@types/prop-types": { + "version": "15.7.14", + "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.14.tgz", + "integrity": "sha512-gNMvNH49DJ7OJYv+KAKn0Xp45p8PLl6zo2YnvDIbTd4J6MER2BmWN49TG7n9LvkyihINxeKW8+3bfS2yDC9dzQ==", + "devOptional": true, "license": "MIT" }, "node_modules/@types/react": { - "version": "19.1.0", - "resolved": "https://registry.npmjs.org/@types/react/-/react-19.1.0.tgz", - "integrity": "sha512-UaicktuQI+9UKyA4njtDOGBD/67t8YEBt2xdfqu8+gP9hqPUPsiXlNPcpS2gVdjmis5GKPG3fCxbQLVgxsQZ8w==", - "dev": true, + "version": "18.3.20", + "resolved": "https://registry.npmjs.org/@types/react/-/react-18.3.20.tgz", + "integrity": "sha512-IPaCZN7PShZK/3t6Q87pfTkRm6oLTd4vztyoj+cbHUF1g3FfVb2tFIL79uCRKEfv16AhqDMBywP2VW3KIZUvcg==", + "devOptional": true, "license": "MIT", "dependencies": { + "@types/prop-types": "*", "csstype": "^3.0.2" } }, "node_modules/@types/react-dom": { - "version": "19.1.1", - "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-19.1.1.tgz", - "integrity": "sha512-jFf/woGTVTjUJsl2O7hcopJ1r0upqoq/vIOoCj0yLh3RIXxWcljlpuZ+vEBRXsymD1jhfeJrlyTy/S1UW+4y1w==", - "dev": true, + "version": "18.3.6", + "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-18.3.6.tgz", + "integrity": "sha512-nf22//wEbKXusP6E9pfOCDwFdHAX4u172eaJI4YkDRQEZiorm6KfYnSC2SWLDMVWUOWPERmJnN0ujeAfTBLvrw==", + "devOptional": true, "license": "MIT", "peerDependencies": { - "@types/react": "^19.0.0" + "@types/react": "^18.0.0" } }, + "node_modules/@ungap/structured-clone": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.3.0.tgz", + "integrity": "sha512-WmoN8qaIAo7WTYWbAZuG8PYEhn5fkz7dZrqTBZ7dtt//lL2Gwms1IcnQ5yHqjDfX8Ft5j4YzDM23f87zBfDe9g==", + "dev": true, + "license": "ISC" + }, "node_modules/@vitejs/plugin-react": { "version": "4.3.4", "resolved": "https://registry.npmjs.org/@vitejs/plugin-react/-/plugin-react-4.3.4.tgz", @@ -1446,11 +1358,19 @@ "url": "https://github.com/sponsors/epoberezkin" } }, + "node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, "node_modules/ansi-styles": { "version": "4.3.0", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, "license": "MIT", "dependencies": { "color-convert": "^2.0.1" @@ -1462,6 +1382,31 @@ "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, + "node_modules/any-promise": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/any-promise/-/any-promise-1.3.0.tgz", + "integrity": "sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A==", + "license": "MIT" + }, + "node_modules/anymatch": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", + "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", + "license": "ISC", + "dependencies": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/arg": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/arg/-/arg-5.0.2.tgz", + "integrity": "sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg==", + "license": "MIT" + }, "node_modules/argparse": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", @@ -1469,37 +1414,164 @@ "dev": true, "license": "Python-2.0" }, - "node_modules/balanced-match": { + "node_modules/array-buffer-byte-length": { "version": "1.0.2", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", - "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "resolved": "https://registry.npmjs.org/array-buffer-byte-length/-/array-buffer-byte-length-1.0.2.tgz", + "integrity": "sha512-LHE+8BuR7RYGDKvnrmcuSq3tDcKv9OFEXQt/HpbZhY7V6h0zlUXutnAD82GiFx9rdieCMjkvtcsPqBwgUl1Iiw==", "dev": true, - "license": "MIT" + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "is-array-buffer": "^3.0.5" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } }, - "node_modules/brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "node_modules/array-includes": { + "version": "3.1.8", + "resolved": "https://registry.npmjs.org/array-includes/-/array-includes-3.1.8.tgz", + "integrity": "sha512-itaWrbYbqpGXkGhZPGUulwnhVf5Hpy1xiCFsGqyIGglbBxmG5vSjxQen3/WGOjPpNEv1RtBLKxbmVXm8HpJStQ==", "dev": true, "license": "MIT", "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.2", + "es-object-atoms": "^1.0.0", + "get-intrinsic": "^1.2.4", + "is-string": "^1.0.7" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/browserslist": { - "version": "4.24.4", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.24.4.tgz", - "integrity": "sha512-KDi1Ny1gSePi1vm0q4oxSF8b4DR44GF4BbmS2YdhPLOEqd8pDviZOGH/GsmRwoWJ2+5Lr085X7naowMwKHDG1A==", + "node_modules/array.prototype.findlast": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/array.prototype.findlast/-/array.prototype.findlast-1.2.5.tgz", + "integrity": "sha512-CVvd6FHg1Z3POpBLxO6E6zr+rSKEQ9L6rZHAaY7lLfhKsWYUBBOuMs0e9o24oopj6H+geRCX0YJ+TJLBK2eHyQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.2", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.0.0", + "es-shim-unscopables": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/array.prototype.flat": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/array.prototype.flat/-/array.prototype.flat-1.3.3.tgz", + "integrity": "sha512-rwG/ja1neyLqCuGZ5YYrznA62D4mZXg0i1cIskIUKSiqF3Cje9/wXAls9B9s1Wa2fomMsIv8czB8jZcPmxCXFg==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.5", + "es-shim-unscopables": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/array.prototype.flatmap": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/array.prototype.flatmap/-/array.prototype.flatmap-1.3.3.tgz", + "integrity": "sha512-Y7Wt51eKJSyi80hFrJCePGGNo5ktJCslFuboqJsbf57CCPcm5zztluPlc4/aD8sWsKvlwatezpV4U1efk8kpjg==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.5", + "es-shim-unscopables": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/array.prototype.tosorted": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/array.prototype.tosorted/-/array.prototype.tosorted-1.1.4.tgz", + "integrity": "sha512-p6Fx8B7b7ZhL/gmUsAy0D15WhvDccw3mnGNbZpi3pmeJdxtWsj2jEaI4Y6oo3XiHfzuSgPwKc04MYt6KgvC/wA==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.3", + "es-errors": "^1.3.0", + "es-shim-unscopables": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/arraybuffer.prototype.slice": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/arraybuffer.prototype.slice/-/arraybuffer.prototype.slice-1.0.4.tgz", + "integrity": "sha512-BNoCY6SXXPQ7gF2opIP4GBE+Xw7U+pHMYKuzjgCN3GwiaIR09UUeKfheyIry77QtrCBlC0KK0q5/TER/tYh3PQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "array-buffer-byte-length": "^1.0.1", + "call-bind": "^1.0.8", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.5", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.6", + "is-array-buffer": "^3.0.4" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/async-function": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/async-function/-/async-function-1.0.0.tgz", + "integrity": "sha512-hsU18Ae8CDTR6Kgu9DYf0EbCr/a5iGL0rytQDobUcdpYOKokk8LEjVphnXkDkgpi0wYVsqrXuP0bZxJaTqdgoA==", "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/autoprefixer": { + "version": "10.4.21", + "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.21.tgz", + "integrity": "sha512-O+A6LWV5LDHSJD3LjHYoNi4VLsj/Whi7k6zG12xTYaU4cQ8oxQGckXNX8cRHK5yOZ/ppVHe0ZBXGzSV9jXdVbQ==", "funding": [ { "type": "opencollective", - "url": "https://opencollective.com/browserslist" + "url": "https://opencollective.com/postcss/" }, { "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/browserslist" + "url": "https://tidelift.com/funding/github/npm/autoprefixer" }, { "type": "github", @@ -1508,9 +1580,103 @@ ], "license": "MIT", "dependencies": { - "caniuse-lite": "^1.0.30001688", - "electron-to-chromium": "^1.5.73", - "node-releases": "^2.0.19", + "browserslist": "^4.24.4", + "caniuse-lite": "^1.0.30001702", + "fraction.js": "^4.3.7", + "normalize-range": "^0.1.2", + "picocolors": "^1.1.1", + "postcss-value-parser": "^4.2.0" + }, + "bin": { + "autoprefixer": "bin/autoprefixer" + }, + "engines": { + "node": "^10 || ^12 || >=14" + }, + "peerDependencies": { + "postcss": "^8.1.0" + } + }, + "node_modules/available-typed-arrays": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.7.tgz", + "integrity": "sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "possible-typed-array-names": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "license": "MIT" + }, + "node_modules/binary-extensions": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz", + "integrity": "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==", + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/braces": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", + "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", + "license": "MIT", + "dependencies": { + "fill-range": "^7.1.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/browserslist": { + "version": "4.24.4", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.24.4.tgz", + "integrity": "sha512-KDi1Ny1gSePi1vm0q4oxSF8b4DR44GF4BbmS2YdhPLOEqd8pDviZOGH/GsmRwoWJ2+5Lr085X7naowMwKHDG1A==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "caniuse-lite": "^1.0.30001688", + "electron-to-chromium": "^1.5.73", + "node-releases": "^2.0.19", "update-browserslist-db": "^1.1.1" }, "bin": { @@ -1520,6 +1686,56 @@ "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" } }, + "node_modules/call-bind": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.8.tgz", + "integrity": "sha512-oKlSFMcMwpUg2ednkhQ454wfWiU/ul3CkJe/PEHcTKuiX6RpbehUiFMXu13HalGZxfUwCQzZG747YXBn1im9ww==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.0", + "es-define-property": "^1.0.0", + "get-intrinsic": "^1.2.4", + "set-function-length": "^1.2.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/call-bind-apply-helpers": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", + "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/call-bound": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.4.tgz", + "integrity": "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "get-intrinsic": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/callsites": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", @@ -1530,11 +1746,19 @@ "node": ">=6" } }, + "node_modules/camelcase-css": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/camelcase-css/-/camelcase-css-2.0.1.tgz", + "integrity": "sha512-QOSvevhslijgYwRx6Rv7zKdMF8lbRmx+uQGx2+vDc+KI/eBnsy9kit5aj23AgGu3pa4t9AgwbnXWqS+iOY+2aA==", + "license": "MIT", + "engines": { + "node": ">= 6" + } + }, "node_modules/caniuse-lite": { "version": "1.0.30001712", "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001712.tgz", "integrity": "sha512-MBqPpGYYdQ7/hfKiet9SCI+nmN5/hp4ZzveOJubl5DTAMa5oggjAuoi0Z4onBpKPFI2ePGnQuQIzF3VxDjDJig==", - "dev": true, "funding": [ { "type": "opencollective", @@ -1568,11 +1792,67 @@ "url": "https://github.com/chalk/chalk?sponsor=1" } }, + "node_modules/chokidar": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz", + "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==", + "license": "MIT", + "dependencies": { + "anymatch": "~3.1.2", + "braces": "~3.0.2", + "glob-parent": "~5.1.2", + "is-binary-path": "~2.1.0", + "is-glob": "~4.0.1", + "normalize-path": "~3.0.0", + "readdirp": "~3.6.0" + }, + "engines": { + "node": ">= 8.10.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + }, + "optionalDependencies": { + "fsevents": "~2.3.2" + } + }, + "node_modules/chokidar/node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/class-variance-authority": { + "version": "0.7.1", + "resolved": "https://registry.npmjs.org/class-variance-authority/-/class-variance-authority-0.7.1.tgz", + "integrity": "sha512-Ka+9Trutv7G8M6WT6SeiRWz792K5qEqIGEGzXKhAE6xOWAY6pPH8U+9IY3oCMv6kqTmLsv7Xh/2w2RigkePMsg==", + "license": "Apache-2.0", + "dependencies": { + "clsx": "^2.1.1" + }, + "funding": { + "url": "https://polar.sh/cva" + } + }, + "node_modules/clsx": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz", + "integrity": "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, "node_modules/color-convert": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, "license": "MIT", "dependencies": { "color-name": "~1.1.4" @@ -1585,9 +1865,17 @@ "version": "1.1.4", "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true, "license": "MIT" }, + "node_modules/commander": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/commander/-/commander-4.1.1.tgz", + "integrity": "sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==", + "license": "MIT", + "engines": { + "node": ">= 6" + } + }, "node_modules/concat-map": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", @@ -1606,7 +1894,6 @@ "version": "7.0.6", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", - "dev": true, "license": "MIT", "dependencies": { "path-key": "^3.1.0", @@ -1617,13 +1904,79 @@ "node": ">= 8" } }, + "node_modules/cssesc": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz", + "integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==", + "license": "MIT", + "bin": { + "cssesc": "bin/cssesc" + }, + "engines": { + "node": ">=4" + } + }, "node_modules/csstype": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz", "integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==", - "dev": true, + "devOptional": true, "license": "MIT" }, + "node_modules/data-view-buffer": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/data-view-buffer/-/data-view-buffer-1.0.2.tgz", + "integrity": "sha512-EmKO5V3OLXh1rtK2wgXRansaK1/mtVdTUEiEI0W8RkvgT05kfxaH29PliLnpLP73yYO6142Q72QNa8Wx/A5CqQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "es-errors": "^1.3.0", + "is-data-view": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/data-view-byte-length": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/data-view-byte-length/-/data-view-byte-length-1.0.2.tgz", + "integrity": "sha512-tuhGbE6CfTM9+5ANGf+oQb72Ky/0+s3xKUpHvShfiz2RxMFgFPjsXuRLBVMtvMs15awe45SRb83D6wH4ew6wlQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "es-errors": "^1.3.0", + "is-data-view": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/inspect-js" + } + }, + "node_modules/data-view-byte-offset": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/data-view-byte-offset/-/data-view-byte-offset-1.0.1.tgz", + "integrity": "sha512-BS8PfmtDGnrgYdOonGZQdLZslWIeCGFP9tpan0hi1Co2Zr2NKADsvGYA8XxuG/4UWgJ6Cjtv+YJnB6MM69QGlQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "is-data-view": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/debug": { "version": "4.4.0", "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz", @@ -1649,149 +2002,435 @@ "dev": true, "license": "MIT" }, + "node_modules/define-data-property": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz", + "integrity": "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-define-property": "^1.0.0", + "es-errors": "^1.3.0", + "gopd": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/define-properties": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.2.1.tgz", + "integrity": "sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==", + "dev": true, + "license": "MIT", + "dependencies": { + "define-data-property": "^1.0.1", + "has-property-descriptors": "^1.0.0", + "object-keys": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/didyoumean": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/didyoumean/-/didyoumean-1.2.2.tgz", + "integrity": "sha512-gxtyfqMg7GKyhQmb056K7M3xszy/myH8w+B4RT+QXBQsvAOdc3XymqDDPHx1BgPgsdAA5SIifona89YtRATDzw==", + "license": "Apache-2.0" + }, + "node_modules/dlv": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/dlv/-/dlv-1.1.3.tgz", + "integrity": "sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA==", + "license": "MIT" + }, + "node_modules/doctrine": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", + "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "esutils": "^2.0.2" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/dunder-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", + "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.1", + "es-errors": "^1.3.0", + "gopd": "^1.2.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/eastasianwidth": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", + "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==", + "license": "MIT" + }, "node_modules/electron-to-chromium": { "version": "1.5.132", "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.132.tgz", "integrity": "sha512-QgX9EBvWGmvSRa74zqfnG7+Eno0Ak0vftBll0Pt2/z5b3bEGYL6OUXLgKPtvx73dn3dvwrlyVkjPKRRlhLYTEg==", - "dev": true, "license": "ISC" }, - "node_modules/esbuild": { - "version": "0.25.2", - "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.2.tgz", - "integrity": "sha512-16854zccKPnC+toMywC+uKNeYSv+/eXkevRAfwRD/G9Cleq66m8XFIrigkbvauLLlCfDL45Q2cWegSg53gGBnQ==", + "node_modules/emoji-regex": { + "version": "9.2.2", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", + "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", + "license": "MIT" + }, + "node_modules/es-abstract": { + "version": "1.23.9", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.23.9.tgz", + "integrity": "sha512-py07lI0wjxAC/DcfK1S6G7iANonniZwTISvdPzk9hzeH0IZIshbuuFxLIU96OyF89Yb9hiqWn8M/bY83KY5vzA==", "dev": true, - "hasInstallScript": true, "license": "MIT", - "bin": { - "esbuild": "bin/esbuild" + "dependencies": { + "array-buffer-byte-length": "^1.0.2", + "arraybuffer.prototype.slice": "^1.0.4", + "available-typed-arrays": "^1.0.7", + "call-bind": "^1.0.8", + "call-bound": "^1.0.3", + "data-view-buffer": "^1.0.2", + "data-view-byte-length": "^1.0.2", + "data-view-byte-offset": "^1.0.1", + "es-define-property": "^1.0.1", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.0.0", + "es-set-tostringtag": "^2.1.0", + "es-to-primitive": "^1.3.0", + "function.prototype.name": "^1.1.8", + "get-intrinsic": "^1.2.7", + "get-proto": "^1.0.0", + "get-symbol-description": "^1.1.0", + "globalthis": "^1.0.4", + "gopd": "^1.2.0", + "has-property-descriptors": "^1.0.2", + "has-proto": "^1.2.0", + "has-symbols": "^1.1.0", + "hasown": "^2.0.2", + "internal-slot": "^1.1.0", + "is-array-buffer": "^3.0.5", + "is-callable": "^1.2.7", + "is-data-view": "^1.0.2", + "is-regex": "^1.2.1", + "is-shared-array-buffer": "^1.0.4", + "is-string": "^1.1.1", + "is-typed-array": "^1.1.15", + "is-weakref": "^1.1.0", + "math-intrinsics": "^1.1.0", + "object-inspect": "^1.13.3", + "object-keys": "^1.1.1", + "object.assign": "^4.1.7", + "own-keys": "^1.0.1", + "regexp.prototype.flags": "^1.5.3", + "safe-array-concat": "^1.1.3", + "safe-push-apply": "^1.0.0", + "safe-regex-test": "^1.1.0", + "set-proto": "^1.0.0", + "string.prototype.trim": "^1.2.10", + "string.prototype.trimend": "^1.0.9", + "string.prototype.trimstart": "^1.0.8", + "typed-array-buffer": "^1.0.3", + "typed-array-byte-length": "^1.0.3", + "typed-array-byte-offset": "^1.0.4", + "typed-array-length": "^1.0.7", + "unbox-primitive": "^1.1.0", + "which-typed-array": "^1.1.18" }, "engines": { - "node": ">=18" + "node": ">= 0.4" }, - "optionalDependencies": { - "@esbuild/aix-ppc64": "0.25.2", - "@esbuild/android-arm": "0.25.2", - "@esbuild/android-arm64": "0.25.2", - "@esbuild/android-x64": "0.25.2", - "@esbuild/darwin-arm64": "0.25.2", - "@esbuild/darwin-x64": "0.25.2", - "@esbuild/freebsd-arm64": "0.25.2", - "@esbuild/freebsd-x64": "0.25.2", - "@esbuild/linux-arm": "0.25.2", - "@esbuild/linux-arm64": "0.25.2", - "@esbuild/linux-ia32": "0.25.2", - "@esbuild/linux-loong64": "0.25.2", - "@esbuild/linux-mips64el": "0.25.2", - "@esbuild/linux-ppc64": "0.25.2", - "@esbuild/linux-riscv64": "0.25.2", - "@esbuild/linux-s390x": "0.25.2", - "@esbuild/linux-x64": "0.25.2", - "@esbuild/netbsd-arm64": "0.25.2", - "@esbuild/netbsd-x64": "0.25.2", - "@esbuild/openbsd-arm64": "0.25.2", - "@esbuild/openbsd-x64": "0.25.2", - "@esbuild/sunos-x64": "0.25.2", - "@esbuild/win32-arm64": "0.25.2", - "@esbuild/win32-ia32": "0.25.2", - "@esbuild/win32-x64": "0.25.2" + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/escalade": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", - "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", + "node_modules/es-define-property": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", + "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", "dev": true, "license": "MIT", "engines": { - "node": ">=6" + "node": ">= 0.4" } }, - "node_modules/escape-string-regexp": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", - "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "node_modules/es-errors": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", + "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", "dev": true, "license": "MIT", "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": ">= 0.4" } }, - "node_modules/eslint": { - "version": "9.24.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.24.0.tgz", - "integrity": "sha512-eh/jxIEJyZrvbWRe4XuVclLPDYSYYYgLy5zXGGxD6j8zjSAxFEzI2fL/8xNq6O2yKqVt+eF2YhV+hxjV6UKXwQ==", + "node_modules/es-iterator-helpers": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/es-iterator-helpers/-/es-iterator-helpers-1.2.1.tgz", + "integrity": "sha512-uDn+FE1yrDzyC0pCo961B2IHbdM8y/ACZsKD4dG6WqrjV53BADjwa7D+1aom2rsNVfLyDgU/eigvlJGJ08OQ4w==", "dev": true, "license": "MIT", "dependencies": { - "@eslint-community/eslint-utils": "^4.2.0", - "@eslint-community/regexpp": "^4.12.1", - "@eslint/config-array": "^0.20.0", - "@eslint/config-helpers": "^0.2.0", - "@eslint/core": "^0.12.0", - "@eslint/eslintrc": "^3.3.1", - "@eslint/js": "9.24.0", - "@eslint/plugin-kit": "^0.2.7", - "@humanfs/node": "^0.16.6", - "@humanwhocodes/module-importer": "^1.0.1", - "@humanwhocodes/retry": "^0.4.2", - "@types/estree": "^1.0.6", - "@types/json-schema": "^7.0.15", - "ajv": "^6.12.4", - "chalk": "^4.0.0", - "cross-spawn": "^7.0.6", + "call-bind": "^1.0.8", + "call-bound": "^1.0.3", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.6", + "es-errors": "^1.3.0", + "es-set-tostringtag": "^2.0.3", + "function-bind": "^1.1.2", + "get-intrinsic": "^1.2.6", + "globalthis": "^1.0.4", + "gopd": "^1.2.0", + "has-property-descriptors": "^1.0.2", + "has-proto": "^1.2.0", + "has-symbols": "^1.1.0", + "internal-slot": "^1.1.0", + "iterator.prototype": "^1.1.4", + "safe-array-concat": "^1.1.3" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-object-atoms": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz", + "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-set-tostringtag": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz", + "integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.6", + "has-tostringtag": "^1.0.2", + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-shim-unscopables": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/es-shim-unscopables/-/es-shim-unscopables-1.1.0.tgz", + "integrity": "sha512-d9T8ucsEhh8Bi1woXCf+TIKDIROLG5WCkxg8geBCbvk22kzwC5G2OnXVMO6FUsvQlgUUXQ2itephWDLqDzbeCw==", + "dev": true, + "license": "MIT", + "dependencies": { + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-to-primitive": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.3.0.tgz", + "integrity": "sha512-w+5mJ3GuFL+NjVtJlvydShqE1eN3h3PbI7/5LAsYJP/2qtuMXjfL2LpHSRqo4b4eSF5K/DH1JXKUAHSB2UW50g==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-callable": "^1.2.7", + "is-date-object": "^1.0.5", + "is-symbol": "^1.0.4" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/esbuild": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.18.20.tgz", + "integrity": "sha512-ceqxoedUrcayh7Y7ZX6NdbbDzGROiyVBgC4PriJThBKSVPWnnFHZAkfI1lJT8QFkOwH4qOS2SJkS4wvpGl8BpA==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=12" + }, + "optionalDependencies": { + "@esbuild/android-arm": "0.18.20", + "@esbuild/android-arm64": "0.18.20", + "@esbuild/android-x64": "0.18.20", + "@esbuild/darwin-arm64": "0.18.20", + "@esbuild/darwin-x64": "0.18.20", + "@esbuild/freebsd-arm64": "0.18.20", + "@esbuild/freebsd-x64": "0.18.20", + "@esbuild/linux-arm": "0.18.20", + "@esbuild/linux-arm64": "0.18.20", + "@esbuild/linux-ia32": "0.18.20", + "@esbuild/linux-loong64": "0.18.20", + "@esbuild/linux-mips64el": "0.18.20", + "@esbuild/linux-ppc64": "0.18.20", + "@esbuild/linux-riscv64": "0.18.20", + "@esbuild/linux-s390x": "0.18.20", + "@esbuild/linux-x64": "0.18.20", + "@esbuild/netbsd-x64": "0.18.20", + "@esbuild/openbsd-x64": "0.18.20", + "@esbuild/sunos-x64": "0.18.20", + "@esbuild/win32-arm64": "0.18.20", + "@esbuild/win32-ia32": "0.18.20", + "@esbuild/win32-x64": "0.18.20" + } + }, + "node_modules/escalade": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", + "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/eslint": { + "version": "8.57.1", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.57.1.tgz", + "integrity": "sha512-ypowyDxpVSYpkXr9WPv2PAZCtNip1Mv5KTW0SCurXv/9iOpcrH9PaqUElksqEB6pChqHGDRCFTyrZlGhnLNGiA==", + "deprecated": "This version is no longer supported. Please see https://eslint.org/version-support for other options.", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/eslint-utils": "^4.2.0", + "@eslint-community/regexpp": "^4.6.1", + "@eslint/eslintrc": "^2.1.4", + "@eslint/js": "8.57.1", + "@humanwhocodes/config-array": "^0.13.0", + "@humanwhocodes/module-importer": "^1.0.1", + "@nodelib/fs.walk": "^1.2.8", + "@ungap/structured-clone": "^1.2.0", + "ajv": "^6.12.4", + "chalk": "^4.0.0", + "cross-spawn": "^7.0.2", "debug": "^4.3.2", + "doctrine": "^3.0.0", "escape-string-regexp": "^4.0.0", - "eslint-scope": "^8.3.0", - "eslint-visitor-keys": "^4.2.0", - "espree": "^10.3.0", - "esquery": "^1.5.0", + "eslint-scope": "^7.2.2", + "eslint-visitor-keys": "^3.4.3", + "espree": "^9.6.1", + "esquery": "^1.4.2", "esutils": "^2.0.2", "fast-deep-equal": "^3.1.3", - "file-entry-cache": "^8.0.0", + "file-entry-cache": "^6.0.1", "find-up": "^5.0.0", "glob-parent": "^6.0.2", + "globals": "^13.19.0", + "graphemer": "^1.4.0", "ignore": "^5.2.0", "imurmurhash": "^0.1.4", "is-glob": "^4.0.0", + "is-path-inside": "^3.0.3", + "js-yaml": "^4.1.0", "json-stable-stringify-without-jsonify": "^1.0.1", + "levn": "^0.4.1", "lodash.merge": "^4.6.2", "minimatch": "^3.1.2", "natural-compare": "^1.4.0", - "optionator": "^0.9.3" + "optionator": "^0.9.3", + "strip-ansi": "^6.0.1", + "text-table": "^0.2.0" }, "bin": { "eslint": "bin/eslint.js" }, "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" }, "funding": { - "url": "https://eslint.org/donate" + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint-plugin-react": { + "version": "7.37.5", + "resolved": "https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-7.37.5.tgz", + "integrity": "sha512-Qteup0SqU15kdocexFNAJMvCJEfa2xUKNV4CC1xsVMrIIqEy3SQ/rqyxCWNzfrd3/ldy6HMlD2e0JDVpDg2qIA==", + "dev": true, + "license": "MIT", + "dependencies": { + "array-includes": "^3.1.8", + "array.prototype.findlast": "^1.2.5", + "array.prototype.flatmap": "^1.3.3", + "array.prototype.tosorted": "^1.1.4", + "doctrine": "^2.1.0", + "es-iterator-helpers": "^1.2.1", + "estraverse": "^5.3.0", + "hasown": "^2.0.2", + "jsx-ast-utils": "^2.4.1 || ^3.0.0", + "minimatch": "^3.1.2", + "object.entries": "^1.1.9", + "object.fromentries": "^2.0.8", + "object.values": "^1.2.1", + "prop-types": "^15.8.1", + "resolve": "^2.0.0-next.5", + "semver": "^6.3.1", + "string.prototype.matchall": "^4.0.12", + "string.prototype.repeat": "^1.0.0" }, - "peerDependencies": { - "jiti": "*" + "engines": { + "node": ">=4" }, - "peerDependenciesMeta": { - "jiti": { - "optional": true - } + "peerDependencies": { + "eslint": "^3 || ^4 || ^5 || ^6 || ^7 || ^8 || ^9.7" } }, "node_modules/eslint-plugin-react-hooks": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-5.2.0.tgz", - "integrity": "sha512-+f15FfK64YQwZdJNELETdn5ibXEUQmW1DZL6KXhNnc2heoy/sg9VJJeT7n8TlMWouzWqSWavFkIhHyIbIAEapg==", + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-4.6.2.tgz", + "integrity": "sha512-QzliNJq4GinDBcD8gPB5v0wh6g8q3SUi6EFF0x8N/BL9PoVs0atuGc47ozMRyOWAKdwaZ5OnbOEa3WR+dSGKuQ==", "dev": true, "license": "MIT", "engines": { "node": ">=10" }, "peerDependencies": { - "eslint": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0 || ^9.0.0" + "eslint": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0" } }, "node_modules/eslint-plugin-react-refresh": { @@ -1804,10 +2443,23 @@ "eslint": ">=8.40" } }, + "node_modules/eslint-plugin-react/node_modules/doctrine": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz", + "integrity": "sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "esutils": "^2.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/eslint-scope": { - "version": "8.3.0", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-8.3.0.tgz", - "integrity": "sha512-pUNxi75F8MJ/GdeKtVLSbYg4ZI34J6C0C7sbL4YOp2exGwen7ZsuBqKzUhXd0qMQ362yET3z+uPwKeg/0C2XCQ==", + "version": "7.2.2", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.2.2.tgz", + "integrity": "sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==", "dev": true, "license": "BSD-2-Clause", "dependencies": { @@ -1815,38 +2467,38 @@ "estraverse": "^5.2.0" }, "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" }, "funding": { "url": "https://opencollective.com/eslint" } }, "node_modules/eslint-visitor-keys": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.0.tgz", - "integrity": "sha512-UyLnSehNt62FFhSwjZlHmeokpRK59rcz29j+F1/aDgbkbRTk7wIc9XzdoasMUbRNKDM0qQt/+BJ4BrpFeABemw==", + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", + "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", "dev": true, "license": "Apache-2.0", "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" }, "funding": { "url": "https://opencollective.com/eslint" } }, "node_modules/espree": { - "version": "10.3.0", - "resolved": "https://registry.npmjs.org/espree/-/espree-10.3.0.tgz", - "integrity": "sha512-0QYC8b24HWY8zjRnDTL6RiHfDbAWn63qb4LMj1Z4b076A4une81+z03Kg7l7mn/48PUTqoLptSXez8oknU8Clg==", + "version": "9.6.1", + "resolved": "https://registry.npmjs.org/espree/-/espree-9.6.1.tgz", + "integrity": "sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==", "dev": true, "license": "BSD-2-Clause", "dependencies": { - "acorn": "^8.14.0", + "acorn": "^8.9.0", "acorn-jsx": "^5.3.2", - "eslint-visitor-keys": "^4.2.0" + "eslint-visitor-keys": "^3.4.1" }, "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" }, "funding": { "url": "https://opencollective.com/eslint" @@ -1905,6 +2557,34 @@ "dev": true, "license": "MIT" }, + "node_modules/fast-glob": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.3.tgz", + "integrity": "sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==", + "license": "MIT", + "dependencies": { + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.2", + "merge2": "^1.3.0", + "micromatch": "^4.0.8" + }, + "engines": { + "node": ">=8.6.0" + } + }, + "node_modules/fast-glob/node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, "node_modules/fast-json-stable-stringify": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", @@ -1919,17 +2599,38 @@ "dev": true, "license": "MIT" }, + "node_modules/fastq": { + "version": "1.19.1", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.19.1.tgz", + "integrity": "sha512-GwLTyxkCXjXbxqIhTsMI2Nui8huMPtnxg7krajPJAjnEG/iiOS7i+zCtWGZR9G0NBKbXKh6X9m9UIsYX/N6vvQ==", + "license": "ISC", + "dependencies": { + "reusify": "^1.0.4" + } + }, "node_modules/file-entry-cache": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-8.0.0.tgz", - "integrity": "sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==", + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz", + "integrity": "sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==", "dev": true, "license": "MIT", "dependencies": { - "flat-cache": "^4.0.0" + "flat-cache": "^3.0.4" + }, + "engines": { + "node": "^10.12.0 || >=12.0.0" + } + }, + "node_modules/fill-range": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", + "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", + "license": "MIT", + "dependencies": { + "to-regex-range": "^5.0.1" }, "engines": { - "node": ">=16.0.0" + "node": ">=8" } }, "node_modules/find-up": { @@ -1950,17 +2651,18 @@ } }, "node_modules/flat-cache": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-4.0.1.tgz", - "integrity": "sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==", + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.2.0.tgz", + "integrity": "sha512-CYcENa+FtcUKLmhhqyctpclsq7QF38pKjZHsGNiSQF5r4FtoKDWabFDl3hzaEQMvT1LHEysw5twgLvpYYb4vbw==", "dev": true, "license": "MIT", "dependencies": { "flatted": "^3.2.9", - "keyv": "^4.5.4" + "keyv": "^4.5.3", + "rimraf": "^3.0.2" }, "engines": { - "node": ">=16" + "node": "^10.12.0 || >=12.0.0" } }, "node_modules/flatted": { @@ -1970,11 +2672,86 @@ "dev": true, "license": "ISC" }, + "node_modules/for-each": { + "version": "0.3.5", + "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.5.tgz", + "integrity": "sha512-dKx12eRCVIzqCxFGplyFKJMPvLEWgmNtUrpTiJIR5u97zEhRG8ySrtboPHZXx7daLxQVrl643cTzbab2tkQjxg==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-callable": "^1.2.7" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/foreground-child": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.1.tgz", + "integrity": "sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==", + "license": "ISC", + "dependencies": { + "cross-spawn": "^7.0.6", + "signal-exit": "^4.0.1" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/fraction.js": { + "version": "4.3.7", + "resolved": "https://registry.npmjs.org/fraction.js/-/fraction.js-4.3.7.tgz", + "integrity": "sha512-ZsDfxO51wGAXREY55a7la9LScWpwv9RxIrYABrlvOFBlH/ShPnrtsXeuUIfXKKOVicNxQ+o8JTbJvjS4M89yew==", + "license": "MIT", + "engines": { + "node": "*" + }, + "funding": { + "type": "patreon", + "url": "https://github.com/sponsors/rawify" + } + }, + "node_modules/framer-motion": { + "version": "10.18.0", + "resolved": "https://registry.npmjs.org/framer-motion/-/framer-motion-10.18.0.tgz", + "integrity": "sha512-oGlDh1Q1XqYPksuTD/usb0I70hq95OUzmL9+6Zd+Hs4XV0oaISBa/UUMSjYiq6m8EUF32132mOJ8xVZS+I0S6w==", + "license": "MIT", + "dependencies": { + "tslib": "^2.4.0" + }, + "optionalDependencies": { + "@emotion/is-prop-valid": "^0.8.2" + }, + "peerDependencies": { + "react": "^18.0.0", + "react-dom": "^18.0.0" + }, + "peerDependenciesMeta": { + "react": { + "optional": true + }, + "react-dom": { + "optional": true + } + } + }, + "node_modules/fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", + "dev": true, + "license": "ISC" + }, "node_modules/fsevents": { "version": "2.3.3", "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", - "dev": true, "hasInstallScript": true, "license": "MIT", "optional": true, @@ -1985,609 +2762,2653 @@ "node": "^8.16.0 || ^10.6.0 || >=11.0.0" } }, - "node_modules/gensync": { - "version": "1.0.0-beta.2", - "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", - "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", - "dev": true, + "node_modules/function-bind": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", "license": "MIT", - "engines": { - "node": ">=6.9.0" + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/glob-parent": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", - "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", + "node_modules/function.prototype.name": { + "version": "1.1.8", + "resolved": "https://registry.npmjs.org/function.prototype.name/-/function.prototype.name-1.1.8.tgz", + "integrity": "sha512-e5iwyodOHhbMr/yNrc7fDYG4qlbIvI5gajyzPnb5TCwyhjApznQh1BMFou9b30SevY43gCJKXycoCBjMbsuW0Q==", "dev": true, - "license": "ISC", + "license": "MIT", "dependencies": { - "is-glob": "^4.0.3" + "call-bind": "^1.0.8", + "call-bound": "^1.0.3", + "define-properties": "^1.2.1", + "functions-have-names": "^1.2.3", + "hasown": "^2.0.2", + "is-callable": "^1.2.7" }, "engines": { - "node": ">=10.13.0" - } - }, - "node_modules/globals": { - "version": "15.15.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-15.15.0.tgz", - "integrity": "sha512-7ACyT3wmyp3I61S4fG682L0VA2RGD9otkqGJIwNUMF1SWUombIIk+af1unuDYgMm082aHYwD+mzJvv9Iu8dsgg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=18" + "node": ">= 0.4" }, "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "node_modules/functions-have-names": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/functions-have-names/-/functions-have-names-1.2.3.tgz", + "integrity": "sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==", "dev": true, "license": "MIT", - "engines": { - "node": ">=8" + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/ignore": { - "version": "5.3.2", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", - "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==", + "node_modules/gensync": { + "version": "1.0.0-beta.2", + "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", + "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", "dev": true, "license": "MIT", "engines": { - "node": ">= 4" + "node": ">=6.9.0" } }, - "node_modules/import-fresh": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.1.tgz", - "integrity": "sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==", + "node_modules/get-intrinsic": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", + "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==", "dev": true, "license": "MIT", "dependencies": { - "parent-module": "^1.0.0", - "resolve-from": "^4.0.0" + "call-bind-apply-helpers": "^1.0.2", + "es-define-property": "^1.0.1", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.1.1", + "function-bind": "^1.1.2", + "get-proto": "^1.0.1", + "gopd": "^1.2.0", + "has-symbols": "^1.1.0", + "hasown": "^2.0.2", + "math-intrinsics": "^1.1.0" }, "engines": { - "node": ">=6" + "node": ">= 0.4" }, "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/imurmurhash": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", - "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", + "node_modules/get-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", + "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", "dev": true, "license": "MIT", + "dependencies": { + "dunder-proto": "^1.0.1", + "es-object-atoms": "^1.0.0" + }, "engines": { - "node": ">=0.8.19" + "node": ">= 0.4" } }, - "node_modules/is-extglob": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", - "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "node_modules/get-symbol-description": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/get-symbol-description/-/get-symbol-description-1.1.0.tgz", + "integrity": "sha512-w9UMqWwJxHNOvoNzSJ2oPF5wvYcvP7jUvYzhp67yEhTi17ZDBBC1z9pTdGuzjD+EFIqLSYRweZjqfiPzQ06Ebg==", "dev": true, "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.6" + }, "engines": { - "node": ">=0.10.0" + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/is-glob": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", - "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "deprecated": "Glob versions prior to v9 are no longer supported", "dev": true, - "license": "MIT", + "license": "ISC", "dependencies": { - "is-extglob": "^2.1.1" + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" }, "engines": { - "node": ">=0.10.0" + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/isexe": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", - "dev": true, - "license": "ISC" - }, - "node_modules/js-tokens": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", - "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", - "dev": true, - "license": "MIT" + "node_modules/glob-parent": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", + "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.3" + }, + "engines": { + "node": ">=10.13.0" + } }, - "node_modules/js-yaml": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", - "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "node_modules/globals": { + "version": "13.24.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.24.0.tgz", + "integrity": "sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==", "dev": true, "license": "MIT", "dependencies": { - "argparse": "^2.0.1" + "type-fest": "^0.20.2" }, - "bin": { - "js-yaml": "bin/js-yaml.js" + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/jsesc": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz", - "integrity": "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==", + "node_modules/globalthis": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/globalthis/-/globalthis-1.0.4.tgz", + "integrity": "sha512-DpLKbNU4WylpxJykQujfCcwYWiV/Jhm50Goo0wrVILAv5jOr9d+H+UR3PhSCD2rCCEIg0uc+G+muBTwD54JhDQ==", "dev": true, "license": "MIT", - "bin": { - "jsesc": "bin/jsesc" + "dependencies": { + "define-properties": "^1.2.1", + "gopd": "^1.0.1" }, "engines": { - "node": ">=6" + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/json-buffer": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", - "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/json-schema-traverse": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", - "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "node_modules/gopd": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", + "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", "dev": true, - "license": "MIT" + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } }, - "node_modules/json-stable-stringify-without-jsonify": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", - "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", + "node_modules/graphemer": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz", + "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==", "dev": true, "license": "MIT" }, - "node_modules/json5": { - "version": "2.2.3", - "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", - "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", + "node_modules/has-bigints": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.1.0.tgz", + "integrity": "sha512-R3pbpkcIqv2Pm3dUwgjclDRVmWpTJW2DcMzcIhEXEx1oh/CEMObMm3KLmRJOdvhM7o4uQBnwr8pzRK2sJWIqfg==", "dev": true, "license": "MIT", - "bin": { - "json5": "lib/cli.js" - }, "engines": { - "node": ">=6" + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/keyv": { - "version": "4.5.4", - "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", - "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", + "node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", "dev": true, "license": "MIT", - "dependencies": { - "json-buffer": "3.0.1" + "engines": { + "node": ">=8" } }, - "node_modules/levn": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", - "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", + "node_modules/has-property-descriptors": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz", + "integrity": "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==", "dev": true, "license": "MIT", "dependencies": { - "prelude-ls": "^1.2.1", - "type-check": "~0.4.0" + "es-define-property": "^1.0.0" }, - "engines": { - "node": ">= 0.8.0" + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/locate-path": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", - "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", + "node_modules/has-proto": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.2.0.tgz", + "integrity": "sha512-KIL7eQPfHQRC8+XluaIw7BHUwwqL19bQn4hzNgdr+1wXoU0KKj6rufu47lhY7KbJR2C6T6+PfyN0Ea7wkSS+qQ==", "dev": true, "license": "MIT", "dependencies": { - "p-locate": "^5.0.0" + "dunder-proto": "^1.0.0" }, "engines": { - "node": ">=10" + "node": ">= 0.4" }, "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/lodash.merge": { - "version": "4.6.2", - "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", - "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", + "node_modules/has-symbols": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", + "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", "dev": true, - "license": "MIT" + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } }, - "node_modules/lru-cache": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", - "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", + "node_modules/has-tostringtag": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz", + "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==", "dev": true, - "license": "ISC", + "license": "MIT", "dependencies": { - "yallist": "^3.0.2" + "has-symbols": "^1.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "dev": true, - "license": "ISC", + "node_modules/hasown": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", + "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", + "license": "MIT", "dependencies": { - "brace-expansion": "^1.1.7" + "function-bind": "^1.1.2" }, "engines": { - "node": "*" + "node": ">= 0.4" } }, - "node_modules/ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "node_modules/ignore": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", + "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==", "dev": true, - "license": "MIT" + "license": "MIT", + "engines": { + "node": ">= 4" + } }, - "node_modules/nanoid": { - "version": "3.3.11", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz", - "integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==", + "node_modules/import-fresh": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.1.tgz", + "integrity": "sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==", "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], "license": "MIT", - "bin": { - "nanoid": "bin/nanoid.cjs" + "dependencies": { + "parent-module": "^1.0.0", + "resolve-from": "^4.0.0" }, "engines": { - "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/natural-compare": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", - "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", + "node_modules/imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", "dev": true, - "license": "MIT" + "license": "MIT", + "engines": { + "node": ">=0.8.19" + } }, - "node_modules/node-releases": { - "version": "2.0.19", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.19.tgz", - "integrity": "sha512-xxOWJsBKtzAq7DY0J+DTzuz58K8e7sJbdgwkbMWQe8UYB6ekmsQ45q0M/tJDsGaZmbC+l7n57UV8Hl5tHxO9uw==", + "node_modules/inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", + "deprecated": "This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful.", "dev": true, - "license": "MIT" + "license": "ISC", + "dependencies": { + "once": "^1.3.0", + "wrappy": "1" + } }, - "node_modules/optionator": { - "version": "0.9.4", - "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz", - "integrity": "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==", + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/internal-slot": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.1.0.tgz", + "integrity": "sha512-4gd7VpWNQNB4UKKCFFVcp1AVv+FMOgs9NKzjHKusc8jTMhd5eL1NqQqOpE0KzMds804/yHlglp3uxgluOqAPLw==", "dev": true, "license": "MIT", "dependencies": { - "deep-is": "^0.1.3", - "fast-levenshtein": "^2.0.6", - "levn": "^0.4.1", - "prelude-ls": "^1.2.1", - "type-check": "^0.4.0", - "word-wrap": "^1.2.5" + "es-errors": "^1.3.0", + "hasown": "^2.0.2", + "side-channel": "^1.1.0" }, "engines": { - "node": ">= 0.8.0" + "node": ">= 0.4" } }, - "node_modules/p-limit": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", - "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "node_modules/is-array-buffer": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/is-array-buffer/-/is-array-buffer-3.0.5.tgz", + "integrity": "sha512-DDfANUiiG2wC1qawP66qlTugJeL5HyzMpfr8lLK+jMQirGzNod0B12cFB/9q838Ru27sBwfw78/rdoU7RERz6A==", "dev": true, "license": "MIT", "dependencies": { - "yocto-queue": "^0.1.0" + "call-bind": "^1.0.8", + "call-bound": "^1.0.3", + "get-intrinsic": "^1.2.6" }, "engines": { - "node": ">=10" + "node": ">= 0.4" }, "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/p-locate": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", - "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", + "node_modules/is-async-function": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-async-function/-/is-async-function-2.1.1.tgz", + "integrity": "sha512-9dgM/cZBnNvjzaMYHVoxxfPj2QXt22Ev7SuuPrs+xav0ukGB0S6d4ydZdEiM48kLx5kDV+QBPrpVnFyefL8kkQ==", "dev": true, "license": "MIT", "dependencies": { - "p-limit": "^3.0.2" + "async-function": "^1.0.0", + "call-bound": "^1.0.3", + "get-proto": "^1.0.1", + "has-tostringtag": "^1.0.2", + "safe-regex-test": "^1.1.0" }, "engines": { - "node": ">=10" + "node": ">= 0.4" }, "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/parent-module": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", - "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", + "node_modules/is-bigint": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-bigint/-/is-bigint-1.1.0.tgz", + "integrity": "sha512-n4ZT37wG78iz03xPRKJrHTdZbe3IicyucEtdRsV5yglwc3GyUfbAfpSeD0FJ41NbUNSt5wbhqfp1fS+BgnvDFQ==", "dev": true, "license": "MIT", "dependencies": { - "callsites": "^3.0.0" + "has-bigints": "^1.0.2" }, "engines": { - "node": ">=6" + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/path-exists": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", - "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", - "dev": true, + "node_modules/is-binary-path": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", + "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", "license": "MIT", + "dependencies": { + "binary-extensions": "^2.0.0" + }, "engines": { "node": ">=8" } }, - "node_modules/path-key": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", - "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "node_modules/is-boolean-object": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.2.2.tgz", + "integrity": "sha512-wa56o2/ElJMYqjCjGkXri7it5FbebW5usLw/nPmCMs5DeZ7eziSYZhSmPRn0txqeW4LnAmQQU7FgqLpsEFKM4A==", "dev": true, "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "has-tostringtag": "^1.0.2" + }, "engines": { - "node": ">=8" - } + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } }, - "node_modules/picocolors": { + "node_modules/is-callable": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.7.tgz", + "integrity": "sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-core-module": { + "version": "2.16.1", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.16.1.tgz", + "integrity": "sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==", + "license": "MIT", + "dependencies": { + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-data-view": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-data-view/-/is-data-view-1.0.2.tgz", + "integrity": "sha512-RKtWF8pGmS87i2D6gqQu/l7EYRlVdfzemCJN/P3UOs//x1QE7mfhvzHIApBTRf7axvT6DMGwSwBXYCT0nfB9xw==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "get-intrinsic": "^1.2.6", + "is-typed-array": "^1.1.13" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-date-object": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.1.0.tgz", + "integrity": "sha512-PwwhEakHVKTdRNVOw+/Gyh0+MzlCl4R6qKvkhuvLtPMggI1WAHt9sOwZxQLSGpUaDnrdyDsomoRgNnCfKNSXXg==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "has-tostringtag": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-finalizationregistry": { "version": "1.1.1", - "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", - "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", + "resolved": "https://registry.npmjs.org/is-finalizationregistry/-/is-finalizationregistry-1.1.1.tgz", + "integrity": "sha512-1pC6N8qWJbWoPtEjgcL2xyhQOP491EQjeUo3qTKcmV8YSDDJrOepfG8pcC7h/QgnQHYSv0mJ3Z/ZWxmatVrysg==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/is-generator-function": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-generator-function/-/is-generator-function-1.1.0.tgz", + "integrity": "sha512-nPUB5km40q9e8UfN/Zc24eLlzdSf9OfKByBw9CIdw4H1giPMeA0OIJvbchsCu4npfI2QcMVBsGEBHKZ7wLTWmQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "get-proto": "^1.0.0", + "has-tostringtag": "^1.0.2", + "safe-regex-test": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "license": "MIT", + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-map": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/is-map/-/is-map-2.0.3.tgz", + "integrity": "sha512-1Qed0/Hr2m+YqxnM09CjA2d/i6YZNfF6R2oRAOj36eUdS6qIV/huPJNSEpKbupewFs+ZsJlxsjjPbc0/afW6Lw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "license": "MIT", + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/is-number-object": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.1.1.tgz", + "integrity": "sha512-lZhclumE1G6VYD8VHe35wFaIif+CTy5SJIi5+3y4psDgWu4wPDoBhF8NxUOinEc7pHgiTsT6MaBb92rKhhD+Xw==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "has-tostringtag": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-path-inside": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz", + "integrity": "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/is-regex": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.2.1.tgz", + "integrity": "sha512-MjYsKHO5O7mCsmRGxWcLWheFqN9DJ/2TmngvjKXihe6efViPqc274+Fx/4fYj/r03+ESvBdTXK0V6tA3rgez1g==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "gopd": "^1.2.0", + "has-tostringtag": "^1.0.2", + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-set": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/is-set/-/is-set-2.0.3.tgz", + "integrity": "sha512-iPAjerrse27/ygGLxw+EBR9agv9Y6uLeYVJMu+QNCoouJ1/1ri0mGrcWpfCqFZuzzx3WjtwxG098X+n4OuRkPg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-shared-array-buffer": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.4.tgz", + "integrity": "sha512-ISWac8drv4ZGfwKl5slpHG9OwPNty4jOWPRIhBpxOoD+hqITiwuipOQ2bNthAzwA3B4fIjO4Nln74N0S9byq8A==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-string": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.1.1.tgz", + "integrity": "sha512-BtEeSsoaQjlSPBemMQIrY1MY0uM6vnS1g5fmufYOtnxLGUZM2178PKbhsk7Ffv58IX+ZtcvoGwccYsh0PglkAA==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "has-tostringtag": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-symbol": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.1.1.tgz", + "integrity": "sha512-9gGx6GTtCQM73BgmHQXfDmLtfjjTUDSyoxTCbp5WtoixAhfgsDirWIcVQ/IHpvI5Vgd5i/J5F7B9cN/WlVbC/w==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "has-symbols": "^1.1.0", + "safe-regex-test": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-typed-array": { + "version": "1.1.15", + "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.15.tgz", + "integrity": "sha512-p3EcsicXjit7SaskXHs1hA91QxgTw46Fv6EFKKGS5DRFLD8yKnohjF3hxoju94b/OcMZoQukzpPpBE9uLVKzgQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "which-typed-array": "^1.1.16" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-weakmap": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/is-weakmap/-/is-weakmap-2.0.2.tgz", + "integrity": "sha512-K5pXYOm9wqY1RgjpL3YTkF39tni1XajUIkawTLUo9EZEVUFga5gSQJF8nNS7ZwJQ02y+1YCNYcMh+HIf1ZqE+w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-weakref": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-weakref/-/is-weakref-1.1.1.tgz", + "integrity": "sha512-6i9mGWSlqzNMEqpCp93KwRS1uUOodk2OJ6b+sq7ZPDSy2WuI5NFIxp/254TytR8ftefexkWn5xNiHUNpPOfSew==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-weakset": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/is-weakset/-/is-weakset-2.0.4.tgz", + "integrity": "sha512-mfcwb6IzQyOKTs84CQMrOwW4gQcaTOAWJ0zzJCl2WSPDrWk/OzDaImWFH3djXhb24g4eudZfLRozAvPGw4d9hQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "get-intrinsic": "^1.2.6" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/isarray": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz", + "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==", "dev": true, + "license": "MIT" + }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", "license": "ISC" }, - "node_modules/postcss": { - "version": "8.5.3", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.3.tgz", - "integrity": "sha512-dle9A3yYxlBSrt8Fu+IpjGT8SY8hN0mlaA6GY8t0P5PjIOZemULz/E2Bnm/2dcUOena75OTNkHI76uZBNUUq3A==", + "node_modules/iterator.prototype": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/iterator.prototype/-/iterator.prototype-1.1.5.tgz", + "integrity": "sha512-H0dkQoCa3b2VEeKQBOxFph+JAbcrQdE7KC0UkqwpLmv2EC4P41QXP+rqo9wYodACiG5/WM5s9oDApTU8utwj9g==", "dev": true, - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/postcss/" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/postcss" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], "license": "MIT", "dependencies": { - "nanoid": "^3.3.8", - "picocolors": "^1.1.1", - "source-map-js": "^1.2.1" + "define-data-property": "^1.1.4", + "es-object-atoms": "^1.0.0", + "get-intrinsic": "^1.2.6", + "get-proto": "^1.0.0", + "has-symbols": "^1.1.0", + "set-function-name": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/jackspeak": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-3.4.3.tgz", + "integrity": "sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==", + "license": "BlueOak-1.0.0", + "dependencies": { + "@isaacs/cliui": "^8.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + }, + "optionalDependencies": { + "@pkgjs/parseargs": "^0.11.0" + } + }, + "node_modules/jiti": { + "version": "1.21.7", + "resolved": "https://registry.npmjs.org/jiti/-/jiti-1.21.7.tgz", + "integrity": "sha512-/imKNG4EbWNrVjoNC/1H5/9GFy+tqjGBHCaSsN+P2RnPqjsLmv6UD3Ej+Kj8nBWaRAwyk7kK5ZUc+OEatnTR3A==", + "license": "MIT", + "bin": { + "jiti": "bin/jiti.js" + } + }, + "node_modules/js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", + "license": "MIT" + }, + "node_modules/js-yaml": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", + "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "dev": true, + "license": "MIT", + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/jsesc": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz", + "integrity": "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==", + "dev": true, + "license": "MIT", + "bin": { + "jsesc": "bin/jsesc" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/json-buffer": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", + "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true, + "license": "MIT" + }, + "node_modules/json-stable-stringify-without-jsonify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", + "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", + "dev": true, + "license": "MIT" + }, + "node_modules/json5": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", + "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", + "dev": true, + "license": "MIT", + "bin": { + "json5": "lib/cli.js" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/jsx-ast-utils": { + "version": "3.3.5", + "resolved": "https://registry.npmjs.org/jsx-ast-utils/-/jsx-ast-utils-3.3.5.tgz", + "integrity": "sha512-ZZow9HBI5O6EPgSJLUb8n2NKgmVWTwCvHGwFuJlMjvLFqlGG6pjirPhtdsseaLZjSibD8eegzmYpUZwoIlj2cQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "array-includes": "^3.1.6", + "array.prototype.flat": "^1.3.1", + "object.assign": "^4.1.4", + "object.values": "^1.1.6" + }, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/keyv": { + "version": "4.5.4", + "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", + "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", + "dev": true, + "license": "MIT", + "dependencies": { + "json-buffer": "3.0.1" + } + }, + "node_modules/levn": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", + "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "prelude-ls": "^1.2.1", + "type-check": "~0.4.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/lilconfig": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-3.1.3.tgz", + "integrity": "sha512-/vlFKAoH5Cgt3Ie+JLhRbwOsCQePABiU3tJ1egGvyQ+33R/vcwM2Zl2QR/LzjsBeItPt3oSVXapn+m4nQDvpzw==", + "license": "MIT", + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/antonk52" + } + }, + "node_modules/lines-and-columns": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", + "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==", + "license": "MIT" + }, + "node_modules/locate-path": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", + "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-locate": "^5.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/lodash.merge": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", + "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/loose-envify": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", + "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", + "license": "MIT", + "dependencies": { + "js-tokens": "^3.0.0 || ^4.0.0" + }, + "bin": { + "loose-envify": "cli.js" + } + }, + "node_modules/lru-cache": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", + "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", + "dev": true, + "license": "ISC", + "dependencies": { + "yallist": "^3.0.2" + } + }, + "node_modules/lucide-react": { + "version": "0.284.0", + "resolved": "https://registry.npmjs.org/lucide-react/-/lucide-react-0.284.0.tgz", + "integrity": "sha512-dVSMHYAya/TeY3+vsk+VQJEKNQN2AhIo0+Dp09B2qpzvcBuu93H98YZykFcjIAfmanFiDd8nqfXFR38L757cyQ==", + "license": "ISC", + "peerDependencies": { + "react": "^16.5.1 || ^17.0.0 || ^18.0.0" + } + }, + "node_modules/math-intrinsics": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", + "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/merge2": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", + "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", + "license": "MIT", + "engines": { + "node": ">= 8" + } + }, + "node_modules/micromatch": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", + "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", + "license": "MIT", + "dependencies": { + "braces": "^3.0.3", + "picomatch": "^2.3.1" + }, + "engines": { + "node": ">=8.6" + } + }, + "node_modules/mini-svg-data-uri": { + "version": "1.4.4", + "resolved": "https://registry.npmjs.org/mini-svg-data-uri/-/mini-svg-data-uri-1.4.4.tgz", + "integrity": "sha512-r9deDe9p5FJUPZAk3A59wGH7Ii9YrjjWw0jmw/liSbHl2CHiyXj6FcDXDu2K3TjVAXqiJdaw3xxwlZZr9E6nHg==", + "license": "MIT", + "bin": { + "mini-svg-data-uri": "cli.js" + } + }, + "node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/minipass": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz", + "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==", + "license": "ISC", + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, + "node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true, + "license": "MIT" + }, + "node_modules/mz": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/mz/-/mz-2.7.0.tgz", + "integrity": "sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==", + "license": "MIT", + "dependencies": { + "any-promise": "^1.0.0", + "object-assign": "^4.0.1", + "thenify-all": "^1.0.0" + } + }, + "node_modules/nanoid": { + "version": "3.3.11", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz", + "integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, + "node_modules/natural-compare": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", + "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", + "dev": true, + "license": "MIT" + }, + "node_modules/node-releases": { + "version": "2.0.19", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.19.tgz", + "integrity": "sha512-xxOWJsBKtzAq7DY0J+DTzuz58K8e7sJbdgwkbMWQe8UYB6ekmsQ45q0M/tJDsGaZmbC+l7n57UV8Hl5tHxO9uw==", + "license": "MIT" + }, + "node_modules/normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/normalize-range": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/normalize-range/-/normalize-range-0.1.2.tgz", + "integrity": "sha512-bdok/XvKII3nUpklnV6P2hxtMNrCboOjAcyBuQnWEhO665FwrSNRxU+AqpsyvO6LgGYPspN+lu5CLtw4jPRKNA==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object-hash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/object-hash/-/object-hash-3.0.0.tgz", + "integrity": "sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw==", + "license": "MIT", + "engines": { + "node": ">= 6" + } + }, + "node_modules/object-inspect": { + "version": "1.13.4", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz", + "integrity": "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/object-keys": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", + "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/object.assign": { + "version": "4.1.7", + "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.7.tgz", + "integrity": "sha512-nK28WOo+QIjBkDduTINE4JkF/UJJKyf2EJxvJKfblDpyg0Q+pkOHNTL0Qwy6NP6FhE/EnzV73BxxqcJaXY9anw==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.3", + "define-properties": "^1.2.1", + "es-object-atoms": "^1.0.0", + "has-symbols": "^1.1.0", + "object-keys": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/object.entries": { + "version": "1.1.9", + "resolved": "https://registry.npmjs.org/object.entries/-/object.entries-1.1.9.tgz", + "integrity": "sha512-8u/hfXFRBD1O0hPUjioLhoWFHRmt6tKA4/vZPyckBr18l1KE9uHrFaFaUi8MDRTpi4uak2goyPTSNJLXX2k2Hw==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.4", + "define-properties": "^1.2.1", + "es-object-atoms": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/object.fromentries": { + "version": "2.0.8", + "resolved": "https://registry.npmjs.org/object.fromentries/-/object.fromentries-2.0.8.tgz", + "integrity": "sha512-k6E21FzySsSK5a21KRADBd/NGneRegFO5pLHfdQLpRDETUNJueLXs3WCzyQ3tFRDYgbq3KHGXfTbi2bs8WQ6rQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.2", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/object.values": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/object.values/-/object.values-1.2.1.tgz", + "integrity": "sha512-gXah6aZrcUxjWg2zR2MwouP2eHlCBzdV4pygudehaKXSGW4v2AsRQUK+lwwXhii6KFZcunEnmSUoYp5CXibxtA==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.3", + "define-properties": "^1.2.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "dev": true, + "license": "ISC", + "dependencies": { + "wrappy": "1" + } + }, + "node_modules/optionator": { + "version": "0.9.4", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz", + "integrity": "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==", + "dev": true, + "license": "MIT", + "dependencies": { + "deep-is": "^0.1.3", + "fast-levenshtein": "^2.0.6", + "levn": "^0.4.1", + "prelude-ls": "^1.2.1", + "type-check": "^0.4.0", + "word-wrap": "^1.2.5" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/own-keys": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/own-keys/-/own-keys-1.0.1.tgz", + "integrity": "sha512-qFOyK5PjiWZd+QQIh+1jhdb9LpxTF0qs7Pm8o5QHYZ0M3vKqSqzsZaEB6oWlxZ+q2sJBMI/Ktgd2N5ZwQoRHfg==", + "dev": true, + "license": "MIT", + "dependencies": { + "get-intrinsic": "^1.2.6", + "object-keys": "^1.1.1", + "safe-push-apply": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/p-limit": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "yocto-queue": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-locate": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", + "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-limit": "^3.0.2" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/package-json-from-dist": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz", + "integrity": "sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==", + "license": "BlueOak-1.0.0" + }, + "node_modules/parent-module": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", + "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", + "dev": true, + "license": "MIT", + "dependencies": { + "callsites": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/path-parse": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", + "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", + "license": "MIT" + }, + "node_modules/path-scurry": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.11.1.tgz", + "integrity": "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==", + "license": "BlueOak-1.0.0", + "dependencies": { + "lru-cache": "^10.2.0", + "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" + }, + "engines": { + "node": ">=16 || 14 >=14.18" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/path-scurry/node_modules/lru-cache": { + "version": "10.4.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", + "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", + "license": "ISC" + }, + "node_modules/picocolors": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", + "license": "ISC" + }, + "node_modules/picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "license": "MIT", + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/pify": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", + "integrity": "sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/pirates": { + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.7.tgz", + "integrity": "sha512-TfySrs/5nm8fQJDcBDuUng3VOUKsd7S+zqvbOTiGXHfxX4wK31ard+hoNuvkicM/2YFzlpDgABOevKSsB4G/FA==", + "license": "MIT", + "engines": { + "node": ">= 6" + } + }, + "node_modules/possible-typed-array-names": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/possible-typed-array-names/-/possible-typed-array-names-1.1.0.tgz", + "integrity": "sha512-/+5VFTchJDoVj3bhoqi6UeymcD00DAwb1nJwamzPvHEszJ4FpF6SNNbUbOS8yI56qHzdV8eK0qEfOSiodkTdxg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/postcss": { + "version": "8.5.3", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.3.tgz", + "integrity": "sha512-dle9A3yYxlBSrt8Fu+IpjGT8SY8hN0mlaA6GY8t0P5PjIOZemULz/E2Bnm/2dcUOena75OTNkHI76uZBNUUq3A==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "nanoid": "^3.3.8", + "picocolors": "^1.1.1", + "source-map-js": "^1.2.1" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, + "node_modules/postcss-import": { + "version": "15.1.0", + "resolved": "https://registry.npmjs.org/postcss-import/-/postcss-import-15.1.0.tgz", + "integrity": "sha512-hpr+J05B2FVYUAXHeK1YyI267J/dDDhMU6B6civm8hSY1jYJnBXxzKDKDswzJmtLHryrjhnDjqqp/49t8FALew==", + "license": "MIT", + "dependencies": { + "postcss-value-parser": "^4.0.0", + "read-cache": "^1.0.0", + "resolve": "^1.1.7" + }, + "engines": { + "node": ">=14.0.0" + }, + "peerDependencies": { + "postcss": "^8.0.0" + } + }, + "node_modules/postcss-import/node_modules/resolve": { + "version": "1.22.10", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.10.tgz", + "integrity": "sha512-NPRy+/ncIMeDlTAsuqwKIiferiawhefFJtkNSW0qZJEqMEb+qBt/77B/jGeeek+F0uOeN05CDa6HXbbIgtVX4w==", + "license": "MIT", + "dependencies": { + "is-core-module": "^2.16.0", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" + }, + "bin": { + "resolve": "bin/resolve" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/postcss-js": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/postcss-js/-/postcss-js-4.0.1.tgz", + "integrity": "sha512-dDLF8pEO191hJMtlHFPRa8xsizHaM82MLfNkUHdUtVEV3tgTp5oj+8qbEqYM57SLfc74KSbw//4SeJma2LRVIw==", + "license": "MIT", + "dependencies": { + "camelcase-css": "^2.0.1" + }, + "engines": { + "node": "^12 || ^14 || >= 16" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + "peerDependencies": { + "postcss": "^8.4.21" + } + }, + "node_modules/postcss-load-config": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/postcss-load-config/-/postcss-load-config-4.0.2.tgz", + "integrity": "sha512-bSVhyJGL00wMVoPUzAVAnbEoWyqRxkjv64tUl427SKnPrENtq6hJwUojroMz2VB+Q1edmi4IfrAPpami5VVgMQ==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "lilconfig": "^3.0.0", + "yaml": "^2.3.4" + }, + "engines": { + "node": ">= 14" + }, + "peerDependencies": { + "postcss": ">=8.0.9", + "ts-node": ">=9.0.0" + }, + "peerDependenciesMeta": { + "postcss": { + "optional": true + }, + "ts-node": { + "optional": true + } + } + }, + "node_modules/postcss-nested": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/postcss-nested/-/postcss-nested-6.2.0.tgz", + "integrity": "sha512-HQbt28KulC5AJzG+cZtj9kvKB93CFCdLvog1WFLf1D+xmMvPGlBstkpTEZfK5+AN9hfJocyBFCNiqyS48bpgzQ==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "postcss-selector-parser": "^6.1.1" + }, + "engines": { + "node": ">=12.0" + }, + "peerDependencies": { + "postcss": "^8.2.14" + } + }, + "node_modules/postcss-selector-parser": { + "version": "6.1.2", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.1.2.tgz", + "integrity": "sha512-Q8qQfPiZ+THO/3ZrOrO0cJJKfpYCagtMUkXbnEfmgUjwXg6z/WBeOyS9APBBPCTSiDV+s4SwQGu8yFsiMRIudg==", + "license": "MIT", + "dependencies": { + "cssesc": "^3.0.0", + "util-deprecate": "^1.0.2" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/postcss-value-parser": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz", + "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==", + "license": "MIT" + }, + "node_modules/prelude-ls": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", + "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/prop-types": { + "version": "15.8.1", + "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz", + "integrity": "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==", + "dev": true, + "license": "MIT", + "dependencies": { + "loose-envify": "^1.4.0", + "object-assign": "^4.1.1", + "react-is": "^16.13.1" + } + }, + "node_modules/punycode": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", + "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/queue-microtask": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", + "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/react": { + "version": "18.3.1", + "resolved": "https://registry.npmjs.org/react/-/react-18.3.1.tgz", + "integrity": "sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ==", + "license": "MIT", + "dependencies": { + "loose-envify": "^1.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/react-dom": { + "version": "18.3.1", + "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.3.1.tgz", + "integrity": "sha512-5m4nQKp+rZRb09LNH59GM4BxTh9251/ylbKIbpe7TpGxfJ+9kv6BLkLBXIjjspbgbnIBNqlI23tRnTWT0snUIw==", + "license": "MIT", + "dependencies": { + "loose-envify": "^1.1.0", + "scheduler": "^0.23.2" + }, + "peerDependencies": { + "react": "^18.3.1" + } + }, + "node_modules/react-is": { + "version": "16.13.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", + "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/react-refresh": { + "version": "0.14.2", + "resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.14.2.tgz", + "integrity": "sha512-jCvmsr+1IUSMUyzOkRcvnVbX3ZYC6g9TDrDbFuFmRDq7PD4yaGbLKNQL6k2jnArV8hjYxh7hVhAZB6s9HDGpZA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/read-cache": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/read-cache/-/read-cache-1.0.0.tgz", + "integrity": "sha512-Owdv/Ft7IjOgm/i0xvNDZ1LrRANRfew4b2prF3OWMQLxLfu3bS8FVhCsrSCMK4lR56Y9ya+AThoTpDCTxCmpRA==", + "license": "MIT", + "dependencies": { + "pify": "^2.3.0" + } + }, + "node_modules/readdirp": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", + "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", + "license": "MIT", + "dependencies": { + "picomatch": "^2.2.1" + }, + "engines": { + "node": ">=8.10.0" + } + }, + "node_modules/reflect.getprototypeof": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/reflect.getprototypeof/-/reflect.getprototypeof-1.0.10.tgz", + "integrity": "sha512-00o4I+DVrefhv+nX0ulyi3biSHCPDe+yLv5o/p6d/UVlirijB8E16FtfwSAi4g3tcqrQ4lRAqQSoFEZJehYEcw==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.9", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.0.0", + "get-intrinsic": "^1.2.7", + "get-proto": "^1.0.1", + "which-builtin-type": "^1.2.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/regexp.prototype.flags": { + "version": "1.5.4", + "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.5.4.tgz", + "integrity": "sha512-dYqgNSZbDwkaJ2ceRd9ojCGjBq+mOm9LmtXnAnEGyHhN/5R7iDW2TRw3h+o/jCFxus3P2LfWIIiwowAjANm7IA==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "define-properties": "^1.2.1", + "es-errors": "^1.3.0", + "get-proto": "^1.0.1", + "gopd": "^1.2.0", + "set-function-name": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/resolve": { + "version": "2.0.0-next.5", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-2.0.0-next.5.tgz", + "integrity": "sha512-U7WjGVG9sH8tvjW5SmGbQuui75FiyjAX72HX15DwBBwF9dNiQZRQAg9nnPhYy+TUnE0+VcrttuvNI8oSxZcocA==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-core-module": "^2.13.0", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" + }, + "bin": { + "resolve": "bin/resolve" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/resolve-from": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", + "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/reusify": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.1.0.tgz", + "integrity": "sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==", + "license": "MIT", + "engines": { + "iojs": ">=1.0.0", + "node": ">=0.10.0" + } + }, + "node_modules/rimraf": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", + "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "deprecated": "Rimraf versions prior to v4 are no longer supported", + "dev": true, + "license": "ISC", + "dependencies": { + "glob": "^7.1.3" + }, + "bin": { + "rimraf": "bin.js" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/rollup": { + "version": "3.29.5", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-3.29.5.tgz", + "integrity": "sha512-GVsDdsbJzzy4S/v3dqWPJ7EfvZJfCHiDqe80IyrF59LYuP+e6U1LJoUqeuqRbwAWoMNoXivMNeNAOf5E22VA1w==", + "dev": true, + "license": "MIT", + "bin": { + "rollup": "dist/bin/rollup" + }, + "engines": { + "node": ">=14.18.0", + "npm": ">=8.0.0" + }, + "optionalDependencies": { + "fsevents": "~2.3.2" + } + }, + "node_modules/run-parallel": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", + "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "dependencies": { + "queue-microtask": "^1.2.2" + } + }, + "node_modules/safe-array-concat": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/safe-array-concat/-/safe-array-concat-1.1.3.tgz", + "integrity": "sha512-AURm5f0jYEOydBj7VQlVvDrjeFgthDdEF5H1dP+6mNpoXOMo1quQqJ4wvJDyRZ9+pO3kGWoOdmV08cSv2aJV6Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.2", + "get-intrinsic": "^1.2.6", + "has-symbols": "^1.1.0", + "isarray": "^2.0.5" + }, + "engines": { + "node": ">=0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/safe-push-apply": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/safe-push-apply/-/safe-push-apply-1.0.0.tgz", + "integrity": "sha512-iKE9w/Z7xCzUMIZqdBsp6pEQvwuEebH4vdpjcDWnyzaI6yl6O9FHvVpmGelvEHNsoY6wGblkxR6Zty/h00WiSA==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "isarray": "^2.0.5" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/safe-regex-test": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/safe-regex-test/-/safe-regex-test-1.1.0.tgz", + "integrity": "sha512-x/+Cz4YrimQxQccJf5mKEbIa1NzeCRNI5Ecl/ekmlYaampdNLPalVyIcCZNNH3MvmqBugV5TMYZXv0ljslUlaw==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "is-regex": "^1.2.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/scheduler": { + "version": "0.23.2", + "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.23.2.tgz", + "integrity": "sha512-UOShsPwz7NrMUqhR6t0hWjFduvOzbtv7toDH1/hIrfRNIDBnnBWd0CwJTGvTpngVlmwGCdP9/Zl/tVrDqcuYzQ==", + "license": "MIT", + "dependencies": { + "loose-envify": "^1.1.0" + } + }, + "node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/set-function-length": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz", + "integrity": "sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==", + "dev": true, + "license": "MIT", + "dependencies": { + "define-data-property": "^1.1.4", + "es-errors": "^1.3.0", + "function-bind": "^1.1.2", + "get-intrinsic": "^1.2.4", + "gopd": "^1.0.1", + "has-property-descriptors": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/set-function-name": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/set-function-name/-/set-function-name-2.0.2.tgz", + "integrity": "sha512-7PGFlmtwsEADb0WYyvCMa1t+yke6daIG4Wirafur5kcf+MhUnPms1UeR0CKQdTZD81yESwMHbtn+TR+dMviakQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "define-data-property": "^1.1.4", + "es-errors": "^1.3.0", + "functions-have-names": "^1.2.3", + "has-property-descriptors": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/set-proto": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/set-proto/-/set-proto-1.0.0.tgz", + "integrity": "sha512-RJRdvCo6IAnPdsvP/7m6bsQqNnn1FCBX5ZNtFL98MmFF/4xAIJTIg1YbHW5DC2W5SKZanrC6i4HsJqlajw/dZw==", + "dev": true, + "license": "MIT", + "dependencies": { + "dunder-proto": "^1.0.1", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "license": "MIT", + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/side-channel": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.0.tgz", + "integrity": "sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "object-inspect": "^1.13.3", + "side-channel-list": "^1.0.0", + "side-channel-map": "^1.0.1", + "side-channel-weakmap": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-list": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.0.tgz", + "integrity": "sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "object-inspect": "^1.13.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-map": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/side-channel-map/-/side-channel-map-1.0.1.tgz", + "integrity": "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-weakmap": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz", + "integrity": "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3", + "side-channel-map": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/signal-exit": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", + "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", + "license": "ISC", + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/source-map-js": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", + "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/string-width": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", + "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", + "license": "MIT", + "dependencies": { + "eastasianwidth": "^0.2.0", + "emoji-regex": "^9.2.2", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/string-width-cjs": { + "name": "string-width", + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/string-width-cjs/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "license": "MIT" + }, + "node_modules/string-width/node_modules/ansi-regex": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.1.0.tgz", + "integrity": "sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA==", + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/string-width/node_modules/strip-ansi": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", + "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", + "license": "MIT", + "dependencies": { + "ansi-regex": "^6.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, + "node_modules/string.prototype.matchall": { + "version": "4.0.12", + "resolved": "https://registry.npmjs.org/string.prototype.matchall/-/string.prototype.matchall-4.0.12.tgz", + "integrity": "sha512-6CC9uyBL+/48dYizRf7H7VAYCMCNTBeM78x/VTUe9bFEaxBepPJDa1Ow99LqI/1yF7kuy7Q3cQsYMrcjGUcskA==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.3", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.6", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.0.0", + "get-intrinsic": "^1.2.6", + "gopd": "^1.2.0", + "has-symbols": "^1.1.0", + "internal-slot": "^1.1.0", + "regexp.prototype.flags": "^1.5.3", + "set-function-name": "^2.0.2", + "side-channel": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/string.prototype.repeat": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/string.prototype.repeat/-/string.prototype.repeat-1.0.0.tgz", + "integrity": "sha512-0u/TldDbKD8bFCQ/4f5+mNRrXwZ8hg2w7ZR8wa16e8z9XpePWl3eGEcUD0OXpEH/VJH/2G3gjUtR3ZOiBe2S/w==", + "dev": true, + "license": "MIT", + "dependencies": { + "define-properties": "^1.1.3", + "es-abstract": "^1.17.5" + } + }, + "node_modules/string.prototype.trim": { + "version": "1.2.10", + "resolved": "https://registry.npmjs.org/string.prototype.trim/-/string.prototype.trim-1.2.10.tgz", + "integrity": "sha512-Rs66F0P/1kedk5lyYyH9uBzuiI/kNRmwJAR9quK6VOtIpZ2G+hMZd+HQbbv25MgCA6gEffoMZYxlTod4WcdrKA==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.2", + "define-data-property": "^1.1.4", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.5", + "es-object-atoms": "^1.0.0", + "has-property-descriptors": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/string.prototype.trimend": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.9.tgz", + "integrity": "sha512-G7Ok5C6E/j4SGfyLCloXTrngQIQU3PWtXGst3yM7Bea9FRURf1S42ZHlZZtsNque2FN2PoUhfZXYLNWwEr4dLQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.2", + "define-properties": "^1.2.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/string.prototype.trimstart": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.8.tgz", + "integrity": "sha512-UXSH262CSZY1tfu3G3Secr6uGLCFVPMhIqHjlgCUtCCcgihYc/xKs9djMTMUOb2j1mVSeU8EU6NWc/iQKU6Gfg==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-ansi-cjs": { + "name": "strip-ansi", + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-json-comments": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", + "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/sucrase": { + "version": "3.35.0", + "resolved": "https://registry.npmjs.org/sucrase/-/sucrase-3.35.0.tgz", + "integrity": "sha512-8EbVDiu9iN/nESwxeSxDKe0dunta1GOlHufmSSXxMD2z2/tMZpDMpvXQGsc+ajGo8y2uYUmixaSRUc/QPoQ0GA==", + "license": "MIT", + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.2", + "commander": "^4.0.0", + "glob": "^10.3.10", + "lines-and-columns": "^1.1.6", + "mz": "^2.7.0", + "pirates": "^4.0.1", + "ts-interface-checker": "^0.1.9" + }, + "bin": { + "sucrase": "bin/sucrase", + "sucrase-node": "bin/sucrase-node" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, + "node_modules/sucrase/node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/sucrase/node_modules/glob": { + "version": "10.4.5", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.4.5.tgz", + "integrity": "sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==", + "license": "ISC", + "dependencies": { + "foreground-child": "^3.1.0", + "jackspeak": "^3.1.2", + "minimatch": "^9.0.4", + "minipass": "^7.1.2", + "package-json-from-dist": "^1.0.0", + "path-scurry": "^1.11.1" + }, + "bin": { + "glob": "dist/esm/bin.mjs" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/sucrase/node_modules/minimatch": { + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", + "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" }, "engines": { - "node": "^10 || ^12 || >=14" + "node": ">=8" } }, - "node_modules/prelude-ls": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", - "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", - "dev": true, + "node_modules/supports-preserve-symlinks-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", + "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", "license": "MIT", "engines": { - "node": ">= 0.8.0" + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/punycode": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", - "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", - "dev": true, + "node_modules/tailwind-merge": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/tailwind-merge/-/tailwind-merge-3.2.0.tgz", + "integrity": "sha512-FQT/OVqCD+7edmmJpsgCsY820RTD5AkBryuG5IUqR5YQZSdj5xlH5nLgH7YPths7WsLPSpSBNneJdM8aS8aeFA==", "license": "MIT", - "engines": { - "node": ">=6" + "funding": { + "type": "github", + "url": "https://github.com/sponsors/dcastil" } }, - "node_modules/react": { - "version": "19.1.0", - "resolved": "https://registry.npmjs.org/react/-/react-19.1.0.tgz", - "integrity": "sha512-FS+XFBNvn3GTAWq26joslQgWNoFu08F4kl0J4CgdNKADkdSGXQyTCnKteIAJy96Br6YbpEU1LSzV5dYtjMkMDg==", + "node_modules/tailwindcss": { + "version": "3.4.17", + "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-3.4.17.tgz", + "integrity": "sha512-w33E2aCvSDP0tW9RZuNXadXlkHXqFzSkQew/aIa2i/Sj8fThxwovwlXHSPXTbAHwEIhBFXAedUhP2tueAKP8Og==", "license": "MIT", + "dependencies": { + "@alloc/quick-lru": "^5.2.0", + "arg": "^5.0.2", + "chokidar": "^3.6.0", + "didyoumean": "^1.2.2", + "dlv": "^1.1.3", + "fast-glob": "^3.3.2", + "glob-parent": "^6.0.2", + "is-glob": "^4.0.3", + "jiti": "^1.21.6", + "lilconfig": "^3.1.3", + "micromatch": "^4.0.8", + "normalize-path": "^3.0.0", + "object-hash": "^3.0.0", + "picocolors": "^1.1.1", + "postcss": "^8.4.47", + "postcss-import": "^15.1.0", + "postcss-js": "^4.0.1", + "postcss-load-config": "^4.0.2", + "postcss-nested": "^6.2.0", + "postcss-selector-parser": "^6.1.2", + "resolve": "^1.22.8", + "sucrase": "^3.35.0" + }, + "bin": { + "tailwind": "lib/cli.js", + "tailwindcss": "lib/cli.js" + }, "engines": { - "node": ">=0.10.0" + "node": ">=14.0.0" } }, - "node_modules/react-dom": { - "version": "19.1.0", - "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.1.0.tgz", - "integrity": "sha512-Xs1hdnE+DyKgeHJeJznQmYMIBG3TKIHJJT95Q58nHLSrElKlGQqDTR2HQ9fx5CN/Gk6Vh/kupBTDLU11/nDk/g==", + "node_modules/tailwindcss-animate": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/tailwindcss-animate/-/tailwindcss-animate-1.0.7.tgz", + "integrity": "sha512-bl6mpH3T7I3UFxuvDEXLxy/VuFxBk5bbzplh7tXI68mwMokNYd1t9qPBHlnyTwfa4JGC4zP516I1hYYtQ/vspA==", "license": "MIT", - "dependencies": { - "scheduler": "^0.26.0" - }, "peerDependencies": { - "react": "^19.1.0" + "tailwindcss": ">=3.0.0 || insiders" } }, - "node_modules/react-refresh": { - "version": "0.14.2", - "resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.14.2.tgz", - "integrity": "sha512-jCvmsr+1IUSMUyzOkRcvnVbX3ZYC6g9TDrDbFuFmRDq7PD4yaGbLKNQL6k2jnArV8hjYxh7hVhAZB6s9HDGpZA==", - "dev": true, + "node_modules/tailwindcss/node_modules/resolve": { + "version": "1.22.10", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.10.tgz", + "integrity": "sha512-NPRy+/ncIMeDlTAsuqwKIiferiawhefFJtkNSW0qZJEqMEb+qBt/77B/jGeeek+F0uOeN05CDa6HXbbIgtVX4w==", "license": "MIT", + "dependencies": { + "is-core-module": "^2.16.0", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" + }, + "bin": { + "resolve": "bin/resolve" + }, "engines": { - "node": ">=0.10.0" + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/resolve-from": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", - "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", + "node_modules/text-table": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", + "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==", "dev": true, + "license": "MIT" + }, + "node_modules/thenify": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/thenify/-/thenify-3.3.1.tgz", + "integrity": "sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw==", "license": "MIT", - "engines": { - "node": ">=4" + "dependencies": { + "any-promise": "^1.0.0" } }, - "node_modules/rollup": { - "version": "4.39.0", - "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.39.0.tgz", - "integrity": "sha512-thI8kNc02yNvnmJp8dr3fNWJ9tCONDhp6TV35X6HkKGGs9E6q7YWCHbe5vKiTa7TAiNcFEmXKj3X/pG2b3ci0g==", - "dev": true, + "node_modules/thenify-all": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/thenify-all/-/thenify-all-1.6.0.tgz", + "integrity": "sha512-RNxQH/qI8/t3thXJDwcstUO4zeqo64+Uy/+sNVRBx4Xn2OX+OZ9oP+iJnNFqplFra2ZUVeKCSa2oVWi3T4uVmA==", "license": "MIT", "dependencies": { - "@types/estree": "1.0.7" - }, - "bin": { - "rollup": "dist/bin/rollup" + "thenify": ">= 3.1.0 < 4" }, "engines": { - "node": ">=18.0.0", - "npm": ">=8.0.0" + "node": ">=0.8" + } + }, + "node_modules/to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "license": "MIT", + "dependencies": { + "is-number": "^7.0.0" }, - "optionalDependencies": { - "@rollup/rollup-android-arm-eabi": "4.39.0", - "@rollup/rollup-android-arm64": "4.39.0", - "@rollup/rollup-darwin-arm64": "4.39.0", - "@rollup/rollup-darwin-x64": "4.39.0", - "@rollup/rollup-freebsd-arm64": "4.39.0", - "@rollup/rollup-freebsd-x64": "4.39.0", - "@rollup/rollup-linux-arm-gnueabihf": "4.39.0", - "@rollup/rollup-linux-arm-musleabihf": "4.39.0", - "@rollup/rollup-linux-arm64-gnu": "4.39.0", - "@rollup/rollup-linux-arm64-musl": "4.39.0", - "@rollup/rollup-linux-loongarch64-gnu": "4.39.0", - "@rollup/rollup-linux-powerpc64le-gnu": "4.39.0", - "@rollup/rollup-linux-riscv64-gnu": "4.39.0", - "@rollup/rollup-linux-riscv64-musl": "4.39.0", - "@rollup/rollup-linux-s390x-gnu": "4.39.0", - "@rollup/rollup-linux-x64-gnu": "4.39.0", - "@rollup/rollup-linux-x64-musl": "4.39.0", - "@rollup/rollup-win32-arm64-msvc": "4.39.0", - "@rollup/rollup-win32-ia32-msvc": "4.39.0", - "@rollup/rollup-win32-x64-msvc": "4.39.0", - "fsevents": "~2.3.2" + "engines": { + "node": ">=8.0" } }, - "node_modules/scheduler": { - "version": "0.26.0", - "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.26.0.tgz", - "integrity": "sha512-NlHwttCI/l5gCPR3D1nNXtWABUmBwvZpEQiD4IXSbIDq8BzLIK/7Ir5gTFSGZDUu37K5cMNp0hFtzO38sC7gWA==", - "license": "MIT" + "node_modules/ts-interface-checker": { + "version": "0.1.13", + "resolved": "https://registry.npmjs.org/ts-interface-checker/-/ts-interface-checker-0.1.13.tgz", + "integrity": "sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA==", + "license": "Apache-2.0" }, - "node_modules/semver": { - "version": "6.3.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", - "dev": true, - "license": "ISC", - "bin": { - "semver": "bin/semver.js" - } + "node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "license": "0BSD" }, - "node_modules/shebang-command": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", - "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "node_modules/type-check": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", + "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", "dev": true, "license": "MIT", "dependencies": { - "shebang-regex": "^3.0.0" + "prelude-ls": "^1.2.1" }, "engines": { - "node": ">=8" + "node": ">= 0.8.0" } }, - "node_modules/shebang-regex": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", - "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "node_modules/type-fest": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", + "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", + "dev": true, + "license": "(MIT OR CC0-1.0)", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/typed-array-buffer": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/typed-array-buffer/-/typed-array-buffer-1.0.3.tgz", + "integrity": "sha512-nAYYwfY3qnzX30IkA6AQZjVbtK6duGontcQm1WSG1MD94YLqK0515GNApXkoxKOWMusVssAHWLh9SeaoefYFGw==", "dev": true, "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "es-errors": "^1.3.0", + "is-typed-array": "^1.1.14" + }, "engines": { - "node": ">=8" + "node": ">= 0.4" } }, - "node_modules/source-map-js": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", - "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", + "node_modules/typed-array-byte-length": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/typed-array-byte-length/-/typed-array-byte-length-1.0.3.tgz", + "integrity": "sha512-BaXgOuIxz8n8pIq3e7Atg/7s+DpiYrxn4vdot3w9KbnBhcRQq6o3xemQdIfynqSeXeDrF32x+WvfzmOjPiY9lg==", "dev": true, - "license": "BSD-3-Clause", + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "for-each": "^0.3.3", + "gopd": "^1.2.0", + "has-proto": "^1.2.0", + "is-typed-array": "^1.1.14" + }, "engines": { - "node": ">=0.10.0" + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/strip-json-comments": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", - "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", + "node_modules/typed-array-byte-offset": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/typed-array-byte-offset/-/typed-array-byte-offset-1.0.4.tgz", + "integrity": "sha512-bTlAFB/FBYMcuX81gbL4OcpH5PmlFHqlCCpAl8AlEzMz5k53oNDvN8p1PNOWLEmI2x4orp3raOFB51tv9X+MFQ==", "dev": true, "license": "MIT", + "dependencies": { + "available-typed-arrays": "^1.0.7", + "call-bind": "^1.0.8", + "for-each": "^0.3.3", + "gopd": "^1.2.0", + "has-proto": "^1.2.0", + "is-typed-array": "^1.1.15", + "reflect.getprototypeof": "^1.0.9" + }, "engines": { - "node": ">=8" + "node": ">= 0.4" }, "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "node_modules/typed-array-length": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/typed-array-length/-/typed-array-length-1.0.7.tgz", + "integrity": "sha512-3KS2b+kL7fsuk/eJZ7EQdnEmQoaho/r6KUef7hxvltNA5DR8NAUM+8wJMbJyZ4G9/7i3v5zPBIMN5aybAh2/Jg==", "dev": true, "license": "MIT", "dependencies": { - "has-flag": "^4.0.0" + "call-bind": "^1.0.7", + "for-each": "^0.3.3", + "gopd": "^1.0.1", + "is-typed-array": "^1.1.13", + "possible-typed-array-names": "^1.0.0", + "reflect.getprototypeof": "^1.0.6" }, "engines": { - "node": ">=8" + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/type-check": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", - "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", + "node_modules/unbox-primitive": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.1.0.tgz", + "integrity": "sha512-nWJ91DjeOkej/TA8pXQ3myruKpKEYgqvpw9lz4OPHj/NWFNluYrjbz9j01CJ8yKQd2g4jFoOkINCTW2I5LEEyw==", "dev": true, "license": "MIT", "dependencies": { - "prelude-ls": "^1.2.1" + "call-bound": "^1.0.3", + "has-bigints": "^1.0.2", + "has-symbols": "^1.1.0", + "which-boxed-primitive": "^1.1.1" }, "engines": { - "node": ">= 0.8.0" + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, "node_modules/update-browserslist-db": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.3.tgz", "integrity": "sha512-UxhIZQ+QInVdunkDAaiazvvT/+fXL5Osr0JZlJulepYu6Jd7qJtDZjlur0emRlT71EN3ScPoE7gvsuIKKNavKw==", - "dev": true, "funding": [ { "type": "opencollective", @@ -2624,49 +5445,48 @@ "punycode": "^2.1.0" } }, + "node_modules/util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", + "license": "MIT" + }, "node_modules/vite": { - "version": "6.2.5", - "resolved": "https://registry.npmjs.org/vite/-/vite-6.2.5.tgz", - "integrity": "sha512-j023J/hCAa4pRIUH6J9HemwYfjB5llR2Ps0CWeikOtdR8+pAURAk0DoJC5/mm9kd+UgdnIy7d6HE4EAvlYhPhA==", + "version": "4.5.12", + "resolved": "https://registry.npmjs.org/vite/-/vite-4.5.12.tgz", + "integrity": "sha512-qrMwavANtSz91nDy3zEiUHMtL09x0mniQsSMvDkNxuCBM1W5vriJ22hEmwTth6DhLSWsZnHBT0yHFAQXt6efGA==", "dev": true, "license": "MIT", "dependencies": { - "esbuild": "^0.25.0", - "postcss": "^8.5.3", - "rollup": "^4.30.1" + "esbuild": "^0.18.10", + "postcss": "^8.4.27", + "rollup": "^3.27.1" }, "bin": { "vite": "bin/vite.js" }, "engines": { - "node": "^18.0.0 || ^20.0.0 || >=22.0.0" + "node": "^14.18.0 || >=16.0.0" }, "funding": { "url": "https://github.com/vitejs/vite?sponsor=1" }, "optionalDependencies": { - "fsevents": "~2.3.3" + "fsevents": "~2.3.2" }, "peerDependencies": { - "@types/node": "^18.0.0 || ^20.0.0 || >=22.0.0", - "jiti": ">=1.21.0", + "@types/node": ">= 14", "less": "*", "lightningcss": "^1.21.0", "sass": "*", - "sass-embedded": "*", "stylus": "*", "sugarss": "*", - "terser": "^5.16.0", - "tsx": "^4.8.1", - "yaml": "^2.4.2" + "terser": "^5.4.0" }, "peerDependenciesMeta": { "@types/node": { "optional": true }, - "jiti": { - "optional": true - }, "less": { "optional": true }, @@ -2676,9 +5496,6 @@ "sass": { "optional": true }, - "sass-embedded": { - "optional": true - }, "stylus": { "optional": true }, @@ -2687,12 +5504,6 @@ }, "terser": { "optional": true - }, - "tsx": { - "optional": true - }, - "yaml": { - "optional": true } } }, @@ -2700,7 +5511,6 @@ "version": "2.0.2", "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", - "dev": true, "license": "ISC", "dependencies": { "isexe": "^2.0.0" @@ -2712,6 +5522,95 @@ "node": ">= 8" } }, + "node_modules/which-boxed-primitive": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/which-boxed-primitive/-/which-boxed-primitive-1.1.1.tgz", + "integrity": "sha512-TbX3mj8n0odCBFVlY8AxkqcHASw3L60jIuF8jFP78az3C2YhmGvqbHBpAjTRH2/xqYunrJ9g1jSyjCjpoWzIAA==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-bigint": "^1.1.0", + "is-boolean-object": "^1.2.1", + "is-number-object": "^1.1.1", + "is-string": "^1.1.1", + "is-symbol": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/which-builtin-type": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/which-builtin-type/-/which-builtin-type-1.2.1.tgz", + "integrity": "sha512-6iBczoX+kDQ7a3+YJBnh3T+KZRxM/iYNPXicqk66/Qfm1b93iu+yOImkg0zHbj5LNOcNv1TEADiZ0xa34B4q6Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "function.prototype.name": "^1.1.6", + "has-tostringtag": "^1.0.2", + "is-async-function": "^2.0.0", + "is-date-object": "^1.1.0", + "is-finalizationregistry": "^1.1.0", + "is-generator-function": "^1.0.10", + "is-regex": "^1.2.1", + "is-weakref": "^1.0.2", + "isarray": "^2.0.5", + "which-boxed-primitive": "^1.1.0", + "which-collection": "^1.0.2", + "which-typed-array": "^1.1.16" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/which-collection": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/which-collection/-/which-collection-1.0.2.tgz", + "integrity": "sha512-K4jVyjnBdgvc86Y6BkaLZEN933SwYOuBFkdmBu9ZfkcAbdVbpITnDmjvZ/aQjRXQrv5EPkTnD1s39GiiqbngCw==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-map": "^2.0.3", + "is-set": "^2.0.3", + "is-weakmap": "^2.0.2", + "is-weakset": "^2.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/which-typed-array": { + "version": "1.1.19", + "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.19.tgz", + "integrity": "sha512-rEvr90Bck4WZt9HHFC4DJMsjvu7x+r6bImz0/BrbWb7A2djJ8hnZMrWnHo9F8ssv0OMErasDhftrfROTyqSDrw==", + "dev": true, + "license": "MIT", + "dependencies": { + "available-typed-arrays": "^1.0.7", + "call-bind": "^1.0.8", + "call-bound": "^1.0.4", + "for-each": "^0.3.5", + "get-proto": "^1.0.1", + "gopd": "^1.2.0", + "has-tostringtag": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/word-wrap": { "version": "1.2.5", "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz", @@ -2722,6 +5621,107 @@ "node": ">=0.10.0" } }, + "node_modules/wrap-ansi": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz", + "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==", + "license": "MIT", + "dependencies": { + "ansi-styles": "^6.1.0", + "string-width": "^5.0.1", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/wrap-ansi-cjs": { + "name": "wrap-ansi", + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/wrap-ansi-cjs/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "license": "MIT" + }, + "node_modules/wrap-ansi-cjs/node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/wrap-ansi/node_modules/ansi-regex": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.1.0.tgz", + "integrity": "sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA==", + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/wrap-ansi/node_modules/ansi-styles": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz", + "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==", + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/wrap-ansi/node_modules/strip-ansi": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", + "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", + "license": "MIT", + "dependencies": { + "ansi-regex": "^6.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, + "node_modules/wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", + "dev": true, + "license": "ISC" + }, "node_modules/yallist": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", @@ -2729,6 +5729,18 @@ "dev": true, "license": "ISC" }, + "node_modules/yaml": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.7.1.tgz", + "integrity": "sha512-10ULxpnOCQXxJvBgxsn9ptjq6uviG/htZKk9veJGhlqn3w/DxQ631zFF+nlQXLwmImeS5amR2dl2U8sg6U9jsQ==", + "license": "ISC", + "bin": { + "yaml": "bin.mjs" + }, + "engines": { + "node": ">= 14" + } + }, "node_modules/yocto-queue": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", diff --git a/frontend/package.json b/frontend/package.json index 902fd98..b286aca 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -10,18 +10,29 @@ "preview": "vite preview" }, "dependencies": { - "react": "^19.0.0", - "react-dom": "^19.0.0" + "@radix-ui/react-checkbox": "^1.1.4", + "@radix-ui/react-label": "^2.1.2", + "@radix-ui/react-slot": "^1.1.2", + "@tailwindcss/forms": "^0.5.7", + "autoprefixer": "^10.4.16", + "class-variance-authority": "^0.7.1", + "framer-motion": "^10.16.4", + "lucide-react": "^0.284.0", + "postcss": "^8.4.31", + "react": "^18.2.0", + "react-dom": "^18.2.0", + "tailwind-merge": "^3.2.0", + "tailwindcss": "^3.3.3", + "tailwindcss-animate": "^1.0.7" }, "devDependencies": { - "@eslint/js": "^9.21.0", - "@types/react": "^19.0.10", - "@types/react-dom": "^19.0.4", - "@vitejs/plugin-react": "^4.3.4", - "eslint": "^9.21.0", - "eslint-plugin-react-hooks": "^5.1.0", - "eslint-plugin-react-refresh": "^0.4.19", - "globals": "^15.15.0", - "vite": "^6.2.0" + "@types/react": "^18.2.15", + "@types/react-dom": "^18.2.7", + "@vitejs/plugin-react": "^4.0.3", + "eslint": "^8.45.0", + "eslint-plugin-react": "^7.32.2", + "eslint-plugin-react-hooks": "^4.6.0", + "eslint-plugin-react-refresh": "^0.4.3", + "vite": "^4.4.5" } } diff --git a/frontend/postcss.config.js b/frontend/postcss.config.js new file mode 100644 index 0000000..387612e --- /dev/null +++ b/frontend/postcss.config.js @@ -0,0 +1,6 @@ +export default { + plugins: { + tailwindcss: {}, + autoprefixer: {}, + }, +} \ No newline at end of file diff --git a/frontend/src/App.css b/frontend/src/App.css index b9d355d..8b13789 100644 --- a/frontend/src/App.css +++ b/frontend/src/App.css @@ -1,42 +1 @@ -#root { - max-width: 1280px; - margin: 0 auto; - padding: 2rem; - text-align: center; -} -.logo { - height: 6em; - padding: 1.5em; - will-change: filter; - transition: filter 300ms; -} -.logo:hover { - filter: drop-shadow(0 0 2em #646cffaa); -} -.logo.react:hover { - filter: drop-shadow(0 0 2em #61dafbaa); -} - -@keyframes logo-spin { - from { - transform: rotate(0deg); - } - to { - transform: rotate(360deg); - } -} - -@media (prefers-reduced-motion: no-preference) { - a:nth-of-type(2) .logo { - animation: logo-spin infinite 20s linear; - } -} - -.card { - padding: 2em; -} - -.read-the-docs { - color: #888; -} diff --git a/frontend/src/App.jsx b/frontend/src/App.jsx index f67355a..64792c5 100644 --- a/frontend/src/App.jsx +++ b/frontend/src/App.jsx @@ -1,33 +1,12 @@ -import { useState } from 'react' -import reactLogo from './assets/react.svg' -import viteLogo from '/vite.svg' -import './App.css' +import Register from './components/auth/Register' +import { AuthLayout } from './components/layouts/auth-layout' function App() { - const [count, setCount] = useState(0) - return ( <> - -

Vite + React

-
- -

- Edit src/App.jsx and save to test HMR -

-
-

- Click on the Vite and React logos to learn more -

+ + + ) } diff --git a/frontend/src/components/auth/Register.jsx b/frontend/src/components/auth/Register.jsx new file mode 100644 index 0000000..79bf684 --- /dev/null +++ b/frontend/src/components/auth/Register.jsx @@ -0,0 +1,470 @@ +"use client" + +import React, { useState } from "react" +import { Button } from "../ui/Button" +import { Input } from "../ui/input" +import { Label } from "../ui/label" +import { Checkbox } from "../ui/checkbox" +import { Card, CardContent, CardFooter } from "../ui/card" +import { useToast } from "../../hooks/use-toast" +import { Eye, EyeOff, Loader2, AlertCircle, User, UserPlus } from "lucide-react" +import { Alert, AlertDescription, AlertTitle } from "../ui/alert" + +export function Register() { + const [role, setRole] = useState('patient') + const [isLoading, setIsLoading] = useState(false) + const [showPassword, setShowPassword] = useState(false) + const [showConfirmPassword, setShowConfirmPassword] = useState(false) + const [fullName, setFullName] = useState("") + const [email, setEmail] = useState("") + const [password, setPassword] = useState("") + const [confirmPassword, setConfirmPassword] = useState("") + const [phoneNumber, setPhoneNumber] = useState("") + const [dateOfBirth, setDateOfBirth] = useState("") + const [gender, setGender] = useState("") + const [licenseNumber, setLicenseNumber] = useState("") + const [specialization, setSpecialization] = useState("") + const [hospitalName, setHospitalName] = useState("") + const [location, setLocation] = useState("") + const [agreeTerms, setAgreeTerms] = useState(false) + const [passwordStrength, setPasswordStrength] = useState(0) + const [errors, setErrors] = useState({}) + const { toast } = useToast() + + const checkPasswordStrength = (password) => { + let strength = 0 + if (password.length >= 8) strength += 1 + if (/[A-Z]/.test(password)) strength += 1 + if (/[0-9]/.test(password)) strength += 1 + if (/[^A-Za-z0-9]/.test(password)) strength += 1 + setPasswordStrength(strength) + } + + const handlePasswordChange = (e) => { + const newPassword = e.target.value + setPassword(newPassword) + checkPasswordStrength(newPassword) + } + + const getPasswordStrengthText = () => { + switch (passwordStrength) { + case 0: + return "Very weak" + case 1: + return "Weak" + case 2: + return "Medium" + case 3: + return "Strong" + case 4: + return "Very strong" + default: + return "" + } + } + + const getPasswordStrengthColor = () => { + switch (passwordStrength) { + case 0: + return "bg-red-500" + case 1: + return "bg-orange-500" + case 2: + return "bg-yellow-500" + case 3: + return "bg-green-500" + case 4: + return "bg-emerald-500" + default: + return "bg-gray-200" + } + } + + const validateForm = () => { + const newErrors = {} + + if (!fullName) newErrors.fullName = "Full name is required" + if (!email) newErrors.email = "Email is required" + else if (!/\S+@\S+\.\S+/.test(email)) newErrors.email = "Invalid email format" + + if (!password) newErrors.password = "Password is required" + else if (password.length < 8) newErrors.password = "Password must be at least 8 characters" + + if (!confirmPassword) newErrors.confirmPassword = "Please confirm your password" + else if (password !== confirmPassword) newErrors.confirmPassword = "Passwords do not match" + + if (!phoneNumber) newErrors.phoneNumber = "Phone number is required" + + if (role === 'patient') { + if (!dateOfBirth) newErrors.dateOfBirth = "Date of birth is required" + if (!gender) newErrors.gender = "Please select your gender" + } + + if (role === 'doctor') { + if (!licenseNumber) newErrors.licenseNumber = "License number is required" + if (!specialization) newErrors.specialization = "Specialization is required" + if (!location) newErrors.location = "Location is required" + } + + if (!agreeTerms) newErrors.terms = "You must agree to the terms and privacy policy" + + setErrors(newErrors) + return Object.keys(newErrors).length === 0 + } + + const handleSubmit = async (e) => { + e.preventDefault() + + if (!validateForm()) return + + setIsLoading(true) + try { + await new Promise((resolve) => setTimeout(resolve, 1500)) + + console.log('Registration successful', { + role, + fullName, + email, + password, + phoneNumber, + ...(role === 'patient' && { + dateOfBirth, + gender + }), + ...(role === 'doctor' && { + licenseNumber, + specialization, + hospitalName, + location + }) + }) + + toast({ + title: "Registration successful", + description: "Your account has been created", + }) + + // Here we would navigate to onboarding + // router.push("/onboarding") + } catch (error) { + toast({ + title: "Registration failed", + description: "Please try again later", + variant: "destructive", + }) + } finally { + setIsLoading(false) + } + } + + const specializations = [ + "Cardiology", + "Dermatology", + "Endocrinology", + "Gastroenterology", + "Neurology", + "Obstetrics and Gynecology", + "Oncology", + "Ophthalmology", + "Orthopedics", + "Pediatrics", + "Psychiatry", + "Pulmonology", + "Radiology", + "Urology" + ] + + return ( +
+
+
+ + +
+ + +
+ +
+ + setFullName(e.target.value)} + className={`h-9 ${errors.fullName ? 'border-red-500' : ''}`} + disabled={isLoading} + /> + {errors.fullName &&

{errors.fullName}

} +
+ +
+ + setEmail(e.target.value)} + className={`h-9 ${errors.email ? 'border-red-500' : ''}`} + disabled={isLoading} + /> + {errors.email &&

{errors.email}

} +
+ +
+
+ +
+ + +
+ {errors.password &&

{errors.password}

} +
+ +
+ +
+ setConfirmPassword(e.target.value)} + className={`h-9 pr-9 ${errors.confirmPassword ? 'border-red-500' : ''}`} + disabled={isLoading} + /> + +
+ {errors.confirmPassword &&

{errors.confirmPassword}

} +
+
+ + {password && ( +
+
+ Password strength: + + {getPasswordStrengthText()} + +
+
+
+
+
+ )} + +
+ + setPhoneNumber(e.target.value)} + className={`h-9 ${errors.phoneNumber ? 'border-red-500' : ''}`} + disabled={isLoading} + /> + {errors.phoneNumber &&

{errors.phoneNumber}

} +
+ + {role === 'patient' && ( + <> +
+
+ + setDateOfBirth(e.target.value)} + className={`h-9 ${errors.dateOfBirth ? 'border-red-500' : ''}`} + disabled={isLoading} + /> + {errors.dateOfBirth &&

{errors.dateOfBirth}

} +
+ +
+ + + {errors.gender &&

{errors.gender}

} +
+
+ + )} + + {role === 'doctor' && ( + <> +
+ + setLicenseNumber(e.target.value)} + className={`h-9 ${errors.licenseNumber ? 'border-red-500' : ''}`} + disabled={isLoading} + /> + {errors.licenseNumber &&

{errors.licenseNumber}

} +
+ +
+ + + {errors.specialization &&

{errors.specialization}

} +
+ +
+ + setHospitalName(e.target.value)} + className="h-9" + disabled={isLoading} + /> +
+ +
+ + setLocation(e.target.value)} + className={`h-9 ${errors.location ? 'border-red-500' : ''}`} + disabled={isLoading} + /> + {errors.location &&

{errors.location}

} +
+ + )} + +
+ setAgreeTerms(!!checked)} + disabled={isLoading} + /> + +
+ {errors.terms &&

{errors.terms}

} +
+ + + + +
+ Already have an account?{" "} + + Sign in + +
+
+
+
+
+
+ ) +} + +export default Register diff --git a/frontend/src/components/layouts/auth-layout.jsx b/frontend/src/components/layouts/auth-layout.jsx new file mode 100644 index 0000000..d18d729 --- /dev/null +++ b/frontend/src/components/layouts/auth-layout.jsx @@ -0,0 +1,81 @@ +import React from "react"; +import { Shield } from "lucide-react"; + +export function AuthLayout({ children, title, subtitle }) { + return ( +
+
+
+
+ +

HealthChain

+
+
+
+

+ Secure, Decentralized Health Records +

+

+ Take control of your health data with blockchain-powered security and seamless sharing. +

+
+ +
+
+
+ +
+
+

Patient-Controlled Access

+

+ You decide who can access your health records and for how long. +

+
+
+ +
+
+ +
+
+

Blockchain Security

+

+ Your data is encrypted and secured with blockchain technology. +

+
+
+ +
+
+ +
+
+

Emergency Access

+

+ Provide critical information to emergency responders when needed. +

+
+
+
+
+
+
+ +
+
+ +

HealthChain

+
+ +
+
+

{title}

+

{subtitle}

+
+ + {children} +
+
+
+ ); +} diff --git a/frontend/src/components/ui/alert.jsx b/frontend/src/components/ui/alert.jsx new file mode 100644 index 0000000..0d68f00 --- /dev/null +++ b/frontend/src/components/ui/alert.jsx @@ -0,0 +1,62 @@ +import React from "react" +import { cva } from "class-variance-authority" + +import { cn } from "../../lib/util" + +const alertVariants = cva( + "relative w-full rounded-lg border p-4 [&>svg~*]:pl-7 [&>svg+div]:translate-y-[-3px] [&>svg]:absolute [&>svg]:left-4 [&>svg]:top-4 [&>svg]:text-foreground", + { + variants: { + variant: { + default: "bg-background text-foreground", + destructive: + "border-destructive/50 text-destructive dark:border-destructive [&>svg]:text-destructive", + }, + }, + defaultVariants: { + variant: "default", + }, + } +) + +const Alert = React.forwardRef((props, ref) => { + const { className, variant, ...rest } = props + + return ( +
+ ) +}) +Alert.displayName = "Alert" + +const AlertTitle = React.forwardRef((props, ref) => { + const { className, ...rest } = props + + return ( +
+ ) +}) +AlertTitle.displayName = "AlertTitle" + +const AlertDescription = React.forwardRef((props, ref) => { + const { className, ...rest } = props + + return ( +
+ ) +}) +AlertDescription.displayName = "AlertDescription" + +export { Alert, AlertTitle, AlertDescription } diff --git a/frontend/src/components/ui/button.jsx b/frontend/src/components/ui/button.jsx new file mode 100644 index 0000000..dca9d86 --- /dev/null +++ b/frontend/src/components/ui/button.jsx @@ -0,0 +1,55 @@ +import React from "react" +import { Slot } from "@radix-ui/react-slot" +import { cva } from "class-variance-authority" + +import { cn } from "../../lib/util" + +const buttonVariants = cva( + "inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium ring-offset-background transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0", + { + variants: { + variant: { + default: "bg-primary text-primary-foreground hover:bg-primary/90", + destructive: "bg-destructive text-destructive-foreground hover:bg-destructive/90", + outline: "border border-input bg-background hover:bg-accent hover:text-accent-foreground", + secondary: "bg-secondary text-secondary-foreground hover:bg-secondary/80", + ghost: "hover:bg-accent hover:text-accent-foreground", + link: "text-primary underline-offset-4 hover:underline", + }, + size: { + default: "h-10 px-4 py-2", + sm: "h-9 rounded-md px-3", + lg: "h-11 rounded-md px-8", + icon: "h-10 w-10", + }, + }, + defaultVariants: { + variant: "default", + size: "default", + }, + } +) + +const Button = React.forwardRef((props, ref) => { + const { + className, + variant, + size, + asChild = false, + ...rest + } = props + + const Comp = asChild ? Slot : "button" + + return ( + + ) +}) + +Button.displayName = "Button" + +export { Button, buttonVariants } diff --git a/frontend/src/components/ui/card.jsx b/frontend/src/components/ui/card.jsx new file mode 100644 index 0000000..95eda46 --- /dev/null +++ b/frontend/src/components/ui/card.jsx @@ -0,0 +1,65 @@ +import * as React from "react" +import { cn } from "../../lib/util" + +const Card = React.forwardRef(({ className, ...props }, ref) => ( +
+)) +Card.displayName = "Card" + +const CardHeader = React.forwardRef(({ className, ...props }, ref) => ( +
+)) +CardHeader.displayName = "CardHeader" + +const CardTitle = React.forwardRef(({ className, ...props }, ref) => ( +
+)) +CardTitle.displayName = "CardTitle" + +const CardDescription = React.forwardRef(({ className, ...props }, ref) => ( +
+)) +CardDescription.displayName = "CardDescription" + +const CardContent = React.forwardRef(({ className, ...props }, ref) => ( +
+)) +CardContent.displayName = "CardContent" + +const CardFooter = React.forwardRef(({ className, ...props }, ref) => ( +
+)) +CardFooter.displayName = "CardFooter" + +export { + Card, + CardHeader, + CardFooter, + CardTitle, + CardDescription, + CardContent +} diff --git a/frontend/src/components/ui/checkbox.jsx b/frontend/src/components/ui/checkbox.jsx new file mode 100644 index 0000000..ff319c0 --- /dev/null +++ b/frontend/src/components/ui/checkbox.jsx @@ -0,0 +1,32 @@ +"use client" + +import React from "react" +import * as CheckboxPrimitive from "@radix-ui/react-checkbox" +import { Check } from "lucide-react" + +import { cn } from "../../lib/util" + +const Checkbox = React.forwardRef((props, ref) => { + const { className, ...rest } = props + + return ( + + + + + + ) +}) + +Checkbox.displayName = "Checkbox" + +export { Checkbox } diff --git a/frontend/src/components/ui/input.jsx b/frontend/src/components/ui/input.jsx new file mode 100644 index 0000000..9247eac --- /dev/null +++ b/frontend/src/components/ui/input.jsx @@ -0,0 +1,22 @@ +import React from "react" + +import { cn } from "../../lib/util" + +const Input = React.forwardRef((props, ref) => { + const { className, type, ...rest } = props + + return ( + + ) +}) +Input.displayName = "Input" + +export { Input } diff --git a/frontend/src/components/ui/label.jsx b/frontend/src/components/ui/label.jsx new file mode 100644 index 0000000..64d3dda --- /dev/null +++ b/frontend/src/components/ui/label.jsx @@ -0,0 +1,27 @@ +"use client" + +import React from "react" +import * as LabelPrimitive from "@radix-ui/react-label" +import { cva } from "class-variance-authority" + +import { cn } from "../../lib/util" + +const labelVariants = cva( + "text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70" +) + +const Label = React.forwardRef((props, ref) => { + const { className, ...rest } = props + + return ( + + ) +}) + +Label.displayName = LabelPrimitive.Root.displayName + +export { Label } diff --git a/frontend/src/hooks/use-mobile.jsx b/frontend/src/hooks/use-mobile.jsx new file mode 100644 index 0000000..a95369e --- /dev/null +++ b/frontend/src/hooks/use-mobile.jsx @@ -0,0 +1,23 @@ +"use client" + +import { useState, useEffect } from "react" + +export function useMobile() { + const [isMobile, setIsMobile] = useState(false) + + useEffect(() => { + const checkIfMobile = () => { + setIsMobile(window.innerWidth < 768) + } + + checkIfMobile() // Initial check + + window.addEventListener("resize", checkIfMobile) + + return () => { + window.removeEventListener("resize", checkIfMobile) + } + }, []) + + return isMobile +} diff --git a/frontend/src/hooks/use-toast.js b/frontend/src/hooks/use-toast.js new file mode 100644 index 0000000..9f9d180 --- /dev/null +++ b/frontend/src/hooks/use-toast.js @@ -0,0 +1,154 @@ +"use client" + +// Inspired by react-hot-toast library +import * as React from "react" + +// Assume these are defined elsewhere +// import { ToastActionElement, ToastProps } from "@/components/ui/toast" + +const TOAST_LIMIT = 1 +const TOAST_REMOVE_DELAY = 1000000 + +let count = 0 + +function genId() { + count = (count + 1) % Number.MAX_SAFE_INTEGER + return count.toString() +} + +const actionTypes = { + ADD_TOAST: "ADD_TOAST", + UPDATE_TOAST: "UPDATE_TOAST", + DISMISS_TOAST: "DISMISS_TOAST", + REMOVE_TOAST: "REMOVE_TOAST", +} + +const toastTimeouts = new Map() + +const addToRemoveQueue = (toastId) => { + if (toastTimeouts.has(toastId)) return + + const timeout = setTimeout(() => { + toastTimeouts.delete(toastId) + dispatch({ + type: "REMOVE_TOAST", + toastId: toastId, + }) + }, TOAST_REMOVE_DELAY) + + toastTimeouts.set(toastId, timeout) +} + +export const reducer = (state, action) => { + switch (action.type) { + case "ADD_TOAST": + return { + ...state, + toasts: [action.toast, ...state.toasts].slice(0, TOAST_LIMIT), + } + + case "UPDATE_TOAST": + return { + ...state, + toasts: state.toasts.map((t) => + t.id === action.toast.id ? { ...t, ...action.toast } : t + ), + } + + case "DISMISS_TOAST": { + const { toastId } = action + if (toastId) { + addToRemoveQueue(toastId) + } else { + state.toasts.forEach((toast) => { + addToRemoveQueue(toast.id) + }) + } + + return { + ...state, + toasts: state.toasts.map((t) => + t.id === toastId || toastId === undefined + ? { ...t, open: false } + : t + ), + } + } + + case "REMOVE_TOAST": + if (action.toastId === undefined) { + return { + ...state, + toasts: [], + } + } + return { + ...state, + toasts: state.toasts.filter((t) => t.id !== action.toastId), + } + + default: + return state + } +} + +const listeners = [] +let memoryState = { toasts: [] } + +function dispatch(action) { + memoryState = reducer(memoryState, action) + listeners.forEach((listener) => listener(memoryState)) +} + +// Create a toast +function toast({ ...props }) { + const id = genId() + + const update = (newProps) => + dispatch({ + type: "UPDATE_TOAST", + toast: { ...newProps, id }, + }) + + const dismiss = () => dispatch({ type: "DISMISS_TOAST", toastId: id }) + + dispatch({ + type: "ADD_TOAST", + toast: { + ...props, + id, + open: true, + onOpenChange: (open) => { + if (!open) dismiss() + }, + }, + }) + + return { + id, + dismiss, + update, + } +} + +function useToast() { + const [state, setState] = React.useState(memoryState) + + React.useEffect(() => { + listeners.push(setState) + return () => { + const index = listeners.indexOf(setState) + if (index > -1) { + listeners.splice(index, 1) + } + } + }, [state]) + + return { + ...state, + toast, + dismiss: (toastId) => dispatch({ type: "DISMISS_TOAST", toastId }), + } +} + +export { useToast, toast } diff --git a/frontend/src/index.css b/frontend/src/index.css index 08a3ac9..ac68442 100644 --- a/frontend/src/index.css +++ b/frontend/src/index.css @@ -1,68 +1,94 @@ -:root { - font-family: system-ui, Avenir, Helvetica, Arial, sans-serif; - line-height: 1.5; - font-weight: 400; - - color-scheme: light dark; - color: rgba(255, 255, 255, 0.87); - background-color: #242424; - - font-synthesis: none; - text-rendering: optimizeLegibility; - -webkit-font-smoothing: antialiased; - -moz-osx-font-smoothing: grayscale; -} - -a { - font-weight: 500; - color: #646cff; - text-decoration: inherit; -} -a:hover { - color: #535bf2; -} +@tailwind base; +@tailwind components; +@tailwind utilities; body { - margin: 0; - display: flex; - place-items: center; - min-width: 320px; - min-height: 100vh; -} - -h1 { - font-size: 3.2em; - line-height: 1.1; + font-family: Arial, Helvetica, sans-serif; } -button { - border-radius: 8px; - border: 1px solid transparent; - padding: 0.6em 1.2em; - font-size: 1em; - font-weight: 500; - font-family: inherit; - background-color: #1a1a1a; - cursor: pointer; - transition: border-color 0.25s; -} -button:hover { - border-color: #646cff; -} -button:focus, -button:focus-visible { - outline: 4px auto -webkit-focus-ring-color; +@layer utilities { + .text-balance { + text-wrap: balance; + } } -@media (prefers-color-scheme: light) { +@layer base { :root { - color: #213547; - background-color: #ffffff; + --background: 0 0% 100%; + --foreground: 0 0% 3.9%; + --card: 0 0% 100%; + --card-foreground: 0 0% 3.9%; + --popover: 0 0% 100%; + --popover-foreground: 0 0% 3.9%; + --primary: 0 0% 9%; + --primary-foreground: 0 0% 98%; + --secondary: 0 0% 96.1%; + --secondary-foreground: 0 0% 9%; + --muted: 0 0% 96.1%; + --muted-foreground: 0 0% 45.1%; + --accent: 0 0% 96.1%; + --accent-foreground: 0 0% 9%; + --destructive: 0 84.2% 60.2%; + --destructive-foreground: 0 0% 98%; + --border: 0 0% 89.8%; + --input: 0 0% 89.8%; + --ring: 0 0% 3.9%; + --chart-1: 12 76% 61%; + --chart-2: 173 58% 39%; + --chart-3: 197 37% 24%; + --chart-4: 43 74% 66%; + --chart-5: 27 87% 67%; + --radius: 0.5rem; + --sidebar-background: 0 0% 98%; + --sidebar-foreground: 240 5.3% 26.1%; + --sidebar-primary: 240 5.9% 10%; + --sidebar-primary-foreground: 0 0% 98%; + --sidebar-accent: 240 4.8% 95.9%; + --sidebar-accent-foreground: 240 5.9% 10%; + --sidebar-border: 220 13% 91%; + --sidebar-ring: 217.2 91.2% 59.8%; } - a:hover { - color: #747bff; + .dark { + --background: 0 0% 3.9%; + --foreground: 0 0% 98%; + --card: 0 0% 3.9%; + --card-foreground: 0 0% 98%; + --popover: 0 0% 3.9%; + --popover-foreground: 0 0% 98%; + --primary: 0 0% 98%; + --primary-foreground: 0 0% 9%; + --secondary: 0 0% 14.9%; + --secondary-foreground: 0 0% 98%; + --muted: 0 0% 14.9%; + --muted-foreground: 0 0% 63.9%; + --accent: 0 0% 14.9%; + --accent-foreground: 0 0% 98%; + --destructive: 0 62.8% 30.6%; + --destructive-foreground: 0 0% 98%; + --border: 0 0% 14.9%; + --input: 0 0% 14.9%; + --ring: 0 0% 83.1%; + --chart-1: 220 70% 50%; + --chart-2: 160 60% 45%; + --chart-3: 30 80% 55%; + --chart-4: 280 65% 60%; + --chart-5: 340 75% 55%; + --sidebar-background: 240 5.9% 10%; + --sidebar-foreground: 240 4.8% 95.9%; + --sidebar-primary: 224.3 76.3% 48%; + --sidebar-primary-foreground: 0 0% 100%; + --sidebar-accent: 240 3.7% 15.9%; + --sidebar-accent-foreground: 240 4.8% 95.9%; + --sidebar-border: 240 3.7% 15.9%; + --sidebar-ring: 217.2 91.2% 59.8%; + } +} + +@layer base { + * { + @apply border-border; } - button { - background-color: #f9f9f9; + body { + @apply bg-background text-foreground; } } diff --git a/frontend/src/lib/util.js b/frontend/src/lib/util.js new file mode 100644 index 0000000..20aa603 --- /dev/null +++ b/frontend/src/lib/util.js @@ -0,0 +1,6 @@ +import { clsx } from "clsx" +import { twMerge } from "tailwind-merge" + +export function cn(...inputs) { + return twMerge(clsx(inputs)) +} diff --git a/frontend/tailwind.config.js b/frontend/tailwind.config.js new file mode 100644 index 0000000..c9d23f6 --- /dev/null +++ b/frontend/tailwind.config.js @@ -0,0 +1,91 @@ +const plugin = require("tailwindcss/plugin"); + +module.exports = { + darkMode: ["class"], + content: [ + "./pages/**/*.{js,ts,jsx,tsx,mdx}", + "./components/**/*.{js,ts,jsx,tsx,mdx}", + "./app/**/*.{js,ts,jsx,tsx,mdx}", + "./src/**/*.{js,ts,jsx,tsx,mdx}", + "*.{js,ts,jsx,tsx,mdx}", + ], + prefix: "", + theme: { + container: { + center: true, + padding: "2rem", + screens: { + "2xl": "1400px", + }, + }, + extend: { + colors: { + border: "hsl(var(--border))", + input: "hsl(var(--input))", + ring: "hsl(var(--ring))", + background: "hsl(var(--background))", + foreground: "hsl(var(--foreground))", + primary: { + DEFAULT: "hsl(var(--primary))", + foreground: "hsl(var(--primary-foreground))", + }, + secondary: { + DEFAULT: "hsl(var(--secondary))", + foreground: "hsl(var(--secondary-foreground))", + }, + destructive: { + DEFAULT: "hsl(var(--destructive))", + foreground: "hsl(var(--destructive-foreground))", + }, + muted: { + DEFAULT: "hsl(var(--muted))", + foreground: "hsl(var(--muted-foreground))", + }, + accent: { + DEFAULT: "hsl(var(--accent))", + foreground: "hsl(var(--accent-foreground))", + }, + popover: { + DEFAULT: "hsl(var(--popover))", + foreground: "hsl(var(--popover-foreground))", + }, + card: { + DEFAULT: "hsl(var(--card))", + foreground: "hsl(var(--card-foreground))", + }, + success: { + DEFAULT: "#10b981", + foreground: "#ffffff", + }, + warning: { + DEFAULT: "#f59e0b", + foreground: "#ffffff", + }, + info: { + DEFAULT: "#3b82f6", + foreground: "#ffffff", + }, + }, + borderRadius: { + lg: "var(--radius)", + md: "calc(var(--radius) - 2px)", + sm: "calc(var(--radius) - 4px)", + }, + keyframes: { + "accordion-down": { + from: { height: "0" }, + to: { height: "var(--radix-accordion-content-height)" }, + }, + "accordion-up": { + from: { height: "var(--radix-accordion-content-height)" }, + to: { height: "0" }, + }, + }, + animation: { + "accordion-down": "accordion-down 0.2s ease-out", + "accordion-up": "accordion-up 0.2s ease-out", + }, + }, + }, + plugins: [require("tailwindcss-animate")], +}; diff --git a/frontend/vite.config.js b/frontend/vite.config.js index 8b0f57b..92cd803 100644 --- a/frontend/vite.config.js +++ b/frontend/vite.config.js @@ -1,7 +1,10 @@ import { defineConfig } from 'vite' import react from '@vitejs/plugin-react' -// https://vite.dev/config/ +// https://vitejs.dev/config/ export default defineConfig({ plugins: [react()], + css: { + postcss: './postcss.config.js', + }, }) From 1f218e35272d152e50f0a4b61667bb215593dc2c Mon Sep 17 00:00:00 2001 From: Muaz <6muazx@gmail.com> Date: Tue, 8 Apr 2025 02:34:01 +0530 Subject: [PATCH 03/14] Login + Register Page Compatibility --- .env | 7 + frontend/package-lock.json | 68 ++++ frontend/package.json | 1 + frontend/src/App.jsx | 22 +- frontend/src/components/auth/Login.jsx | 309 ++++++++++++++++++ frontend/src/components/auth/Register.jsx | 90 ++--- .../src/components/layouts/auth-layout.jsx | 4 +- 7 files changed, 453 insertions(+), 48 deletions(-) create mode 100644 .env create mode 100644 frontend/src/components/auth/Login.jsx diff --git a/.env b/.env new file mode 100644 index 0000000..2d92345 --- /dev/null +++ b/.env @@ -0,0 +1,7 @@ +SECRET_KEY=django-insecure-*$l30g@bo6!s*5y4i(z@@8aq(cc*k07cp0h4vk^jp$-mufw1rt + +DB_NAME=postgres +DB_USER=postgres +DB_PASSWORD=testing123 +DB_HOST=localhost +DB_PORT=5432 \ No newline at end of file diff --git a/frontend/package-lock.json b/frontend/package-lock.json index bbf59e8..fdcdc23 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -19,6 +19,7 @@ "postcss": "^8.4.31", "react": "^18.2.0", "react-dom": "^18.2.0", + "react-router-dom": "^7.5.0", "tailwind-merge": "^3.2.0", "tailwindcss": "^3.3.3", "tailwindcss-animate": "^1.0.7" @@ -1263,6 +1264,12 @@ "@babel/types": "^7.20.7" } }, + "node_modules/@types/cookie": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/@types/cookie/-/cookie-0.6.0.tgz", + "integrity": "sha512-4Kh9a6B2bQciAhf7FSuMRRkUWecJgJu9nPnx3yzpsfXX/c50REIqpHY4C82bXP90qrLtXtkDxTZosYO3UpOwlA==", + "license": "MIT" + }, "node_modules/@types/prop-types": { "version": "15.7.14", "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.14.tgz", @@ -1890,6 +1897,15 @@ "dev": true, "license": "MIT" }, + "node_modules/cookie": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-1.0.2.tgz", + "integrity": "sha512-9Kr/j4O16ISv8zBBhJoi4bXOYNTkFLOqSL3UDB0njXxCXNezjeyVrJyGOWtgfs/q2km1gwBcfH8q1yEGoMYunA==", + "license": "MIT", + "engines": { + "node": ">=18" + } + }, "node_modules/cross-spawn": { "version": "7.0.6", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", @@ -4461,6 +4477,46 @@ "node": ">=0.10.0" } }, + "node_modules/react-router": { + "version": "7.5.0", + "resolved": "https://registry.npmjs.org/react-router/-/react-router-7.5.0.tgz", + "integrity": "sha512-estOHrRlDMKdlQa6Mj32gIks4J+AxNsYoE0DbTTxiMy2mPzZuWSDU+N85/r1IlNR7kGfznF3VCUlvc5IUO+B9g==", + "license": "MIT", + "dependencies": { + "@types/cookie": "^0.6.0", + "cookie": "^1.0.1", + "set-cookie-parser": "^2.6.0", + "turbo-stream": "2.4.0" + }, + "engines": { + "node": ">=20.0.0" + }, + "peerDependencies": { + "react": ">=18", + "react-dom": ">=18" + }, + "peerDependenciesMeta": { + "react-dom": { + "optional": true + } + } + }, + "node_modules/react-router-dom": { + "version": "7.5.0", + "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-7.5.0.tgz", + "integrity": "sha512-fFhGFCULy4vIseTtH5PNcY/VvDJK5gvOWcwJVHQp8JQcWVr85ENhJ3UpuF/zP1tQOIFYNRJHzXtyhU1Bdgw0RA==", + "license": "MIT", + "dependencies": { + "react-router": "7.5.0" + }, + "engines": { + "node": ">=20.0.0" + }, + "peerDependencies": { + "react": ">=18", + "react-dom": ">=18" + } + }, "node_modules/read-cache": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/read-cache/-/read-cache-1.0.0.tgz", @@ -4695,6 +4751,12 @@ "semver": "bin/semver.js" } }, + "node_modules/set-cookie-parser": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/set-cookie-parser/-/set-cookie-parser-2.7.1.tgz", + "integrity": "sha512-IOc8uWeOZgnb3ptbCURJWNjWUPcO3ZnTTdzsurqERrP6nPyv+paC55vJM0LpOlT2ne+Ix+9+CRG1MNLlyZ4GjQ==", + "license": "MIT" + }, "node_modules/set-function-length": { "version": "1.2.2", "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz", @@ -5282,6 +5344,12 @@ "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", "license": "0BSD" }, + "node_modules/turbo-stream": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/turbo-stream/-/turbo-stream-2.4.0.tgz", + "integrity": "sha512-FHncC10WpBd2eOmGwpmQsWLDoK4cqsA/UT/GqNoaKOQnT8uzhtCbg3EoUDMvqpOSAI0S26mr0rkjzbOO6S3v1g==", + "license": "ISC" + }, "node_modules/type-check": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", diff --git a/frontend/package.json b/frontend/package.json index b286aca..b5e5b8a 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -21,6 +21,7 @@ "postcss": "^8.4.31", "react": "^18.2.0", "react-dom": "^18.2.0", + "react-router-dom": "^7.5.0", "tailwind-merge": "^3.2.0", "tailwindcss": "^3.3.3", "tailwindcss-animate": "^1.0.7" diff --git a/frontend/src/App.jsx b/frontend/src/App.jsx index 64792c5..22b09f0 100644 --- a/frontend/src/App.jsx +++ b/frontend/src/App.jsx @@ -1,13 +1,21 @@ -import Register from './components/auth/Register' -import { AuthLayout } from './components/layouts/auth-layout' +import { BrowserRouter as Router, Routes, Route, Navigate } from 'react-router-dom' +import LoginPage from './components/auth/Login' +import RegisterPage from './components/auth/Register' +import AuthLayout from './components/layouts/auth-layout' function App() { return ( - <> - - - - + + + } /> + + } /> + + } /> + + ) } diff --git a/frontend/src/components/auth/Login.jsx b/frontend/src/components/auth/Login.jsx new file mode 100644 index 0000000..d0ac00e --- /dev/null +++ b/frontend/src/components/auth/Login.jsx @@ -0,0 +1,309 @@ +"use client" + +import React, { useState } from "react" +import { Button } from "../ui/Button" +import { Input } from "../ui/input" +import { Label } from "../ui/label" +import { Checkbox } from "../ui/checkbox" +import { Card, CardContent, CardFooter } from "../ui/card" +import { useToast } from "../../hooks/use-toast" +import { Eye, EyeOff, Loader2, AlertCircle, User, UserPlus } from "lucide-react" +import { Alert, AlertDescription, AlertTitle } from "../ui/alert" +import AuthLayout from "../layouts/auth-layout" +import { useNavigate, Link } from "react-router-dom" + +function Login() { + const [role, setRole] = useState('patient') + const [isLoading, setIsLoading] = useState(false) + const [showPassword, setShowPassword] = useState(false) + const [fullName, setFullName] = useState("") + const [email, setEmail] = useState("") + const [password, setPassword] = useState("") + const [phoneNumber, setPhoneNumber] = useState("") + const [dateOfBirth, setDateOfBirth] = useState("") + const [gender, setGender] = useState("") + const [licenseNumber, setLicenseNumber] = useState("") + const [specialization, setSpecialization] = useState("") + const [hospitalName, setHospitalName] = useState("") + const [location, setLocation] = useState("") + const [rememberMe, setRememberMe] = useState(false) + const [errors, setErrors] = useState({}) + const { toast } = useToast() + const navigate = useNavigate() + + const handlePasswordChange = (e) => { + const newPassword = e.target.value + setPassword(newPassword) + } + + const validateForm = () => { + const newErrors = {} + + if (!fullName) newErrors.fullName = "Full name is required" + if (!email) newErrors.email = "Email is required" + else if (!/\S+@\S+\.\S+/.test(email)) newErrors.email = "Invalid email format" + + if (!password) newErrors.password = "Password is required" + else if (password.length < 8) newErrors.password = "Password must be at least 8 characters" + + if (!phoneNumber) newErrors.phoneNumber = "Phone number is required" + + if (role === 'patient') { + if (!dateOfBirth) newErrors.dateOfBirth = "Date of birth is required" + if (!gender) newErrors.gender = "Please select your gender" + } + + if (role === 'doctor') { + if (!licenseNumber) newErrors.licenseNumber = "License number is required" + if (!specialization) newErrors.specialization = "Specialization is required" + if (!location) newErrors.location = "Location is required" + } + + setErrors(newErrors) + return Object.keys(newErrors).length === 0 + } + + const handleSubmit = async (e) => { + e.preventDefault() + + if (!validateForm()) return + + setIsLoading(true) + try { + await new Promise((resolve) => setTimeout(resolve, 1500)) + + console.log('Registration successful', { + role, + fullName, + email, + password, + phoneNumber, + ...(role === 'patient' && { + dateOfBirth, + gender + }), + ...(role === 'doctor' && { + licenseNumber, + specialization, + hospitalName, + location + }) + }) + + toast({ + title: "Registration successful", + description: "Your account has been created", + }) + + // Navigate to dashboard or home page after successful login + navigate('/dashboard') + + } catch (error) { + toast({ + title: "Registration failed", + description: "Please try again later", + variant: "destructive", + }) + } finally { + setIsLoading(false) + } + } + + const specializations = [ + "Cardiology", + "Dermatology", + "Endocrinology", + "Gastroenterology", + "Neurology", + "Obstetrics and Gynecology", + "Oncology", + "Ophthalmology", + "Orthopedics", + "Pediatrics", + "Psychiatry", + "Pulmonology", + "Radiology", + "Urology" + ] + + return ( +
+
+
+ + +
+ + +
+ +
+ + setFullName(e.target.value)} + className={`h-9 ${errors.fullName ? 'border-red-500' : ''}`} + disabled={isLoading} + /> + {errors.fullName &&

{errors.fullName}

} +
+ +
+ + setEmail(e.target.value)} + className={`h-9 ${errors.email ? 'border-red-500' : ''}`} + disabled={isLoading} + /> + {errors.email &&

{errors.email}

} +
+ +
+ +
+ + +
+ {errors.password &&

{errors.password}

} +
+ +
+ + setPhoneNumber(e.target.value)} + className={`h-9 ${errors.phoneNumber ? 'border-red-500' : ''}`} + disabled={isLoading} + /> + {errors.phoneNumber &&

{errors.phoneNumber}

} +
+ + {role === 'doctor' && ( + <> +
+ + setLicenseNumber(e.target.value)} + className={`h-9 ${errors.licenseNumber ? 'border-red-500' : ''}`} + disabled={isLoading} + /> + {errors.licenseNumber &&

{errors.licenseNumber}

} +
+ +
+ + + {errors.specialization &&

{errors.specialization}

} +
+ + )} + +
+ setRememberMe(!!checked)} + disabled={isLoading} + /> + +
+
+ + + + +
+ Don't have an account?{" "} + + Sign Up + +
+
+
+
+
+
+ ) +} + +function LoginPage() { + return ( + + + + ) +} + +export default LoginPage diff --git a/frontend/src/components/auth/Register.jsx b/frontend/src/components/auth/Register.jsx index 79bf684..3886178 100644 --- a/frontend/src/components/auth/Register.jsx +++ b/frontend/src/components/auth/Register.jsx @@ -2,15 +2,17 @@ import React, { useState } from "react" import { Button } from "../ui/Button" -import { Input } from "../ui/input" +import { Input } from "../ui/input" import { Label } from "../ui/label" import { Checkbox } from "../ui/checkbox" import { Card, CardContent, CardFooter } from "../ui/card" import { useToast } from "../../hooks/use-toast" import { Eye, EyeOff, Loader2, AlertCircle, User, UserPlus } from "lucide-react" import { Alert, AlertDescription, AlertTitle } from "../ui/alert" +import AuthLayout from "../layouts/auth-layout" +import { useNavigate, Link } from "react-router-dom" -export function Register() { +function Register() { const [role, setRole] = useState('patient') const [isLoading, setIsLoading] = useState(false) const [showPassword, setShowPassword] = useState(false) @@ -30,6 +32,7 @@ export function Register() { const [passwordStrength, setPasswordStrength] = useState(0) const [errors, setErrors] = useState({}) const { toast } = useToast() + const navigate = useNavigate() const checkPasswordStrength = (password) => { let strength = 0 @@ -82,70 +85,71 @@ export function Register() { const validateForm = () => { const newErrors = {} - + if (!fullName) newErrors.fullName = "Full name is required" if (!email) newErrors.email = "Email is required" else if (!/\S+@\S+\.\S+/.test(email)) newErrors.email = "Invalid email format" - + if (!password) newErrors.password = "Password is required" else if (password.length < 8) newErrors.password = "Password must be at least 8 characters" - + if (!confirmPassword) newErrors.confirmPassword = "Please confirm your password" else if (password !== confirmPassword) newErrors.confirmPassword = "Passwords do not match" - + if (!phoneNumber) newErrors.phoneNumber = "Phone number is required" - + if (role === 'patient') { if (!dateOfBirth) newErrors.dateOfBirth = "Date of birth is required" if (!gender) newErrors.gender = "Please select your gender" } - + if (role === 'doctor') { if (!licenseNumber) newErrors.licenseNumber = "License number is required" if (!specialization) newErrors.specialization = "Specialization is required" if (!location) newErrors.location = "Location is required" } - + if (!agreeTerms) newErrors.terms = "You must agree to the terms and privacy policy" - + setErrors(newErrors) return Object.keys(newErrors).length === 0 } const handleSubmit = async (e) => { e.preventDefault() - + if (!validateForm()) return - + setIsLoading(true) try { await new Promise((resolve) => setTimeout(resolve, 1500)) - - console.log('Registration successful', { - role, + + console.log('Registration successful', { + role, fullName, - email, + email, password, phoneNumber, - ...(role === 'patient' && { + ...(role === 'patient' && { dateOfBirth, gender }), - ...(role === 'doctor' && { + ...(role === 'doctor' && { licenseNumber, specialization, hospitalName, location }) }) - + toast({ title: "Registration successful", description: "Your account has been created", }) - - // Here we would navigate to onboarding - // router.push("/onboarding") + + // Navigate to login page after successful registration + navigate('/login') + } catch (error) { toast({ title: "Registration failed", @@ -181,11 +185,10 @@ export function Register() { + + + + {/* Active Sharing */} + + + Active Sharing + + + +
3
+

+ 2 healthcare providers, 1 organization +

+
+ + + +
+ + {/* Security Status */} + + + Security Status + + + +
+
Secure
+ Protected +
+

Last verified 2 hours ago

+
+ + + +
+ + {/* Emergency Access */} + + + Emergency Access + + + +
+
Ready
+ + Enabled + +
+

PIN: **** (Last updated 30 days ago)

+
+ + + +
+
+ + {/* Tabs Section */} + + + + Overview + + + Recent Activity + + + Appointments + + + + {/* Overview Tab */} + +
+ + + Health Summary + Your key health metrics and information + + + + + Overview + Medications + Allergies + + + {/* Overview Tab */} + +
+
+
Blood Type
+
+ + A+ +
+
+ +
+
Height & Weight
+
+ + 175 cm, 72 kg +
+
+
+ +
+
Vital Signs
+
+
+
+
+ + Blood Pressure +
+ Normal +
+
120/80
+
Last updated: 2 days ago
+
+ +
+
+
+ + Heart Rate +
+ Normal +
+
72 bpm
+
Last updated: 2 days ago
+
+
+
+
+ + {/* Other tabs content */} + + {/* Medications content */} + + + {/* Allergies content */} + +
+
+
+ + {/* Notifications */} + + + Notifications + Recent alerts and updates + + +
+
+
+ +
+
+

Appointment Reminder

+

+ You have an appointment with Dr. Johnson tomorrow at 10:00 AM. +

+

2 hours ago

+
+
+ +
+
+ +
+
+

Lab Results Available

+

+ Your recent lab results have been uploaded to your records. +

+

Yesterday

+
+
+ +
+
+ +
+
+

Sharing Request

+

+ Central Hospital has requested access to your medical history. +

+

2 days ago

+
+
+ +
+
+ +
+
+

Access Expired

+

+ Dr. Chen's access to your records has expired. +

+

3 days ago

+
+
+
+
+
+
+ + {/* Lower Grid */} +
+ + +
+ Upcoming Appointments + Your scheduled healthcare visits +
+ +
+ + + + +
+ + + +
+ Recent Activity + Recent actions on your health records +
+ +
+ + + + +
+ + + +
+ Health Trends + Tracking your key health metrics +
+ +
+ + {/* Health metric placeholders */} +
+
+ Blood Pressure + Stable +
+
+
+
+
+ +
+
+ Weight + Slight increase +
+
+
+
+
+ +
+
+ Heart Rate + Normal +
+
+
+
+
+
+ + + +
+
+
+ + {/* Activity Tab */} + + + + Recent Activity + A detailed log of recent actions on your health records + + + + + + {/* Appointments Tab */} + + + + Upcoming Appointments + Your scheduled healthcare visits + + + + + + + +
+
+ ) +} + +export default DashboardOverview diff --git a/frontend/src/components/dashboard/health-summary.jsx b/frontend/src/components/dashboard/health-summary.jsx new file mode 100644 index 0000000..3adacf6 --- /dev/null +++ b/frontend/src/components/dashboard/health-summary.jsx @@ -0,0 +1,180 @@ +import React from 'react' +import { Tabs, TabsContent, TabsList, TabsTrigger } from "../ui/tabs"; +import { Badge } from "../ui/badge"; +import { Activity, Pill, AlertTriangle, Heart, Droplets, Dumbbell } from "lucide-react"; + +const HealthSummary = () => { + return ( + + + Overview + Medications + Allergies + + + +
+
+
Blood Type
+
+ + A+ +
+
+ +
+
Height & Weight
+
+ + 175 cm, 72 kg +
+
+
+ +
+
Vital Signs
+
+
+
+
+ + Blood Pressure +
+ Normal +
+
120/80
+
Last updated: 2 days ago
+
+ +
+
+
+ + Heart Rate +
+ Normal +
+
72 bpm
+
Last updated: 2 days ago
+
+
+
+ +
+
Chronic Conditions
+
+
+ + Asthma (Mild) +
+
+

Diagnosed: Jan 2018

+

Managed with: Albuterol inhaler as needed

+
+
+
+
+ + +
+
+
+
+ + Lisinopril +
+ Active +
+
+

10mg, Once daily

+

For blood pressure management

+

Prescribed by: Dr. Sarah Johnson

+

Started: March 2023

+
+
+ +
+
+
+ + Albuterol Inhaler +
+ As Needed +
+
+

2 puffs as needed for shortness of breath

+

For asthma symptoms

+

Prescribed by: Dr. Emily Rodriguez

+

Started: January 2018

+
+
+ +
+
+
+ + Vitamin D Supplement +
+ Active +
+
+

1000 IU, Once daily

+

For vitamin D deficiency

+

Prescribed by: Dr. Emily Rodriguez

+

Started: September 2022

+
+
+
+
+ + +
+
+
+ + Penicillin +
+
+

+ Severity: Severe +

+

Reaction: Hives, difficulty breathing, swelling

+

Diagnosed: 2010

+
+
+ +
+
+ + Shellfish +
+
+

+ Severity: Moderate +

+

Reaction: Nausea, vomiting, skin rash

+

Diagnosed: 2015

+
+
+ +
+
+ + Pollen +
+
+

+ Severity: Mild +

+

Reaction: Sneezing, itchy eyes, congestion

+

Diagnosed: 2008

+
+
+
+
+
+ ); +} + +export { HealthSummary } +export default HealthSummary diff --git a/frontend/src/components/dashboard/recent-activity-list.jsx b/frontend/src/components/dashboard/recent-activity-list.jsx new file mode 100644 index 0000000..1d219b1 --- /dev/null +++ b/frontend/src/components/dashboard/recent-activity-list.jsx @@ -0,0 +1,240 @@ +"use client" + +import React from 'react' +import { + FileText, + Eye, + Lock, + Share2, + Download, + User, + AlertCircle +} from 'lucide-react' +import { Badge } from "../ui/badge" + +// Mock data for demonstration +const mockActivities = [ + { + id: "1", + type: "view", + description: "Dr. Sarah Johnson viewed your medical history", + timestamp: "2023-10-15T14:30:00", + actor: "Dr. Sarah Johnson", + status: "authorized", + }, + { + id: "2", + type: "login", + description: "You logged in from a new device", + timestamp: "2023-10-14T09:15:00", + actor: "You", + status: "authorized", + }, + { + id: "3", + type: "upload", + description: "Central Hospital uploaded new lab results", + timestamp: "2023-10-10T16:45:00", + actor: "Central Hospital", + status: "authorized", + }, + { + id: "4", + type: "share", + description: "You granted access to Dr. Michael Chen", + timestamp: "2023-10-08T11:20:00", + actor: "You", + status: "authorized", + }, + { + id: "5", + type: "download", + description: "You downloaded your vaccination records", + timestamp: "2023-10-05T13:45:00", + actor: "You", + status: "authorized", + }, + { + id: "6", + type: "edit", + description: "You updated your emergency contact information", + timestamp: "2023-10-03T10:30:00", + actor: "You", + status: "authorized", + }, + { + id: "7", + type: "login_attempt", + description: "Failed login attempt from unknown device", + timestamp: "2023-10-01T22:15:00", + actor: "Unknown", + status: "blocked", + }, + { + id: "8", + type: "view", + description: "Emergency access by paramedic using PIN", + timestamp: "2023-09-28T08:20:00", + actor: "Emergency Services", + status: "emergency", + }, +] + +export const RecentActivityList = ({ limit = 5 }) => { + // Dummy data for recent activities + const activities = [ + { + id: 1, + type: 'view', + icon: Eye, + title: 'Blood Test Results viewed', + actor: 'You', + date: 'Today, 2:30 PM', + color: 'text-blue-500', + bg: 'bg-blue-100' + }, + { + id: 2, + type: 'share', + icon: Share2, + title: 'X-Ray Report shared', + actor: 'You', + with: 'Dr. Johnson', + date: 'Today, 11:15 AM', + color: 'text-purple-500', + bg: 'bg-purple-100' + }, + { + id: 3, + type: 'upload', + icon: FileText, + title: 'New Vaccination Record added', + actor: 'Dr. Smith', + date: 'Yesterday, 4:20 PM', + color: 'text-green-500', + bg: 'bg-green-100' + }, + { + id: 4, + type: 'access', + icon: Lock, + title: 'Emergency Information accessed', + actor: 'Central Hospital', + date: '2 days ago, 8:45 AM', + color: 'text-amber-500', + bg: 'bg-amber-100' + }, + { + id: 5, + type: 'download', + icon: Download, + title: 'Medical History downloaded', + actor: 'You', + date: '3 days ago, 1:30 PM', + color: 'text-indigo-500', + bg: 'bg-indigo-100' + }, + { + id: 6, + type: 'permission', + icon: User, + title: 'Access permission changed', + actor: 'You', + for: 'Dr. Williams', + date: '4 days ago, 9:20 AM', + color: 'text-gray-500', + bg: 'bg-gray-100' + }, + { + id: 7, + type: 'alert', + icon: AlertCircle, + title: 'Sharing request received', + actor: 'Medical Research Lab', + date: '5 days ago, 3:45 PM', + status: 'pending', + color: 'text-red-500', + bg: 'bg-red-100' + } + ] + + const limitedActivities = activities.slice(0, limit) + + const getActivityIcon = (type) => { + switch (type) { + case "view": + return + case "login": + return + case "upload": + return + case "download": + return + case "share": + return + case "edit": + return + case "login_attempt": + return + default: + return + } + } + + const getStatusBadge = (status) => { + switch (status) { + case "authorized": + return ( + + Authorized + + ) + case "blocked": + return ( + + Blocked + + ) + case "emergency": + return ( + + Emergency + + ) + default: + return {status} + } + } + + return ( +
+ {limitedActivities.map((activity) => ( +
+
+ +
+
+

{activity.title}

+

+ {activity.actor} + {activity.with && with {activity.with}} + {activity.for && for {activity.for}} +

+

{activity.date}

+
+
+ ))} +
+ ) +} + +export default RecentActivityList diff --git a/frontend/src/components/dashboard/sidebar.jsx b/frontend/src/components/dashboard/sidebar.jsx new file mode 100644 index 0000000..4d419aa --- /dev/null +++ b/frontend/src/components/dashboard/sidebar.jsx @@ -0,0 +1,218 @@ +"use client" + +import React from 'react'; +import { Link, useLocation } from 'react-router-dom'; +import { + FileText, + Share2, + Shield, + AlertCircle, + User, + Settings, + LogOut, + Calendar, + UserPlus, + PieChart, + Home +} from 'lucide-react'; + +const Sidebar = ({ collapsed, setCollapsed }) => { + const location = useLocation(); + const path = location.pathname; + + // Check if current path is dashboard or root + const isDashboard = path === '/' || path === '/dashboard'; + + return ( +
+
+ {!collapsed && ( +
HealthChain
+ )} + +
+ +
+
+ {!collapsed && 'Overview'} +
+ } + label="Dashboard" + to="/dashboard" + collapsed={collapsed} + active={isDashboard} + color="blue" + /> + +
+ {!collapsed && 'Health Records'} +
+ } + label="Health Records" + to="/health-records" + collapsed={collapsed} + active={path === '/health-records'} + color="indigo" + /> + } + label="Sharing Controls" + to="/sharing" + collapsed={collapsed} + active={path === '/sharing'} + color="purple" + /> + } + label="Security Settings" + to="/security" + collapsed={collapsed} + active={path === '/security'} + color="green" + /> + } + label="Emergency Access" + to="/emergency" + collapsed={collapsed} + active={path === '/emergency'} + color="amber" + /> + +
+ {!collapsed && 'Health Management'} +
+ } + label="Providers" + to="/providers" + collapsed={collapsed} + active={path === '/providers'} + color="teal" + /> + } + label="Appointments" + to="/appointments" + collapsed={collapsed} + active={path === '/appointments'} + color="rose" + /> + } + label="Analytics" + to="/analytics" + collapsed={collapsed} + active={path === '/analytics'} + color="cyan" + /> + +
+ {!collapsed && 'Account'} +
+ } + label="Profile" + to="/profile" + collapsed={collapsed} + active={path === '/profile'} + color="blue" + /> + } + label="Settings" + to="/settings" + collapsed={collapsed} + active={path === '/settings'} + color="gray" + /> + } + label="Logout" + to="/logout" + collapsed={collapsed} + active={path === '/logout'} + color="red" + /> +
+
+ ); +}; + +const getColorStyles = (color, active) => { + const colors = { + blue: { + active: "bg-gray-100 text-gray-800", + inactive: "text-gray-600 hover:bg-black/5 hover:text-gray-800" + }, + indigo: { + active: "bg-gray-100 text-gray-800", + inactive: "text-gray-600 hover:bg-black/5 hover:text-gray-800" + }, + purple: { + active: "bg-gray-100 text-gray-800", + inactive: "text-gray-600 hover:bg-black/5 hover:text-gray-800" + }, + green: { + active: "bg-gray-100 text-gray-800", + inactive: "text-gray-600 hover:bg-black/5 hover:text-gray-800" + }, + amber: { + active: "bg-gray-100 text-gray-800", + inactive: "text-gray-600 hover:bg-black/5 hover:text-gray-800" + }, + teal: { + active: "bg-gray-100 text-gray-800", + inactive: "text-gray-600 hover:bg-black/5 hover:text-gray-800" + }, + rose: { + active: "bg-gray-100 text-gray-800", + inactive: "text-gray-600 hover:bg-black/5 hover:text-gray-800" + }, + cyan: { + active: "bg-gray-100 text-gray-800", + inactive: "text-gray-600 hover:bg-black/5 hover:text-gray-800" + }, + gray: { + active: "bg-gray-100 text-gray-800", + inactive: "text-gray-600 hover:bg-black/5 hover:text-gray-800" + }, + red: { + active: "bg-gray-100 text-gray-800", + inactive: "text-gray-600 hover:bg-black/5 hover:text-gray-800" + } + }; + + return active ? colors[color].active : colors[color].inactive; +}; + +const NavItem = ({ icon, label, to, collapsed, active = false, color = "blue" }) => { + const colorClasses = getColorStyles(color, active); + + return ( + +
{icon}
+ {!collapsed &&
{label}
} + + ); +}; + +export default Sidebar; diff --git a/frontend/src/components/dashboard/upcoming-appointments.jsx b/frontend/src/components/dashboard/upcoming-appointments.jsx new file mode 100644 index 0000000..3067325 --- /dev/null +++ b/frontend/src/components/dashboard/upcoming-appointments.jsx @@ -0,0 +1,152 @@ +import React from "react" +import { Calendar, Clock, MapPin, Video, Phone, MoreHorizontal } from "lucide-react" +import { Button } from "../ui/Button" +import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuTrigger } from "../ui/dropdown-menu" +import { Badge } from "../ui/badge" + +export const UpcomingAppointments = ({ limit = 5 }) => { + // Dummy data for upcoming appointments + const appointments = [ + { + id: 1, + doctor: 'Dr. Sarah Johnson', + specialty: 'Cardiologist', + type: 'Check-up', + date: 'Tomorrow', + time: '10:00 AM', + location: 'Heart Care Center', + isVirtual: false, + status: 'confirmed' + }, + { + id: 2, + doctor: 'Dr. Michael Chen', + specialty: 'Dermatologist', + type: 'Consultation', + date: 'June 15, 2023', + time: '2:30 PM', + location: 'Video Call', + isVirtual: true, + status: 'confirmed' + }, + { + id: 3, + doctor: 'Dr. Emily Rodriguez', + specialty: 'Neurologist', + type: 'Follow-up', + date: 'June 22, 2023', + time: '9:15 AM', + location: 'Neurology Associates', + isVirtual: false, + status: 'pending' + }, + { + id: 4, + doctor: 'Dr. James Wilson', + specialty: 'Orthopedist', + type: 'Physical Therapy', + date: 'June 30, 2023', + time: '11:45 AM', + location: 'Sports Medicine Clinic', + isVirtual: false, + status: 'confirmed' + }, + { + id: 5, + doctor: 'Dr. Lisa Wong', + specialty: 'Psychiatrist', + type: 'Therapy Session', + date: 'July 5, 2023', + time: '4:00 PM', + location: 'Video Call', + isVirtual: true, + status: 'confirmed' + } + ] + + const limitedAppointments = appointments.slice(0, limit) + + const getAppointmentTypeIcon = (type) => { + switch (type) { + case "video": + return
+ ) +} diff --git a/frontend/src/index.css b/frontend/src/index.css index ac68442..71ead7e 100644 --- a/frontend/src/index.css +++ b/frontend/src/index.css @@ -14,30 +14,25 @@ body { @layer base { :root { - --background: 0 0% 100%; - --foreground: 0 0% 3.9%; + --background: 210 40% 98%; + --foreground: 222.2 84% 4.9%; --card: 0 0% 100%; - --card-foreground: 0 0% 3.9%; + --card-foreground: 222.2 84% 4.9%; --popover: 0 0% 100%; - --popover-foreground: 0 0% 3.9%; - --primary: 0 0% 9%; - --primary-foreground: 0 0% 98%; - --secondary: 0 0% 96.1%; - --secondary-foreground: 0 0% 9%; - --muted: 0 0% 96.1%; - --muted-foreground: 0 0% 45.1%; - --accent: 0 0% 96.1%; - --accent-foreground: 0 0% 9%; + --popover-foreground: 222.2 84% 4.9%; + --primary: 221.2 83.2% 53.3%; + --primary-foreground: 210 40% 98%; + --secondary: 210 40% 96.1%; + --secondary-foreground: 222.2 47.4% 11.2%; + --muted: 210 40% 96.1%; + --muted-foreground: 215.4 16.3% 46.9%; + --accent: 210 40% 96.1%; + --accent-foreground: 222.2 47.4% 11.2%; --destructive: 0 84.2% 60.2%; - --destructive-foreground: 0 0% 98%; - --border: 0 0% 89.8%; - --input: 0 0% 89.8%; - --ring: 0 0% 3.9%; - --chart-1: 12 76% 61%; - --chart-2: 173 58% 39%; - --chart-3: 197 37% 24%; - --chart-4: 43 74% 66%; - --chart-5: 27 87% 67%; + --destructive-foreground: 210 40% 98%; + --border: 214.3 31.8% 91.4%; + --input: 214.3 31.8% 91.4%; + --ring: 221.2 83.2% 53.3%; --radius: 0.5rem; --sidebar-background: 0 0% 98%; --sidebar-foreground: 240 5.3% 26.1%; @@ -49,25 +44,25 @@ body { --sidebar-ring: 217.2 91.2% 59.8%; } .dark { - --background: 0 0% 3.9%; - --foreground: 0 0% 98%; - --card: 0 0% 3.9%; - --card-foreground: 0 0% 98%; - --popover: 0 0% 3.9%; - --popover-foreground: 0 0% 98%; - --primary: 0 0% 98%; - --primary-foreground: 0 0% 9%; - --secondary: 0 0% 14.9%; - --secondary-foreground: 0 0% 98%; - --muted: 0 0% 14.9%; - --muted-foreground: 0 0% 63.9%; - --accent: 0 0% 14.9%; - --accent-foreground: 0 0% 98%; + --background: 222.2 84% 4.9%; + --foreground: 210 40% 98%; + --card: 222.2 84% 4.9%; + --card-foreground: 210 40% 98%; + --popover: 222.2 84% 4.9%; + --popover-foreground: 210 40% 98%; + --primary: 217.2 91.2% 59.8%; + --primary-foreground: 222.2 47.4% 11.2%; + --secondary: 217.2 32.6% 17.5%; + --secondary-foreground: 210 40% 98%; + --muted: 217.2 32.6% 17.5%; + --muted-foreground: 215 20.2% 65.1%; + --accent: 217.2 32.6% 17.5%; + --accent-foreground: 210 40% 98%; --destructive: 0 62.8% 30.6%; - --destructive-foreground: 0 0% 98%; - --border: 0 0% 14.9%; - --input: 0 0% 14.9%; - --ring: 0 0% 83.1%; + --destructive-foreground: 210 40% 98%; + --border: 217.2 32.6% 17.5%; + --input: 217.2 32.6% 17.5%; + --ring: 224.3 76.3% 48%; --chart-1: 220 70% 50%; --chart-2: 160 60% 45%; --chart-3: 30 80% 55%; diff --git a/frontend/src/lib/util.js b/frontend/src/lib/util.js index 20aa6031c9b2fb524feb6f39a92d030ecbb2fd4a..5c44added60edcab799d8616873a0ea1040b9896 100644 GIT binary patch literal 152 zcmZ9ETM9r>6h+s!G6#S6i#q_bFa;6vimQ}N9lI-%kMn4qv-WvdED;$6v1(;xV8V$x tDv^ksUcF?|%tdQ`++@<$>9%; { + const [timePeriod, setTimePeriod] = useState('month'); + const [showBloodPressureDetails, setShowBloodPressureDetails] = useState(false); + const [showWeightDetails, setShowWeightDetails] = useState(false); + const [isExporting, setIsExporting] = useState(false); + + // Sample analytics data + const healthMetrics = { + bloodPressure: [ + { date: "Jan", systolic: 120, diastolic: 80 }, + { date: "Feb", systolic: 118, diastolic: 76 }, + { date: "Mar", systolic: 122, diastolic: 78 }, + { date: "Apr", systolic: 121, diastolic: 77 }, + { date: "May", systolic: 119, diastolic: 75 }, + { date: "Jun", systolic: 117, diastolic: 74 } + ], + weight: [ + { date: "Jan", value: 72.5 }, + { date: "Feb", value: 72.1 }, + { date: "Mar", value: 71.8 }, + { date: "Apr", value: 71.0 }, + { date: "May", value: 70.5 }, + { date: "Jun", value: 70.2 } + ], + recordsAccessed: [ + { type: "Healthcare Providers", count: 12 }, + { type: "Self", count: 8 }, + { type: "Emergency", count: 1 }, + { type: "Research", count: 3 } + ], + appointments: { + completed: 8, + upcoming: 3, + cancelled: 1 + } + }; + + // Calculate max values to scale charts + const maxSystolic = Math.max(...healthMetrics.bloodPressure.map(item => item.systolic)); + const maxDiastolic = Math.max(...healthMetrics.bloodPressure.map(item => item.diastolic)); + const maxBloodPressure = Math.max(maxSystolic, maxDiastolic); + + const maxWeight = Math.max(...healthMetrics.weight.map(item => item.value)); + const minWeight = Math.min(...healthMetrics.weight.map(item => item.value)); + + const totalRecordsAccessed = healthMetrics.recordsAccessed.reduce((sum, item) => sum + item.count, 0); + + // Handle export data + const handleExportData = () => { + setIsExporting(true); + + // Simulate export process + setTimeout(() => { + const dataStr = "data:text/json;charset=utf-8," + encodeURIComponent(JSON.stringify(healthMetrics)); + const downloadAnchorNode = document.createElement('a'); + downloadAnchorNode.setAttribute("href", dataStr); + downloadAnchorNode.setAttribute("download", `health_metrics_${timePeriod}.json`); + document.body.appendChild(downloadAnchorNode); + downloadAnchorNode.click(); + downloadAnchorNode.remove(); + + setIsExporting(false); + + // Show success message + alert('Data export completed successfully!'); + }, 1500); + }; + + return ( +
+
+

Health Analytics

+

Monitor your health trends and data

+
+ + {/* Time Filter and Export */} +
+
+ {['week', 'month', 'quarter', 'year'].map((period) => ( + + ))} +
+ + +
+ + {/* Health Metrics */} +
+ {/* Blood Pressure Chart */} +
+
+

Blood Pressure

+
+ + Systolic + + Diastolic +
+
+
+ {/* Chart Implementation */} +
+
+ {healthMetrics.bloodPressure.map((item, index) => ( +
+
+ {/* Systolic Bar */} +
+
+ {item.systolic} +
+
+ + {/* Diastolic Bar */} +
+
+ {item.diastolic} +
+
+
+
{item.date}
+
+ ))} +
+
+
+
+
+ + Down 3% from last {timePeriod} +
+ +
+ + {/* Details Panel */} + {showBloodPressureDetails && ( +
+
+

Normal range: 90-120/60-80 mmHg

+
+
+

Average Systolic

+

{(healthMetrics.bloodPressure.reduce((sum, item) => sum + item.systolic, 0) / healthMetrics.bloodPressure.length).toFixed(1)} mmHg

+
+
+

Average Diastolic

+

{(healthMetrics.bloodPressure.reduce((sum, item) => sum + item.diastolic, 0) / healthMetrics.bloodPressure.length).toFixed(1)} mmHg

+
+
+
+ +

Your blood pressure is within a healthy range. Continue your current lifestyle habits.

+
+
+
+ )} +
+ + {/* Weight Chart */} +
+
+

Weight (kg)

+
+ + Weight +
+
+
+ {/* Chart Implementation */} +
+ + {/* Line chart for weight */} + { + // Calculate position in the chart. Using a tight range to make changes more visible + const range = maxWeight - minWeight + 2; + const yPercentage = 1 - ((item.value - minWeight + 1) / range); + const xPercentage = index / (healthMetrics.weight.length - 1); + return `${xPercentage * 100}% ${yPercentage * 100}%`; + }).join(' ')} + fill="none" + stroke="rgb(34, 197, 94)" + strokeWidth="2" + strokeLinecap="round" + strokeLinejoin="round" + /> + + {/* Data points */} + {healthMetrics.weight.map((item, index) => { + const range = maxWeight - minWeight + 2; + const yPercentage = 1 - ((item.value - minWeight + 1) / range); + const xPercentage = index / (healthMetrics.weight.length - 1); + return ( + + + + {item.value} + + + ); + })} + + + {/* X-axis labels */} +
+ {healthMetrics.weight.map((item, index) => ( +
+ {item.date} +
+ ))} +
+
+
+
+
+ + Down 2.3 kg over 6 months +
+ +
+ + {/* Details Panel */} + {showWeightDetails && ( +
+
+

Target range: 68-75 kg

+
+
+

Starting Weight

+

{healthMetrics.weight[0].value} kg

+
+
+

Current Weight

+

{healthMetrics.weight[healthMetrics.weight.length - 1].value} kg

+
+
+
+ +

You've achieved a healthy gradual weight loss. Keep up your good habits!

+
+
+
+ )} +
+ + {/* Records Access */} +
+
+

Records Access

+
+ Last 6 months +
+
+
+ {/* Pie Chart Implementation */} +
+
+ + {healthMetrics.recordsAccessed.map((item, index) => { + // Calculate chart segments + const percentage = item.count / totalRecordsAccessed; + const previousPercentages = healthMetrics.recordsAccessed + .slice(0, index) + .reduce((sum, prev) => sum + prev.count / totalRecordsAccessed, 0); + + // Calculate the angles for the SVG arc + const startAngle = previousPercentages * 2 * Math.PI; + const endAngle = (previousPercentages + percentage) * 2 * Math.PI; + + // Calculate points on circle + const startX = 50 + 40 * Math.sin(startAngle); + const startY = 50 - 40 * Math.cos(startAngle); + const endX = 50 + 40 * Math.sin(endAngle); + const endY = 50 - 40 * Math.cos(endAngle); + + // Create SVG path for the arc + const largeArcFlag = percentage > 0.5 ? 1 : 0; + const pathData = [ + `M 50 50`, + `L ${startX} ${startY}`, + `A 40 40 0 ${largeArcFlag} 1 ${endX} ${endY}`, + `Z` + ].join(' '); + + // Use different colors for each segment + const colors = ['#1e293b', '#475569', '#94a3b8', '#cbd5e1']; + + return ( + + ); + })} + + +
+ Total + {totalRecordsAccessed} +
+
+
+
+
+ {healthMetrics.recordsAccessed.map((item, index) => { + const colors = ['bg-gray-800', 'bg-gray-600', 'bg-gray-400', 'bg-gray-300']; + const percentage = Math.round((item.count / totalRecordsAccessed) * 100); + + return ( +
+ + {item.type}: {item.count} ({percentage}%) +
+ ); + })} +
+
+ + {/* Appointments */} +
+
+

Appointments

+
+ Last 6 months +
+
+
+ {/* Bar Chart Implementation */} +
+
+ {Object.entries(healthMetrics.appointments).map(([key, value], index) => { + const total = Object.values(healthMetrics.appointments).reduce((sum, val) => sum + val, 0); + const percentage = value / total; + + const colors = { + completed: 'bg-green-500', + upcoming: 'bg-blue-500', + cancelled: 'bg-gray-300' + }; + + return ( +
+
{value}
+
+
{key}
+
+ ); + })} +
+
+
+
+
+
+
Completed
+
{healthMetrics.appointments.completed}
+
+
+
Upcoming
+
{healthMetrics.appointments.upcoming}
+
+
+
Cancelled
+
{healthMetrics.appointments.cancelled}
+
+
+
+
+
+ + {/* Health Summary */} +
+

Health Insights

+
+
+ +
+

Weight is on a healthy downward trend

+

You've consistently maintained a healthy weight reduction over the past 6 months.

+
+
+
+ +
+

Blood pressure has improved

+

Your blood pressure readings are trending towards a healthier range.

+
+
+
+ +
+

Regular check-ups maintained

+

You've been consistent with your scheduled appointments.

+
+
+
+
+
+ ); +}; + +export default Analytics; \ No newline at end of file diff --git a/frontend/src/pages/appointments.jsx b/frontend/src/pages/appointments.jsx new file mode 100644 index 0000000..22a5c71 --- /dev/null +++ b/frontend/src/pages/appointments.jsx @@ -0,0 +1,679 @@ +import React, { useState } from 'react'; +import { Calendar, Clock, Filter, Plus, MapPin, Video, Phone, MoreVertical, Star, User, Mail, ChevronDown, Check, X, AlertCircle } from 'lucide-react'; +import { Link } from 'react-router-dom'; + +const Appointments = () => { + const [filterStatus, setFilterStatus] = useState('upcoming'); + const [sortBy, setSortBy] = useState('date'); + const [viewMode, setViewMode] = useState('card'); + const [isDropdownOpen, setIsDropdownOpen] = useState(false); + const [showCardMenu, setShowCardMenu] = useState(null); + + // Sample data for appointments with Indian phone numbers + const appointments = [ + { + id: 1, + doctor: 'Dr. Sarah Johnson', + specialty: 'Cardiologist', + type: 'Check-up', + date: 'Tomorrow', + time: '10:00 AM', + location: 'Heart Care Center', + address: '123 Medical Way, Suite 200', + phone: '+91 98765 43210', + isVirtual: false, + status: 'confirmed', + notes: 'Bring previous test results', + rating: 4.8, + image: null + }, + { + id: 2, + doctor: 'Dr. Michael Chen', + specialty: 'Dermatologist', + type: 'Consultation', + date: 'June 15, 2023', + time: '2:30 PM', + location: 'Video Call', + phone: '+91 87654 32109', + isVirtual: true, + status: 'confirmed', + notes: 'Have photos of affected areas ready', + rating: 4.5, + image: null + }, + { + id: 3, + doctor: 'Dr. Emily Rodriguez', + specialty: 'Neurologist', + type: 'Follow-up', + date: 'June 22, 2023', + time: '9:15 AM', + location: 'Neurology Associates', + address: '789 Research Drive', + phone: '+91 76543 21098', + isVirtual: false, + status: 'pending', + notes: 'MRI results will be discussed', + rating: 4.9, + image: null + }, + { + id: 4, + doctor: 'Dr. James Wilson', + specialty: 'Orthopedist', + type: 'Physical Therapy', + date: 'June 30, 2023', + time: '11:45 AM', + location: 'Sports Medicine Clinic', + address: '321 Wellness Road', + phone: '+91 65432 10987', + isVirtual: false, + status: 'confirmed', + notes: 'Wear comfortable clothing', + rating: 4.7, + image: null + }, + { + id: 5, + doctor: 'Dr. Priya Sharma', + specialty: 'General Physician', + type: 'Annual Check-up', + date: 'July 5, 2023', + time: '10:30 AM', + location: 'City Hospital', + address: '456 Health Avenue', + phone: '+91 90123 45678', + isVirtual: false, + status: 'confirmed', + notes: 'Fast for 8 hours before appointment', + rating: 4.6, + image: null + } + ]; + + // Filter appointments based on status + const filteredAppointments = appointments.filter(appointment => { + if (filterStatus === 'all') return true; + if (filterStatus === 'confirmed') return appointment.status === 'confirmed'; + if (filterStatus === 'pending') return appointment.status === 'pending'; + if (filterStatus === 'upcoming') return true; // For demonstration, all are considered upcoming + return true; + }).sort((a, b) => { + if (sortBy === 'date') { + // Simple string comparison for demonstration + return a.date.localeCompare(b.date); + } else if (sortBy === 'doctor') { + return a.doctor.localeCompare(b.doctor); + } else if (sortBy === 'rating') { + return b.rating - a.rating; + } + return 0; + }); + + // Function to get status badge styles + const getStatusBadge = (status) => { + switch(status) { + case 'confirmed': + return 'bg-gray-200 text-gray-800'; + case 'pending': + return 'bg-gray-100 text-gray-700'; + case 'cancelled': + return 'bg-gray-300 text-gray-900'; + default: + return 'bg-gray-100 text-gray-600'; + } + }; + + // Function to get appointment type badge styles + const getAppointmentTypeBadge = (isVirtual) => { + return isVirtual + ? 'bg-gray-100 text-gray-700 border border-gray-200' + : 'bg-gray-100 text-gray-700 border border-gray-200'; + }; + + // Handle sort change + const handleSortChange = (option) => { + setSortBy(option); + setIsDropdownOpen(false); + }; + + // Handle reschedule appointment + const handleReschedule = (id) => { + // Implement reschedule logic + console.log('Rescheduling appointment', id); + alert('Appointment reschedule request sent.'); + setShowCardMenu(null); + }; + + // Handle cancel appointment + const handleCancel = (id) => { + // Implement cancel logic + console.log('Cancelling appointment', id); + alert('Appointment has been cancelled.'); + setShowCardMenu(null); + }; + + // Handle confirm appointment + const handleConfirm = (id) => { + // Implement confirm logic + console.log('Confirming appointment', id); + alert('Appointment has been confirmed.'); + }; + + // Toggle card menu visibility + const toggleCardMenu = (id) => { + setShowCardMenu(showCardMenu === id ? null : id); + }; + + // Close menus when clicking outside + const closeMenus = () => { + setIsDropdownOpen(false); + setShowCardMenu(null); + }; + + return ( +
+ {/* Page Header */} +
+
+
+

Appointments

+

Manage and track your upcoming medical appointments

+
+ +
+ + {/* Quick Stats */} +
+
+
+
+ +
+
+

Total Appointments

+

{appointments.length}

+
+
+
+
+
+
+ +
+
+

Confirmed

+

{appointments.filter(a => a.status === 'confirmed').length}

+
+
+
+
+
+
+ +
+
+

Pending

+

{appointments.filter(a => a.status === 'pending').length}

+
+
+
+
+
+
+
+
+

Virtual Visits

+

{appointments.filter(a => a.isVirtual).length}

+
+
+
+
+ + {/* Filters and Controls */} +
+
+
+ {/* Status Filter */} +
+ {['upcoming', 'confirmed', 'pending', 'all'].map((status) => ( + + ))} +
+ + {/* Controls - Sort & View */} +
+
+ + + {isDropdownOpen && ( +
+
+ {['date', 'doctor', 'rating'].map((option) => ( + + ))} +
+
+ )} +
+ +
+ + +
+
+
+
+ + {/* Stats Summary */} +
+
+

Total Appointments

+

{appointments.length}

+
+
+

Upcoming

+

{appointments.filter(a => a.status === 'confirmed').length}

+
+
+

Pending Confirmation

+

{appointments.filter(a => a.status === 'pending').length}

+
+
+ + {/* Appointments List */} +
+ {filteredAppointments.length === 0 ? ( +
+
+ +
+

No appointments found

+

There are no appointments matching your current filters.

+ +
+ ) : ( +
+ {filteredAppointments.map((appointment) => ( + viewMode === 'card' ? ( +
+ {/* Status Indicator */} +
+ +
+ {/* Doctor Info */} +
+
+ {appointment.image ? ( + {appointment.doctor} + ) : ( + appointment.doctor.split(' ')[1][0] + appointment.doctor.split(' ')[0][0] + )} +
+
+
+
+

{appointment.doctor}

+

{appointment.specialty}

+
+
+ + {appointment.rating} +
+
+
+ + {appointment.status.charAt(0).toUpperCase() + appointment.status.slice(1)} + + + {appointment.type} + +
+
+
+ + {/* Appointment Details */} +
+
+ +
+

{appointment.date}

+

{appointment.time}

+
+
+ +
+ {appointment.isVirtual ? ( + <> +
+ +
+ +

{appointment.phone}

+
+ + {appointment.notes && ( +
+ +

{appointment.notes}

+
+ )} +
+ + {/* Actions */} +
+ {appointment.status === 'pending' ? ( + <> + + + + ) : appointment.isVirtual ? ( + + ) : ( + + )} +
+ + {showCardMenu === appointment.id && ( +
+
+ + +
+
+ )} +
+
+
+
+ ) : ( +
+
+ {/* Provider Info */} +
+
+ {appointment.image ? ( + {appointment.doctor} + ) : ( + appointment.doctor.split(' ')[1][0] + appointment.doctor.split(' ')[0][0] + )} +
+
+
+

{appointment.doctor}

+ + {appointment.status.charAt(0).toUpperCase() + appointment.status.slice(1)} + + + {appointment.isVirtual ? 'Video Call' : 'In Person'} + +
+

{appointment.specialty} • {appointment.type}

+
+
+ + {/* Date/Time & Location */} +
+
+ + {appointment.date}, {appointment.time} +
+ +
+ {appointment.isVirtual ? ( + <> +
+
+ + {/* Actions */} +
+ {appointment.status === 'pending' ? ( + <> + + + + ) : appointment.isVirtual ? ( + + ) : ( + + )} +
+ + {showCardMenu === appointment.id && ( +
+
+ + +
+
+ )} +
+
+
+
+ ) + ))} +
+ )} +
+
+
+
+ ); +}; + +export default Appointments; \ No newline at end of file diff --git a/frontend/src/pages/dashboard.jsx b/frontend/src/pages/dashboard.jsx new file mode 100644 index 0000000..0b3bac1 --- /dev/null +++ b/frontend/src/pages/dashboard.jsx @@ -0,0 +1,590 @@ +import React, { useState } from 'react'; +import { FileText, Share2, Shield, AlertTriangle, Check, Clock, Heart, Calendar, MoreHorizontal } from 'lucide-react'; +import { Link } from 'react-router-dom'; + +const Dashboard = () => { + const [activeTab, setActiveTab] = useState('overview'); + + // Sample upcoming appointments + const upcomingAppointments = [ + { + id: 1, + doctor: 'Dr. Sarah Johnson', + specialty: 'Cardiologist', + date: '10/20/2023', + time: '10:00 AM', + location: 'Central Hospital', + type: 'In Person' + }, + { + id: 2, + doctor: 'Dr. Michael Chen', + specialty: 'Dermatologist', + date: '10/25/2023', + time: '2:30 PM', + location: 'Virtual', + type: 'Video Call' + }, + { + id: 3, + doctor: 'Dr. Emily Rodriguez', + specialty: 'General Practitioner', + date: '11/5/2023', + time: '9:15 AM', + location: 'Community Clinic', + type: 'In Person' + } + ]; + + // Sample activity records + const recentActivity = [ + { + id: 1, + type: 'view', + actor: 'Dr. Sarah Johnson', + action: 'viewed your medical history', + date: '10/15/2023', + time: '2:30:00 PM', + authorized: true + }, + { + id: 2, + type: 'login', + actor: 'You', + action: 'logged in from a new device', + date: '10/14/2023', + time: '9:15:00 AM', + authorized: true + }, + { + id: 3, + type: 'upload', + actor: 'Central Hospital', + action: 'uploaded new lab results', + date: '10/10/2023', + time: '4:40:00 PM', + authorized: true + } + ]; + + return ( +
+
+

Dashboard

+

Welcome back to your secure health records

+
+ + {/* Overview Cards */} +
+ {/* Health Records Card */} +
+
+

Health Records

+ +
+

24

+

+3 added this month

+
+ + + +
+
+ + {/* Active Sharing Card */} +
+
+

Active Sharing

+ +
+

3

+

2 healthcare providers, 1 organization

+
+ + + +
+
+ + {/* Security Status Card */} +
+
+

Security Status

+ +
+
+

Secure

+ Protected +
+

Last verified 2 hours ago

+
+ + + +
+
+ + {/* Emergency Access Card */} +
+
+

Emergency Access

+ +
+
+

Ready

+ Enabled +
+

PIN **** (Last updated 30 days ago)

+
+ + + +
+
+
+ + {/* Content Tabs */} +
+
+ + + +
+
+ + {/* Health Summary Section */} + {activeTab === 'overview' && ( +
+
+
+

Health Summary

+

Your key health metrics and information

+
+ +
+
+ + + +
+
+ +
+
+
+

Blood Type

+
+
+ A +
+ A+ +
+
+ +
+

Height & Weight

+
+
+ H +
+ 175 cm, 72 kg +
+
+
+ +
+

Vital Signs

+
+
+
+ +
+
+

Blood Pressure

+ Normal +
+

120/80

+

Last updated: 2 days ago

+
+
+
+ +
+
+ + + + + +
+
+

Heart Rate

+ Normal +
+

72 bpm

+

Last updated: 2 days ago

+
+
+
+
+
+ +
+

Chronic Conditions

+
+
+ + + + + + + + +
+

Asthma (Mild)

+

Diagnosed: Jan 2018

+

Managed with: Albuterol inhaler as needed

+
+
+
+
+
+
+ +
+ {/* Upcoming Appointments */} +
+
+
+

Upcoming Appointments

+

Your scheduled healthcare visits

+
+ +
+ +
+ {upcomingAppointments.map(appointment => ( +
+
+
+ DR +
+
+
+
+

{appointment.doctor}

+

{appointment.specialty}

+
+ + {appointment.type} + +
+
+ {appointment.date} + {appointment.time} +
+
+ + + + + {appointment.location} +
+
+ +
+
+ ))} +
+ +
+ + Manage Appointments + +
+
+ + {/* Health Trends */} +
+
+
+

Health Trends

+

Tracking your key health metrics

+
+ + + + +
+ +
+
+
+

Blood Pressure

+ 120/80 +
+
+
+
+

Last reading: 2 days ago

+

Normal

+
+ +
+
+

Blood Glucose

+ 95 mg/dL +
+
+
+
+

Last reading: 1 week ago

+

Normal

+
+ +
+
+

Weight

+ 72 kg +
+
+
+
+

Last reading: 3 days ago

+

Stable

+
+
+ +
+ + View Health Analytics + +
+
+
+
+ )} + + {/* Recent Activity Tab */} + {activeTab === 'activity' && ( +
+
+

Recent Activity

+

Recent actions on your health records

+
+ +
+ {recentActivity.map(activity => ( +
+
+ + {activity.type === 'view' ? ( + + ) : activity.type === 'login' ? ( + + ) : ( + + )} + +
+
+
+
+

{activity.actor} {activity.action}

+

By: {activity.actor}

+
+ + {activity.authorized ? 'Authorized' : 'Unauthorized'} + +
+

{activity.date}, {activity.time}

+
+
+ ))} +
+ +
+ +
+
+ )} + + {/* Appointments Tab */} + {activeTab === 'appointments' && ( +
+
+

Appointments

+

Manage your healthcare appointments

+
+ +
+ {upcomingAppointments.map(appointment => ( +
+
+
+ DR +
+
+
+
+

{appointment.doctor}

+

{appointment.specialty}

+
+ + {appointment.type} + +
+
+ {appointment.date} + {appointment.time} +
+
+ + + + + {appointment.location} +
+
+
+ + +
+
+
+ ))} +
+ +
+ + Schedule New Appointment + +
+
+ )} + + {/* Notifications Section */} +
+
+

Notifications

+

Recent alerts and updates

+
+ +
+
+
+ +
+
+

Appointment Reminder

+

You have an appointment with Dr. Johnson tomorrow at 10:00 AM.

+

2 hours ago

+
+
+ +
+
+ +
+
+

Lab Results Available

+

Your recent lab results have been uploaded to your records.

+

Yesterday

+
+
+ +
+
+ + + + + + +
+
+

Sharing Request

+

Central Hospital has requested access to your medical history.

+

2 days ago

+
+
+ +
+
+ + + + + +
+
+

Access Expired

+

Dr. Chen's access to your records has expired.

+

3 days ago

+
+
+
+ +
+ +
+
+
+ ); +}; + +export default Dashboard; \ No newline at end of file diff --git a/frontend/src/pages/emergency-access.jsx b/frontend/src/pages/emergency-access.jsx new file mode 100644 index 0000000..c0aceb5 --- /dev/null +++ b/frontend/src/pages/emergency-access.jsx @@ -0,0 +1,124 @@ +import React from 'react'; +import { AlertTriangle, FileText, CheckCircle } from 'lucide-react'; +import { Button } from '../components/ui/Button'; +import { Switch } from '../components/ui/switch'; + +const EmergencyAccess = () => { + return ( +
+
+

Emergency Access

+

Configure emergency access settings for critical situations

+
+ + {/* Alert Banner */} +
+
+ +
+

Emergency Access Configuration

+

+ These settings determine how your health data can be accessed in emergency situations. Configure carefully as this could be life-saving in critical situations. +

+
+
+
+ + {/* Emergency Access Settings */} +
+ {/* Emergency Access Override */} +
+
+

Emergency Access Override

+

Allow emergency personnel to access critical health information

+
+ +
+ + {/* Emergency PIN Setup Card */} +
+
+ +

Emergency PIN Setup

+
+

Create a PIN that emergency responders can use to access your critical health information

+ + {/* Emergency PIN Input */} +
+ + +

This PIN should be shared with emergency contacts or stored in your emergency information

+
+ + {/* Access Time Limit */} +
+ + +

Emergency access will automatically expire after this time period

+
+ + {/* Save Button */} + +
+ + {/* Critical Health Information Card */} +
+
+ +

Critical Health Information

+
+

Specify critical health information that should be available during emergencies

+ + {/* Allergies & Reactions */} +
+ + +
+
+ + +
+
+
+
+
+ + {/* Emergency Contacts */} +
+
+
+

Emergency Contacts

+

People who should be contacted in an emergency

+
+ +
+ +
+
+
+
+

Sarah Johnson

+

Relationship: Spouse

+

Phone: (555) 123-4567

+

Email: sarah.johnson@example.com

+
+ +
+
+ +
+
+
+

Dr. Michael Chen

+

Relationship: Primary Doctor

+

Phone: (555) 987-6543

+

Email: dr.chen@medical.example.com

+
+ +
+
+
+
+ + {/* Access Instructions */} +
+
+ +
+

How Emergency Access Works

+
    +
  1. Medical personnel enters your emergency PIN
  2. +
  3. They gain time-limited access to critical health information
  4. +
  5. The system logs all access for your review later
  6. +
  7. You receive a notification when emergency access is used
  8. +
+
+
+
+ + {/* Save Changes */} +
+ +
+
+ ); +}; + +export default EmergencyAccess; \ No newline at end of file diff --git a/frontend/src/pages/health-records.jsx b/frontend/src/pages/health-records.jsx new file mode 100644 index 0000000..d79346c --- /dev/null +++ b/frontend/src/pages/health-records.jsx @@ -0,0 +1,130 @@ +import React from 'react'; +import { Search, Filter, MoreVertical, FileText } from 'lucide-react'; +import { Button } from '../components/ui/Button'; + +const HealthRecords = () => { + // Sample data for health records + const records = [ + { + id: 1, + type: 'Lab Results', + provider: 'City Hospital', + date: '10/15/2023', + category: 'Blood Work', + status: 'Verified' + }, + { + id: 2, + type: 'Prescription', + provider: 'Dr. Smith', + date: '9/22/2023', + category: 'Medication', + status: 'Active' + }, + { + id: 3, + type: 'Imaging', + provider: 'Medical Imaging Center', + date: '8/5/2023', + category: 'X-Ray', + status: 'Verified' + }, + { + id: 4, + type: 'Visit Summary', + provider: 'Urgent Care', + date: '7/12/2023', + category: 'Consultation', + status: 'Verified' + }, + { + id: 5, + type: 'Vaccination', + provider: 'Community Clinic', + date: '6/30/2023', + category: 'Immunization', + status: 'Verified' + } + ]; + + return ( +
+
+

Health Records

+

View and manage your complete health history

+
+ + {/* Search and Filter */} +
+
+ + +
+ +
+ + +
+
+ + {/* Records Table */} +
+ + + + + + + + + + + + + {records.map((record) => ( + + + + + + + + + ))} + +
TypeProviderDateCategoryStatusActions
+
+ + {record.type} +
+
{record.provider}{record.date}{record.category} + + {record.status} + + + +
+
+
+ ); +}; + +export default HealthRecords; \ No newline at end of file diff --git a/frontend/src/pages/profile.jsx b/frontend/src/pages/profile.jsx new file mode 100644 index 0000000..8f78afd --- /dev/null +++ b/frontend/src/pages/profile.jsx @@ -0,0 +1,297 @@ +import React, { useState } from 'react'; +import { User, Mail, Phone, MapPin, Edit, Camera, Fingerprint, Shield, Lock, Bell, LogOut, ChevronRight } from 'lucide-react'; +import { Button } from '../components/ui/Button'; + +const Profile = () => { + const [faceIDEnabled, setFaceIDEnabled] = useState(true); + + return ( +
+
+

My Profile

+

Manage your personal information and preferences

+
+ + {/* Profile Header */} +
+
+
+
+ + {/* This would be an actual image in a real app */} + {/* Profile */} +
+ +
+ +
+
+

John Smith

+ +
+

Member since October 2022

+ +
+
+ + john.smith@example.com +
+
+ + +91 98765 43210 +
+
+ + Mumbai, Maharashtra +
+
+
+
+
+ + {/* Security & Authentication */} +
+
+

Security & Authentication

+

Manage your account security settings

+
+ +
+ {/* Face ID Authentication */} +
+
+
+
+ +
+
+

Face ID Authentication

+

+ Use Face ID to quickly and securely access your health records +

+
+
+ +
+ + {faceIDEnabled && ( +
+
Face ID is enabled
+

Your device will use facial recognition to verify your identity when accessing sensitive information.

+ +
+ + +
+
+ )} +
+ + {/* Password */} +
+
+
+ +
+
+

Password

+

+ Last changed 30 days ago +

+
+
+ +
+ + {/* Two-factor authentication */} +
+
+
+ +
+
+

Two-factor authentication

+

+ Enabled via SMS to +91 98765 4**** +

+
+
+ +
+
+
+ + {/* Personal Information */} +
+
+

Personal Information

+ +
+ +
+
+ +

John Andrew Smith

+
+
+ +

May 24, 1985

+
+
+ +

Male

+
+
+ +

English

+
+
+ +

123 Main Street, Andheri East

+

Mumbai, Maharashtra 400069

+
+
+ +

Sarah Smith (Sister)

+

+91 89765 43210

+
+
+
+ + {/* Communication Preferences */} +
+
+

Communication Preferences

+

Manage your notification preferences

+
+ +
+
+
+
+ +
+
+

Email Updates

+

+ Receive updates about your health records +

+
+
+ +
+ +
+
+
+ +
+
+

Appointment Reminders

+

+ Receive reminders about upcoming appointments +

+
+
+ +
+ +
+
+
+ +
+
+

Sharing Notifications

+

+ Be notified when your records are accessed +

+
+
+ +
+ +
+
+
+ +
+
+

Marketing Communications

+

+ Receive product updates and health tips +

+
+
+ +
+
+
+ + {/* Session & Device Management */} +
+
+

Session & Device Management

+

Manage active sessions and devices

+
+ +
+
+
+
+ + + + +
+
+

iPhone 13 Pro

+

+ Mumbai, IN • Active now • This device +

+
+
+
+ +
+ +
+
+
+
+ ); +}; + +export default Profile; \ No newline at end of file diff --git a/frontend/src/pages/providers.jsx b/frontend/src/pages/providers.jsx new file mode 100644 index 0000000..346f784 --- /dev/null +++ b/frontend/src/pages/providers.jsx @@ -0,0 +1,354 @@ +import React, { useState } from 'react'; +import { Search, UserPlus, Star, MoreVertical, MapPin, Phone, Mail, ExternalLink, Share2, Heart, Filter, ChevronDown } from 'lucide-react'; +import { Link } from 'react-router-dom'; + +const Providers = () => { + const [searchQuery, setSearchQuery] = useState(''); + const [filter, setFilter] = useState('all'); + const [viewMode, setViewMode] = useState('grid'); + + // Sample data for providers with Indian phone numbers + const providers = [ + { + id: 1, + name: 'Dr. Sarah Johnson', + specialty: 'Cardiologist', + hospital: 'Central Hospital', + address: 'Andheri East, Mumbai 400069', + phone: '+91 98765 43210', + email: 'dr.johnson@centralhospital.com', + rating: 4.8, + reviews: 127, + education: 'MBBS, MD - Cardiology', + experience: '15 years', + languages: ['English', 'Hindi'], + availability: 'Mon, Wed, Fri', + isAcceptingPatients: true, + isFavorite: true, + image: null + }, + { + id: 2, + name: 'Dr. Michael Chen', + specialty: 'Dermatologist', + hospital: 'City Medical Center', + address: 'Bandra West, Mumbai 400050', + phone: '+91 87654 32109', + email: 'dr.chen@citymedical.com', + rating: 4.6, + reviews: 98, + education: 'MBBS, MD - Dermatology', + experience: '10 years', + languages: ['English', 'Mandarin', 'Hindi'], + availability: 'Tue, Thu, Sat', + isAcceptingPatients: true, + isFavorite: true, + image: null + }, + { + id: 3, + name: 'Dr. Emily Rodriguez', + specialty: 'Neurologist', + hospital: 'University Medical Center', + address: 'Powai, Mumbai 400076', + phone: '+91 76543 21098', + email: 'dr.rodriguez@universitymedical.com', + rating: 4.9, + reviews: 156, + education: 'MBBS, DM - Neurology', + experience: '12 years', + languages: ['English', 'Spanish', 'Hindi'], + availability: 'Mon to Fri', + isAcceptingPatients: true, + isFavorite: false, + image: null + }, + { + id: 4, + name: 'Dr. David Kim', + specialty: 'Pediatrician', + hospital: 'Children\'s Hospital', + address: 'Dadar, Mumbai 400028', + phone: '+91 65432 10987', + email: 'dr.kim@childrenshospital.com', + rating: 4.7, + reviews: 183, + education: 'MBBS, MD - Pediatrics', + experience: '8 years', + languages: ['English', 'Korean', 'Hindi'], + availability: 'Mon to Sat', + isAcceptingPatients: false, + isFavorite: false, + image: null + }, + { + id: 5, + name: 'Dr. Priya Sharma', + specialty: 'General Physician', + hospital: 'Wellness Clinic', + address: 'Juhu, Mumbai 400049', + phone: '+91 54321 09876', + email: 'dr.sharma@wellnessclinic.com', + rating: 4.5, + reviews: 210, + education: 'MBBS, DNB - Family Medicine', + experience: '7 years', + languages: ['English', 'Hindi', 'Marathi'], + availability: 'Mon to Sat', + isAcceptingPatients: true, + isFavorite: false, + image: null + } + ]; + + // Filter and search providers + const filteredProviders = providers.filter(provider => { + // Filter by search query + const matchesSearch = searchQuery === '' + ? true + : provider.name.toLowerCase().includes(searchQuery.toLowerCase()) || + provider.specialty.toLowerCase().includes(searchQuery.toLowerCase()) || + provider.hospital.toLowerCase().includes(searchQuery.toLowerCase()); + + // Filter by category + const matchesFilter = filter === 'all' + ? true + : filter === 'favorites' + ? provider.isFavorite + : filter === 'accepting' + ? provider.isAcceptingPatients + : true; + + return matchesSearch && matchesFilter; + }); + + // Get initials for avatar + const getInitials = (name) => { + return name.split(' ').map(n => n[0]).join(''); + }; + + return ( +
+
+
+

Healthcare Providers

+

Find and connect with healthcare professionals

+
+ +
+ + {/* Search and Filter Controls */} +
+
+
+ + setSearchQuery(e.target.value)} + /> +
+ +
+ {/* Filter dropdown */} +
+
+ + Filter: {filter === 'all' ? 'All Providers' : filter === 'favorites' ? 'Favorites' : 'Accepting Patients'} + +
+ {/* Filter dropdown would go here */} +
+ + {/* View mode toggle */} +
+ + +
+
+
+ + {/* Provider List */} +
+ {filteredProviders.length === 0 ? ( +
+
+ +
+

No providers found

+

Try adjusting your search or filter criteria

+ +
+ ) : ( +
+ {filteredProviders.map((provider) => ( + viewMode === 'grid' ? ( +
+
+ {/* Provider Info */} +
+
+ {provider.image ? ( + {provider.name} + ) : ( + getInitials(provider.name) + )} +
+
+
+
+

{provider.name}

+

{provider.specialty}

+

{provider.hospital}

+
+ +
+
+
+ + {provider.rating} +
+ ({provider.reviews} reviews) + {provider.isAcceptingPatients && ( + + Accepting Patients + + )} +
+
+
+ + {/* Contact Info */} +
+
+ +

{provider.address}

+
+
+ +

{provider.phone}

+
+
+ +

{provider.email}

+
+
+ + {/* Actions */} +
+ + +
+
+
+ ) : ( +
+
+ {/* Provider Info */} +
+
+ {provider.image ? ( + {provider.name} + ) : ( + getInitials(provider.name) + )} +
+
+
+

{provider.name}

+ + {provider.isAcceptingPatients && ( + + Accepting Patients + + )} +
+
+

{provider.specialty}

+ +

{provider.hospital}

+
+
+ + {provider.rating} + ({provider.reviews}) +
+
+
+ + {/* Contact Info */} +
+
+ + {provider.phone} +
+
+ + {provider.address} +
+
+ + {/* Actions */} +
+ + + +
+
+
+ ) + ))} +
+ )} +
+
+
+ ); +}; + +export default Providers; \ No newline at end of file diff --git a/frontend/src/pages/security-settings.jsx b/frontend/src/pages/security-settings.jsx new file mode 100644 index 0000000..dddda0e --- /dev/null +++ b/frontend/src/pages/security-settings.jsx @@ -0,0 +1,220 @@ +import React from 'react'; +import { Key, Shield, Clock, RefreshCw, Eye, EyeOff, Phone } from 'lucide-react'; +import { Button } from '../components/ui/Button'; +import { Switch } from '../components/ui/switch'; + +const SecuritySettings = () => { + return ( +
+
+

Security Settings

+

Manage your encryption keys and security preferences

+
+ + {/* Blockchain Security Section */} +
+
+

Blockchain Security

+

Manage your encryption keys and security settings

+
+ + {/* Encryption Keys Card */} +
+
+ +

Encryption Keys

+
+

Your private key is used to encrypt and decrypt your health records

+ + {/* Public Key */} +
+ +
+ + +
+
+ + {/* Private Key */} +
+ +
+ + +
+

Never share your private key with anyone. It gives full access to your health records.

+
+ + {/* Buttons */} +
+ + +
+
+ + {/* Security Settings Card */} +
+
+ +

Security Settings

+
+

Configure additional security measures for your account

+ + {/* Two-Factor Authentication */} +
+
+

Two-Factor Authentication

+

Require a verification code when logging in

+
+ +
+ + {/* Mobile Authentication App */} +
+
+
+ +
+
+

Mobile Authentication App

+

Use an authentication app like Google Authenticator or Authy

+ +
+
+
+ + {/* Security Notifications */} +
+
+

Security Notifications

+

Get notified about important security events

+
+ +
+
+ + {/* Recent Access Logs */} +
+
+ +

Recent Access Logs

+
+

Review recent access to your health records

+ + {/* Logs Table */} +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Date & TimeActionUserIP AddressStatus
10/15/2023, 2:30:00 PMRecord AccessDr. Sarah Johnson192.168.1.1 + + Authorized + +
10/14/2023, 9:15:00 AMLoginYou192.168.1.100 + + Authorized + +
10/10/2023, 4:45:00 PMRecord UpdateCentral Hospital192.168.2.50 + + Authorized + +
10/5/2023, 11:20:00 AMLogin AttemptUnknown203.0.113.42 + + Blocked + +
+
+ + +
+ + {/* Security Best Practices */} +
+
+ +
+

Security Best Practices

+
    +
  • + + Never share your private key with anyone +
  • +
  • + + Enable two-factor authentication for additional security +
  • +
  • + + Regularly review access logs for unauthorized activity +
  • +
  • + + Backup your encryption keys in a secure location +
  • +
+
+
+
+
+
+ ); +}; + +export default SecuritySettings; \ No newline at end of file diff --git a/frontend/src/pages/security.jsx b/frontend/src/pages/security.jsx new file mode 100644 index 0000000..d30d8b7 --- /dev/null +++ b/frontend/src/pages/security.jsx @@ -0,0 +1,201 @@ +import React from 'react'; +import { Key, ShieldCheck, History, Clock, AlertTriangle } from 'lucide-react'; +import { Button } from '../components/ui/Button'; + +const SecuritySettings = () => { + return ( +
+
+

Security Settings

+

Manage your encryption keys and security preferences

+
+ + {/* Blockchain Security */} +
+

Blockchain Security

+

Manage your encryption keys and security settings

+ +
+
+
+

+ + Encryption Keys +

+

Your private key is used to encrypt and decrypt your health records

+
+ +
+ +
+
+ +
+ + +
+
+ +
+ +
+ + +
+

Never share your private key with anyone. It gives full access to your health records.

+
+ + +
+
+
+ + {/* Two-Factor Authentication */} +
+
+
+

Two-Factor Authentication

+

Require a verification code when logging in

+
+
+ + +
+
+ +
+
+
+ + + + +
+
+

Mobile Authentication App

+

Use an authentication app like Google Authenticator or Authy

+ +
+
+
+
+ + {/* Security Notifications */} +
+
+
+

Security Notifications

+

Get notified about important security events

+
+
+ + +
+
+
+ + {/* Recent Access Logs */} +
+
+
+

+ + Recent Access Logs +

+

Review recent access to your health records

+
+ +
+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Date & TimeActionUserIP AddressStatus
10/15/2023, 2:30:00 PMRecord AccessDr. Sarah Johnson192.168.1.1Authorized
10/14/2023, 9:15:00 AMLoginYou192.168.1.100Authorized
10/10/2023, 4:45:00 PMRecord UpdateCentral Hospital192.168.2.50Authorized
10/5/2023, 11:20:00 AMLogin AttemptUnknown203.0.113.42Blocked
+
+
+ + {/* Security Best Practices */} +
+
+ +
+

Security Best Practices

+
    +
  • Never share your private key with anyone
  • +
  • Enable two-factor authentication for additional security
  • +
  • Regularly review access logs for unauthorized activity
  • +
  • Backup your encryption keys in a secure location
  • +
+
+
+
+
+ ); +}; + +export default SecuritySettings; \ No newline at end of file diff --git a/frontend/src/pages/settings.jsx b/frontend/src/pages/settings.jsx new file mode 100644 index 0000000..1215b22 --- /dev/null +++ b/frontend/src/pages/settings.jsx @@ -0,0 +1,675 @@ +import React, { useState } from 'react'; +import { + Bell, + Shield, + Lock, + Globe, + PencilLine, + Download, + Trash2, + LogOut, + Moon, + Sun, + Eye, + EyeOff, + Check, + X, + Smartphone, + Save, + AlertTriangle +} from 'lucide-react'; + +const Settings = () => { + // State management for settings + const [notifications, setNotifications] = useState({ + email: true, + sms: true, + push: true, + marketing: false + }); + + const [security, setSecurity] = useState({ + twoFactor: true, + biometric: true, + sessionTimeout: '30', + loginNotifications: true + }); + + const [privacy, setPrivacy] = useState({ + profileVisibility: 'contacts', + dataSharing: true, + anonymousAnalytics: true + }); + + const [appearance, setAppearance] = useState({ + theme: 'light', + reducedMotion: false + }); + + const [dataManagement, setDataManagement] = useState({ + autoBackup: true, + backupFrequency: 'weekly' + }); + + const [password, setPassword] = useState({ + current: '', + new: '', + confirm: '', + showNew: false + }); + + // Handle notification toggle + const handleNotificationToggle = (type) => { + setNotifications({ + ...notifications, + [type]: !notifications[type] + }); + + // Simulate saving to backend + setTimeout(() => { + alert(`${type.charAt(0).toUpperCase() + type.slice(1)} notifications ${!notifications[type] ? 'enabled' : 'disabled'}`); + }, 300); + }; + + // Handle security settings toggle + const handleSecurityToggle = (type) => { + setSecurity({ + ...security, + [type]: !security[type] + }); + + // Show confirmation for security settings + setTimeout(() => { + alert(`${type === 'twoFactor' ? 'Two-factor authentication' : type === 'biometric' ? 'Biometric authentication' : 'Login notifications'} ${!security[type] ? 'enabled' : 'disabled'}`); + }, 300); + }; + + // Handle timeout selection + const handleSessionTimeoutChange = (e) => { + setSecurity({ + ...security, + sessionTimeout: e.target.value + }); + }; + + // Handle privacy settings toggle + const handlePrivacyToggle = (type) => { + setPrivacy({ + ...privacy, + [type]: !privacy[type] + }); + }; + + // Handle profile visibility change + const handleVisibilityChange = (value) => { + setPrivacy({ + ...privacy, + profileVisibility: value + }); + }; + + // Handle appearance settings + const handleAppearanceChange = (setting, value) => { + setAppearance({ + ...appearance, + [setting]: value + }); + + if (setting === 'theme') { + // Would actually change the app theme here + document.documentElement.classList.toggle('dark', value === 'dark'); + } + }; + + // Handle data management settings + const handleDataSettingChange = (setting, value) => { + setDataManagement({ + ...dataManagement, + [setting]: typeof value === 'boolean' ? value : value.target.value + }); + }; + + // Handle password change + const handlePasswordChange = (field, value) => { + setPassword({ + ...password, + [field]: value + }); + }; + + // Submit password change + const handlePasswordSubmit = (e) => { + e.preventDefault(); + + // Validation + if (!password.current) { + alert('Please enter your current password'); + return; + } + + if (password.new !== password.confirm) { + alert('New passwords do not match'); + return; + } + + if (password.new.length < 8) { + alert('Password must be at least 8 characters'); + return; + } + + // Simulated password change + alert('Password changed successfully!'); + + // Clear form + setPassword({ + current: '', + new: '', + confirm: '', + showNew: false + }); + }; + + // Handle account deletion request + const handleDeleteAccount = () => { + const confirmDelete = window.confirm( + 'Are you sure you want to delete your account? This action cannot be undone. All your data will be permanently removed.' + ); + + if (confirmDelete) { + alert('Account deletion request submitted. You will receive a confirmation email.'); + } + }; + + // Handle data export + const handleExportData = () => { + alert('Your data export has been initiated. You will receive a download link via email when ready.'); + }; + + // Handle logout + const handleLogout = () => { + alert('You have been logged out successfully.'); + // In a real app, this would redirect to login page + // window.location.href = '/login'; + }; + + return ( +
+
+

Settings

+

Manage your account preferences and settings

+
+ + {/* Notifications Settings */} +
+
+
+ +

Notifications

+
+
+ +
+
+
+

Email Notifications

+

Receive updates and alerts via email

+
+ +
+ +
+
+

SMS Notifications

+

Receive updates and alerts via SMS

+
+ +
+ +
+
+

Push Notifications

+

Receive updates and alerts via mobile push

+
+ +
+ +
+
+

Marketing Communications

+

Receive product updates and health tips

+
+ +
+
+
+ + {/* Security Settings */} +
+
+
+ +

Security

+
+
+ +
+
+
+

Two-Factor Authentication

+

Add an extra layer of security to your account

+
+ +
+ +
+
+

Biometric Authentication

+

Use Face ID or fingerprint to secure your account

+
+ +
+ +
+
+

Session Timeout

+

Automatically log out after inactivity

+
+ +
+ +
+
+

Login Notifications

+

Get notified of new device logins

+
+ +
+ + {/* Password Change Form */} +
+

Change Password

+
+
+ + handlePasswordChange('current', e.target.value)} + className="w-full p-2 border border-gray-300 rounded-md" + placeholder="Enter current password" + /> +
+ +
+ +
+ handlePasswordChange('new', e.target.value)} + className="w-full p-2 border border-gray-300 rounded-md pr-10" + placeholder="Enter new password" + /> + +
+
+ +
+ + handlePasswordChange('confirm', e.target.value)} + className="w-full p-2 border border-gray-300 rounded-md" + placeholder="Confirm new password" + /> +
+ +
+ +
+
+
+
+
+ + {/* Privacy Settings */} +
+
+
+ +

Privacy

+
+
+ +
+
+

Profile Visibility

+

Control who can see your profile information

+
+
+ handleVisibilityChange('public')} + className="h-4 w-4 text-blue-600 focus:ring-blue-500" + /> + +
+
+ handleVisibilityChange('contacts')} + className="h-4 w-4 text-blue-600 focus:ring-blue-500" + /> + +
+
+ handleVisibilityChange('private')} + className="h-4 w-4 text-blue-600 focus:ring-blue-500" + /> + +
+
+
+ +
+
+

Data Sharing

+

Allow sharing of aggregated health data for research

+
+ +
+ +
+
+

Anonymous Analytics

+

Help improve our service with anonymous usage data

+
+ +
+
+
+ + {/* Appearance Settings */} +
+
+
+ +

Appearance

+
+
+ +
+
+

Theme

+
+ + + +
+
+ +
+
+

Reduced Motion

+

Minimize animations and transitions

+
+ +
+
+
+ + {/* Data Management */} +
+
+
+ +

Data Management

+
+
+ +
+
+
+

Automatic Backup

+

Regularly back up your health data

+
+ +
+ + {dataManagement.autoBackup && ( +
+
+

Backup Frequency

+

How often to back up your data

+
+ +
+ )} + +
+ + + {/* Danger Zone */} +
+
+ +

Danger Zone

+
+

+ The following actions are irreversible. Please proceed with caution. +

+ +
+
+
+
+ + {/* Account Actions */} +
+ + + +
+
+ ); +}; + +export default Settings; \ No newline at end of file diff --git a/frontend/src/pages/sharing-controls.jsx b/frontend/src/pages/sharing-controls.jsx new file mode 100644 index 0000000..829cadf --- /dev/null +++ b/frontend/src/pages/sharing-controls.jsx @@ -0,0 +1,110 @@ +import React from 'react'; +import { AlertTriangle, MoreVertical, RefreshCw, Trash2, Users } from 'lucide-react'; +import { Button } from '../components/ui/Button'; + +const SharingControls = () => { + // Sample data for sharing permissions + const sharingPermissions = [ + { + id: 1, + recipient: 'Dr. Sarah Johnson', + type: 'Healthcare Provider', + accessLevel: 'Full Medical History', + expires: '12/31/2023', + status: 'Active' + }, + { + id: 2, + recipient: 'Central Hospital', + type: 'Organization', + accessLevel: 'Lab Results Only', + expires: '11/15/2023', + status: 'Active' + }, + { + id: 3, + recipient: 'Dr. Michael Chen', + type: 'Specialist', + accessLevel: 'Cardiology Records', + expires: '10/30/2023', + status: 'Expired' + } + ]; + + return ( +
+
+

Sharing Controls

+

Manage who can access your health information and for how long

+
+ + {/* Active Sharing Permissions */} +
+
+
+

Active Sharing Permissions

+

Manage who has access to your health records

+
+ +
+ + {/* Sharing Table */} +
+ + + + + + + + + + + + + {sharingPermissions.map((permission) => ( + + + + + + + + + ))} + +
RecipientTypeAccess LevelExpiresStatusActions
{permission.recipient}{permission.type}{permission.accessLevel}{permission.expires} +
+ + {permission.status} +
+
+ + +
+
+ + {/* Encryption Notice */} +
+
+ +

+ All data sharing is encrypted end-to-end and requires your private key for decryption. +

+
+
+
+
+ ); +}; + +export default SharingControls; \ No newline at end of file diff --git a/frontend/src/pages/sharing.jsx b/frontend/src/pages/sharing.jsx new file mode 100644 index 0000000..8f2db0a --- /dev/null +++ b/frontend/src/pages/sharing.jsx @@ -0,0 +1,359 @@ +import React, { useState } from 'react'; +import { Search, UserPlus, Clock, ShieldCheck, MoreVertical, Filter, X, CheckCircle, AlertCircle, AlertTriangle, Calendar } from 'lucide-react'; + +const SharingControls = () => { + const [activeTab, setActiveTab] = useState('active'); + const [searchTerm, setSearchTerm] = useState(''); + + // Sample data for shared users + const sharedUsers = [ + { + id: 1, + name: 'Dr. Sarah Johnson', + type: 'Doctor', + specialty: 'Cardiologist', + hospital: 'City Hospital', + date: '15 May 2023', + status: 'active', + expiresIn: '30 days', + accessLevel: 'Full access', + records: ['Medical History', 'Prescriptions', 'Lab Results', 'Vital Records'] + }, + { + id: 2, + name: 'Dr. Michael Chen', + type: 'Doctor', + specialty: 'Dermatologist', + hospital: 'Skin Care Clinic', + date: '10 April 2023', + status: 'active', + expiresIn: '15 days', + accessLevel: 'Limited access', + records: ['Medical History', 'Dermatology Records'] + }, + { + id: 3, + name: 'Dr. Emily Rodriguez', + type: 'Doctor', + specialty: 'Neurologist', + hospital: 'Neurology Associates', + date: '22 March 2023', + status: 'active', + expiresIn: '5 days', + accessLevel: 'Full access', + records: ['Medical History', 'Prescriptions', 'Lab Results', 'MRI Scans'] + }, + { + id: 4, + name: 'Dr. James Wilson', + type: 'Doctor', + specialty: 'Orthopedist', + hospital: 'Sports Medicine Clinic', + date: '5 June 2023', + status: 'pending', + expiresIn: null, + accessLevel: 'Limited access', + records: ['X-Rays', 'Physical Therapy Notes'] + }, + { + id: 5, + name: 'Westside Medical Group', + type: 'Organization', + specialty: 'General Practice', + hospital: null, + date: '18 January 2023', + status: 'expired', + expiresIn: null, + accessLevel: 'Limited access', + records: ['Medical History', 'Prescriptions'] + }, + { + id: 6, + name: 'Dr. Priya Sharma', + type: 'Doctor', + specialty: 'General Physician', + hospital: 'Family Health Center', + date: '20 February 2023', + status: 'pending', + expiresIn: null, + accessLevel: 'Full access', + records: ['Medical History', 'Prescriptions', 'Lab Results', 'Vital Records'] + }, + { + id: 7, + name: 'National Research Hospital', + type: 'Organization', + specialty: 'Research', + hospital: null, + date: '12 December 2022', + status: 'expired', + expiresIn: null, + accessLevel: 'Limited access', + records: ['Anonymized Medical History'] + } + ]; + + // Filter based on active tab and search term + const filteredUsers = sharedUsers.filter(user => { + const matchesTab = + (activeTab === 'active' && user.status === 'active') || + (activeTab === 'pending' && user.status === 'pending') || + (activeTab === 'expired' && user.status === 'expired') || + activeTab === 'all'; + + const matchesSearch = + user.name.toLowerCase().includes(searchTerm.toLowerCase()) || + user.type.toLowerCase().includes(searchTerm.toLowerCase()) || + user.specialty.toLowerCase().includes(searchTerm.toLowerCase()); + + return matchesTab && matchesSearch; + }); + + // Handle sharing approval/denial + const handleApprove = (id) => { + alert(`Approved sharing request with ID: ${id}`); + // Would update the user status to 'active' in a real app + }; + + const handleDeny = (id) => { + alert(`Denied sharing request with ID: ${id}`); + // Would remove the pending request in a real app + }; + + // Handle revoking access + const handleRevokeAccess = (id) => { + const confirmed = window.confirm("Are you sure you want to revoke access? This action cannot be undone."); + if (confirmed) { + alert(`Access revoked for ID: ${id}`); + // Would update the user status to 'expired' in a real app + } + }; + + // Handle extending access + const handleExtendAccess = (id) => { + alert(`Extended access for ID: ${id} by 30 days`); + // Would update the expiry date in a real app + }; + + const getStatusBadge = (status) => { + switch(status) { + case 'active': + return 'bg-green-100 text-green-800'; + case 'pending': + return 'bg-yellow-100 text-yellow-800'; + case 'expired': + return 'bg-gray-100 text-gray-800'; + default: + return 'bg-gray-100 text-gray-800'; + } + }; + + const getStatusIcon = (status) => { + switch(status) { + case 'active': + return ; + case 'pending': + return ; + case 'expired': + return ; + default: + return ; + } + }; + + return ( +
+
+
+

Sharing Controls

+

Manage who can access your health records

+
+ +
+ + {/* Filters and search */} +
+
+
+ {/* Tab navigation */} + + + {/* Search bar */} +
+
+ +
+ setSearchTerm(e.target.value)} + /> +
+ + {/* Filter dropdown would go here in a real app */} + +
+
+ + {/* Sharing list */} +
+ {filteredUsers.length === 0 ? ( +
+
+ +
+

No records shared

+

+ {activeTab === 'pending' + ? "You don't have any pending share requests." + : activeTab === 'expired' + ? "You don't have any expired shares." + : "You haven't shared your records with anyone yet."} +

+ {activeTab !== 'pending' && activeTab !== 'expired' && ( + + )} +
+ ) : ( +
+ {filteredUsers.map((user) => ( +
+
+ {/* User info */} +
+
+

{user.name}

+ + {getStatusIcon(user.status)} + {user.status.charAt(0).toUpperCase() + user.status.slice(1)} + +
+ +
+ {user.type} + {user.specialty && • {user.specialty}} + {user.hospital && • {user.hospital}} +
+ +
+ {user.records.map((record, idx) => ( + + {record} + + ))} +
+
+ + {/* Status and actions */} +
+
+ + Shared: {user.date} +
+ + {user.status === 'active' && user.expiresIn && ( +
+ + Expires in: {user.expiresIn} +
+ )} + + {user.status === 'pending' && ( +
+ + +
+ )} + + {user.status === 'active' && ( +
+ + +
+ )} + + {user.status === 'expired' && ( + + )} +
+
+
+ ))} +
+ )} +
+
+ + {/* Security notice */} +
+
+
+ +
+
+

Important Information About Sharing

+
+

+ When you share your health records, you grant access to potentially sensitive information. + Always verify the recipient before sharing, and regularly review who has access to your data. +

+
+
+
+
+
+ ); +}; + +export default SharingControls; \ No newline at end of file diff --git a/frontend/tailwind.config.js b/frontend/tailwind.config.js index c9d23f6..dd8a0da 100644 --- a/frontend/tailwind.config.js +++ b/frontend/tailwind.config.js @@ -1,23 +1,10 @@ -const plugin = require("tailwindcss/plugin"); - -module.exports = { - darkMode: ["class"], +/** @type {import('tailwindcss').Config} */ +export default { content: [ - "./pages/**/*.{js,ts,jsx,tsx,mdx}", - "./components/**/*.{js,ts,jsx,tsx,mdx}", - "./app/**/*.{js,ts,jsx,tsx,mdx}", - "./src/**/*.{js,ts,jsx,tsx,mdx}", - "*.{js,ts,jsx,tsx,mdx}", + "./index.html", + "./src/**/*.{js,ts,jsx,tsx}", ], - prefix: "", theme: { - container: { - center: true, - padding: "2rem", - screens: { - "2xl": "1400px", - }, - }, extend: { colors: { border: "hsl(var(--border))", @@ -53,39 +40,13 @@ module.exports = { DEFAULT: "hsl(var(--card))", foreground: "hsl(var(--card-foreground))", }, - success: { - DEFAULT: "#10b981", - foreground: "#ffffff", - }, - warning: { - DEFAULT: "#f59e0b", - foreground: "#ffffff", - }, - info: { - DEFAULT: "#3b82f6", - foreground: "#ffffff", - }, }, borderRadius: { lg: "var(--radius)", md: "calc(var(--radius) - 2px)", sm: "calc(var(--radius) - 4px)", }, - keyframes: { - "accordion-down": { - from: { height: "0" }, - to: { height: "var(--radix-accordion-content-height)" }, - }, - "accordion-up": { - from: { height: "var(--radix-accordion-content-height)" }, - to: { height: "0" }, - }, - }, - animation: { - "accordion-down": "accordion-down 0.2s ease-out", - "accordion-up": "accordion-up 0.2s ease-out", - }, }, }, - plugins: [require("tailwindcss-animate")], -}; + plugins: [], +} From b4798b00920eefe147a187ba8aaa5c4b91122fc2 Mon Sep 17 00:00:00 2001 From: Muaz <6muazx@gmail.com> Date: Tue, 8 Apr 2025 08:04:40 +0530 Subject: [PATCH 05/14] login + register backend --- .env | 10 +- .../api/__pycache__/models.cpython-312.pyc | Bin 209 -> 1789 bytes .../__pycache__/serializers.cpython-312.pyc | Bin 0 -> 1423 bytes backend/api/__pycache__/urls.cpython-312.pyc | Bin 0 -> 487 bytes backend/api/__pycache__/views.cpython-312.pyc | Bin 0 -> 884 bytes backend/api/migrations/0001_initial.py | 51 +++++ .../__pycache__/0001_initial.cpython-312.pyc | Bin 0 -> 4473 bytes .../0002_alter_user_email.cpython-312.pyc | Bin 0 -> 788 bytes backend/api/models.py | 31 ++- backend/api/serializers.py | 32 +++ backend/api/views.py | 11 +- .../__pycache__/settings.cpython-312.pyc | Bin 3632 -> 3660 bytes .../backend/__pycache__/urls.cpython-312.pyc | Bin 1039 -> 946 bytes backend/backend/api/migrations/__init__.py | 0 backend/backend/settings.py | 29 ++- backend/backend/urls.py | 28 +-- backend/db.sqlite3 | Bin 0 -> 135168 bytes backend/src/index.js | 42 ++++ backend/src/routes/auth.routes.js | 12 + frontend/package-lock.json | 10 + frontend/package.json | 1 + frontend/src/App.jsx | 14 +- frontend/src/api.js | 23 ++ frontend/src/components/Dashboard.jsx | 139 ++++++++++++ frontend/src/components/auth/Login.jsx | 211 +++++++++++++----- .../src/components/auth/ProtectedRoute.jsx | 84 +++++++ frontend/src/components/auth/Register.jsx | 66 +++--- frontend/src/constants.js | 9 + frontend/src/hooks/use-mobile.jsx | 2 +- frontend/src/services/api.js | 181 +++++++++++++++ 30 files changed, 856 insertions(+), 130 deletions(-) create mode 100644 backend/api/__pycache__/serializers.cpython-312.pyc create mode 100644 backend/api/__pycache__/urls.cpython-312.pyc create mode 100644 backend/api/__pycache__/views.cpython-312.pyc create mode 100644 backend/api/migrations/0001_initial.py create mode 100644 backend/api/migrations/__pycache__/0001_initial.cpython-312.pyc create mode 100644 backend/api/migrations/__pycache__/0002_alter_user_email.cpython-312.pyc create mode 100644 backend/api/serializers.py create mode 100644 backend/backend/api/migrations/__init__.py create mode 100644 backend/db.sqlite3 create mode 100644 backend/src/index.js create mode 100644 backend/src/routes/auth.routes.js create mode 100644 frontend/src/api.js create mode 100644 frontend/src/components/Dashboard.jsx create mode 100644 frontend/src/components/auth/ProtectedRoute.jsx create mode 100644 frontend/src/constants.js create mode 100644 frontend/src/services/api.js diff --git a/.env b/.env index 2d92345..54821ce 100644 --- a/.env +++ b/.env @@ -1,7 +1,13 @@ SECRET_KEY=django-insecure-*$l30g@bo6!s*5y4i(z@@8aq(cc*k07cp0h4vk^jp$-mufw1rt -DB_NAME=postgres +DB_NAME=healthchaindb DB_USER=postgres DB_PASSWORD=testing123 DB_HOST=localhost -DB_PORT=5432 \ No newline at end of file +DB_PORT=5432 + +NODE_ENV=development +PORT=5000 +MONGODB_URI=mongodb://localhost:27017/healthchain +JWT_SECRET=your-super-secret-jwt-key-change-this-in-production +FRONTEND_URL=http://localhost:5173 \ No newline at end of file diff --git a/backend/api/__pycache__/models.cpython-312.pyc b/backend/api/__pycache__/models.cpython-312.pyc index 4f65946208648dae6df215bea995ae9d495a9764..979970b2170702507171a8d71e841491f5b9cad5 100644 GIT binary patch literal 1789 zcma)6O-vg{6rTO@+L&DowxLRaK!u@Q)S{|Mb0{c;ViQ_{K_Sh-vbtLDj&~vz zf_m_Q14nK-RF!h1A_tBfdyLczRxH^XDW{&eQIV<=r@q-WE~rK7O8(x@d^2z6{rERU zi6Z#sbMH3SMTCB5#?=?NiB@&cL2073u~K-^!HC@TspwaSS9uz)>RuV!9u2Pt3iu?` zF+7J*DeX&D0H0XVElkB#K*lIy-EV|Uwgs`(A4=zXsfoL_#r zlwbbzb0$AwvY;E=*f#TewVV%zW~RCW8D}>gffI+}=PCeusQupPzTCb$*@z626nMxK zfN}MD&=JfxV)J?NHNXq>Big#Y3O@E71p@s9(+-TzJa?SFxW?dr7B`@r*X*NMa2iXWM$-PXvud2&gjVS8ax5!;Bl^HD=(4unL+8AZl zKmPXh{id>BUpkl2(A2@R*5G7gaPl~Qa=$tFsGe;PPIi5XlXP=%0et_Qc`N`g`3TO8 zjqN{g#l{-3vBP^1CpKGO{!8RTQ-2_UiwGCWaKR(lps@*i-`vveO=re%Y>$)+GkVP{ z%>=zpSohJM4#6z=wF(k*Gg+6BxnMRQl3Ccodds~7a4v8h_ZHoIi$>mwBV2~tpZgOr H>0jY*39zvA delta 141 zcmey%dyz5jG%qg~0}#x<_&MDMNIwQ~V1NnA_$&ZqOlL@8NMX!j$YqRTWMrsh(qw)K zlGbFr#g?0&lA2TOr^$4SGbJlAFFju`C8>xRC^b6ILy5IOG6ATtKBlYA#7Hi>1|i-?P2Wde?os zCZuvj4>`CO;Fb%A2yqC?C*U)1aRpMXr&a=S;?~whNI5aHYp3mzC;K-uZ|2RsnK!e) zE-d5-tY03yYJH;-@)ruTrB9j3HZTW-5k`H|r52^gE56cIEtL|5aC$s7wKRo%PMEq+ zn8wvk;(O*UrN?SYt<0WoX8uOOq_WWBvE7fju)87SKJ4T{@EqT}&qd^>M^c1aCTp?^ zN);9lv&m$BtQ$ipQjZc?V9HOQYvR(QYW1)OpwOe3@%{B8u%q zIp;ewc7ZuDx5K90@I>5}nHCQi7g7_U&*glt9R}PE`rQWX6@1U-LG)UVdfZLw>cn0c z$c1(o^}N{e(;$W)x~U?!D?AJm_6P(>L=R_XLD~bq<2=5@AK`0@V5h@60 z0XEI7H0;E;ZOfdE&)fGo@bk8PukZMn8b}(JN9xJr%S_YbK8xf$|2h_qy|e3xR`fHO zfIo8b#it+D(KxF21GnweKjYC(9QNv4VKW9pUcbG4tA2ZZ1L^xn8;-jJ^{G2OuMX#C zJyq-NNey2?7~nPw|HuZ|C*#HPP=Bdp&g6!f=Zj={^)MbS-T33;dq>wsE9>KPmk&2a zXU%ct(qUy(UK_8h9aTmbZjLXRM;oJyADq&e{rRhhcSlRtUsUo-xkvNjGQ<#90o-Yq z0!s)^8qUSU0yrQs1(K`*J)ZKE#AuPM&TIx|8$dgmH-st6s7D6$C;BaYpbV5PVk(=W z3OZAhdQW&kEG~c~vtgsd-8hnYS8(hsY)Dg;TEzXPEPMr1hz-YVKu2n*jqonjYE@i8 z2buKT{R%$J)@hAv>GlekL^!Mu$x~xt82oOmJS~>K8$233DXu-n--kyvfT1=nmWKJH zM4P5A&chM0jPNc37EN47s3IhV#OMMOPXL(++i{=#tCaQJ3j#2?q3Y$-8ui$aCDw6* zR#O3pebPtcLdfstx@p~ewSY7mPW0j{`4j%#>Z)Ue^ls+T1XJqxyY~{D?xrf>-ZJCz8|ME#?{c&ae9|A(+=08thaMb_+ literal 0 HcmV?d00001 diff --git a/backend/api/__pycache__/urls.cpython-312.pyc b/backend/api/__pycache__/urls.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..8f556f55f239457a166e7045b77b0d1592e4239f GIT binary patch literal 487 zcmX@j%ge<81kpZU(ysvN#~=<2ut6E0TY!w|3@HpLj5!Rsj8Tk?3@J=0%sEWC%u&pY z3``8Ej43QYk~xb5rWHZ2hB8xF*RZW-hH$_-*a_<3Kmue3O|NWTawq<@P8#sq20OUzBJ;sC42P1V1}l$+|O z$##o7B`Yy6JzuZ1D5tmxc z=DM{0MQQ!Z(uO;@uCO@YWZ~$Lx*)1KA?CWM=0#D>%c9yFRIad?e_&A;i=TDP`%zot+R6H@y4acklPRFU4XW@e{p0Z-2rF{SeCR zWTs#;;@|)|$iW18SjSjkiAbKT%L$f=;$`#<$BL67Sx?o~gwGPq%jr2Uujet65Xgtq zka|HvPmrVTBS(Ww_v$R={63UZtQUv5dj3o(bsT3Y_zqBxN^J>5C#9Vl~+Af*rD0wg`!}-3aKAK}c0zK)k>$rj435u^6K{L0|3z^<2i> zz)xvlpV35Ae6hZE-w<(`5&3qx-?}_`4xVJ64$H#A2 zeb;!+t7nb=kc#dTA=0rGF80w$dGY<|-RQWyJT5OEm9Ou$PO7)QDpyWwD<5lPWr;4$ zoHpyFc){sfemiKePC!FD3Kp0_c}TBuRs1m4;Nqu@ pF+N2%PEjL~x4)rB-_^>ITHRC56&c_B@anj-Iga3e0xuOwsE<}+SSu* z=t|*gRi#9!m0C5`j|Ng4x`qY|v8pHzor!1BaABlM^C(63*YE&purOAI96wuUib2=^ z$bt_ys(kp#*?9ER6Fz+O0p!gS$Xg#ketZIX`+r7$LXp>MM&M`Y4!Vmb&?kk-s(POK zz=?bG#59_zK9Ony7MVqJzvs~W|Iv3f*l%O#w4ar}O9GD-?pK%yIPKE*gXn_oTc|MO zGVr8J%9&}qZ+LiQRFo7G>tfD;M^e@eQ&go47BkW_QNd~oV6GL|vpE?}+uYn#)MW?Y zv?eBWESVVD=d!w-k@RhG6K~r-fXb30=dm4liuJf=V5-mdW+lVe(sXq29DJu#eQZHL z4@|cA)J(QifQ}OSjmbeer7t(c!?zvG~1I?voLK1IbJtjQBCRw>AAUP$Q@IopQ?hM6-28ITQ zL_t%P?byLLB&r^}0qeS^iy3SfQVJXTC0N!8W9WqDZU0lq4U$YvH;#h-F6DVYoeShm zqP$MX%sEN3cGM}CU)xTelZ)(yz7f`m90Os7a8 z!Gd!#CakCiR&e5|N@UeAAt8d66yUaiO;Q6=BBA9}$d2uC4%z1*W?0hE1H^u&edYVao<48rm%Ucb>VWS0_Ol$ zz)~VjvIefp`jOU{O?y!^uxY!9^1PoAU5wg{dYk}~maqdlRtS4JB?H0&|)#Pa8<<#)&OR8RE@+r0vs4agp~bD^3q#jKLk zCB?4O;u}z_2C%Nnp_rtM6=c9h*16_-Gm;9et{aQdR@*C5=S9)>iz3{Aa|(ueKopyt9h$I%aUxUEY;xqolyaBV! zkE@e6*J-@hb1JaxIuOXFsb$w6Ye{oU(((H2^7Q)b@O3f{k})nNHX%IgQdVAn_ix4e zFnno>M#Cr4JuWjNd^Rzhb^`Xna0%=wW;}4|(pQ=lO#}KjZ8CwZH6*6upuC zP|NqBCab0Y>!;k!QR?8iO#g4(<3$JgMI##XV z&-X(eR_EBx({krnv2$#GH43#>u6-@4jI<2np7tQ6a@nYBb?#dy|8Fl4HVl3UiI#cSu3d2=0pJkH%tOI zTQ`bv+j%5=oi+8 zL9}v{#D3cpwSN!nL%v2xzD9~&BTxX1rLG57aA7|rSly#9E|b zr`E?xJrAwmqxY@7?kROU`04_DHMRG+)N$;^2=QX1*bZKU3v;YOjQ@hWN8+`{e?bP! zKOw^%*Q|@UH%EY>dDniZ$LhQFVzu0NtJrsIuh&}If{hvOQ=%csQAJOnTtWJjPmnBJ zMnni<{&1lAV#DDu({*M4O6&(W-_!st)!4o>{fEWhE|$YX#qdx$e4`k?Q4UWO!xMiC zPg%ix(0t7eZ*K`c{~_Pq==s&W9){{+EPKuDOYr``gI2t9~1lZ5CT-u!-V-h1=jZ{~ZY;(%EF7vt6!1n^Um z*(y!JXib1)pg>Uw5%N%^c^U!@9s$)3f$9vk%~{fX|Ey22r*{pvbSjCfaVH8|DanH* z&bS#Rl!aMyo&#V{$%qPY3=BL(!P6*eAy3zU8Ew_@zbPQvpz_RYpZni088v8$nkNgh zc22Ar5*4kk?K<3kI_pU^sFO~>%Z*0kCJy2t4@k(3B4a5pv4{lW=^s&Z`)x&$B|>S+ zvdq=RT;vT7S=`F^xLL%(LBV+W6-#%LjNzC>OvT7~72}5V@zQR^sQRYstqpI2Insgs`aBA z{c62et$)nE-u{;SsJ&4fAI55A5XP9{ob7kBZ+O?)Bgx9Yx_wLC+ zE5Nrw^>!I$7iiKY7(E4=;;L{>V~l1{=pE%KhiX`FEZD?@=A0@;yp z_VVRZ-o(GUoO|+Ex6jvq6UUG%qg~0}wpE_&Gg$BCjN4-9+_^7U>L80_hAX9E%vE1c5XskQPeeiV{xY z2J+Zbcz`7DBF0R{6uvnu>5Ne#Dg05QDFP{i6ZdH|3Qdk`_ucES(~sqL89Ehbc;ii6NCOMQM%l zYDSRz7#P5Ol{Km`KF}TPDQYR|b6BHfQ#7LFQZ%FFD_J$Q5_N$ZxLmRlH}7RiVBD<0 zV#7Fj9jnOX>8vj&$FQnzZeY8>$SA%!kRy_jNt1E%D$Z;T8=&!;jJMcxGxJJIQj1@L zYy|RIQW7hRfi#;_YGP4pktXY8V=jG{TcSb!A&w!g@jx$4*FEQPfldY9wGtrN*c-hJ=8_kDMMw!fuP2_)hz|6Q4m zAoK%@afCafIGvLa+D9fbB^x=CAxVHEcEpJqQ3o41tfO|!kqtS-*p54jp*X6cN+^P8 z)QnN7c5Q5YzSgGC@fdZ)Js?7zEV5ji88ZLo&ZscsUzARCScD2yWaUyt;({V66zZjl zD;E?gp}=OkBjy4zzHudHq;@B=lRtsXN%tu-xZF?)pyU7M~-JUZ2FkN2L~_LZ;Nyil_cp1-8x2hkILBV+H+Q((aUC1C@)q|xo`YP(JnAubRG~EhlYMmg_uOV5-O1U&>C7F? z1heEd9Qb?Bp5Ro9el{2@VlpXkUn<-q=>ZcI7fOy;YC_-N9WuN>R(U~;VM^$IoM+l# zCC7$o3@92KiC<*wP=|O6Ah=WvX9Cdp1h>c;(F|+GVjsgA8dK;vil|I=NJm5rWYaE{ zJYpic6Rt%@7FntzTO2`q9r=%55)N28_P53&tteM*E702^en2TE0%yFC%ETD=mE@Qo zbxBdwhZCAr{aRgb6<7gN3gJ@_J6vwJS882D$=M1PgtL2X8@B_0GbdWF_COe&Lj4;H zX@*Q$B4Ne2p|cHG|HH=CheUTR$a3%pqaBgvW15Kpm{9ez((HsAe|9QkFi?T<*jlz6 z;C^joJh5vsj?fz3kgRK0DpUTMZ7-LD2n=jGwe3NL+YSQ%ySb=8!0sZ0~L*+qc&3zH0aSCH&ega#$Q)%-`jbj&pWjT>)GN zetH%DPFs%SoViPm_X8v6##eOxY!=O2dVB5N`bm5opKKg&eDQ9cw%jH6&4RM8^e4t5 BIYj^f diff --git a/backend/backend/api/migrations/__init__.py b/backend/backend/api/migrations/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/backend/backend/settings.py b/backend/backend/settings.py index 3738572..5ced405 100644 --- a/backend/backend/settings.py +++ b/backend/backend/settings.py @@ -30,7 +30,7 @@ # SECURITY WARNING: don't run with debug turned on in production! DEBUG = True -ALLOWED_HOSTS = [] +ALLOWED_HOSTS = ["*"] # Application definition @@ -51,12 +51,12 @@ MIDDLEWARE = [ 'django.middleware.security.SecurityMiddleware', 'django.contrib.sessions.middleware.SessionMiddleware', + 'corsheaders.middleware.CorsMiddleware', 'django.middleware.common.CommonMiddleware', 'django.middleware.csrf.CsrfViewMiddleware', 'django.contrib.auth.middleware.AuthenticationMiddleware', 'django.contrib.messages.middleware.MessageMiddleware', 'django.middleware.clickjacking.XFrameOptionsMiddleware', - 'corsheaders.middleware.CorsMiddleware', ] ROOT_URLCONF = 'backend.urls' @@ -125,21 +125,19 @@ USE_TZ = True + REST_FRAMEWORK = { - 'DEFAULT_AUTHENTICATION_CLASSES': ( - 'rest_framework_simplejwt.authentication.JWTAuthentication', - ), - 'DEFAULT_PERMISSION_CLASSES': ( - 'rest_framework.permissions.IsAuthenticated', + "DEFAULT_AUTHENTICATION_CLASSES": ( + "rest_framework_simplejwt.authentication.JWTAuthentication", ), + "DEFAULT_PERMISSION_CLASSES": [ + "rest_framework.permissions.IsAuthenticated", + ], } SIMPLE_JWT = { - 'ACCESS_TOKEN_LIFETIME': timedelta(minutes=60), - 'REFRESH_TOKEN_LIFETIME': timedelta(days=1), - 'ROTATE_REFRESH_TOKENS': True, - 'BLACKLIST_AFTER_ROTATION': True, - 'AUTH_HEADER_TYPES': ('Bearer',), + "ACCESS_TOKEN_LIFETIME": timedelta(minutes=30), + "REFRESH_TOKEN_LIFETIME": timedelta(days=1), } # Static files (CSS, JavaScript, Images) @@ -151,3 +149,10 @@ # https://docs.djangoproject.com/en/4.2/ref/settings/#default-auto-field DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField' + +# Use the custom User model +AUTH_USER_MODEL = 'api.User' + +# CORS Configuration +CORS_ALLOW_ALL_ORIGINS = True +CORS_ALLOWS_CREDENTIALS = True \ No newline at end of file diff --git a/backend/backend/urls.py b/backend/backend/urls.py index 97def4c..b693eee 100644 --- a/backend/backend/urls.py +++ b/backend/backend/urls.py @@ -1,22 +1,12 @@ -""" -URL configuration for backend project. - -The `urlpatterns` list routes URLs to views. For more information please see: - https://docs.djangoproject.com/en/4.2/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 +from django.urls import path, include +from api.views import CreateUserView +from rest_framework_simplejwt.views import TokenObtainPairView, TokenRefreshView urlpatterns = [ - path('admin/', admin.site.urls), -] + path("admin/", admin.site.urls), + path("api/user/register/", CreateUserView.as_view(), name="register"), + path("api/token/", TokenObtainPairView.as_view(), name="get_token"), + path("api/token/refresh/", TokenRefreshView.as_view(), name="refresh"), + path("api-auth/", include("rest_framework.urls")), +] \ No newline at end of file diff --git a/backend/db.sqlite3 b/backend/db.sqlite3 new file mode 100644 index 0000000000000000000000000000000000000000..d2247e194b76fd1b931563aec857833e44078178 GIT binary patch literal 135168 zcmeI5TWlNIdB-{8O*e8ROY%5tD@)PV+KlWKucEj{IaiQn!Gvj#qs;$ zFNHn~-3^`b@Az|LVc+|M+&Iz?Zk-4#mo7<9a&4_t)wMlC-EZkl^RTBkYeuVOH0rH^ z8OJVf7dLJfW4AY6xn7LL2inDB=i){s9y97~eOGVBwr<{zZQZ$kJ@)$c=8cW*yRp}b zcVioOZr|M8B9AwUTer{0;#>+VRWf#|pj~LY_-b*xxOKUB%f6(zLLzpBWo~mln_HW= zH#e?dzbg`pSIDbfEw!&Am-GGhJ=HES+BFgfD@s^dS&^PZ+*Eg)jr~2FrFl9#fS{TC z5#q)Ljfjlfj!0|ucu2XtDEW+fMSs+KP^BKEYWwX5KUcdEm9jpKzk6{ZtSl}{?`^v9 zTKVDrKo3Q7PiwW_ZZs(bA8O6=J*{~zm0nMD1CgSsskI7_Z}nclNtsl%_Vlu$RgKSTZ5rwv zI+AX8_ZqD|qpejvEs^$^RU2iGBbP-YGapi}E)5KKHsF{&Ek&I`e4gpd&V`kwC8<+0 zL%y41+e~@FzH4%*z#kGyA}|x3GsnWpTPsrM2Jfj{i9J-RHjiAnPiWZ2hl&p` z=H45-^EO(!uhn-Ou8bL2Nb8(pk8!k5!1fpo@aSmko+1p0t%==d6P=YP^)M3e=wUtO#ArdfOR03S zT-qu0*Bu#2x9Q&D>}is%0;KkSWL)X^3(~>qVXUnJ9z@AU6&cTZtPyweHi9VbFeiTRtg2_N#5>%JtF!y#6+Lo*i-aSoznC&+Cwe39k~;_ zYd^fRdj_A;u94_m3x$)gRkcQgbXCo-l`5G^ro=<0Pk}(+ zc|574btbSWTJCd3M;Ft(t+lrQ!{p({IRJe zYHlZtx%|d^@(;_!e$sQ@GN>Jg+)yZ&kXDVJ=Utjq&o4ySaq{~<`7h*Okgv;Q(QiiI ziRL5!5&6A{8aY1wchkQ%U7TK+`rg!6r|wLhB18Ou00@8p2!H?xfB*=9z(=1zW&Cru!CLU(Y6Uo5zAa<;@MPQZwpQG{aWgX<@PXSk)>OzB1jYt2$~lHeYIxNsrB5;5%NUUH9e)& z{XM>zzSnF#dhA$!=?^TO%jfc!r^&+&%R^GMnz_!vS4p2DBUM;Sr>;(slRLsmvfylb zzI!r~qf|Dv{^}$-x@9?9cRI2*Q=Y~pnN6otuT7Ajw=6$-;pZXk0T7=?leRl3u=HlL z(Nt>`%qwAX@tWl# z=d72#y+r#Y)xO;;wZ4|mt-l;1A68o@vrZpfHjTY@-?mQW3&}$Ia*#Y&ZB5Ey&z$iFyleIs}-=)W*7zBj;^ z5!t$5B;a2f=f(JP)`H|;m3qEgFi-WLn-Ixj1wk3}XDmzSqC%+Nnk4g(`~{!B(k zX3FtsKw6YS><v$G97N`aP}$q*ZB5__EW(X1Z`_r_&k$aT;S@?Zn`*z{FXJd$#8A zf%(A1iX_}xiGH3ad|HWPGl7X^S^%hY|BQ(J{eMXQyifj6{s;N5un@;cp0w4ea zAOHd&00JNY0w4eaAOHd&@RK4i6$<#~7R3JlTrl8UJSi4ETH^P~K)|EhRsNjZk>8;L_yGYB009sH0T2KI5C8!X009sH0T38YAn2E*&y!Uk{<{1ta!Gzko{oMe z`X|xfj5ecN(HEos$On)8Co?v+1u+{~{H@4+ww&2!H?xfB*=900@8p z2zUsbIuZ0q&z}uFH1xOC{g&Rmtkq-eBzA{RVhZJ+UI|t7s@~SS`R1u5 zwuO1hEuLE-i^b)TR;hFy&}W_(pQpU#3qgMMmfmU^je1vrIDcV|Y!a!U(BQBVGAz4e zrkzmYuoGfLN^F+wPOk)+9)}T=Bu-am$l^jiz*UtS^|oGbw;%86U1cII#u8U$GEQ9# z2zefJA<uTm_@hQq* zzTg*PyB0#GX)|fs2z^}}p^TrOnjo9!&-$69t^pInPtJ`~?n-8itJu?qKgdX|z+BDHCd^oCySd6K7LgY_+jV zZQXp_rA7%co^sAlRx3Fn)M+PV+LmM7Qiya~3W;6IxI~uAt4yHNjLGZTVE^AUVBr)5 zKmY_l00ck)1V8`;KmY_l00fR80mu9Qk6`UkB@h4s5C8!X009sH0T2KI5C8!X00Bk- z^MAwv2!H?xfB*=900@8p2!H?xfB*;_eFFIY|Iu$_)DQ$f00ck)1V8`;KmY_l00ck) z1laz6By`3npNW1D4Mi?a{b2G(lQ$>6IDS9;rO=0=yP-4w9e-{t?0bI%RtL9Egq2H| zq$jzyR;udSp26S9VIJ%^BD4l({C`~I*yZiw#_eM4_QosMi?R4XE%Dg7xKWA640=Du zF1@N`>*np))}8CuW3O*--q_f_8+)yIH@0!-_RY;L@_3`Tb^ClQ&fajMvQi~umkQd2 zwu`S8w~Jesi?{4ciYp{yS6JpY*R#2`d3$r?`t`dav3P~N+UU6N9z`za`|W#<_fd>? zjl{u<5>{4Lq$d$~%klSZ*zIMW&JG}G=6-~@aX}*@>(ltV7Z<|H;-d84rVFo?AMOwIP$c)XR_pCXlS1&J)}%LrG|#2d z>xph4QZ!YyR-3&L#Hhz(6|JqejhfCQoAQj7+S;dZV{uGD+-OvtC0S}+tLYBkxuoqo zc4uqzjXOnhvtu;r8beGi8N*HVnr2k(i=}f}`(i?DTie;`6-w36%5CEzDfQNY)mXhx zO{EheyZWMYlha3unew4X)uTj_4`hKm%_;d;9T)Oxr8aryqXtwW}7qR%#>t8Ca zl5Y1^qpa6ky%%s&CKatcy=-VzITlvl zT9G<8cu(a@9Am3^(~-(vyF{agn%>Fg*Ys@3V>c;eZ*E;FeuhSKCVK=g@z~9+9&1)p z&t>C@&aK(7@_0o$kVlePZd7zuc{Q2RN|}64Q}^q}1COkr5N&qR*ofNuyB@hF6sneo zc;aAr#=ONyc0kzJJaXkepzDn7iJdvEN{+i2xJy>F~xy$_3rgt+R{Z`a*A+XIBB z$GKxQopX#IiOx%MSlKN|9nEfivwPV9TF!u@NL|mCbLB!>=M;O4qkRIl$7p~@M_cz4 zVL)t6>^__5tVF4Yk$6WB>nSHj3({RmrIY2-PNBc<$Vj?P_YP-IlWY|rwf7_AO2=Q2 z4o(kaZ58k!N|q~`bgq=NyIW69rw8}C9$TA=i>d0m%*b_P_p0uj(RkAwY)yuhLP2_x zxBFj@i2e;R(Wf`|6g^a@G`;kF%y#xhTh_>(&|UlCot?Y43ygM+MCV#4tTZxG=N%qA zeMSs=6x>s*T1l^}H5#O=YJRO$$y72W9x{Cj1p3BHAH(6E%_!+pjMc2GDNZrMWl=}i z42G3VMmi|)*yvNTPg2Mo6@4rY6%w|-fy1Y%x_@@!^|26}$=u~N5fA4r{lUIoFYBID zzpDnG%yVpzK2IeRox4(4SzVP5G$);!vD@U2O)XJ#J7LV_H{O$fST6RHp6ixD?KtFy zLb-&rYV>{HO+==?=9~5Xv2U_9u{f@V|2F*T(6!*N2fiJZB0n?ztug=f{M7fPpPv3@ zGWn?dbml)5R&FJvPQlsl#Je?B{n4J$)K&fruI0*W>9w_tXaHfdBw9y21GzK%mUs0h z;f1xK<66{**3N~ML_#_^>x>d%?Y@X-zUR(lj;+f4oc`Eu^Q`0H-nj@t4Q$y{t+jZp ztv|9?9w^r-vv268V78JE6~jkqa>}O4n0@}lXP+#7hb#kh zr*^M?GOW}t(R9;%k{h)2=y`_ABy*+wj;>iz-Lq8roO`esXg9#GcwQT%hIJzQxx;*d z&Mt-(jVf~IDHRb*jp|OOkj+)ras#D(V$>>lJ&qt=6rs_tb&4LP!aA+Y zwMr$Q%$g6=gLs_~Rq@PX2br6#NmGL%*ZMIkNulU+M75SrhLxMNppbG`nm^XI>?it+ zwzHnguiI^88Ylx=W>9gDR@02d5dUO`&-8O)jiW*uDE&XbtoUc@NN;!8JnAUT{8-FC1PgP89Y>A;Ii~sF@ z2KE|y_cO59V2%so0qQBsNr~yA@0A~GuVMNdz82qW2%QV@NA}gTBkfkCtoi71FW)$OF%eR3CI&fhKOd_p_xX5d<_rzO7py^8 zYfgLEJ9j#)s3hqPJ_>tnUCW9z zY^&vLsjRQ-C9mPhr%3axX?Fb#mgp%FPyEu)$HU5n3)1ISoYR&rJ+Goq3Pob~)SE4W zx_7x{*rsWtbYCyG+3ibAU1*n;#$l1%)b|`)Ev}sjGGg02jk>y1)pp~tR!yr`*|zI` zy=Cmu4o`RUDt7r=@$zfbL@xVJ|5PlQASdEo`Zf9wBD7uF{P9kpywL3GvHf7@LY3g* zEHGL>Clyxo{NL^!iwd(%zCQb134H&bkC8Zl00@8p2!H?xfB*=900@8p2!OzmCV=_> zk#1g84g^2|1V8`;KmY_l00ck)1V8`;I04N65eOgv0w4eaAOHd&00JNY0w4eaAaL{v zVE%vf+ZZ(j0T2KI5C8!X009sH0T2KI5C8$p{}BTq00JNY0w4eaAOHd&00JNY0w8eo z31I$z^xGIU1OX5L0T2KI5C8!X009sH0T2KI%>NMsAOHd&00JNY0w4eaAOHd&00JOz z^a(^*6=!%oPtoxt|AYewfB*=900@8p2!H?xfB*=900@A { + try { + await mongoose.connect(process.env.MONGODB_URI) + logger.info('Connected to MongoDB') + + app.listen(PORT, () => { + logger.info(`Server running on port ${PORT}`) + }) + } catch (error) { + logger.error('MongoDB connection error:', error) + process.exit(1) + } +} + +startServer() \ No newline at end of file diff --git a/backend/src/routes/auth.routes.js b/backend/src/routes/auth.routes.js new file mode 100644 index 0000000..69cfbb1 --- /dev/null +++ b/backend/src/routes/auth.routes.js @@ -0,0 +1,12 @@ +import express from 'express' +import { register, login, logout, getCurrentUser } from '../controllers/auth.controller.js' +import { verifyToken } from '../middleware/auth.middleware.js' + +const router = express.Router() + +router.post('/register', register) +router.post('/login', login) +router.post('/logout', logout) +router.get('/me', verifyToken, getCurrentUser) + +export default router \ No newline at end of file diff --git a/frontend/package-lock.json b/frontend/package-lock.json index fdcdc23..cf78669 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -15,6 +15,7 @@ "autoprefixer": "^10.4.16", "class-variance-authority": "^0.7.1", "framer-motion": "^10.16.4", + "jwt-decode": "^4.0.0", "lucide-react": "^0.284.0", "postcss": "^8.4.31", "react": "^18.2.0", @@ -3678,6 +3679,15 @@ "node": ">=4.0" } }, + "node_modules/jwt-decode": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/jwt-decode/-/jwt-decode-4.0.0.tgz", + "integrity": "sha512-+KJGIyHgkGuIq3IEBNftfhW/LfWhXUIY6OmyVWjliu5KH1y0fw7VQ8YndE2O4qZdMSd9SqbnC8GOcZEy0Om7sA==", + "license": "MIT", + "engines": { + "node": ">=18" + } + }, "node_modules/keyv": { "version": "4.5.4", "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", diff --git a/frontend/package.json b/frontend/package.json index b5e5b8a..2aeed4d 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -17,6 +17,7 @@ "autoprefixer": "^10.4.16", "class-variance-authority": "^0.7.1", "framer-motion": "^10.16.4", + "jwt-decode": "^4.0.0", "lucide-react": "^0.284.0", "postcss": "^8.4.31", "react": "^18.2.0", diff --git a/frontend/src/App.jsx b/frontend/src/App.jsx index 22b09f0..77f58d6 100644 --- a/frontend/src/App.jsx +++ b/frontend/src/App.jsx @@ -1,18 +1,20 @@ import { BrowserRouter as Router, Routes, Route, Navigate } from 'react-router-dom' import LoginPage from './components/auth/Login' import RegisterPage from './components/auth/Register' -import AuthLayout from './components/layouts/auth-layout' +import Dashboard from './components/Dashboard' +import ProtectedRoute from './components/auth/ProtectedRoute' function App() { return ( } /> - - } /> - + } /> + } /> + + + } /> diff --git a/frontend/src/api.js b/frontend/src/api.js new file mode 100644 index 0000000..04fa668 --- /dev/null +++ b/frontend/src/api.js @@ -0,0 +1,23 @@ +import axios from "axios"; +import { ACCESS_TOKEN } from "./constants"; + +const apiUrl = "/choreo-apis/awbo/backend/rest-api-be2/v1.0"; + +const api = axios.create({ + baseURL: import.meta.env.VITE_API_URL ? import.meta.env.VITE_API_URL : apiUrl, +}); + +api.interceptors.request.use( + (config) => { + const token = localStorage.getItem(ACCESS_TOKEN); + if (token) { + config.headers.Authorization = `Bearer ${token}`; + } + return config; + }, + (error) => { + return Promise.reject(error); + } +); + +export default api; \ No newline at end of file diff --git a/frontend/src/components/Dashboard.jsx b/frontend/src/components/Dashboard.jsx new file mode 100644 index 0000000..cde4a56 --- /dev/null +++ b/frontend/src/components/Dashboard.jsx @@ -0,0 +1,139 @@ +import React, { useEffect, useState } from 'react' +import { useNavigate } from 'react-router-dom' +import { Button } from './ui/Button' +import { Loader2, LogOut } from 'lucide-react' +import api from '../services/api' +import { useToast } from '../hooks/use-toast' +import { ACCESS_TOKEN, REFRESH_TOKEN } from '../constants' + +const Dashboard = () => { + const [loading, setLoading] = useState(true) + const [userData, setUserData] = useState(null) + const navigate = useNavigate() + const { toast } = useToast() + + useEffect(() => { + const checkAuth = () => { + const token = localStorage.getItem(ACCESS_TOKEN) + if (!token) { + toast({ + title: "Authentication required", + description: "Please sign in to access the dashboard", + variant: "destructive", + }) + navigate('/login') + return false + } + return true + } + + if (checkAuth()) { + // Get user data from localStorage + const userData = { + username: localStorage.getItem('username') || 'User', + fullName: localStorage.getItem('fullName') || '', + phoneNumber: localStorage.getItem('phoneNumber') || '', + role: localStorage.getItem('role') || 'patient', + dateOfBirth: localStorage.getItem('dateOfBirth') || '', + gender: localStorage.getItem('gender') || '', + } + + if (userData.role === 'doctor') { + userData.licenseNumber = localStorage.getItem('licenseNumber') || ''; + userData.specialization = localStorage.getItem('specialization') || ''; + userData.hospitalName = localStorage.getItem('hospitalName') || ''; + userData.location = localStorage.getItem('location') || ''; + } + + setUserData(userData) + setLoading(false) + } + }, [navigate, toast]) + + const handleLogout = async () => { + try { + // Use the API service for logout + await api.logout(); + toast({ + title: "Logged out", + description: "You have been successfully logged out", + }); + navigate('/login'); + } catch (error) { + console.error('Logout error:', error); + // Fallback: manually clear tokens if API fails + localStorage.removeItem(ACCESS_TOKEN); + localStorage.removeItem(REFRESH_TOKEN); + toast({ + title: "Logged out", + description: "You have been logged out", + }); + navigate('/login'); + } + }; + + if (loading) { + return ( +
+ +

Loading dashboard...

+
+ ) + } + + return ( +
+
+
+

Dashboard

+ +
+ +
+
+

Welcome, {userData?.fullName || 'User'}

+

You have successfully logged in to the application.

+
+ +
+
+

User Information

+
+ {userData && Object.entries(userData) + .filter(([key]) => key !== 'token' && userData[key]) + .map(([key, value]) => ( +

+ {key.replace(/([A-Z])/g, ' $1').trim()}: + {value} +

+ )) + } +
+
+ +
+

Authentication Status

+
+

Successfully authenticated

+

API connection established

+

JWT Token active

+
+

Token expires in: 1 hour

+
+
+
+
+
+
+
+ ) +} + +export default Dashboard \ No newline at end of file diff --git a/frontend/src/components/auth/Login.jsx b/frontend/src/components/auth/Login.jsx index d0ac00e..b1b6cb1 100644 --- a/frontend/src/components/auth/Login.jsx +++ b/frontend/src/components/auth/Login.jsx @@ -11,6 +11,8 @@ import { Eye, EyeOff, Loader2, AlertCircle, User, UserPlus } from "lucide-react" import { Alert, AlertDescription, AlertTitle } from "../ui/alert" import AuthLayout from "../layouts/auth-layout" import { useNavigate, Link } from "react-router-dom" +import { ACCESS_TOKEN, REFRESH_TOKEN } from '../../constants' +import axios from "axios" function Login() { const [role, setRole] = useState('patient') @@ -20,12 +22,12 @@ function Login() { const [email, setEmail] = useState("") const [password, setPassword] = useState("") const [phoneNumber, setPhoneNumber] = useState("") - const [dateOfBirth, setDateOfBirth] = useState("") - const [gender, setGender] = useState("") + const [_dateOfBirth, _setDateOfBirth] = useState("") + const [_gender, _setGender] = useState("") const [licenseNumber, setLicenseNumber] = useState("") const [specialization, setSpecialization] = useState("") - const [hospitalName, setHospitalName] = useState("") - const [location, setLocation] = useState("") + const [_hospitalName, _setHospitalName] = useState("") + const [_location, _setLocation] = useState("") const [rememberMe, setRememberMe] = useState(false) const [errors, setErrors] = useState({}) const { toast } = useToast() @@ -38,76 +40,149 @@ function Login() { const validateForm = () => { const newErrors = {} + console.log("Validating form with values:", { + email, + password + }); - if (!fullName) newErrors.fullName = "Full name is required" - if (!email) newErrors.email = "Email is required" - else if (!/\S+@\S+\.\S+/.test(email)) newErrors.email = "Invalid email format" - - if (!password) newErrors.password = "Password is required" - else if (password.length < 8) newErrors.password = "Password must be at least 8 characters" - - if (!phoneNumber) newErrors.phoneNumber = "Phone number is required" - - if (role === 'patient') { - if (!dateOfBirth) newErrors.dateOfBirth = "Date of birth is required" - if (!gender) newErrors.gender = "Please select your gender" + // For login, we only strictly need username (email) and password to match backend requirements + if (!email) { + newErrors.email = "Email is required"; + console.log("Validation failed: missing email"); + } else if (!/\S+@\S+\.\S+/.test(email)) { + newErrors.email = "Invalid email format"; + console.log("Validation failed: invalid email format"); } - if (role === 'doctor') { - if (!licenseNumber) newErrors.licenseNumber = "License number is required" - if (!specialization) newErrors.specialization = "Specialization is required" - if (!location) newErrors.location = "Location is required" + if (!password) { + newErrors.password = "Password is required"; + console.log("Validation failed: missing password"); + } else if (password.length < 3) { // Relaxed password requirement for testing + newErrors.password = "Password is too short"; + console.log("Validation failed: password too short"); } - setErrors(newErrors) - return Object.keys(newErrors).length === 0 + // Other fields are NOT required for login in our simplified version + // This makes testing easier while connecting to the backend + + setErrors(newErrors); + const isValid = Object.keys(newErrors).length === 0; + console.log("Form validation result:", isValid ? "PASSED" : "FAILED", newErrors); + return isValid; } - const handleSubmit = async (e) => { - e.preventDefault() + const handleSubmit = async () => { + // No parameter needed since e.preventDefault() is now called in the form's onSubmit handler + + console.log('Handle submit function called'); + + // Double-check form validation + if (!validateForm()) { + console.log('Form validation failed'); + setIsLoading(false); // Make sure to set loading to false if validation fails + return; + } - if (!validateForm()) return + console.log('Form validation passed, proceeding with login'); - setIsLoading(true) try { - await new Promise((resolve) => setTimeout(resolve, 1500)) - - console.log('Registration successful', { - role, - fullName, - email, - password, - phoneNumber, - ...(role === 'patient' && { - dateOfBirth, - gender - }), - ...(role === 'doctor' && { - licenseNumber, - specialization, - hospitalName, - location - }) - }) + // IMPORTANT: Only send username and password to the token endpoint + const credentials = { + username: email, + password: password + }; - toast({ - title: "Registration successful", - description: "Your account has been created", - }) + console.log('Sending login request with credentials:', credentials); + + // Make the API call with just username/password + // Use axios directly if needed to debug + try { + const response = await axios.post(`${import.meta.env.VITE_API_URL || 'http://localhost:8000'}/api/token/`, credentials, { + headers: { + 'Content-Type': 'application/json', + } + }); + + console.log('Login API response:', response.status, response.data); + + // Extract tokens + const { access, refresh } = response.data; + + // Store tokens + localStorage.setItem(ACCESS_TOKEN, access); + localStorage.setItem(REFRESH_TOKEN, refresh); + + // Store fullName and other fields even if they're not required for validation + localStorage.setItem('username', email); + if (fullName) localStorage.setItem('fullName', fullName); + if (phoneNumber) localStorage.setItem('phoneNumber', phoneNumber); + localStorage.setItem('role', role); - // Navigate to dashboard or home page after successful login - navigate('/dashboard') + if (role === 'doctor' && licenseNumber && specialization) { + localStorage.setItem('licenseNumber', licenseNumber); + localStorage.setItem('specialization', specialization); + } + + console.log('Login successful, showing toast notification'); + + toast({ + title: "Login successful", + description: "Welcome back!", + }); + + console.log('Navigating to dashboard in 1 second'); + + // Use setTimeout to ensure toast is shown before navigation + setTimeout(() => { + console.log('Executing navigation to /dashboard'); + navigate('/dashboard'); + }, 1000); + + } catch (apiError) { + console.error('API call error:', apiError); + + if (apiError.response) { + console.error('Error response:', apiError.response.status, apiError.response.data); + throw new Error(apiError.response.data?.detail || 'Login failed'); + } else if (apiError.request) { + console.error('No response received'); + + // For testing purposes, you can uncomment this to bypass backend + /* + console.log('Bypassing backend for testing - storing dummy tokens'); + localStorage.setItem(ACCESS_TOKEN, 'dummy-token'); + localStorage.setItem(REFRESH_TOKEN, 'dummy-refresh'); + localStorage.setItem('username', email); + if (fullName) localStorage.setItem('fullName', fullName); + + toast({ + title: "Login successful (test mode)", + description: "Welcome back!", + }); + + setTimeout(() => { + navigate('/dashboard'); + }, 1000); + + return; + */ + + throw new Error('No response from server. Check your network connection.'); + } else { + throw apiError; + } + } } catch (error) { + console.error('Login error:', error.message); toast({ - title: "Registration failed", - description: "Please try again later", + title: "Login failed", + description: error.message || "Invalid email or password", variant: "destructive", - }) - } finally { - setIsLoading(false) + }); + setIsLoading(false); } - } + }; const specializations = [ "Cardiology", @@ -155,7 +230,27 @@ function Login() {
-
+ { + console.log('Form submitted'); + e.preventDefault(); // Explicitly prevent default form behavior + + // Set loading state immediately for visual feedback + setIsLoading(true); + + try { + handleSubmit(); + } catch (err) { + console.error('Form submission error:', err); + setIsLoading(false); + toast({ + title: "Error", + description: "Something went wrong with form submission. Please try again.", + variant: "destructive", + }); + } + }} + >
diff --git a/frontend/src/components/auth/ProtectedRoute.jsx b/frontend/src/components/auth/ProtectedRoute.jsx new file mode 100644 index 0000000..d294c64 --- /dev/null +++ b/frontend/src/components/auth/ProtectedRoute.jsx @@ -0,0 +1,84 @@ +import React, { useEffect, useState } from 'react' +import { Navigate, useLocation } from 'react-router-dom' +import { Loader2 } from 'lucide-react' +import { ACCESS_TOKEN, REFRESH_TOKEN } from '../../constants' +import { jwtDecode } from 'jwt-decode' + +const ProtectedRoute = ({ children }) => { + const [loading, setLoading] = useState(true) + const [isAuthenticated, setIsAuthenticated] = useState(false) + const location = useLocation() + + useEffect(() => { + const checkTokenValidity = (token) => { + try { + const decoded = jwtDecode(token); + const currentTime = Date.now() / 1000; + + // Check if token is expired + if (decoded.exp && decoded.exp < currentTime) { + console.log('Token expired'); + return false; + } + + return true; + } catch (error) { + console.error('Token validation error:', error); + return false; + } + }; + + const checkAuth = async () => { + try { + const token = localStorage.getItem(ACCESS_TOKEN) + + if (!token) { + setIsAuthenticated(false) + setLoading(false) + return + } + + // Validate the token + const isValid = checkTokenValidity(token) + + if (isValid) { + setIsAuthenticated(true) + } else { + // Try to use refresh token + const refreshToken = localStorage.getItem(REFRESH_TOKEN) + if (refreshToken && checkTokenValidity(refreshToken)) { + // In a real app, would call refresh token endpoint here + setIsAuthenticated(true) + } else { + setIsAuthenticated(false) + } + } + + setLoading(false) + } catch (error) { + console.error('Auth check error:', error) + setIsAuthenticated(false) + setLoading(false) + } + } + + checkAuth() + }, []) + + if (loading) { + return ( +
+ +

Verifying authentication...

+
+ ) + } + + if (!isAuthenticated) { + return + } + + return children +} + +export default ProtectedRoute \ No newline at end of file diff --git a/frontend/src/components/auth/Register.jsx b/frontend/src/components/auth/Register.jsx index 3886178..cd42563 100644 --- a/frontend/src/components/auth/Register.jsx +++ b/frontend/src/components/auth/Register.jsx @@ -11,6 +11,7 @@ import { Eye, EyeOff, Loader2, AlertCircle, User, UserPlus } from "lucide-react" import { Alert, AlertDescription, AlertTitle } from "../ui/alert" import AuthLayout from "../layouts/auth-layout" import { useNavigate, Link } from "react-router-dom" +import api from "../../services/api" function Register() { const [role, setRole] = useState('patient') @@ -116,50 +117,55 @@ function Register() { } const handleSubmit = async (e) => { - e.preventDefault() + e.preventDefault(); + if (!validateForm()) return; + setIsLoading(true); - if (!validateForm()) return - - setIsLoading(true) try { - await new Promise((resolve) => setTimeout(resolve, 1500)) - - console.log('Registration successful', { - role, - fullName, - email, - password, - phoneNumber, - ...(role === 'patient' && { - dateOfBirth, - gender - }), - ...(role === 'doctor' && { - licenseNumber, - specialization, - hospitalName, - location - }) - }) + const userData = { + username: email, + password: password, + email: email, + first_name: fullName.split(' ')[0] || '', + last_name: fullName.split(' ').slice(1).join(' ') || '', + date_of_birth: dateOfBirth, + gender: gender, + role: role, + phone_number: phoneNumber, + }; + + if (role === 'doctor') { + userData.license_number = licenseNumber; + userData.specialization = specialization; + userData.hospital_name = hospitalName; + userData.location = location; + } + + localStorage.setItem('fullName', fullName); + localStorage.setItem('email', email); + localStorage.setItem('password', email); + localStorage.setItem('phone_number', phoneNumber); + localStorage.setItem('role', role); + + await api.register(userData); toast({ title: "Registration successful", description: "Your account has been created", - }) + }); - // Navigate to login page after successful registration - navigate('/login') + navigate('/login'); } catch (error) { toast({ title: "Registration failed", - description: "Please try again later", + description: error.message || "Please try again later", variant: "destructive", - }) + }); } finally { - setIsLoading(false) + setIsLoading(false); } - } + }; const specializations = [ "Cardiology", diff --git a/frontend/src/constants.js b/frontend/src/constants.js new file mode 100644 index 0000000..f69ae82 --- /dev/null +++ b/frontend/src/constants.js @@ -0,0 +1,9 @@ +export const ACCESS_TOKEN = "access"; +export const REFRESH_TOKEN = "refresh"; + +// API Endpoints +export const API_ENDPOINTS = { + REGISTER: '/api/user/register/', + LOGIN: '/api/token/', + REFRESH_TOKEN: '/api/token/refresh/', +}; \ No newline at end of file diff --git a/frontend/src/hooks/use-mobile.jsx b/frontend/src/hooks/use-mobile.jsx index a95369e..e725cf6 100644 --- a/frontend/src/hooks/use-mobile.jsx +++ b/frontend/src/hooks/use-mobile.jsx @@ -10,7 +10,7 @@ export function useMobile() { setIsMobile(window.innerWidth < 768) } - checkIfMobile() // Initial check + checkIfMobile() window.addEventListener("resize", checkIfMobile) diff --git a/frontend/src/services/api.js b/frontend/src/services/api.js new file mode 100644 index 0000000..00510f4 --- /dev/null +++ b/frontend/src/services/api.js @@ -0,0 +1,181 @@ +import axios from 'axios'; +import { ACCESS_TOKEN, REFRESH_TOKEN } from '../constants'; + +const API_URL = import.meta.env.VITE_API_URL || 'http://localhost:8000'; + +// Create axios instance +const api = axios.create({ + baseURL: API_URL, + headers: { + 'Content-Type': 'application/json', + 'Accept': 'application/json' + } +}); + +// Request interceptor +api.interceptors.request.use( + (config) => { + const token = localStorage.getItem(ACCESS_TOKEN); + if (token) { + config.headers.Authorization = `Bearer ${token}`; + } + return config; + }, + (error) => Promise.reject(error) +); + +// Response interceptor +api.interceptors.response.use( + (response) => response, + async (error) => { + const originalRequest = error.config; + + if (error.response?.status === 401 && !originalRequest._retry) { + originalRequest._retry = true; + + try { + const refreshToken = localStorage.getItem(REFRESH_TOKEN); + if (!refreshToken) { + throw new Error('No refresh token available'); + } + + const response = await axios.post(`${API_URL}/api/token/refresh/`, { + refresh: refreshToken + }); + + const { access } = response.data; + localStorage.setItem(ACCESS_TOKEN, access); + originalRequest.headers.Authorization = `Bearer ${access}`; + return api(originalRequest); + } catch (refreshError) { + localStorage.removeItem(ACCESS_TOKEN); + localStorage.removeItem(REFRESH_TOKEN); + window.location.href = '/login'; + return Promise.reject(refreshError); + } + } + + return Promise.reject(error); + } +); + +// Auth API functions +export const register = async (userData) => { + try { + console.log('Registering user with data:', userData); + const response = await api.post('/api/user/register/', userData); + + // Store additional user data in localStorage + if (userData.email) localStorage.setItem('username', userData.email); + if (userData.role) localStorage.setItem('role', userData.role); + + return response.data; + } catch (error) { + console.error('Registration error:', error.response?.data || error.message); + // Handle different error formats + let errorMessage = 'Registration failed'; + + if (error.response?.data) { + const data = error.response.data; + if (typeof data === 'string') { + errorMessage = data; + } else if (data.detail) { + errorMessage = data.detail; + } else { + // Check for field-specific errors + const fields = ['username', 'password', 'email', 'date_of_birth', 'gender']; + for (const field of fields) { + if (data[field]) { + errorMessage = `${field}: ${Array.isArray(data[field]) ? data[field][0] : data[field]}`; + break; + } + } + } + } + + throw new Error(errorMessage); + } +}; + +export const login = async (credentials) => { + try { + console.log('Attempting login with:', credentials); + + // Ensure we're sending exactly what the endpoint expects + if (!credentials.username || !credentials.password) { + console.error('Missing required login credentials'); + throw new Error('Username and password are required'); + } + + // Make the API call + console.log('Sending request to:', `${API_URL}/api/token/`); + const response = await api.post('/api/token/', credentials); + console.log('Login response:', response.status, response.statusText); + + const { access, refresh } = response.data; + + // Ensure tokens are stored correctly + if (access && refresh) { + localStorage.setItem(ACCESS_TOKEN, access); + localStorage.setItem(REFRESH_TOKEN, refresh); + console.log('Login successful, tokens stored'); + return response.data; + } else { + console.error('Invalid token response:', response.data); + throw new Error('Invalid token response from server'); + } + } catch (error) { + console.error('Login error details:', error); + + if (error.response) { + console.error('Server error status:', error.response.status); + console.error('Server response:', error.response.data); + + // Handle various error formats from Django + if (error.response.data) { + const data = error.response.data; + + if (data.detail) { + throw new Error(data.detail); + } else if (data.non_field_errors) { + throw new Error(Array.isArray(data.non_field_errors) + ? data.non_field_errors[0] + : data.non_field_errors); + } else if (data.username) { + throw new Error(Array.isArray(data.username) ? data.username[0] : data.username); + } else if (data.password) { + throw new Error(Array.isArray(data.password) ? data.password[0] : data.password); + } + } + } else if (error.request) { + // The request was made but no response was received + console.error('No response received from server'); + throw new Error('No response from server. Please check your network connection.'); + } + + // Default error message + throw new Error(error.message || 'Login failed. Please check your credentials and try again.'); + } +}; + +export const logout = async () => { + try { + localStorage.removeItem(ACCESS_TOKEN); + localStorage.removeItem(REFRESH_TOKEN); + return { success: true }; + } catch (error) { + throw new Error('Logout failed'); + } +}; + +export const getUserProfile = async () => { + // This is just a placeholder since there's no user profile endpoint in the backend + return { username: localStorage.getItem('username') || 'User' }; +}; + +export default { + register, + login, + logout, + getUserProfile +}; \ No newline at end of file From 15cd4df4525b668083d8e2045d5a2bd5bb5f8dae Mon Sep 17 00:00:00 2001 From: Muaz <6muazx@gmail.com> Date: Tue, 8 Apr 2025 14:05:11 +0530 Subject: [PATCH 06/14] dashboard ui tweaks and login integration --- frontend/package-lock.json | 802 +++++++++++++++++- frontend/package.json | 7 + frontend/src/App.jsx | 208 ++++- frontend/src/components/Dashboard.jsx | 139 --- frontend/src/components/auth/Login.jsx | 29 + .../src/components/auth/ProtectedRoute.jsx | 75 +- frontend/src/components/auth/Register.jsx | 7 +- .../components/dashboard/AddRecordModal.jsx | 232 +++++ .../dashboard/dashboard-overview.jsx | 783 +++++++++++++++++ .../components/dashboard/health-summary.jsx | 180 ++++ .../dashboard/recent-activity-list.jsx | 240 ++++++ frontend/src/components/dashboard/sidebar.jsx | 218 +++++ .../dashboard/upcoming-appointments.jsx | 152 ++++ frontend/src/components/footer.jsx | 29 + frontend/src/components/header.jsx | 93 ++ .../src/components/health-records-list.jsx | 188 ++++ .../components/layouts/dashboard-layout.jsx | 104 +++ frontend/src/components/security-settings.jsx | 102 +++ frontend/src/components/sharing-controls.jsx | 112 +++ frontend/src/components/ui/alert.jsx | 9 +- frontend/src/components/ui/badge.jsx | 37 + frontend/src/components/ui/button.jsx | 86 +- frontend/src/components/ui/card.jsx | 8 +- frontend/src/components/ui/checkbox.jsx | 9 +- frontend/src/components/ui/dialog.jsx | 99 +++ frontend/src/components/ui/dropdown-menu.jsx | 157 ++++ frontend/src/components/ui/input.jsx | 9 +- frontend/src/components/ui/label.jsx | 9 +- .../src/components/ui/loading-spinner.jsx | 9 + frontend/src/components/ui/radio-group.jsx | 39 + frontend/src/components/ui/scroll-area.jsx | 41 + frontend/src/components/ui/select.jsx | 151 ++++ frontend/src/components/ui/switch.jsx | 39 + frontend/src/components/ui/table.jsx | 122 +++ frontend/src/components/ui/tabs.jsx | 60 ++ frontend/src/components/ui/textarea.jsx | 19 + frontend/src/index.css | 73 +- frontend/src/lib/util.js | Bin 135 -> 152 bytes frontend/src/lib/utils.js | 6 + frontend/src/pages/Onboarding.jsx | 469 ++++++++++ frontend/src/pages/dashboard.jsx | 584 +++++++++++++ frontend/src/pages/health-records.jsx | 423 +++++++++ .../pages/patient-dashboard/Onboarding.jsx | 469 ++++++++++ .../src/pages/patient-dashboard/analytics.jsx | 464 ++++++++++ .../pages/patient-dashboard/appointments.jsx | 679 +++++++++++++++ .../src/pages/patient-dashboard/dashboard.jsx | 584 +++++++++++++ .../patient-dashboard/emergency-access.jsx | 124 +++ .../src/pages/patient-dashboard/emergency.jsx | 207 +++++ .../patient-dashboard/health-records.jsx | 423 +++++++++ .../src/pages/patient-dashboard/profile.jsx | 336 ++++++++ .../src/pages/patient-dashboard/providers.jsx | 354 ++++++++ .../patient-dashboard/security-settings.jsx | 220 +++++ .../src/pages/patient-dashboard/security.jsx | 201 +++++ .../src/pages/patient-dashboard/settings.jsx | 675 +++++++++++++++ .../patient-dashboard/sharing-controls.jsx | 109 +++ .../src/pages/patient-dashboard/sharing.jsx | 359 ++++++++ frontend/src/services/appointment-service.js | 124 +++ .../src/services/health-record-service.js | 211 +++++ frontend/tailwind.config.js | 51 +- 59 files changed, 11364 insertions(+), 384 deletions(-) delete mode 100644 frontend/src/components/Dashboard.jsx create mode 100644 frontend/src/components/dashboard/AddRecordModal.jsx create mode 100644 frontend/src/components/dashboard/dashboard-overview.jsx create mode 100644 frontend/src/components/dashboard/health-summary.jsx create mode 100644 frontend/src/components/dashboard/recent-activity-list.jsx create mode 100644 frontend/src/components/dashboard/sidebar.jsx create mode 100644 frontend/src/components/dashboard/upcoming-appointments.jsx create mode 100644 frontend/src/components/footer.jsx create mode 100644 frontend/src/components/header.jsx create mode 100644 frontend/src/components/health-records-list.jsx create mode 100644 frontend/src/components/layouts/dashboard-layout.jsx create mode 100644 frontend/src/components/security-settings.jsx create mode 100644 frontend/src/components/sharing-controls.jsx create mode 100644 frontend/src/components/ui/badge.jsx create mode 100644 frontend/src/components/ui/dialog.jsx create mode 100644 frontend/src/components/ui/dropdown-menu.jsx create mode 100644 frontend/src/components/ui/loading-spinner.jsx create mode 100644 frontend/src/components/ui/radio-group.jsx create mode 100644 frontend/src/components/ui/scroll-area.jsx create mode 100644 frontend/src/components/ui/select.jsx create mode 100644 frontend/src/components/ui/switch.jsx create mode 100644 frontend/src/components/ui/table.jsx create mode 100644 frontend/src/components/ui/tabs.jsx create mode 100644 frontend/src/components/ui/textarea.jsx create mode 100644 frontend/src/lib/utils.js create mode 100644 frontend/src/pages/Onboarding.jsx create mode 100644 frontend/src/pages/dashboard.jsx create mode 100644 frontend/src/pages/health-records.jsx create mode 100644 frontend/src/pages/patient-dashboard/Onboarding.jsx create mode 100644 frontend/src/pages/patient-dashboard/analytics.jsx create mode 100644 frontend/src/pages/patient-dashboard/appointments.jsx create mode 100644 frontend/src/pages/patient-dashboard/dashboard.jsx create mode 100644 frontend/src/pages/patient-dashboard/emergency-access.jsx create mode 100644 frontend/src/pages/patient-dashboard/emergency.jsx create mode 100644 frontend/src/pages/patient-dashboard/health-records.jsx create mode 100644 frontend/src/pages/patient-dashboard/profile.jsx create mode 100644 frontend/src/pages/patient-dashboard/providers.jsx create mode 100644 frontend/src/pages/patient-dashboard/security-settings.jsx create mode 100644 frontend/src/pages/patient-dashboard/security.jsx create mode 100644 frontend/src/pages/patient-dashboard/settings.jsx create mode 100644 frontend/src/pages/patient-dashboard/sharing-controls.jsx create mode 100644 frontend/src/pages/patient-dashboard/sharing.jsx create mode 100644 frontend/src/services/appointment-service.js create mode 100644 frontend/src/services/health-record-service.js diff --git a/frontend/package-lock.json b/frontend/package-lock.json index cf78669..053f0cc 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -9,11 +9,18 @@ "version": "0.0.0", "dependencies": { "@radix-ui/react-checkbox": "^1.1.4", + "@radix-ui/react-dialog": "^1.1.6", + "@radix-ui/react-dropdown-menu": "^2.1.6", "@radix-ui/react-label": "^2.1.2", + "@radix-ui/react-radio-group": "^1.2.3", + "@radix-ui/react-scroll-area": "^1.2.3", + "@radix-ui/react-select": "^2.1.6", "@radix-ui/react-slot": "^1.1.2", + "@radix-ui/react-tabs": "^1.1.3", "@tailwindcss/forms": "^0.5.7", "autoprefixer": "^10.4.16", "class-variance-authority": "^0.7.1", + "clsx": "^2.1.1", "framer-motion": "^10.16.4", "jwt-decode": "^4.0.0", "lucide-react": "^0.284.0", @@ -798,6 +805,44 @@ "node": "^12.22.0 || ^14.17.0 || >=16.0.0" } }, + "node_modules/@floating-ui/core": { + "version": "1.6.9", + "resolved": "https://registry.npmjs.org/@floating-ui/core/-/core-1.6.9.tgz", + "integrity": "sha512-uMXCuQ3BItDUbAMhIXw7UPXRfAlOAvZzdK9BWpE60MCn+Svt3aLn9jsPTi/WNGlRUu2uI0v5S7JiIUsbsvh3fw==", + "license": "MIT", + "dependencies": { + "@floating-ui/utils": "^0.2.9" + } + }, + "node_modules/@floating-ui/dom": { + "version": "1.6.13", + "resolved": "https://registry.npmjs.org/@floating-ui/dom/-/dom-1.6.13.tgz", + "integrity": "sha512-umqzocjDgNRGTuO7Q8CU32dkHkECqI8ZdMZ5Swb6QAM0t5rnlrN3lGo1hdpscRd3WS8T6DKYK4ephgIH9iRh3w==", + "license": "MIT", + "dependencies": { + "@floating-ui/core": "^1.6.0", + "@floating-ui/utils": "^0.2.9" + } + }, + "node_modules/@floating-ui/react-dom": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/@floating-ui/react-dom/-/react-dom-2.1.2.tgz", + "integrity": "sha512-06okr5cgPzMNBy+Ycse2A6udMi4bqwW/zgBF/rwjcNqWkyr82Mcg8b0vjX8OJpZFy/FKjJmw6wV7t44kK6kW7A==", + "license": "MIT", + "dependencies": { + "@floating-ui/dom": "^1.0.0" + }, + "peerDependencies": { + "react": ">=16.8.0", + "react-dom": ">=16.8.0" + } + }, + "node_modules/@floating-ui/utils": { + "version": "0.2.9", + "resolved": "https://registry.npmjs.org/@floating-ui/utils/-/utils-0.2.9.tgz", + "integrity": "sha512-MDWhGtE+eHw5JW7lq4qhc5yRLS11ERl1c7Z6Xd0a58DozHES6EnNNwUWbMiG4J9Cgj053Bhk8zvlhFYKVhULwg==", + "license": "MIT" + }, "node_modules/@humanwhocodes/config-array": { "version": "0.13.0", "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.13.0.tgz", @@ -973,26 +1018,419 @@ "node": ">=14" } }, + "node_modules/@radix-ui/number": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@radix-ui/number/-/number-1.1.0.tgz", + "integrity": "sha512-V3gRzhVNU1ldS5XhAPTom1fOIo4ccrjjJgmE+LI2h/WaFpHmx0MQApT+KZHnx8abG6Avtfcz4WoEciMnpFT3HQ==", + "license": "MIT" + }, "node_modules/@radix-ui/primitive": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/@radix-ui/primitive/-/primitive-1.1.1.tgz", "integrity": "sha512-SJ31y+Q/zAyShtXJc8x83i9TYdbAfHZ++tUZnvjJJqFjzsdUnKsxPL6IEtBlxKkU7yzer//GQtZSV4GbldL3YA==", "license": "MIT" }, - "node_modules/@radix-ui/react-checkbox": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/@radix-ui/react-checkbox/-/react-checkbox-1.1.4.tgz", - "integrity": "sha512-wP0CPAHq+P5I4INKe3hJrIa1WoNqqrejzW+zoU0rOvo1b9gDEJJFl2rYfO1PYJUQCc2H1WZxIJmyv9BS8i5fLw==", + "node_modules/@radix-ui/react-arrow": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@radix-ui/react-arrow/-/react-arrow-1.1.2.tgz", + "integrity": "sha512-G+KcpzXHq24iH0uGG/pF8LyzpFJYGD4RfLjCIBfGdSLXvjLHST31RUiRVrupIBMvIppMgSzQ6l66iAxl03tdlg==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-primitive": "2.0.2" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-checkbox": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/@radix-ui/react-checkbox/-/react-checkbox-1.1.4.tgz", + "integrity": "sha512-wP0CPAHq+P5I4INKe3hJrIa1WoNqqrejzW+zoU0rOvo1b9gDEJJFl2rYfO1PYJUQCc2H1WZxIJmyv9BS8i5fLw==", + "license": "MIT", + "dependencies": { + "@radix-ui/primitive": "1.1.1", + "@radix-ui/react-compose-refs": "1.1.1", + "@radix-ui/react-context": "1.1.1", + "@radix-ui/react-presence": "1.1.2", + "@radix-ui/react-primitive": "2.0.2", + "@radix-ui/react-use-controllable-state": "1.1.0", + "@radix-ui/react-use-previous": "1.1.0", + "@radix-ui/react-use-size": "1.1.0" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-collection": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@radix-ui/react-collection/-/react-collection-1.1.2.tgz", + "integrity": "sha512-9z54IEKRxIa9VityapoEYMuByaG42iSy1ZXlY2KcuLSEtq8x4987/N6m15ppoMffgZX72gER2uHe1D9Y6Unlcw==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-compose-refs": "1.1.1", + "@radix-ui/react-context": "1.1.1", + "@radix-ui/react-primitive": "2.0.2", + "@radix-ui/react-slot": "1.1.2" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-compose-refs": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-compose-refs/-/react-compose-refs-1.1.1.tgz", + "integrity": "sha512-Y9VzoRDSJtgFMUCoiZBDVo084VQ5hfpXxVE+NgkdNsjiDBByiImMZKKhxMwCbdHvhlENG6a833CbFkOQvTricw==", + "license": "MIT", + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-context": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-context/-/react-context-1.1.1.tgz", + "integrity": "sha512-UASk9zi+crv9WteK/NU4PLvOoL3OuE6BWVKNF6hPRBtYBDXQ2u5iu3O59zUlJiTVvkyuycnqrztsHVJwcK9K+Q==", + "license": "MIT", + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-dialog": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/@radix-ui/react-dialog/-/react-dialog-1.1.6.tgz", + "integrity": "sha512-/IVhJV5AceX620DUJ4uYVMymzsipdKBzo3edo+omeskCKGm9FRHM0ebIdbPnlQVJqyuHbuBltQUOG2mOTq2IYw==", + "license": "MIT", + "dependencies": { + "@radix-ui/primitive": "1.1.1", + "@radix-ui/react-compose-refs": "1.1.1", + "@radix-ui/react-context": "1.1.1", + "@radix-ui/react-dismissable-layer": "1.1.5", + "@radix-ui/react-focus-guards": "1.1.1", + "@radix-ui/react-focus-scope": "1.1.2", + "@radix-ui/react-id": "1.1.0", + "@radix-ui/react-portal": "1.1.4", + "@radix-ui/react-presence": "1.1.2", + "@radix-ui/react-primitive": "2.0.2", + "@radix-ui/react-slot": "1.1.2", + "@radix-ui/react-use-controllable-state": "1.1.0", + "aria-hidden": "^1.2.4", + "react-remove-scroll": "^2.6.3" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-direction": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@radix-ui/react-direction/-/react-direction-1.1.0.tgz", + "integrity": "sha512-BUuBvgThEiAXh2DWu93XsT+a3aWrGqolGlqqw5VU1kG7p/ZH2cuDlM1sRLNnY3QcBS69UIz2mcKhMxDsdewhjg==", + "license": "MIT", + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-dismissable-layer": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/@radix-ui/react-dismissable-layer/-/react-dismissable-layer-1.1.5.tgz", + "integrity": "sha512-E4TywXY6UsXNRhFrECa5HAvE5/4BFcGyfTyK36gP+pAW1ed7UTK4vKwdr53gAJYwqbfCWC6ATvJa3J3R/9+Qrg==", + "license": "MIT", + "dependencies": { + "@radix-ui/primitive": "1.1.1", + "@radix-ui/react-compose-refs": "1.1.1", + "@radix-ui/react-primitive": "2.0.2", + "@radix-ui/react-use-callback-ref": "1.1.0", + "@radix-ui/react-use-escape-keydown": "1.1.0" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-dropdown-menu": { + "version": "2.1.6", + "resolved": "https://registry.npmjs.org/@radix-ui/react-dropdown-menu/-/react-dropdown-menu-2.1.6.tgz", + "integrity": "sha512-no3X7V5fD487wab/ZYSHXq3H37u4NVeLDKI/Ks724X/eEFSSEFYZxWgsIlr1UBeEyDaM29HM5x9p1Nv8DuTYPA==", + "license": "MIT", + "dependencies": { + "@radix-ui/primitive": "1.1.1", + "@radix-ui/react-compose-refs": "1.1.1", + "@radix-ui/react-context": "1.1.1", + "@radix-ui/react-id": "1.1.0", + "@radix-ui/react-menu": "2.1.6", + "@radix-ui/react-primitive": "2.0.2", + "@radix-ui/react-use-controllable-state": "1.1.0" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-focus-guards": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-focus-guards/-/react-focus-guards-1.1.1.tgz", + "integrity": "sha512-pSIwfrT1a6sIoDASCSpFwOasEwKTZWDw/iBdtnqKO7v6FeOzYJ7U53cPzYFVR3geGGXgVHaH+CdngrrAzqUGxg==", + "license": "MIT", + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-focus-scope": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@radix-ui/react-focus-scope/-/react-focus-scope-1.1.2.tgz", + "integrity": "sha512-zxwE80FCU7lcXUGWkdt6XpTTCKPitG1XKOwViTxHVKIJhZl9MvIl2dVHeZENCWD9+EdWv05wlaEkRXUykU27RA==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-compose-refs": "1.1.1", + "@radix-ui/react-primitive": "2.0.2", + "@radix-ui/react-use-callback-ref": "1.1.0" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-id": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@radix-ui/react-id/-/react-id-1.1.0.tgz", + "integrity": "sha512-EJUrI8yYh7WOjNOqpoJaf1jlFIH2LvtgAl+YcFqNCa+4hj64ZXmPkAKOFs/ukjz3byN6bdb/AVUqHkI8/uWWMA==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-use-layout-effect": "1.1.0" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-label": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/@radix-ui/react-label/-/react-label-2.1.2.tgz", + "integrity": "sha512-zo1uGMTaNlHehDyFQcDZXRJhUPDuukcnHz0/jnrup0JA6qL+AFpAnty+7VKa9esuU5xTblAZzTGYJKSKaBxBhw==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-primitive": "2.0.2" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-menu": { + "version": "2.1.6", + "resolved": "https://registry.npmjs.org/@radix-ui/react-menu/-/react-menu-2.1.6.tgz", + "integrity": "sha512-tBBb5CXDJW3t2mo9WlO7r6GTmWV0F0uzHZVFmlRmYpiSK1CDU5IKojP1pm7oknpBOrFZx/YgBRW9oorPO2S/Lg==", + "license": "MIT", + "dependencies": { + "@radix-ui/primitive": "1.1.1", + "@radix-ui/react-collection": "1.1.2", + "@radix-ui/react-compose-refs": "1.1.1", + "@radix-ui/react-context": "1.1.1", + "@radix-ui/react-direction": "1.1.0", + "@radix-ui/react-dismissable-layer": "1.1.5", + "@radix-ui/react-focus-guards": "1.1.1", + "@radix-ui/react-focus-scope": "1.1.2", + "@radix-ui/react-id": "1.1.0", + "@radix-ui/react-popper": "1.2.2", + "@radix-ui/react-portal": "1.1.4", + "@radix-ui/react-presence": "1.1.2", + "@radix-ui/react-primitive": "2.0.2", + "@radix-ui/react-roving-focus": "1.1.2", + "@radix-ui/react-slot": "1.1.2", + "@radix-ui/react-use-callback-ref": "1.1.0", + "aria-hidden": "^1.2.4", + "react-remove-scroll": "^2.6.3" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-popper": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/@radix-ui/react-popper/-/react-popper-1.2.2.tgz", + "integrity": "sha512-Rvqc3nOpwseCyj/rgjlJDYAgyfw7OC1tTkKn2ivhaMGcYt8FSBlahHOZak2i3QwkRXUXgGgzeEe2RuqeEHuHgA==", + "license": "MIT", + "dependencies": { + "@floating-ui/react-dom": "^2.0.0", + "@radix-ui/react-arrow": "1.1.2", + "@radix-ui/react-compose-refs": "1.1.1", + "@radix-ui/react-context": "1.1.1", + "@radix-ui/react-primitive": "2.0.2", + "@radix-ui/react-use-callback-ref": "1.1.0", + "@radix-ui/react-use-layout-effect": "1.1.0", + "@radix-ui/react-use-rect": "1.1.0", + "@radix-ui/react-use-size": "1.1.0", + "@radix-ui/rect": "1.1.0" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-portal": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/@radix-ui/react-portal/-/react-portal-1.1.4.tgz", + "integrity": "sha512-sn2O9k1rPFYVyKd5LAJfo96JlSGVFpa1fS6UuBJfrZadudiw5tAmru+n1x7aMRQ84qDM71Zh1+SzK5QwU0tJfA==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-primitive": "2.0.2", + "@radix-ui/react-use-layout-effect": "1.1.0" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-presence": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@radix-ui/react-presence/-/react-presence-1.1.2.tgz", + "integrity": "sha512-18TFr80t5EVgL9x1SwF/YGtfG+l0BS0PRAlCWBDoBEiDQjeKgnNZRVJp/oVBl24sr3Gbfwc/Qpj4OcWTQMsAEg==", "license": "MIT", "dependencies": { - "@radix-ui/primitive": "1.1.1", "@radix-ui/react-compose-refs": "1.1.1", - "@radix-ui/react-context": "1.1.1", - "@radix-ui/react-presence": "1.1.2", - "@radix-ui/react-primitive": "2.0.2", - "@radix-ui/react-use-controllable-state": "1.1.0", - "@radix-ui/react-use-previous": "1.1.0", - "@radix-ui/react-use-size": "1.1.0" + "@radix-ui/react-use-layout-effect": "1.1.0" }, "peerDependencies": { "@types/react": "*", @@ -1009,43 +1447,76 @@ } } }, - "node_modules/@radix-ui/react-compose-refs": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/@radix-ui/react-compose-refs/-/react-compose-refs-1.1.1.tgz", - "integrity": "sha512-Y9VzoRDSJtgFMUCoiZBDVo084VQ5hfpXxVE+NgkdNsjiDBByiImMZKKhxMwCbdHvhlENG6a833CbFkOQvTricw==", + "node_modules/@radix-ui/react-primitive": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/@radix-ui/react-primitive/-/react-primitive-2.0.2.tgz", + "integrity": "sha512-Ec/0d38EIuvDF+GZjcMU/Ze6MxntVJYO/fRlCPhCaVUyPY9WTalHJw54tp9sXeJo3tlShWpy41vQRgLRGOuz+w==", "license": "MIT", + "dependencies": { + "@radix-ui/react-slot": "1.1.2" + }, "peerDependencies": { "@types/react": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "peerDependenciesMeta": { "@types/react": { "optional": true + }, + "@types/react-dom": { + "optional": true } } }, - "node_modules/@radix-ui/react-context": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/@radix-ui/react-context/-/react-context-1.1.1.tgz", - "integrity": "sha512-UASk9zi+crv9WteK/NU4PLvOoL3OuE6BWVKNF6hPRBtYBDXQ2u5iu3O59zUlJiTVvkyuycnqrztsHVJwcK9K+Q==", + "node_modules/@radix-ui/react-radio-group": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/@radix-ui/react-radio-group/-/react-radio-group-1.2.3.tgz", + "integrity": "sha512-xtCsqt8Rp09FK50ItqEqTJ7Sxanz8EM8dnkVIhJrc/wkMMomSmXHvYbhv3E7Zx4oXh98aaLt9W679SUYXg4IDA==", "license": "MIT", + "dependencies": { + "@radix-ui/primitive": "1.1.1", + "@radix-ui/react-compose-refs": "1.1.1", + "@radix-ui/react-context": "1.1.1", + "@radix-ui/react-direction": "1.1.0", + "@radix-ui/react-presence": "1.1.2", + "@radix-ui/react-primitive": "2.0.2", + "@radix-ui/react-roving-focus": "1.1.2", + "@radix-ui/react-use-controllable-state": "1.1.0", + "@radix-ui/react-use-previous": "1.1.0", + "@radix-ui/react-use-size": "1.1.0" + }, "peerDependencies": { "@types/react": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "peerDependenciesMeta": { "@types/react": { "optional": true + }, + "@types/react-dom": { + "optional": true } } }, - "node_modules/@radix-ui/react-label": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/@radix-ui/react-label/-/react-label-2.1.2.tgz", - "integrity": "sha512-zo1uGMTaNlHehDyFQcDZXRJhUPDuukcnHz0/jnrup0JA6qL+AFpAnty+7VKa9esuU5xTblAZzTGYJKSKaBxBhw==", + "node_modules/@radix-ui/react-roving-focus": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@radix-ui/react-roving-focus/-/react-roving-focus-1.1.2.tgz", + "integrity": "sha512-zgMQWkNO169GtGqRvYrzb0Zf8NhMHS2DuEB/TiEmVnpr5OqPU3i8lfbxaAmC2J/KYuIQxyoQQ6DxepyXp61/xw==", "license": "MIT", "dependencies": { - "@radix-ui/react-primitive": "2.0.2" + "@radix-ui/primitive": "1.1.1", + "@radix-ui/react-collection": "1.1.2", + "@radix-ui/react-compose-refs": "1.1.1", + "@radix-ui/react-context": "1.1.1", + "@radix-ui/react-direction": "1.1.0", + "@radix-ui/react-id": "1.1.0", + "@radix-ui/react-primitive": "2.0.2", + "@radix-ui/react-use-callback-ref": "1.1.0", + "@radix-ui/react-use-controllable-state": "1.1.0" }, "peerDependencies": { "@types/react": "*", @@ -1062,13 +1533,20 @@ } } }, - "node_modules/@radix-ui/react-presence": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@radix-ui/react-presence/-/react-presence-1.1.2.tgz", - "integrity": "sha512-18TFr80t5EVgL9x1SwF/YGtfG+l0BS0PRAlCWBDoBEiDQjeKgnNZRVJp/oVBl24sr3Gbfwc/Qpj4OcWTQMsAEg==", + "node_modules/@radix-ui/react-scroll-area": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/@radix-ui/react-scroll-area/-/react-scroll-area-1.2.3.tgz", + "integrity": "sha512-l7+NNBfBYYJa9tNqVcP2AGvxdE3lmE6kFTBXdvHgUaZuy+4wGCL1Cl2AfaR7RKyimj7lZURGLwFO59k4eBnDJQ==", "license": "MIT", "dependencies": { + "@radix-ui/number": "1.1.0", + "@radix-ui/primitive": "1.1.1", "@radix-ui/react-compose-refs": "1.1.1", + "@radix-ui/react-context": "1.1.1", + "@radix-ui/react-direction": "1.1.0", + "@radix-ui/react-presence": "1.1.2", + "@radix-ui/react-primitive": "2.0.2", + "@radix-ui/react-use-callback-ref": "1.1.0", "@radix-ui/react-use-layout-effect": "1.1.0" }, "peerDependencies": { @@ -1086,13 +1564,33 @@ } } }, - "node_modules/@radix-ui/react-primitive": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/@radix-ui/react-primitive/-/react-primitive-2.0.2.tgz", - "integrity": "sha512-Ec/0d38EIuvDF+GZjcMU/Ze6MxntVJYO/fRlCPhCaVUyPY9WTalHJw54tp9sXeJo3tlShWpy41vQRgLRGOuz+w==", + "node_modules/@radix-ui/react-select": { + "version": "2.1.6", + "resolved": "https://registry.npmjs.org/@radix-ui/react-select/-/react-select-2.1.6.tgz", + "integrity": "sha512-T6ajELxRvTuAMWH0YmRJ1qez+x4/7Nq7QIx7zJ0VK3qaEWdnWpNbEDnmWldG1zBDwqrLy5aLMUWcoGirVj5kMg==", "license": "MIT", "dependencies": { - "@radix-ui/react-slot": "1.1.2" + "@radix-ui/number": "1.1.0", + "@radix-ui/primitive": "1.1.1", + "@radix-ui/react-collection": "1.1.2", + "@radix-ui/react-compose-refs": "1.1.1", + "@radix-ui/react-context": "1.1.1", + "@radix-ui/react-direction": "1.1.0", + "@radix-ui/react-dismissable-layer": "1.1.5", + "@radix-ui/react-focus-guards": "1.1.1", + "@radix-ui/react-focus-scope": "1.1.2", + "@radix-ui/react-id": "1.1.0", + "@radix-ui/react-popper": "1.2.2", + "@radix-ui/react-portal": "1.1.4", + "@radix-ui/react-primitive": "2.0.2", + "@radix-ui/react-slot": "1.1.2", + "@radix-ui/react-use-callback-ref": "1.1.0", + "@radix-ui/react-use-controllable-state": "1.1.0", + "@radix-ui/react-use-layout-effect": "1.1.0", + "@radix-ui/react-use-previous": "1.1.0", + "@radix-ui/react-visually-hidden": "1.1.2", + "aria-hidden": "^1.2.4", + "react-remove-scroll": "^2.6.3" }, "peerDependencies": { "@types/react": "*", @@ -1127,6 +1625,36 @@ } } }, + "node_modules/@radix-ui/react-tabs": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/@radix-ui/react-tabs/-/react-tabs-1.1.3.tgz", + "integrity": "sha512-9mFyI30cuRDImbmFF6O2KUJdgEOsGh9Vmx9x/Dh9tOhL7BngmQPQfwW4aejKm5OHpfWIdmeV6ySyuxoOGjtNng==", + "license": "MIT", + "dependencies": { + "@radix-ui/primitive": "1.1.1", + "@radix-ui/react-context": "1.1.1", + "@radix-ui/react-direction": "1.1.0", + "@radix-ui/react-id": "1.1.0", + "@radix-ui/react-presence": "1.1.2", + "@radix-ui/react-primitive": "2.0.2", + "@radix-ui/react-roving-focus": "1.1.2", + "@radix-ui/react-use-controllable-state": "1.1.0" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, "node_modules/@radix-ui/react-use-callback-ref": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/@radix-ui/react-use-callback-ref/-/react-use-callback-ref-1.1.0.tgz", @@ -1160,6 +1688,24 @@ } } }, + "node_modules/@radix-ui/react-use-escape-keydown": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-escape-keydown/-/react-use-escape-keydown-1.1.0.tgz", + "integrity": "sha512-L7vwWlR1kTTQ3oh7g1O0CBF3YCyyTj8NmhLR+phShpyA50HCfBFKVJTpshm9PzLiKmehsrQzTYTpX9HvmC9rhw==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-use-callback-ref": "1.1.0" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, "node_modules/@radix-ui/react-use-layout-effect": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/@radix-ui/react-use-layout-effect/-/react-use-layout-effect-1.1.0.tgz", @@ -1190,6 +1736,24 @@ } } }, + "node_modules/@radix-ui/react-use-rect": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-rect/-/react-use-rect-1.1.0.tgz", + "integrity": "sha512-0Fmkebhr6PiseyZlYAOtLS+nb7jLmpqTrJyv61Pe68MKYW6OWdRE2kI70TaYY27u7H0lajqM3hSMMLFq18Z7nQ==", + "license": "MIT", + "dependencies": { + "@radix-ui/rect": "1.1.0" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, "node_modules/@radix-ui/react-use-size": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/@radix-ui/react-use-size/-/react-use-size-1.1.0.tgz", @@ -1208,6 +1772,35 @@ } } }, + "node_modules/@radix-ui/react-visually-hidden": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@radix-ui/react-visually-hidden/-/react-visually-hidden-1.1.2.tgz", + "integrity": "sha512-1SzA4ns2M1aRlvxErqhLHsBHoS5eI5UUcI2awAMgGUp4LoaoWOKYmvqDY2s/tltuPkh3Yk77YF/r3IRj+Amx4Q==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-primitive": "2.0.2" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/rect": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@radix-ui/rect/-/rect-1.1.0.tgz", + "integrity": "sha512-A9+lCBZoaMJlVKcRBz2YByCG+Cp2t6nAnMnNba+XiWxnj6r4JUFqfsgwocMBZU9LPtdxC6wB56ySYpc7LQIoJg==", + "license": "MIT" + }, "node_modules/@tailwindcss/forms": { "version": "0.5.10", "resolved": "https://registry.npmjs.org/@tailwindcss/forms/-/forms-0.5.10.tgz", @@ -1422,6 +2015,18 @@ "dev": true, "license": "Python-2.0" }, + "node_modules/aria-hidden": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/aria-hidden/-/aria-hidden-1.2.4.tgz", + "integrity": "sha512-y+CcFFwelSXpLZk/7fMB2mUbGtX9lKycf1MWJ7CaTIERyitVlyQx6C+sxcROU2BAJ24OiZyK+8wj2i8AlBoS3A==", + "license": "MIT", + "dependencies": { + "tslib": "^2.0.0" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/array-buffer-byte-length": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/array-buffer-byte-length/-/array-buffer-byte-length-1.0.2.tgz", @@ -2055,6 +2660,12 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/detect-node-es": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/detect-node-es/-/detect-node-es-1.1.0.tgz", + "integrity": "sha512-ypdmJU/TbBby2Dxibuv7ZLW3Bs1QEmM7nHjEANfohJLvE0XVujisn1qPJcZxg+qDucsr+bP6fLD1rPS3AhJ7EQ==", + "license": "MIT" + }, "node_modules/didyoumean": { "version": "1.2.2", "resolved": "https://registry.npmjs.org/didyoumean/-/didyoumean-1.2.2.tgz", @@ -2854,6 +3465,15 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/get-nonce": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/get-nonce/-/get-nonce-1.0.1.tgz", + "integrity": "sha512-FJhYRoDaiatfEkUK8HKlicmu/3SGFD51q3itKDGoSTysQJBnfOcxU5GxnhE1E6soB76MbT0MBtnKJuXyAx+96Q==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, "node_modules/get-proto": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", @@ -4487,6 +5107,53 @@ "node": ">=0.10.0" } }, + "node_modules/react-remove-scroll": { + "version": "2.6.3", + "resolved": "https://registry.npmjs.org/react-remove-scroll/-/react-remove-scroll-2.6.3.tgz", + "integrity": "sha512-pnAi91oOk8g8ABQKGF5/M9qxmmOPxaAnopyTHYfqYEwJhyFrbbBtHuSgtKEoH0jpcxx5o3hXqH1mNd9/Oi+8iQ==", + "license": "MIT", + "dependencies": { + "react-remove-scroll-bar": "^2.3.7", + "react-style-singleton": "^2.2.3", + "tslib": "^2.1.0", + "use-callback-ref": "^1.3.3", + "use-sidecar": "^1.1.3" + }, + "engines": { + "node": ">=10" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/react-remove-scroll-bar": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/react-remove-scroll-bar/-/react-remove-scroll-bar-2.3.8.tgz", + "integrity": "sha512-9r+yi9+mgU33AKcj6IbT9oRCO78WriSj6t/cF8DWBZJ9aOGPOTEDvdUDz1FwKim7QXWwmHqtdHnRJfhAxEG46Q==", + "license": "MIT", + "dependencies": { + "react-style-singleton": "^2.2.2", + "tslib": "^2.0.0" + }, + "engines": { + "node": ">=10" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, "node_modules/react-router": { "version": "7.5.0", "resolved": "https://registry.npmjs.org/react-router/-/react-router-7.5.0.tgz", @@ -4527,6 +5194,28 @@ "react-dom": ">=18" } }, + "node_modules/react-style-singleton": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/react-style-singleton/-/react-style-singleton-2.2.3.tgz", + "integrity": "sha512-b6jSvxvVnyptAiLjbkWLE/lOnR4lfTtDAl+eUC7RZy+QQWc6wRzIV2CE6xBuMmDxc2qIihtDCZD5NPOFl7fRBQ==", + "license": "MIT", + "dependencies": { + "get-nonce": "^1.0.0", + "tslib": "^2.0.0" + }, + "engines": { + "node": ">=10" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, "node_modules/read-cache": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/read-cache/-/read-cache-1.0.0.tgz", @@ -5523,6 +6212,49 @@ "punycode": "^2.1.0" } }, + "node_modules/use-callback-ref": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/use-callback-ref/-/use-callback-ref-1.3.3.tgz", + "integrity": "sha512-jQL3lRnocaFtu3V00JToYz/4QkNWswxijDaCVNZRiRTO3HQDLsdu1ZtmIUvV4yPp+rvWm5j0y0TG/S61cuijTg==", + "license": "MIT", + "dependencies": { + "tslib": "^2.0.0" + }, + "engines": { + "node": ">=10" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/use-sidecar": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/use-sidecar/-/use-sidecar-1.1.3.tgz", + "integrity": "sha512-Fedw0aZvkhynoPYlA5WXrMCAMm+nSWdZt6lzJQ7Ok8S6Q+VsHmHpRWndVRJ8Be0ZbkfPc5LRYH+5XrzXcEeLRQ==", + "license": "MIT", + "dependencies": { + "detect-node-es": "^1.1.0", + "tslib": "^2.0.0" + }, + "engines": { + "node": ">=10" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, "node_modules/util-deprecate": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", diff --git a/frontend/package.json b/frontend/package.json index 2aeed4d..29e112c 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -11,11 +11,18 @@ }, "dependencies": { "@radix-ui/react-checkbox": "^1.1.4", + "@radix-ui/react-dialog": "^1.1.6", + "@radix-ui/react-dropdown-menu": "^2.1.6", "@radix-ui/react-label": "^2.1.2", + "@radix-ui/react-radio-group": "^1.2.3", + "@radix-ui/react-scroll-area": "^1.2.3", + "@radix-ui/react-select": "^2.1.6", "@radix-ui/react-slot": "^1.1.2", + "@radix-ui/react-tabs": "^1.1.3", "@tailwindcss/forms": "^0.5.7", "autoprefixer": "^10.4.16", "class-variance-authority": "^0.7.1", + "clsx": "^2.1.1", "framer-motion": "^10.16.4", "jwt-decode": "^4.0.0", "lucide-react": "^0.284.0", diff --git a/frontend/src/App.jsx b/frontend/src/App.jsx index 77f58d6..cdc2801 100644 --- a/frontend/src/App.jsx +++ b/frontend/src/App.jsx @@ -1,24 +1,204 @@ -import { BrowserRouter as Router, Routes, Route, Navigate } from 'react-router-dom' -import LoginPage from './components/auth/Login' -import RegisterPage from './components/auth/Register' -import Dashboard from './components/Dashboard' -import ProtectedRoute from './components/auth/ProtectedRoute' +import React, { useState, Suspense } from 'react'; +import { BrowserRouter as Router, Routes, Route, Navigate } from 'react-router-dom'; + +import LoginPage from './components/auth/Login'; +import RegisterPage from './components/auth/Register'; +import AuthLayout from './components/layouts/auth-layout'; +import { OnboardingFlow } from './pages/Onboarding'; + +import ProtectedRoute from './components/auth/ProtectedRoute'; +import Sidebar from './components/dashboard/sidebar'; + +const DashboardOverview = React.lazy(() => import('./components/dashboard/dashboard-overview')); +const HealthRecords = React.lazy(() => import('./pages/patient-dashboard/health-records')); +const SharingControls = React.lazy(() => import('./pages/patient-dashboard/sharing')); +const SecuritySettings = React.lazy(() => import('./pages/patient-dashboard/security')); +const EmergencyAccess = React.lazy(() => import('./pages/patient-dashboard/emergency')); +const Providers = React.lazy(() => import('./pages/patient-dashboard/providers')); +const Appointments = React.lazy(() => import('./pages/patient-dashboard/appointments')); +const Analytics = React.lazy(() => import('./pages/patient-dashboard/analytics')); +const Profile = React.lazy(() => import('./pages/patient-dashboard/profile')); +const Settings = React.lazy(() => import('./pages/patient-dashboard/settings')); + +const LoadingSpinner = () => ( +
+
+
+); + +const DashboardLayout = ({ children }) => { + const [collapsed, setCollapsed] = useState(false); + + return ( +
+ +
+
+
+

HealthChain

+
+
+
+ {children} +
+
+ © 2023 HealthChain. All rights reserved. +
+
+
+ ); +}; function App() { return ( + + } + /> + + } + /> + + + + } + /> + + + + }> + + + + + } + /> + + + }> + + + + + } + /> + + + }> + + + + + } + /> + + + }> + + + + + } + /> + + + }> + + + + + } + /> + + + }> + + + + + } + /> + + + }> + + + + + } + /> + + + }> + + + + + } + /> + + + }> + + + + + } + /> + + + }> + + + + + } + /> + } /> - } /> - } /> - - - - } /> - ) + ); } -export default App +export default App; diff --git a/frontend/src/components/Dashboard.jsx b/frontend/src/components/Dashboard.jsx deleted file mode 100644 index cde4a56..0000000 --- a/frontend/src/components/Dashboard.jsx +++ /dev/null @@ -1,139 +0,0 @@ -import React, { useEffect, useState } from 'react' -import { useNavigate } from 'react-router-dom' -import { Button } from './ui/Button' -import { Loader2, LogOut } from 'lucide-react' -import api from '../services/api' -import { useToast } from '../hooks/use-toast' -import { ACCESS_TOKEN, REFRESH_TOKEN } from '../constants' - -const Dashboard = () => { - const [loading, setLoading] = useState(true) - const [userData, setUserData] = useState(null) - const navigate = useNavigate() - const { toast } = useToast() - - useEffect(() => { - const checkAuth = () => { - const token = localStorage.getItem(ACCESS_TOKEN) - if (!token) { - toast({ - title: "Authentication required", - description: "Please sign in to access the dashboard", - variant: "destructive", - }) - navigate('/login') - return false - } - return true - } - - if (checkAuth()) { - // Get user data from localStorage - const userData = { - username: localStorage.getItem('username') || 'User', - fullName: localStorage.getItem('fullName') || '', - phoneNumber: localStorage.getItem('phoneNumber') || '', - role: localStorage.getItem('role') || 'patient', - dateOfBirth: localStorage.getItem('dateOfBirth') || '', - gender: localStorage.getItem('gender') || '', - } - - if (userData.role === 'doctor') { - userData.licenseNumber = localStorage.getItem('licenseNumber') || ''; - userData.specialization = localStorage.getItem('specialization') || ''; - userData.hospitalName = localStorage.getItem('hospitalName') || ''; - userData.location = localStorage.getItem('location') || ''; - } - - setUserData(userData) - setLoading(false) - } - }, [navigate, toast]) - - const handleLogout = async () => { - try { - // Use the API service for logout - await api.logout(); - toast({ - title: "Logged out", - description: "You have been successfully logged out", - }); - navigate('/login'); - } catch (error) { - console.error('Logout error:', error); - // Fallback: manually clear tokens if API fails - localStorage.removeItem(ACCESS_TOKEN); - localStorage.removeItem(REFRESH_TOKEN); - toast({ - title: "Logged out", - description: "You have been logged out", - }); - navigate('/login'); - } - }; - - if (loading) { - return ( -
- -

Loading dashboard...

-
- ) - } - - return ( -
-
-
-

Dashboard

- -
- -
-
-

Welcome, {userData?.fullName || 'User'}

-

You have successfully logged in to the application.

-
- -
-
-

User Information

-
- {userData && Object.entries(userData) - .filter(([key]) => key !== 'token' && userData[key]) - .map(([key, value]) => ( -

- {key.replace(/([A-Z])/g, ' $1').trim()}: - {value} -

- )) - } -
-
- -
-

Authentication Status

-
-

Successfully authenticated

-

API connection established

-

JWT Token active

-
-

Token expires in: 1 hour

-
-
-
-
-
-
-
- ) -} - -export default Dashboard \ No newline at end of file diff --git a/frontend/src/components/auth/Login.jsx b/frontend/src/components/auth/Login.jsx index b1b6cb1..c97f375 100644 --- a/frontend/src/components/auth/Login.jsx +++ b/frontend/src/components/auth/Login.jsx @@ -30,9 +30,28 @@ function Login() { const [_location, _setLocation] = useState("") const [rememberMe, setRememberMe] = useState(false) const [errors, setErrors] = useState({}) + const [showOnboardingSuccess, setShowOnboardingSuccess] = useState(false) const { toast } = useToast() const navigate = useNavigate() + // Check if user was redirected from onboarding + React.useEffect(() => { + const onboardingCompleted = localStorage.getItem('onboardingCompleted'); + if (onboardingCompleted === 'true') { + // Only show the message if they just completed onboarding (not on subsequent visits) + const savedEmail = localStorage.getItem('email'); + if (savedEmail) { + setEmail(savedEmail); + setShowOnboardingSuccess(true); + // Show a toast notification + toast({ + title: "Profile setup complete", + description: "Your account has been created successfully. Please log in.", + }); + } + } + }, [toast]); + const handlePasswordChange = (e) => { const newPassword = e.target.value setPassword(newPassword) @@ -229,6 +248,16 @@ function Login() {
+ {showOnboardingSuccess && ( + + + Profile Setup Complete + + Your account has been created successfully. Please log in with your credentials. + + + )} + { diff --git a/frontend/src/components/auth/ProtectedRoute.jsx b/frontend/src/components/auth/ProtectedRoute.jsx index d294c64..2ea49f5 100644 --- a/frontend/src/components/auth/ProtectedRoute.jsx +++ b/frontend/src/components/auth/ProtectedRoute.jsx @@ -1,83 +1,64 @@ import React, { useEffect, useState } from 'react' import { Navigate, useLocation } from 'react-router-dom' import { Loader2 } from 'lucide-react' -import { ACCESS_TOKEN, REFRESH_TOKEN } from '../../constants' -import { jwtDecode } from 'jwt-decode' +import { ACCESS_TOKEN } from '../../constants' const ProtectedRoute = ({ children }) => { - const [loading, setLoading] = useState(true) + const [isLoading, setIsLoading] = useState(true) const [isAuthenticated, setIsAuthenticated] = useState(false) + const [needsOnboarding, setNeedsOnboarding] = useState(false) const location = useLocation() useEffect(() => { - const checkTokenValidity = (token) => { - try { - const decoded = jwtDecode(token); - const currentTime = Date.now() / 1000; - - // Check if token is expired - if (decoded.exp && decoded.exp < currentTime) { - console.log('Token expired'); - return false; - } - - return true; - } catch (error) { - console.error('Token validation error:', error); - return false; - } - }; - - const checkAuth = async () => { + const checkAuthentication = async () => { try { + // Simulate checking token validity const token = localStorage.getItem(ACCESS_TOKEN) + const onboardingCompleted = localStorage.getItem('onboardingCompleted') + const accountCreated = localStorage.getItem('accountCreated') - if (!token) { - setIsAuthenticated(false) - setLoading(false) - return - } - - // Validate the token - const isValid = checkTokenValidity(token) + // Check authentication + const authenticated = !!token + setIsAuthenticated(authenticated) - if (isValid) { - setIsAuthenticated(true) - } else { - // Try to use refresh token - const refreshToken = localStorage.getItem(REFRESH_TOKEN) - if (refreshToken && checkTokenValidity(refreshToken)) { - // In a real app, would call refresh token endpoint here - setIsAuthenticated(true) - } else { - setIsAuthenticated(false) - } + // If account was just created and onboarding is not completed yet, + // user needs to go through onboarding + if (accountCreated === 'true' && onboardingCompleted !== 'true') { + setNeedsOnboarding(true) } - setLoading(false) + setIsLoading(false) } catch (error) { console.error('Auth check error:', error) setIsAuthenticated(false) - setLoading(false) + setIsLoading(false) } } - checkAuth() + checkAuthentication() }, []) - if (loading) { + if (isLoading) { + // Loading state while checking authentication return ( -
+
-

Verifying authentication...

+ Loading...
) } + // Redirect to onboarding if needed (account created but onboarding not completed) + if (needsOnboarding) { + return + } + + // Redirect to login if not authenticated if (!isAuthenticated) { return } + // If authenticated, render the children return children } diff --git a/frontend/src/components/auth/Register.jsx b/frontend/src/components/auth/Register.jsx index cd42563..973a8bc 100644 --- a/frontend/src/components/auth/Register.jsx +++ b/frontend/src/components/auth/Register.jsx @@ -143,19 +143,18 @@ function Register() { localStorage.setItem('fullName', fullName); localStorage.setItem('email', email); - localStorage.setItem('password', email); localStorage.setItem('phone_number', phoneNumber); localStorage.setItem('role', role); + localStorage.setItem('accountCreated', 'true'); await api.register(userData); toast({ title: "Registration successful", - description: "Your account has been created", + description: "Please complete your health profile setup", }); - navigate('/login'); - + navigate('/onboarding'); } catch (error) { toast({ title: "Registration failed", diff --git a/frontend/src/components/dashboard/AddRecordModal.jsx b/frontend/src/components/dashboard/AddRecordModal.jsx new file mode 100644 index 0000000..173862e --- /dev/null +++ b/frontend/src/components/dashboard/AddRecordModal.jsx @@ -0,0 +1,232 @@ +import React, { useState } from 'react'; +import { + Dialog, + DialogContent, + DialogDescription, + DialogFooter, + DialogHeader, + DialogTitle, +} from "../ui/dialog"; +import { Button } from "../ui/Button"; +import { Input } from "../ui/input"; +import { Label } from "../ui/label"; +import { + Select, + SelectContent, + SelectItem, + SelectTrigger, + SelectValue, +} from "../ui/select"; +import { useToast } from "../../hooks/use-toast"; +import healthRecordService from '../../services/health-record-service'; +import { Loader2, Calendar } from 'lucide-react'; + +const AddRecordModal = ({ isOpen, onClose, onSuccess }) => { + const [recordName, setRecordName] = useState(''); + const [recordType, setRecordType] = useState(''); + const [recordProvider, setRecordProvider] = useState(''); + const [recordDate, setRecordDate] = useState(''); + const [recordFile, setRecordFile] = useState(null); + const [isLoading, setIsLoading] = useState(false); + const { toast } = useToast(); + + // Reset form when modal opens + React.useEffect(() => { + if (isOpen) { + setRecordName(''); + setRecordType(''); + setRecordProvider(''); + setRecordDate(new Date().toISOString().split('T')[0]); // Default to today + setRecordFile(null); + } + }, [isOpen]); + + const handleSubmit = async (e) => { + e.preventDefault(); + + if (!recordName || !recordType || !recordProvider) { + toast({ + title: "Missing information", + description: "Please provide name, type, and provider for the record", + variant: "destructive" + }); + return; + } + + setIsLoading(true); + + try { + await healthRecordService.addRecord({ + name: recordName, + type: getRecordTypeLabel(recordType), + provider: recordProvider, + date: recordDate, + file: recordFile + }); + + toast({ + title: "Record added", + description: "Health record has been added successfully" + }); + + // Reset form + setRecordName(''); + setRecordType(''); + setRecordProvider(''); + setRecordDate(''); + setRecordFile(null); + + // Close modal and notify parent + if (onSuccess) { + onSuccess(); + } + onClose(); + } catch (error) { + console.error('Error adding record:', error); + toast({ + title: "Error", + description: "Failed to add health record", + variant: "destructive" + }); + } finally { + setIsLoading(false); + } + }; + + // Convert record type value to label + const getRecordTypeLabel = (value) => { + const typeMap = { + 'lab-test': 'Lab Test', + 'imaging': 'Imaging', + 'prescription': 'Prescription', + 'vaccination': 'Vaccination', + 'visit-summary': 'Visit Summary', + 'specialist-referral': 'Specialist Referral', + 'surgery-report': 'Surgery Report', + 'mental-health': 'Mental Health', + 'physical-therapy': 'Physical Therapy', + 'dental-record': 'Dental Record', + 'vision-exam': 'Vision Exam', + 'allergy-test': 'Allergy Test', + 'other': 'Other' + }; + return typeMap[value] || value; + }; + + return ( + + + + Add Health Record + + Upload a new health record to your secure repository + + + + +
+ + setRecordName(e.target.value)} + required + /> +
+ +
+
+ + +
+ +
+ +
+ + setRecordDate(e.target.value)} + className="pl-10" + required + /> +
+
+
+ +
+ + +
+ +
+ + setRecordFile(e.target.files[0])} + /> +

+ Supported formats: PDF, JPG, PNG (max 10MB) +

+
+ + + + + + +
+
+ ); +}; + +export default AddRecordModal; \ No newline at end of file diff --git a/frontend/src/components/dashboard/dashboard-overview.jsx b/frontend/src/components/dashboard/dashboard-overview.jsx new file mode 100644 index 0000000..80b9183 --- /dev/null +++ b/frontend/src/components/dashboard/dashboard-overview.jsx @@ -0,0 +1,783 @@ +import React, { useState, useEffect } from "react" +import { useNavigate } from "react-router-dom" +import { + Card, + CardContent, + CardDescription, + CardFooter, + CardHeader, + CardTitle, +} from "../ui/card" +import { Button } from "../ui/Button" +import { Badge } from "../ui/badge" +import { + Tabs, + TabsContent, + TabsList, + TabsTrigger, +} from "../ui/tabs" + +import { + FileText, + Share2, + AlertTriangle, + Lock, + Plus, + Calendar, + Clock, + Activity, + ChevronRight, + Bell, + CheckCircle, + XCircle, + AlertCircle, + Heart, + Droplets, + Dumbbell, + User, + LogOut +} from "lucide-react" + +import { RecentActivityList } from "./recent-activity-list" +import { UpcomingAppointments } from "./upcoming-appointments" +import HealthSummary from "./health-summary" +import { useToast } from "../../hooks/use-toast" +import { ACCESS_TOKEN, REFRESH_TOKEN } from "../../constants" +import api from "../../services/api" +import { Link } from 'react-router-dom'; +import healthRecordService from '../../services/health-record-service'; +import appointmentService from '../../services/appointment-service'; +import AddRecordModal from './AddRecordModal'; + +const DashboardOverview = () => { + const [loading, setLoading] = useState(true) + const [userData, setUserData] = useState(null) + const [healthData, setHealthData] = useState({ + bloodType: 'A+', + height: '175', + weight: '72', + bloodPressure: '120/80', + heartRate: '72', + chronicConditions: ['Asthma (Mild)'], + medications: [], + allergies: [] + }) + const navigate = useNavigate() + const { toast } = useToast() + const [healthRecords, setHealthRecords] = useState([]); + const [appointmentCount, setAppointmentCount] = useState(0); + const [isAddingRecord, setIsAddingRecord] = useState(false); + + // Safe JSON parsing function + const safeJsonParse = (jsonString, fallback) => { + if (!jsonString) return fallback + + try { + return JSON.parse(jsonString) + } catch (error) { + // If it's not JSON, treat as a single item array if it's a non-empty string + return jsonString.trim() ? [jsonString.trim()] : fallback + } + } + + useEffect(() => { + const checkAuth = () => { + const token = localStorage.getItem(ACCESS_TOKEN) + if (!token) { + toast({ + title: "Authentication required", + description: "Please sign in to access the dashboard", + variant: "destructive", + }) + navigate('/login') + return false + } + return true + } + + if (checkAuth()) { + // Load user authentication data + const userData = { + username: localStorage.getItem('username') || 'User', + fullName: localStorage.getItem('fullName') || '', + phoneNumber: localStorage.getItem('phoneNumber') || '', + role: localStorage.getItem('role') || 'patient', + dateOfBirth: localStorage.getItem('dateOfBirth') || '', + gender: localStorage.getItem('gender') || '', + } + + if (userData.role === 'doctor') { + userData.licenseNumber = localStorage.getItem('licenseNumber') || '' + userData.specialization = localStorage.getItem('specialization') || '' + userData.hospitalName = localStorage.getItem('hospitalName') || '' + userData.location = localStorage.getItem('location') || '' + } + + // Load health data from localStorage + const healthData = { + bloodType: localStorage.getItem('bloodType') || 'A+', + height: localStorage.getItem('height') || '175', + weight: localStorage.getItem('weight') || '72', + bloodPressure: localStorage.getItem('bloodPressure') || '120/80', + heartRate: localStorage.getItem('heartRate') || '72', + // Parse safely with fallbacks + chronicConditions: safeJsonParse(localStorage.getItem('chronicConditions'), ['Asthma (Mild)']), + medications: safeJsonParse(localStorage.getItem('medications'), []), + allergies: safeJsonParse(localStorage.getItem('allergies'), []), + // Additional health data + emergencyContactName: localStorage.getItem('emergencyContactName') || '', + emergencyContactRelation: localStorage.getItem('emergencyContactRelation') || '', + emergencyContactPhone: localStorage.getItem('emergencyContactPhone') || '', + } + + setUserData(userData) + setHealthData(healthData) + setLoading(false) + + // Check if onboarding is completed after login + const onboardingCompleted = localStorage.getItem('onboardingCompleted') + if (!onboardingCompleted) { + toast({ + title: "Complete your profile", + description: "Please complete your health profile setup", + }) + navigate('/onboarding') + return + } + } + }, [navigate, toast]) + + useEffect(() => { + // Load health records count + const fetchHealthRecords = async () => { + try { + const records = await healthRecordService.getRecords(); + setHealthRecords(records); + } catch (error) { + console.error('Error fetching health records:', error); + } + }; + + // Load appointment count + const fetchAppointments = async () => { + try { + const appointments = await appointmentService.getAppointments(); + setAppointmentCount(appointments.length); + } catch (error) { + console.error('Error fetching appointments:', error); + } + }; + + fetchHealthRecords(); + fetchAppointments(); + }, []); + + const handleLogout = async () => { + try { + await api.logout() + toast({ + title: "Logged out", + description: "You have been successfully logged out", + }) + navigate('/login') + } catch (error) { + console.error('Logout error:', error) + localStorage.removeItem(ACCESS_TOKEN) + localStorage.removeItem(REFRESH_TOKEN) + toast({ + title: "Logged out", + description: "You have been logged out", + }) + navigate('/login') + } + } + + const handleAddRecord = () => { + setIsAddingRecord(true); + }; + + const handleManageSharing = () => { + navigate('/sharing'); + }; + + const handleSecuritySettings = () => { + navigate('/security'); + }; + + const handleEmergencyInfo = () => { + navigate('/emergency'); + }; + + const handleManageAppointments = () => { + navigate('/appointments'); + }; + + const handleHealthAnalytics = () => { + navigate('/analytics'); + }; + + const handleAddMedication = () => { + // Show medication adding modal or navigate to medication add page + const medication = prompt('Enter new medication:'); + if (medication) { + const updatedMedications = [...healthData.medications, medication]; + localStorage.setItem('medications', JSON.stringify(updatedMedications)); + setHealthData({ + ...healthData, + medications: updatedMedications + }); + toast({ + title: "Medication added", + description: "Your medication has been added to your profile" + }); + } + }; + + const handleAddAllergy = () => { + // Show allergy adding modal or navigate to allergy add page + const allergy = prompt('Enter new allergy:'); + if (allergy) { + const updatedAllergies = [...healthData.allergies, allergy]; + localStorage.setItem('allergies', JSON.stringify(updatedAllergies)); + setHealthData({ + ...healthData, + allergies: updatedAllergies + }); + toast({ + title: "Allergy added", + description: "Your allergy has been added to your profile" + }); + } + }; + + const handleRecordAdded = async () => { + try { + const records = await healthRecordService.getRecords(); + setHealthRecords(records); + toast({ + title: "Record added", + description: "Your health record has been added successfully" + }); + } catch (error) { + console.error('Error refreshing records:', error); + } + }; + + if (loading) { + return ( +
+
+

Loading dashboard...

+
+ ) + } + + return ( +
+ {/* Header */} +
+
+

Dashboard

+

Welcome back, {userData?.fullName || 'User'}

+
+ +
+ + {/* Top Cards */} +
+ {/* Health Records */} + + + Health Records + + + +
{healthRecords.length}
+

+3 added this month

+
+ + + +
+ + {/* Active Sharing */} + + + Active Sharing + + + +
{appointmentCount}
+

+ {appointmentCount > 1 ? `${appointmentCount - 1} healthcare providers, 1 organization` : '1 healthcare provider'} +

+
+ + + +
+ + {/* Security Status */} + + + Security Status + + + +
+
Secure
+ Protected +
+

Last verified 2 hours ago

+
+ + + +
+ + {/* Emergency Access */} + + + Emergency Access + + + +
+
Ready
+ + Enabled + +
+

PIN: **** (Last updated 30 days ago)

+
+ + + +
+
+ + {/* Tabs Section */} + + + + Overview + + + Recent Activity + + + Appointments + + + + {/* Overview Tab */} + +
+ + + Health Summary + Your key health metrics and information + + + + + Overview + Medications + Allergies + + + {/* Overview Tab */} + +
+
+
Blood Type
+
+ + {healthData.bloodType} +
+
+ +
+
Height & Weight
+
+ + {healthData.height} cm, {healthData.weight} kg +
+
+
+ +
+
Vital Signs
+
+
+
+
+ + Blood Pressure +
+ Normal +
+
{healthData.bloodPressure}
+
Last updated: 2 days ago
+
+ +
+
+
+ + Heart Rate +
+ Normal +
+
{healthData.heartRate} bpm
+
Last updated: 2 days ago
+
+
+
+ + {healthData.chronicConditions && healthData.chronicConditions.length > 0 && ( +
+
Chronic Conditions
+
+
    + {healthData.chronicConditions.map((condition, index) => ( +
  • + +
    +

    {condition}

    +

    Diagnosed: Jan 2018

    +
    +
  • + ))} +
+
+
+ )} +
+ + {/* Medications Tab */} + +
+ {healthData.medications && healthData.medications.length > 0 ? ( +
+ {healthData.medications.map((medication, index) => ( +
+
+
+ + + +
+
+

{medication}

+

Take as prescribed

+
+
+
+ ))} +
+ ) : ( +
+

No medications recorded

+ +
+ )} +
+
+ + {/* Allergies Tab */} + +
+ {healthData.allergies && healthData.allergies.length > 0 ? ( +
+ {healthData.allergies.map((allergy, index) => ( +
+
+
+ + + +
+
+

{allergy}

+

Avoid exposure

+
+
+
+ ))} +
+ ) : ( +
+

No allergies recorded

+ +
+ )} +
+
+
+
+
+ + {/* Notifications */} + + + Notifications + Recent alerts and updates + + +
+
+
+ +
+
+

Appointment Reminder

+

+ You have an appointment with Dr. Johnson tomorrow at 10:00 AM. +

+

2 hours ago

+
+
+ +
+
+ +
+
+

Lab Results Available

+

+ Your recent lab results have been uploaded to your records. +

+

Yesterday

+
+
+ +
+
+ +
+
+

Sharing Request

+

+ Central Hospital has requested access to your medical history. +

+

2 days ago

+
+
+ +
+
+ +
+
+

Access Expired

+

+ Dr. Chen's access to your records has expired. +

+

3 days ago

+
+
+
+
+
+
+ + {/* Lower Grid */} +
+ + +
+ Upcoming Appointments + Your scheduled healthcare visits +
+ +
+ + + + +
+ + + +
+ Recent Activity + Recent actions on your health records +
+ +
+ + + + +
+ + + +
+ Health Trends + Tracking your key health metrics +
+ +
+ + {/* Health metric placeholders */} +
+
+ Blood Pressure + Stable +
+
+
+
+
+ +
+
+ Weight + Slight increase +
+
+
+
+
+ +
+
+ Heart Rate + Normal +
+
+
+
+
+
+ + + +
+
+
+ + {/* Activity Tab */} + + + + Recent Activity + A detailed log of recent actions on your health records + + + + + + {/* Appointments Tab */} + + + + Upcoming Appointments + Your scheduled healthcare visits + + + + + + + +
+ + {isAddingRecord && ( + setIsAddingRecord(false)} + onSuccess={handleRecordAdded} + /> + )} +
+ ) +} + +export default DashboardOverview diff --git a/frontend/src/components/dashboard/health-summary.jsx b/frontend/src/components/dashboard/health-summary.jsx new file mode 100644 index 0000000..3adacf6 --- /dev/null +++ b/frontend/src/components/dashboard/health-summary.jsx @@ -0,0 +1,180 @@ +import React from 'react' +import { Tabs, TabsContent, TabsList, TabsTrigger } from "../ui/tabs"; +import { Badge } from "../ui/badge"; +import { Activity, Pill, AlertTriangle, Heart, Droplets, Dumbbell } from "lucide-react"; + +const HealthSummary = () => { + return ( + + + Overview + Medications + Allergies + + + +
+
+
Blood Type
+
+ + A+ +
+
+ +
+
Height & Weight
+
+ + 175 cm, 72 kg +
+
+
+ +
+
Vital Signs
+
+
+
+
+ + Blood Pressure +
+ Normal +
+
120/80
+
Last updated: 2 days ago
+
+ +
+
+
+ + Heart Rate +
+ Normal +
+
72 bpm
+
Last updated: 2 days ago
+
+
+
+ +
+
Chronic Conditions
+
+
+ + Asthma (Mild) +
+
+

Diagnosed: Jan 2018

+

Managed with: Albuterol inhaler as needed

+
+
+
+
+ + +
+
+
+
+ + Lisinopril +
+ Active +
+
+

10mg, Once daily

+

For blood pressure management

+

Prescribed by: Dr. Sarah Johnson

+

Started: March 2023

+
+
+ +
+
+
+ + Albuterol Inhaler +
+ As Needed +
+
+

2 puffs as needed for shortness of breath

+

For asthma symptoms

+

Prescribed by: Dr. Emily Rodriguez

+

Started: January 2018

+
+
+ +
+
+
+ + Vitamin D Supplement +
+ Active +
+
+

1000 IU, Once daily

+

For vitamin D deficiency

+

Prescribed by: Dr. Emily Rodriguez

+

Started: September 2022

+
+
+
+
+ + +
+
+
+ + Penicillin +
+
+

+ Severity: Severe +

+

Reaction: Hives, difficulty breathing, swelling

+

Diagnosed: 2010

+
+
+ +
+
+ + Shellfish +
+
+

+ Severity: Moderate +

+

Reaction: Nausea, vomiting, skin rash

+

Diagnosed: 2015

+
+
+ +
+
+ + Pollen +
+
+

+ Severity: Mild +

+

Reaction: Sneezing, itchy eyes, congestion

+

Diagnosed: 2008

+
+
+
+
+
+ ); +} + +export { HealthSummary } +export default HealthSummary diff --git a/frontend/src/components/dashboard/recent-activity-list.jsx b/frontend/src/components/dashboard/recent-activity-list.jsx new file mode 100644 index 0000000..1d219b1 --- /dev/null +++ b/frontend/src/components/dashboard/recent-activity-list.jsx @@ -0,0 +1,240 @@ +"use client" + +import React from 'react' +import { + FileText, + Eye, + Lock, + Share2, + Download, + User, + AlertCircle +} from 'lucide-react' +import { Badge } from "../ui/badge" + +// Mock data for demonstration +const mockActivities = [ + { + id: "1", + type: "view", + description: "Dr. Sarah Johnson viewed your medical history", + timestamp: "2023-10-15T14:30:00", + actor: "Dr. Sarah Johnson", + status: "authorized", + }, + { + id: "2", + type: "login", + description: "You logged in from a new device", + timestamp: "2023-10-14T09:15:00", + actor: "You", + status: "authorized", + }, + { + id: "3", + type: "upload", + description: "Central Hospital uploaded new lab results", + timestamp: "2023-10-10T16:45:00", + actor: "Central Hospital", + status: "authorized", + }, + { + id: "4", + type: "share", + description: "You granted access to Dr. Michael Chen", + timestamp: "2023-10-08T11:20:00", + actor: "You", + status: "authorized", + }, + { + id: "5", + type: "download", + description: "You downloaded your vaccination records", + timestamp: "2023-10-05T13:45:00", + actor: "You", + status: "authorized", + }, + { + id: "6", + type: "edit", + description: "You updated your emergency contact information", + timestamp: "2023-10-03T10:30:00", + actor: "You", + status: "authorized", + }, + { + id: "7", + type: "login_attempt", + description: "Failed login attempt from unknown device", + timestamp: "2023-10-01T22:15:00", + actor: "Unknown", + status: "blocked", + }, + { + id: "8", + type: "view", + description: "Emergency access by paramedic using PIN", + timestamp: "2023-09-28T08:20:00", + actor: "Emergency Services", + status: "emergency", + }, +] + +export const RecentActivityList = ({ limit = 5 }) => { + // Dummy data for recent activities + const activities = [ + { + id: 1, + type: 'view', + icon: Eye, + title: 'Blood Test Results viewed', + actor: 'You', + date: 'Today, 2:30 PM', + color: 'text-blue-500', + bg: 'bg-blue-100' + }, + { + id: 2, + type: 'share', + icon: Share2, + title: 'X-Ray Report shared', + actor: 'You', + with: 'Dr. Johnson', + date: 'Today, 11:15 AM', + color: 'text-purple-500', + bg: 'bg-purple-100' + }, + { + id: 3, + type: 'upload', + icon: FileText, + title: 'New Vaccination Record added', + actor: 'Dr. Smith', + date: 'Yesterday, 4:20 PM', + color: 'text-green-500', + bg: 'bg-green-100' + }, + { + id: 4, + type: 'access', + icon: Lock, + title: 'Emergency Information accessed', + actor: 'Central Hospital', + date: '2 days ago, 8:45 AM', + color: 'text-amber-500', + bg: 'bg-amber-100' + }, + { + id: 5, + type: 'download', + icon: Download, + title: 'Medical History downloaded', + actor: 'You', + date: '3 days ago, 1:30 PM', + color: 'text-indigo-500', + bg: 'bg-indigo-100' + }, + { + id: 6, + type: 'permission', + icon: User, + title: 'Access permission changed', + actor: 'You', + for: 'Dr. Williams', + date: '4 days ago, 9:20 AM', + color: 'text-gray-500', + bg: 'bg-gray-100' + }, + { + id: 7, + type: 'alert', + icon: AlertCircle, + title: 'Sharing request received', + actor: 'Medical Research Lab', + date: '5 days ago, 3:45 PM', + status: 'pending', + color: 'text-red-500', + bg: 'bg-red-100' + } + ] + + const limitedActivities = activities.slice(0, limit) + + const getActivityIcon = (type) => { + switch (type) { + case "view": + return + case "login": + return + case "upload": + return + case "download": + return + case "share": + return + case "edit": + return + case "login_attempt": + return + default: + return + } + } + + const getStatusBadge = (status) => { + switch (status) { + case "authorized": + return ( + + Authorized + + ) + case "blocked": + return ( + + Blocked + + ) + case "emergency": + return ( + + Emergency + + ) + default: + return {status} + } + } + + return ( +
+ {limitedActivities.map((activity) => ( +
+
+ +
+
+

{activity.title}

+

+ {activity.actor} + {activity.with && with {activity.with}} + {activity.for && for {activity.for}} +

+

{activity.date}

+
+
+ ))} +
+ ) +} + +export default RecentActivityList diff --git a/frontend/src/components/dashboard/sidebar.jsx b/frontend/src/components/dashboard/sidebar.jsx new file mode 100644 index 0000000..4d419aa --- /dev/null +++ b/frontend/src/components/dashboard/sidebar.jsx @@ -0,0 +1,218 @@ +"use client" + +import React from 'react'; +import { Link, useLocation } from 'react-router-dom'; +import { + FileText, + Share2, + Shield, + AlertCircle, + User, + Settings, + LogOut, + Calendar, + UserPlus, + PieChart, + Home +} from 'lucide-react'; + +const Sidebar = ({ collapsed, setCollapsed }) => { + const location = useLocation(); + const path = location.pathname; + + // Check if current path is dashboard or root + const isDashboard = path === '/' || path === '/dashboard'; + + return ( +
+
+ {!collapsed && ( +
HealthChain
+ )} + +
+ +
+
+ {!collapsed && 'Overview'} +
+ } + label="Dashboard" + to="/dashboard" + collapsed={collapsed} + active={isDashboard} + color="blue" + /> + +
+ {!collapsed && 'Health Records'} +
+ } + label="Health Records" + to="/health-records" + collapsed={collapsed} + active={path === '/health-records'} + color="indigo" + /> + } + label="Sharing Controls" + to="/sharing" + collapsed={collapsed} + active={path === '/sharing'} + color="purple" + /> + } + label="Security Settings" + to="/security" + collapsed={collapsed} + active={path === '/security'} + color="green" + /> + } + label="Emergency Access" + to="/emergency" + collapsed={collapsed} + active={path === '/emergency'} + color="amber" + /> + +
+ {!collapsed && 'Health Management'} +
+ } + label="Providers" + to="/providers" + collapsed={collapsed} + active={path === '/providers'} + color="teal" + /> + } + label="Appointments" + to="/appointments" + collapsed={collapsed} + active={path === '/appointments'} + color="rose" + /> + } + label="Analytics" + to="/analytics" + collapsed={collapsed} + active={path === '/analytics'} + color="cyan" + /> + +
+ {!collapsed && 'Account'} +
+ } + label="Profile" + to="/profile" + collapsed={collapsed} + active={path === '/profile'} + color="blue" + /> + } + label="Settings" + to="/settings" + collapsed={collapsed} + active={path === '/settings'} + color="gray" + /> + } + label="Logout" + to="/logout" + collapsed={collapsed} + active={path === '/logout'} + color="red" + /> +
+
+ ); +}; + +const getColorStyles = (color, active) => { + const colors = { + blue: { + active: "bg-gray-100 text-gray-800", + inactive: "text-gray-600 hover:bg-black/5 hover:text-gray-800" + }, + indigo: { + active: "bg-gray-100 text-gray-800", + inactive: "text-gray-600 hover:bg-black/5 hover:text-gray-800" + }, + purple: { + active: "bg-gray-100 text-gray-800", + inactive: "text-gray-600 hover:bg-black/5 hover:text-gray-800" + }, + green: { + active: "bg-gray-100 text-gray-800", + inactive: "text-gray-600 hover:bg-black/5 hover:text-gray-800" + }, + amber: { + active: "bg-gray-100 text-gray-800", + inactive: "text-gray-600 hover:bg-black/5 hover:text-gray-800" + }, + teal: { + active: "bg-gray-100 text-gray-800", + inactive: "text-gray-600 hover:bg-black/5 hover:text-gray-800" + }, + rose: { + active: "bg-gray-100 text-gray-800", + inactive: "text-gray-600 hover:bg-black/5 hover:text-gray-800" + }, + cyan: { + active: "bg-gray-100 text-gray-800", + inactive: "text-gray-600 hover:bg-black/5 hover:text-gray-800" + }, + gray: { + active: "bg-gray-100 text-gray-800", + inactive: "text-gray-600 hover:bg-black/5 hover:text-gray-800" + }, + red: { + active: "bg-gray-100 text-gray-800", + inactive: "text-gray-600 hover:bg-black/5 hover:text-gray-800" + } + }; + + return active ? colors[color].active : colors[color].inactive; +}; + +const NavItem = ({ icon, label, to, collapsed, active = false, color = "blue" }) => { + const colorClasses = getColorStyles(color, active); + + return ( + +
{icon}
+ {!collapsed &&
{label}
} + + ); +}; + +export default Sidebar; diff --git a/frontend/src/components/dashboard/upcoming-appointments.jsx b/frontend/src/components/dashboard/upcoming-appointments.jsx new file mode 100644 index 0000000..3067325 --- /dev/null +++ b/frontend/src/components/dashboard/upcoming-appointments.jsx @@ -0,0 +1,152 @@ +import React from "react" +import { Calendar, Clock, MapPin, Video, Phone, MoreHorizontal } from "lucide-react" +import { Button } from "../ui/Button" +import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuTrigger } from "../ui/dropdown-menu" +import { Badge } from "../ui/badge" + +export const UpcomingAppointments = ({ limit = 5 }) => { + // Dummy data for upcoming appointments + const appointments = [ + { + id: 1, + doctor: 'Dr. Sarah Johnson', + specialty: 'Cardiologist', + type: 'Check-up', + date: 'Tomorrow', + time: '10:00 AM', + location: 'Heart Care Center', + isVirtual: false, + status: 'confirmed' + }, + { + id: 2, + doctor: 'Dr. Michael Chen', + specialty: 'Dermatologist', + type: 'Consultation', + date: 'June 15, 2023', + time: '2:30 PM', + location: 'Video Call', + isVirtual: true, + status: 'confirmed' + }, + { + id: 3, + doctor: 'Dr. Emily Rodriguez', + specialty: 'Neurologist', + type: 'Follow-up', + date: 'June 22, 2023', + time: '9:15 AM', + location: 'Neurology Associates', + isVirtual: false, + status: 'pending' + }, + { + id: 4, + doctor: 'Dr. James Wilson', + specialty: 'Orthopedist', + type: 'Physical Therapy', + date: 'June 30, 2023', + time: '11:45 AM', + location: 'Sports Medicine Clinic', + isVirtual: false, + status: 'confirmed' + }, + { + id: 5, + doctor: 'Dr. Lisa Wong', + specialty: 'Psychiatrist', + type: 'Therapy Session', + date: 'July 5, 2023', + time: '4:00 PM', + location: 'Video Call', + isVirtual: true, + status: 'confirmed' + } + ] + + const limitedAppointments = appointments.slice(0, limit) + + const getAppointmentTypeIcon = (type) => { + switch (type) { + case "video": + return