diff --git a/.env b/.env
index 98c8196862..cf7b403546 100644
--- a/.env
+++ b/.env
@@ -5,33 +5,41 @@ DOMAIN=localhost
# Environment: local, staging, production
ENVIRONMENT=local
-PROJECT_NAME="Full Stack FastAPI Project"
+PROJECT_NAME="SOCIA"
STACK_NAME=full-stack-fastapi-project
# Backend
BACKEND_CORS_ORIGINS="http://localhost,http://localhost:5173,https://localhost,https://localhost:5173,http://localhost.tiangolo.com"
-SECRET_KEY=changethis
-FIRST_SUPERUSER=admin@example.com
-FIRST_SUPERUSER_PASSWORD=changethis
+SECRET_KEY=PDma3zqc5pq9LZVfVj7qL5bB6mC4OCIN0sKdjSiKOlI
+FIRST_SUPERUSER=arpit.singh@example.com
+FIRST_SUPERUSER_PASSWORD=SOciaSOcia
+# Otpless
+CLIENT_ID = "CLIENT_ID"
+CLIENT_SECRET = "CLIENT_SECRET"
# Emails
-SMTP_HOST=
-SMTP_USER=
-SMTP_PASSWORD=
-EMAILS_FROM_EMAIL=info@example.com
+SMTP_HOST=arpit.singh@example.com
+SMTP_USER=arpit.singh@example.com
+SMTP_PASSWORD=SOciaSOcia
+EMAILS_FROM_EMAIL=arpit.singh@example.com
SMTP_TLS=True
SMTP_SSL=False
SMTP_PORT=587
# Postgres
-POSTGRES_SERVER=localhost
-POSTGRES_PORT=5432
-POSTGRES_DB=app
-POSTGRES_USER=postgres
-POSTGRES_PASSWORD=changethis
+POSTGRES_SERVER=aws-0-ap-south-1.pooler.supabase.com
+POSTGRES_PORT=6543
+POSTGRES_USER=postgres.uiwsgdtnmovxahfgxfkj
+POSTGRES_PASSWORD=Aa1sociaaicos
+POSTGRES_DB=sociadb
SENTRY_DSN=
# Configure these with your own Docker registry images
DOCKER_IMAGE_BACKEND=backend
-DOCKER_IMAGE_FRONTEND=frontend
+
+# MAPS Config
+GOOGLE_API_KEY = "AIzaSyBGEPYgPDaNeWKqxXko0GWJ1Ipl75q1OlM"
+GOOGLE_REVERSE_GEOCODE_URL = "https://maps.googleapis.com/maps/api/geocode/json"
+GOOGLE_PLACES_URL = "https://maps.googleapis.com/maps/api/place/textsearch/json"
+GOOGLE_PLACE_DETAILS_URL = "https://maps.googleapis.com/maps/api/place/details/json"
\ No newline at end of file
diff --git a/.github/workflows/generate-client.yml b/.github/workflows/generate-client.yml
index a81f78cb51..e69de29bb2 100644
--- a/.github/workflows/generate-client.yml
+++ b/.github/workflows/generate-client.yml
@@ -1,49 +0,0 @@
-name: Generate Client
-
-on:
- pull_request:
- types:
- - opened
- - synchronize
-
-jobs:
- generate-client:
- permissions:
- contents: write
- runs-on: ubuntu-latest
- steps:
- - uses: actions/checkout@v4
- with:
- ref: ${{ github.head_ref }}
- token: ${{ secrets.FULL_STACK_FASTAPI_TEMPLATE_REPO_TOKEN }}
- - uses: actions/setup-node@v4
- with:
- node-version: lts/*
- - uses: actions/setup-python@v5
- with:
- python-version: '3.10'
- - name: Install dependencies
- run: npm ci
- working-directory: frontend
- - run: pip install ./backend
- - run: bash scripts/generate-client.sh
- - name: Commit changes
- run: |
- git config --local user.email "github-actions@github.com"
- git config --local user.name "github-actions"
- git add frontend/src/client
- git diff --staged --quiet || git commit -m "✨ Autogenerate frontend client"
- git push
-
- # https://github.com/marketplace/actions/alls-green#why
- generate-client-alls-green: # This job does nothing and is only used for the branch protection
- if: always()
- needs:
- - generate-client
- runs-on: ubuntu-latest
- steps:
- - name: Decide whether the needed jobs succeeded or failed
- uses: re-actors/alls-green@release/v1
- with:
- jobs: ${{ toJSON(needs) }}
-
\ No newline at end of file
diff --git a/.github/workflows/playwright.yml b/.github/workflows/playwright.yml
deleted file mode 100644
index fc208993f6..0000000000
--- a/.github/workflows/playwright.yml
+++ /dev/null
@@ -1,67 +0,0 @@
-name: Playwright Tests
-
-on:
- push:
- branches:
- - master
- pull_request:
- types:
- - opened
- - synchronize
- workflow_dispatch:
- inputs:
- debug_enabled:
- description: 'Run the build with tmate debugging enabled (https://github.com/marketplace/actions/debugging-with-tmate)'
- required: false
- default: 'false'
-
-jobs:
-
- test:
- timeout-minutes: 60
- runs-on: ubuntu-latest
- steps:
- - uses: actions/checkout@v4
- - uses: actions/setup-node@v4
- with:
- node-version: lts/*
- - uses: actions/setup-python@v5
- with:
- python-version: '3.10'
- - name: Setup tmate session
- uses: mxschmitt/action-tmate@v3
- if: ${{ github.event_name == 'workflow_dispatch' && github.event.inputs.debug_enabled == 'true' }}
- with:
- limit-access-to-actor: true
- - name: Install dependencies
- run: npm ci
- working-directory: frontend
- - name: Install Playwright Browsers
- run: npx playwright install --with-deps
- working-directory: frontend
- - run: docker compose build
- - run: docker compose down -v --remove-orphans
- - run: docker compose up -d
- - name: Run Playwright tests
- run: npx playwright test
- working-directory: frontend
- - run: docker compose down -v --remove-orphans
- - uses: actions/upload-artifact@v4
- if: always()
- with:
- name: playwright-report
- path: frontend/playwright-report/
- retention-days: 30
- include-hidden-files: true
-
- # https://github.com/marketplace/actions/alls-green#why
- e2e-alls-green: # This job does nothing and is only used for the branch protection
- if: always()
- needs:
- - test
- runs-on: ubuntu-latest
- steps:
- - name: Decide whether the needed jobs succeeded or failed
- uses: re-actors/alls-green@release/v1
- with:
- jobs: ${{ toJSON(needs) }}
diff --git a/.gitignore b/.gitignore
index a6dd346572..530b37eca8 100644
--- a/.gitignore
+++ b/.gitignore
@@ -4,3 +4,6 @@ node_modules/
/playwright-report/
/blob-report/
/playwright/.cache/
+.history/
+.DS_Store
+
diff --git a/.vscode/launch.json b/.vscode/launch.json
index 24eae850d0..c1888bf5cd 100644
--- a/.vscode/launch.json
+++ b/.vscode/launch.json
@@ -16,13 +16,6 @@
"cwd": "${workspaceFolder}/backend",
"jinja": true,
"envFile": "${workspaceFolder}/.env",
- },
- {
- "type": "chrome",
- "request": "launch",
- "name": "Debug Frontend: Launch Chrome against http://localhost:5173",
- "url": "http://localhost:5173",
- "webRoot": "${workspaceFolder}/frontend"
- },
+ }
]
}
diff --git a/README.md b/README.md
index afe124f3fb..80188b0e26 100644
--- a/README.md
+++ b/README.md
@@ -216,10 +216,6 @@ The input variables, with their default values (some auto generated) are:
Backend docs: [backend/README.md](./backend/README.md).
-## Frontend Development
-
-Frontend docs: [frontend/README.md](./frontend/README.md).
-
## Deployment
Deployment docs: [deployment.md](./deployment.md).
diff --git a/backend/.gitignore b/backend/.gitignore
index 63f67bcd21..101092a3ec 100644
--- a/backend/.gitignore
+++ b/backend/.gitignore
@@ -5,4 +5,4 @@ app.egg-info
.coverage
htmlcov
.cache
-.venv
+.venv
\ No newline at end of file
diff --git a/backend/app/alembic/versions/.keep b/backend/6a0826f924e8,
old mode 100755
new mode 100644
similarity index 100%
rename from backend/app/alembic/versions/.keep
rename to backend/6a0826f924e8,
diff --git a/backend/Dockerfile b/backend/Dockerfile
index c3187aeb28..57fa537edd 100644
--- a/backend/Dockerfile
+++ b/backend/Dockerfile
@@ -26,3 +26,5 @@ COPY ./prestart.sh /app/
COPY ./tests-start.sh /app/
COPY ./app /app/app
+
+RUN poetry run pip list
\ No newline at end of file
diff --git a/backend/app/alembic/env.py b/backend/app/alembic/env.py
index 7f29c04680..ddab70f856 100755
--- a/backend/app/alembic/env.py
+++ b/backend/app/alembic/env.py
@@ -22,7 +22,7 @@
from app.core.config import settings # noqa
target_metadata = SQLModel.metadata
-
+print("target_metadata in env:", SQLModel.metadata.tables)
# other values from the config, defined by the needs of env.py,
# can be acquired:
# my_important_option = config.get_main_option("my_important_option")
@@ -78,7 +78,7 @@ def run_migrations_online():
context.run_migrations()
-if context.is_offline_mode():
- run_migrations_offline()
-else:
- run_migrations_online()
+# if context.is_offline_mode():
+# run_migrations_offline()
+# else:
+run_migrations_online()
diff --git a/backend/app/alembic/versions/1a31ce608336_add_cascade_delete_relationships.py b/backend/app/alembic/versions/1a31ce608336_add_cascade_delete_relationships.py
deleted file mode 100644
index 10e47a1456..0000000000
--- a/backend/app/alembic/versions/1a31ce608336_add_cascade_delete_relationships.py
+++ /dev/null
@@ -1,37 +0,0 @@
-"""Add cascade delete relationships
-
-Revision ID: 1a31ce608336
-Revises: d98dd8ec85a3
-Create Date: 2024-07-31 22:24:34.447891
-
-"""
-from alembic import op
-import sqlalchemy as sa
-import sqlmodel.sql.sqltypes
-
-
-# revision identifiers, used by Alembic.
-revision = '1a31ce608336'
-down_revision = 'd98dd8ec85a3'
-branch_labels = None
-depends_on = None
-
-
-def upgrade():
- # ### commands auto generated by Alembic - please adjust! ###
- op.alter_column('item', 'owner_id',
- existing_type=sa.UUID(),
- nullable=False)
- op.drop_constraint('item_owner_id_fkey', 'item', type_='foreignkey')
- op.create_foreign_key(None, 'item', 'user', ['owner_id'], ['id'], ondelete='CASCADE')
- # ### end Alembic commands ###
-
-
-def downgrade():
- # ### commands auto generated by Alembic - please adjust! ###
- op.drop_constraint(None, 'item', type_='foreignkey')
- op.create_foreign_key('item_owner_id_fkey', 'item', 'user', ['owner_id'], ['id'])
- op.alter_column('item', 'owner_id',
- existing_type=sa.UUID(),
- nullable=True)
- # ### end Alembic commands ###
diff --git a/backend/app/alembic/versions/20cc9e9039fd_create_qr_codes_table.py b/backend/app/alembic/versions/20cc9e9039fd_create_qr_codes_table.py
new file mode 100644
index 0000000000..1ec5e79bcc
--- /dev/null
+++ b/backend/app/alembic/versions/20cc9e9039fd_create_qr_codes_table.py
@@ -0,0 +1,29 @@
+"""Create qrcode table
+
+Revision ID: 20cc9e9039fd
+Revises: 91f6f2bc36a4
+Create Date: 2024-11-01 10:17:32.083891
+
+"""
+from alembic import op
+import sqlalchemy as sa
+import sqlmodel.sql.sqltypes
+
+
+# revision identifiers, used by Alembic.
+revision = '20cc9e9039fd'
+down_revision = '91f6f2bc36a4'
+branch_labels = None
+depends_on = None
+
+
+def upgrade():
+ # ### commands auto generated by Alembic - please adjust! ###
+ pass
+ # ### end Alembic commands ###
+
+
+def downgrade():
+ # ### commands auto generated by Alembic - please adjust! ###
+ pass
+ # ### end Alembic commands ###
diff --git a/backend/app/alembic/versions/3078d16ee962_add_explicit_uservenueassociation_for_.py b/backend/app/alembic/versions/3078d16ee962_add_explicit_uservenueassociation_for_.py
new file mode 100644
index 0000000000..1e37e4bab3
--- /dev/null
+++ b/backend/app/alembic/versions/3078d16ee962_add_explicit_uservenueassociation_for_.py
@@ -0,0 +1,31 @@
+"""Add explicit UserVenueAssociation for UserBusiness and Venue
+
+Revision ID: 3078d16ee962
+Revises: 8c693686becd
+Create Date: 2024-10-24 14:55:49.010006
+
+"""
+from alembic import op
+import sqlalchemy as sa
+import sqlmodel.sql.sqltypes
+
+
+# revision identifiers, used by Alembic.
+revision = '3078d16ee962'
+down_revision = '8c693686becd'
+branch_labels = None
+depends_on = None
+
+
+def upgrade():
+ # ### commands auto generated by Alembic - please adjust! ###
+ op.add_column('user_venue_association', sa.Column('id', sa.Uuid(), nullable=False))
+ op.add_column('user_venue_association', sa.Column('role', sqlmodel.sql.sqltypes.AutoString(), nullable=True))
+ # ### end Alembic commands ###
+
+
+def downgrade():
+ # ### commands auto generated by Alembic - please adjust! ###
+ op.drop_column('user_venue_association', 'role')
+ op.drop_column('user_venue_association', 'id')
+ # ### end Alembic commands ###
diff --git a/backend/app/alembic/versions/4951a24acf4c_make_phone_number_nullable_in_user_.py b/backend/app/alembic/versions/4951a24acf4c_make_phone_number_nullable_in_user_.py
new file mode 100644
index 0000000000..b5d6bc57bd
--- /dev/null
+++ b/backend/app/alembic/versions/4951a24acf4c_make_phone_number_nullable_in_user_.py
@@ -0,0 +1,51 @@
+"""Make phone_number nullable in user_business
+
+Revision ID: 4951a24acf4c
+Revises: e2955dcf9b00
+Create Date: 2024-10-26 15:19:32.622093
+
+"""
+from alembic import op
+import sqlalchemy as sa
+import sqlmodel.sql.sqltypes
+
+
+# revision identifiers, used by Alembic.
+revision = '4951a24acf4c'
+down_revision = 'e2955dcf9b00'
+branch_labels = None
+depends_on = None
+
+
+def upgrade():
+ # ### commands auto generated by Alembic - please adjust! ###
+ op.alter_column('user_business', 'email',
+ existing_type=sa.VARCHAR(length=255),
+ nullable=False)
+ op.alter_column('user_business', 'phone_number',
+ existing_type=sa.VARCHAR(),
+ nullable=True)
+ op.drop_index('ix_user_business_phone_number', table_name='user_business')
+ op.drop_index('ix_user_public_email', table_name='user_public')
+ op.add_column('user_venue_association', sa.Column('user_id', sa.Uuid(), nullable=False))
+ op.drop_constraint('user_venue_association_user_business_id_fkey', 'user_venue_association', type_='foreignkey')
+ op.create_foreign_key(None, 'user_venue_association', 'user_business', ['user_id'], ['id'])
+ op.drop_column('user_venue_association', 'user_business_id')
+ # ### end Alembic commands ###
+
+
+def downgrade():
+ # ### commands auto generated by Alembic - please adjust! ###
+ op.add_column('user_venue_association', sa.Column('user_business_id', sa.UUID(), autoincrement=False, nullable=False))
+ op.drop_constraint(None, 'user_venue_association', type_='foreignkey')
+ op.create_foreign_key('user_venue_association_user_business_id_fkey', 'user_venue_association', 'user_business', ['user_business_id'], ['id'])
+ op.drop_column('user_venue_association', 'user_id')
+ op.create_index('ix_user_public_email', 'user_public', ['email'], unique=True)
+ op.create_index('ix_user_business_phone_number', 'user_business', ['phone_number'], unique=True)
+ op.alter_column('user_business', 'phone_number',
+ existing_type=sa.VARCHAR(),
+ nullable=False)
+ op.alter_column('user_business', 'email',
+ existing_type=sa.VARCHAR(length=255),
+ nullable=True)
+ # ### end Alembic commands ###
diff --git a/backend/app/alembic/versions/6a0826f924e8_create_carousel_poster_table.py b/backend/app/alembic/versions/6a0826f924e8_create_carousel_poster_table.py
new file mode 100644
index 0000000000..5e0a00d968
--- /dev/null
+++ b/backend/app/alembic/versions/6a0826f924e8_create_carousel_poster_table.py
@@ -0,0 +1,66 @@
+"""Create carousel_poster table
+
+Revision ID: 6a0826f924e8
+Revises: bc0a875f3f51
+Create Date: 2024-11-01 22:48:54.989213
+
+"""
+from alembic import op
+import sqlalchemy as sa
+import sqlmodel.sql.sqltypes
+
+
+# revision identifiers, used by Alembic.
+revision = '6a0826f924e8'
+down_revision = 'bc0a875f3f51'
+branch_labels = None
+depends_on = None
+
+
+def upgrade():
+ # ### commands auto generated by Alembic - please adjust! ###
+ op.create_table('carousel_poster',
+ sa.Column('created_at', sa.DateTime(), nullable=False),
+ sa.Column('updated_at', sa.DateTime(), nullable=False),
+ sa.Column('id', sa.Uuid(), nullable=False),
+ sa.Column('h3_index', sqlmodel.sql.sqltypes.AutoString(), nullable=True),
+ sa.Column('image_url', sqlmodel.sql.sqltypes.AutoString(), nullable=False),
+ sa.Column('deep_link', sqlmodel.sql.sqltypes.AutoString(), nullable=False),
+ sa.Column('expires_at', sa.DateTime(), nullable=False),
+ sa.Column('event_id', sa.Uuid(), nullable=True),
+ sa.Column('venue_id', sa.Uuid(), nullable=True),
+ sa.ForeignKeyConstraint(['event_id'], ['event.id'], ),
+ sa.ForeignKeyConstraint(['venue_id'], ['venue.id'], ),
+ sa.PrimaryKeyConstraint('id')
+ )
+ op.create_index(op.f('ix_carousel_poster_h3_index'), 'carousel_poster', ['h3_index'], unique=False)
+ op.create_index(op.f('ix_carousel_poster_id'), 'carousel_poster', ['id'], unique=False)
+ op.add_column('event', sa.Column('venue_id', sa.Uuid(), nullable=False))
+ op.drop_constraint('event_nightclub_id_fkey', 'event', type_='foreignkey')
+ op.create_foreign_key(None, 'event', 'venue', ['venue_id'], ['id'])
+ op.drop_column('event', 'nightclub_id')
+ op.alter_column('venue', 'latitude',
+ existing_type=sa.DOUBLE_PRECISION(precision=53),
+ nullable=False)
+ op.alter_column('venue', 'longitude',
+ existing_type=sa.DOUBLE_PRECISION(precision=53),
+ nullable=False)
+ # ### end Alembic commands ###
+
+
+def downgrade():
+ # ### commands auto generated by Alembic - please adjust! ###
+ op.alter_column('venue', 'longitude',
+ existing_type=sa.DOUBLE_PRECISION(precision=53),
+ nullable=True)
+ op.alter_column('venue', 'latitude',
+ existing_type=sa.DOUBLE_PRECISION(precision=53),
+ nullable=True)
+ op.add_column('event', sa.Column('nightclub_id', sa.UUID(), autoincrement=False, nullable=False))
+ op.drop_constraint(None, 'event', type_='foreignkey')
+ op.create_foreign_key('event_nightclub_id_fkey', 'event', 'nightclub', ['nightclub_id'], ['id'])
+ op.drop_column('event', 'venue_id')
+ op.drop_index(op.f('ix_carousel_poster_id'), table_name='carousel_poster')
+ op.drop_index(op.f('ix_carousel_poster_h3_index'), table_name='carousel_poster')
+ op.drop_table('carousel_poster')
+ # ### end Alembic commands ###
diff --git a/backend/app/alembic/versions/7fd1b196213e_initial_migration.py b/backend/app/alembic/versions/7fd1b196213e_initial_migration.py
new file mode 100644
index 0000000000..0565126562
--- /dev/null
+++ b/backend/app/alembic/versions/7fd1b196213e_initial_migration.py
@@ -0,0 +1,486 @@
+"""Initial migration
+
+Revision ID: 7fd1b196213e
+Revises:
+Create Date: 2024-10-23 17:54:34.693255
+
+"""
+from alembic import op
+import sqlalchemy as sa
+import sqlmodel.sql.sqltypes
+
+
+# revision identifiers, used by Alembic.
+revision = '7fd1b196213e'
+down_revision = None
+branch_labels = None
+depends_on = None
+
+
+def upgrade():
+ # ### commands auto generated by Alembic - please adjust! ###
+ op.create_table('user_business',
+ sa.Column('created_at', sa.DateTime(), nullable=False),
+ sa.Column('updated_at', sa.DateTime(), nullable=False),
+ sa.Column('email', sqlmodel.sql.sqltypes.AutoString(length=255), nullable=True),
+ sa.Column('phone_number', sqlmodel.sql.sqltypes.AutoString(), nullable=False),
+ sa.Column('is_active', sa.Boolean(), nullable=False),
+ sa.Column('is_superuser', sa.Boolean(), nullable=False),
+ sa.Column('full_name', sqlmodel.sql.sqltypes.AutoString(length=255), nullable=True),
+ sa.Column('refresh_token', sqlmodel.sql.sqltypes.AutoString(), nullable=True),
+ sa.Column('id', sa.Uuid(), nullable=False),
+ sa.Column('registration_date', sa.DateTime(), nullable=False),
+ sa.PrimaryKeyConstraint('id')
+ )
+ op.create_index(op.f('ix_user_business_email'), 'user_business', ['email'], unique=True)
+ op.create_index(op.f('ix_user_business_id'), 'user_business', ['id'], unique=False)
+ op.create_index(op.f('ix_user_business_phone_number'), 'user_business', ['phone_number'], unique=True)
+ op.create_table('user_public',
+ sa.Column('created_at', sa.DateTime(), nullable=False),
+ sa.Column('updated_at', sa.DateTime(), nullable=False),
+ sa.Column('email', sqlmodel.sql.sqltypes.AutoString(length=255), nullable=True),
+ sa.Column('phone_number', sqlmodel.sql.sqltypes.AutoString(), nullable=False),
+ sa.Column('is_active', sa.Boolean(), nullable=False),
+ sa.Column('is_superuser', sa.Boolean(), nullable=False),
+ sa.Column('full_name', sqlmodel.sql.sqltypes.AutoString(length=255), nullable=True),
+ sa.Column('refresh_token', sqlmodel.sql.sqltypes.AutoString(), nullable=True),
+ sa.Column('id', sa.Uuid(), nullable=False),
+ sa.Column('date_of_birth', sa.DateTime(), nullable=True),
+ sa.Column('gender', sqlmodel.sql.sqltypes.AutoString(), nullable=True),
+ sa.Column('registration_date', sa.DateTime(), nullable=False),
+ sa.Column('profile_picture', sqlmodel.sql.sqltypes.AutoString(), nullable=True),
+ sa.Column('preferences', sqlmodel.sql.sqltypes.AutoString(), nullable=True),
+ sa.PrimaryKeyConstraint('id')
+ )
+ op.create_index(op.f('ix_user_public_email'), 'user_public', ['email'], unique=True)
+ op.create_index(op.f('ix_user_public_id'), 'user_public', ['id'], unique=False)
+ op.create_index(op.f('ix_user_public_phone_number'), 'user_public', ['phone_number'], unique=True)
+ op.create_table('venue',
+ sa.Column('created_at', sa.DateTime(), nullable=False),
+ sa.Column('updated_at', sa.DateTime(), nullable=False),
+ sa.Column('id', sa.Uuid(), nullable=False),
+ sa.Column('name', sqlmodel.sql.sqltypes.AutoString(), nullable=False),
+ sa.Column('address', sqlmodel.sql.sqltypes.AutoString(), nullable=True),
+ sa.Column('latitude', sa.Float(), nullable=True),
+ sa.Column('longitude', sa.Float(), nullable=True),
+ sa.Column('capacity', sa.Integer(), nullable=True),
+ sa.Column('description', sqlmodel.sql.sqltypes.AutoString(), nullable=True),
+ sa.Column('google_rating', sa.Float(), nullable=True),
+ sa.Column('instagram_handle', sqlmodel.sql.sqltypes.AutoString(), nullable=True),
+ sa.Column('instagram_token', sqlmodel.sql.sqltypes.AutoString(), nullable=True),
+ sa.Column('google_map_link', sqlmodel.sql.sqltypes.AutoString(), nullable=True),
+ sa.Column('mobile_number', sqlmodel.sql.sqltypes.AutoString(), nullable=True),
+ sa.Column('email', sqlmodel.sql.sqltypes.AutoString(), nullable=True),
+ sa.Column('opening_time', sa.Time(), nullable=True),
+ sa.Column('closing_time', sa.Time(), nullable=True),
+ sa.Column('avg_expense_for_two', sa.Float(), nullable=True),
+ sa.Column('zomato_link', sqlmodel.sql.sqltypes.AutoString(), nullable=True),
+ sa.Column('swiggy_link', sqlmodel.sql.sqltypes.AutoString(), nullable=True),
+ sa.PrimaryKeyConstraint('id')
+ )
+ op.create_index(op.f('ix_venue_id'), 'venue', ['id'], unique=False)
+ op.create_index(op.f('ix_venue_name'), 'venue', ['name'], unique=False)
+ op.create_table('foodcourt',
+ sa.Column('created_at', sa.DateTime(), nullable=False),
+ sa.Column('updated_at', sa.DateTime(), nullable=False),
+ sa.Column('id', sa.Uuid(), nullable=False),
+ sa.Column('total_qsrs', sa.Integer(), nullable=True),
+ sa.Column('seating_capacity', sa.Integer(), nullable=True),
+ sa.Column('venue_id', sa.Uuid(), nullable=False),
+ sa.ForeignKeyConstraint(['venue_id'], ['venue.id'], ),
+ sa.PrimaryKeyConstraint('id')
+ )
+ op.create_index(op.f('ix_foodcourt_id'), 'foodcourt', ['id'], unique=False)
+ op.create_index(op.f('ix_foodcourt_venue_id'), 'foodcourt', ['venue_id'], unique=False)
+ op.create_table('menu',
+ sa.Column('name', sqlmodel.sql.sqltypes.AutoString(), nullable=False),
+ sa.Column('description', sqlmodel.sql.sqltypes.AutoString(), nullable=True),
+ sa.Column('menu_type', sqlmodel.sql.sqltypes.AutoString(), nullable=True),
+ sa.Column('venue_id', sa.Uuid(), nullable=False),
+ sa.Column('id', sa.Uuid(), nullable=False),
+ sa.ForeignKeyConstraint(['venue_id'], ['venue.id'], ),
+ sa.PrimaryKeyConstraint('id')
+ )
+ op.create_index(op.f('ix_menu_id'), 'menu', ['id'], unique=False)
+ op.create_table('nightclub',
+ sa.Column('created_at', sa.DateTime(), nullable=False),
+ sa.Column('updated_at', sa.DateTime(), nullable=False),
+ sa.Column('id', sa.Uuid(), nullable=False),
+ sa.Column('venue_id', sa.Uuid(), nullable=False),
+ sa.ForeignKeyConstraint(['venue_id'], ['venue.id'], ),
+ sa.PrimaryKeyConstraint('id')
+ )
+ op.create_index(op.f('ix_nightclub_id'), 'nightclub', ['id'], unique=False)
+ op.create_index(op.f('ix_nightclub_venue_id'), 'nightclub', ['venue_id'], unique=False)
+ op.create_table('payment_source_nightclub',
+ sa.Column('user_id', sa.Uuid(), nullable=False),
+ sa.Column('source_type', sqlmodel.sql.sqltypes.AutoString(), nullable=False),
+ sa.Column('gateway_transaction_id', sa.Uuid(), nullable=True),
+ sa.Column('payment_time', sa.DateTime(), nullable=False),
+ sa.Column('amount', sa.Float(), nullable=False),
+ sa.Column('status', sqlmodel.sql.sqltypes.AutoString(), nullable=False),
+ sa.Column('id', sa.Uuid(), nullable=False),
+ sa.Column('retry_count', sa.Integer(), nullable=False),
+ sa.Column('last_attempt_time', sa.DateTime(), nullable=True),
+ sa.ForeignKeyConstraint(['user_id'], ['user_public.id'], ),
+ sa.PrimaryKeyConstraint('id')
+ )
+ op.create_index(op.f('ix_payment_source_nightclub_id'), 'payment_source_nightclub', ['id'], unique=False)
+ op.create_table('payment_source_qsr',
+ sa.Column('user_id', sa.Uuid(), nullable=False),
+ sa.Column('source_type', sqlmodel.sql.sqltypes.AutoString(), nullable=False),
+ sa.Column('gateway_transaction_id', sa.Uuid(), nullable=True),
+ sa.Column('payment_time', sa.DateTime(), nullable=False),
+ sa.Column('amount', sa.Float(), nullable=False),
+ sa.Column('status', sqlmodel.sql.sqltypes.AutoString(), nullable=False),
+ sa.Column('id', sa.Uuid(), nullable=False),
+ sa.Column('retry_count', sa.Integer(), nullable=False),
+ sa.Column('last_attempt_time', sa.DateTime(), nullable=True),
+ sa.ForeignKeyConstraint(['user_id'], ['user_public.id'], ),
+ sa.PrimaryKeyConstraint('id')
+ )
+ op.create_index(op.f('ix_payment_source_qsr_id'), 'payment_source_qsr', ['id'], unique=False)
+ op.create_table('payment_source_restaurant',
+ sa.Column('user_id', sa.Uuid(), nullable=False),
+ sa.Column('source_type', sqlmodel.sql.sqltypes.AutoString(), nullable=False),
+ sa.Column('gateway_transaction_id', sa.Uuid(), nullable=True),
+ sa.Column('payment_time', sa.DateTime(), nullable=False),
+ sa.Column('amount', sa.Float(), nullable=False),
+ sa.Column('status', sqlmodel.sql.sqltypes.AutoString(), nullable=False),
+ sa.Column('id', sa.Uuid(), nullable=False),
+ sa.Column('retry_count', sa.Integer(), nullable=False),
+ sa.Column('last_attempt_time', sa.DateTime(), nullable=True),
+ sa.ForeignKeyConstraint(['user_id'], ['user_public.id'], ),
+ sa.PrimaryKeyConstraint('id')
+ )
+ op.create_index(op.f('ix_payment_source_restaurant_id'), 'payment_source_restaurant', ['id'], unique=False)
+ op.create_table('pickup_location',
+ sa.Column('id', sa.Uuid(), nullable=False),
+ sa.Column('venue_id', sa.Uuid(), nullable=False),
+ sa.Column('name', sqlmodel.sql.sqltypes.AutoString(), nullable=False),
+ sa.Column('description', sqlmodel.sql.sqltypes.AutoString(), nullable=True),
+ sa.ForeignKeyConstraint(['venue_id'], ['venue.id'], ),
+ sa.PrimaryKeyConstraint('id')
+ )
+ op.create_index(op.f('ix_pickup_location_id'), 'pickup_location', ['id'], unique=False)
+ op.create_table('restaurant',
+ sa.Column('created_at', sa.DateTime(), nullable=False),
+ sa.Column('updated_at', sa.DateTime(), nullable=False),
+ sa.Column('id', sa.Uuid(), nullable=False),
+ sa.Column('venue_id', sa.Uuid(), nullable=False),
+ sa.Column('cuisine_type', sqlmodel.sql.sqltypes.AutoString(), nullable=True),
+ sa.ForeignKeyConstraint(['venue_id'], ['venue.id'], ),
+ sa.PrimaryKeyConstraint('id')
+ )
+ op.create_index(op.f('ix_restaurant_id'), 'restaurant', ['id'], unique=False)
+ op.create_index(op.f('ix_restaurant_venue_id'), 'restaurant', ['venue_id'], unique=False)
+ op.create_table('user_venue_association',
+ sa.Column('user_business_id', sa.Uuid(), nullable=False),
+ sa.Column('venue_id', sa.Uuid(), nullable=False),
+ sa.ForeignKeyConstraint(['user_business_id'], ['user_business.id'], ),
+ sa.ForeignKeyConstraint(['venue_id'], ['venue.id'], ),
+ sa.PrimaryKeyConstraint('user_business_id', 'venue_id')
+ )
+ op.create_table('event',
+ sa.Column('id', sa.Uuid(), nullable=False),
+ sa.Column('nightclub_id', sa.Uuid(), nullable=False),
+ sa.Column('title', sqlmodel.sql.sqltypes.AutoString(), nullable=False),
+ sa.Column('start_time', sa.DateTime(), nullable=False),
+ sa.Column('end_time', sa.DateTime(), nullable=False),
+ sa.Column('image_url', sqlmodel.sql.sqltypes.AutoString(), nullable=True),
+ sa.Column('age_restriction', sa.Integer(), nullable=True),
+ sa.Column('dress_code', sqlmodel.sql.sqltypes.AutoString(), nullable=True),
+ sa.ForeignKeyConstraint(['nightclub_id'], ['nightclub.id'], ),
+ sa.PrimaryKeyConstraint('id')
+ )
+ op.create_index(op.f('ix_event_id'), 'event', ['id'], unique=False)
+ op.create_table('group',
+ sa.Column('id', sa.Uuid(), nullable=False),
+ sa.Column('nightclub_id', sa.Uuid(), nullable=True),
+ sa.Column('created_at', sa.DateTime(), nullable=False),
+ sa.Column('admin_user_id', sa.Uuid(), nullable=False),
+ sa.Column('table_number', sqlmodel.sql.sqltypes.AutoString(), nullable=True),
+ sa.ForeignKeyConstraint(['admin_user_id'], ['user_public.id'], ),
+ sa.ForeignKeyConstraint(['nightclub_id'], ['nightclub.id'], ),
+ sa.PrimaryKeyConstraint('id')
+ )
+ op.create_index(op.f('ix_group_id'), 'group', ['id'], unique=False)
+ op.create_table('menu_category',
+ sa.Column('id', sa.Uuid(), nullable=False),
+ sa.Column('menu_id', sa.Uuid(), nullable=False),
+ sa.Column('name', sqlmodel.sql.sqltypes.AutoString(), nullable=False),
+ sa.ForeignKeyConstraint(['menu_id'], ['menu.id'], ),
+ sa.PrimaryKeyConstraint('id')
+ )
+ op.create_index(op.f('ix_menu_category_id'), 'menu_category', ['id'], unique=False)
+ op.create_table('nightclub_order',
+ sa.Column('user_id', sa.Uuid(), nullable=False),
+ sa.Column('note', sqlmodel.sql.sqltypes.AutoString(), nullable=True),
+ sa.Column('order_time', sa.DateTime(), nullable=False),
+ sa.Column('total_amount', sa.Float(), nullable=False),
+ sa.Column('taxes_and_charges', sa.Float(), nullable=True),
+ sa.Column('cover_charge_used', sa.Float(), nullable=True),
+ sa.Column('status', sqlmodel.sql.sqltypes.AutoString(), nullable=False),
+ sa.Column('service_type', sqlmodel.sql.sqltypes.AutoString(), nullable=True),
+ sa.Column('id', sa.Uuid(), nullable=False),
+ sa.Column('venue_id', sa.Uuid(), nullable=True),
+ sa.Column('payment_id', sa.Uuid(), nullable=True),
+ sa.Column('pickup_location_id', sa.Uuid(), nullable=True),
+ sa.ForeignKeyConstraint(['payment_id'], ['payment_source_nightclub.id'], ),
+ sa.ForeignKeyConstraint(['pickup_location_id'], ['pickup_location.id'], ),
+ sa.ForeignKeyConstraint(['user_id'], ['user_public.id'], ),
+ sa.ForeignKeyConstraint(['venue_id'], ['nightclub.id'], ),
+ sa.PrimaryKeyConstraint('id')
+ )
+ op.create_index(op.f('ix_nightclub_order_id'), 'nightclub_order', ['id'], unique=False)
+ op.create_table('qsr',
+ sa.Column('created_at', sa.DateTime(), nullable=False),
+ sa.Column('updated_at', sa.DateTime(), nullable=False),
+ sa.Column('id', sa.Uuid(), nullable=False),
+ sa.Column('foodcourt_id', sa.Uuid(), nullable=True),
+ sa.Column('drive_thru', sa.Boolean(), nullable=True),
+ sa.Column('venue_id', sa.Uuid(), nullable=False),
+ sa.ForeignKeyConstraint(['foodcourt_id'], ['foodcourt.id'], ),
+ sa.ForeignKeyConstraint(['venue_id'], ['venue.id'], ),
+ sa.PrimaryKeyConstraint('id')
+ )
+ op.create_index(op.f('ix_qsr_foodcourt_id'), 'qsr', ['foodcourt_id'], unique=False)
+ op.create_index(op.f('ix_qsr_id'), 'qsr', ['id'], unique=False)
+ op.create_index(op.f('ix_qsr_venue_id'), 'qsr', ['venue_id'], unique=False)
+ op.create_table('restaurant_order',
+ sa.Column('user_id', sa.Uuid(), nullable=False),
+ sa.Column('pickup_location_id', sa.Uuid(), nullable=True),
+ sa.Column('note', sqlmodel.sql.sqltypes.AutoString(), nullable=True),
+ sa.Column('order_time', sa.DateTime(), nullable=False),
+ sa.Column('total_amount', sa.Float(), nullable=False),
+ sa.Column('taxes_and_charges', sa.Float(), nullable=True),
+ sa.Column('cover_charge_used', sa.Float(), nullable=True),
+ sa.Column('status', sqlmodel.sql.sqltypes.AutoString(), nullable=False),
+ sa.Column('service_type', sqlmodel.sql.sqltypes.AutoString(), nullable=True),
+ sa.Column('id', sa.Uuid(), nullable=False),
+ sa.Column('venue_id', sa.Uuid(), nullable=True),
+ sa.Column('payment_id', sa.Uuid(), nullable=True),
+ sa.ForeignKeyConstraint(['payment_id'], ['payment_source_restaurant.id'], ),
+ sa.ForeignKeyConstraint(['pickup_location_id'], ['pickup_location.id'], ),
+ sa.ForeignKeyConstraint(['user_id'], ['user_public.id'], ),
+ sa.ForeignKeyConstraint(['venue_id'], ['restaurant.id'], ),
+ sa.PrimaryKeyConstraint('id')
+ )
+ op.create_index(op.f('ix_restaurant_order_id'), 'restaurant_order', ['id'], unique=False)
+ op.create_table('clubvisit',
+ sa.Column('id', sa.Uuid(), nullable=False),
+ sa.Column('user_id', sa.Uuid(), nullable=False),
+ sa.Column('group_id', sa.Uuid(), nullable=True),
+ sa.Column('nightclub_id', sa.Uuid(), nullable=False),
+ sa.Column('entry_time', sa.DateTime(), nullable=False),
+ sa.Column('exit_time', sa.DateTime(), nullable=True),
+ sa.Column('cover_charge', sa.Float(), nullable=True),
+ sa.Column('total_bill', sa.Float(), nullable=True),
+ sa.ForeignKeyConstraint(['group_id'], ['group.id'], ),
+ sa.ForeignKeyConstraint(['nightclub_id'], ['nightclub.id'], ),
+ sa.ForeignKeyConstraint(['user_id'], ['user_public.id'], ),
+ sa.PrimaryKeyConstraint('id')
+ )
+ op.create_table('event_booking',
+ sa.Column('id', sa.Uuid(), nullable=False),
+ sa.Column('user_id', sa.Uuid(), nullable=False),
+ sa.Column('event_id', sa.Uuid(), nullable=False),
+ sa.Column('booking_time', sa.DateTime(), nullable=False),
+ sa.Column('total_amount', sa.Float(), nullable=False),
+ sa.Column('status', sqlmodel.sql.sqltypes.AutoString(), nullable=False),
+ sa.ForeignKeyConstraint(['event_id'], ['event.id'], ),
+ sa.ForeignKeyConstraint(['user_id'], ['user_public.id'], ),
+ sa.PrimaryKeyConstraint('id')
+ )
+ op.create_table('group_nightclub_order_link',
+ sa.Column('group_id', sa.Uuid(), nullable=False),
+ sa.Column('nightclub_order_id', sa.Uuid(), nullable=False),
+ sa.ForeignKeyConstraint(['group_id'], ['group.id'], ),
+ sa.ForeignKeyConstraint(['nightclub_order_id'], ['nightclub_order.id'], ),
+ sa.PrimaryKeyConstraint('group_id', 'nightclub_order_id')
+ )
+ op.create_table('group_wallet',
+ sa.Column('id', sa.Uuid(), nullable=False),
+ sa.Column('group_id', sa.Uuid(), nullable=False),
+ sa.Column('balance', sa.Float(), nullable=False),
+ sa.ForeignKeyConstraint(['group_id'], ['group.id'], ),
+ sa.PrimaryKeyConstraint('id'),
+ sa.UniqueConstraint('group_id')
+ )
+ op.create_index(op.f('ix_group_wallet_id'), 'group_wallet', ['id'], unique=False)
+ op.create_table('groupmembers',
+ sa.Column('group_id', sa.Uuid(), nullable=False),
+ sa.Column('user_id', sa.Uuid(), nullable=False),
+ sa.ForeignKeyConstraint(['group_id'], ['group.id'], ),
+ sa.ForeignKeyConstraint(['user_id'], ['user_public.id'], ),
+ sa.PrimaryKeyConstraint('group_id', 'user_id')
+ )
+ op.create_table('menu_sub_category',
+ sa.Column('id', sa.Uuid(), nullable=False),
+ sa.Column('category_id', sa.Uuid(), nullable=False),
+ sa.Column('name', sqlmodel.sql.sqltypes.AutoString(), nullable=False),
+ sa.ForeignKeyConstraint(['category_id'], ['menu_category.id'], ),
+ sa.PrimaryKeyConstraint('id')
+ )
+ op.create_index(op.f('ix_menu_sub_category_id'), 'menu_sub_category', ['id'], unique=False)
+ op.create_table('qsr_order',
+ sa.Column('user_id', sa.Uuid(), nullable=False),
+ sa.Column('pickup_location_id', sa.Uuid(), nullable=True),
+ sa.Column('note', sqlmodel.sql.sqltypes.AutoString(), nullable=True),
+ sa.Column('order_time', sa.DateTime(), nullable=False),
+ sa.Column('total_amount', sa.Float(), nullable=False),
+ sa.Column('taxes_and_charges', sa.Float(), nullable=True),
+ sa.Column('cover_charge_used', sa.Float(), nullable=True),
+ sa.Column('status', sqlmodel.sql.sqltypes.AutoString(), nullable=False),
+ sa.Column('service_type', sqlmodel.sql.sqltypes.AutoString(), nullable=True),
+ sa.Column('id', sa.Uuid(), nullable=False),
+ sa.Column('venue_id', sa.Uuid(), nullable=False),
+ sa.Column('payment_id', sa.Uuid(), nullable=False),
+ sa.ForeignKeyConstraint(['payment_id'], ['payment_source_qsr.id'], ),
+ sa.ForeignKeyConstraint(['pickup_location_id'], ['pickup_location.id'], ),
+ sa.ForeignKeyConstraint(['user_id'], ['user_public.id'], ),
+ sa.ForeignKeyConstraint(['venue_id'], ['qsr.id'], ),
+ sa.PrimaryKeyConstraint('id')
+ )
+ op.create_index(op.f('ix_qsr_order_id'), 'qsr_order', ['id'], unique=False)
+ op.create_table('event_offering',
+ sa.Column('id', sa.Uuid(), nullable=False),
+ sa.Column('event_id', sa.Uuid(), nullable=False),
+ sa.Column('event_booking_id', sa.Uuid(), nullable=False),
+ sa.Column('offering_type', sqlmodel.sql.sqltypes.AutoString(), nullable=False),
+ sa.Column('description', sqlmodel.sql.sqltypes.AutoString(), nullable=False),
+ sa.Column('price', sa.Float(), nullable=False),
+ sa.Column('total_guests_per_pass', sa.Integer(), nullable=False),
+ sa.Column('cover_charge', sa.Float(), nullable=True),
+ sa.Column('additional_charges', sa.Float(), nullable=True),
+ sa.Column('availability', sa.Integer(), nullable=False),
+ sa.ForeignKeyConstraint(['event_booking_id'], ['event_booking.id'], ),
+ sa.ForeignKeyConstraint(['event_id'], ['event.id'], ),
+ sa.PrimaryKeyConstraint('id')
+ )
+ op.create_index(op.f('ix_event_offering_id'), 'event_offering', ['id'], unique=False)
+ op.create_table('groupwallettopup',
+ sa.Column('id', sa.Uuid(), nullable=False),
+ sa.Column('group_wallet_id', sa.Uuid(), nullable=False),
+ sa.Column('amount', sa.Float(), nullable=False),
+ sa.Column('topup_time', sa.DateTime(), nullable=False),
+ sa.ForeignKeyConstraint(['group_wallet_id'], ['group_wallet.id'], ),
+ sa.PrimaryKeyConstraint('id')
+ )
+ op.create_index(op.f('ix_groupwallettopup_id'), 'groupwallettopup', ['id'], unique=False)
+ op.create_table('menu_item',
+ sa.Column('category_id', sa.Uuid(), nullable=False),
+ sa.Column('sub_category_id', sa.Uuid(), nullable=True),
+ sa.Column('name', sqlmodel.sql.sqltypes.AutoString(), nullable=False),
+ sa.Column('price', sa.Float(), nullable=False),
+ sa.Column('description', sqlmodel.sql.sqltypes.AutoString(), nullable=True),
+ sa.Column('image_url', sqlmodel.sql.sqltypes.AutoString(), nullable=True),
+ sa.Column('is_veg', sa.Boolean(), nullable=True),
+ sa.Column('ingredients', sqlmodel.sql.sqltypes.AutoString(), nullable=True),
+ sa.Column('abv', sa.Float(), nullable=True),
+ sa.Column('ibu', sa.Integer(), nullable=True),
+ sa.Column('id', sa.Uuid(), nullable=False),
+ sa.ForeignKeyConstraint(['category_id'], ['menu_category.id'], ),
+ sa.ForeignKeyConstraint(['sub_category_id'], ['menu_sub_category.id'], ),
+ sa.PrimaryKeyConstraint('id')
+ )
+ op.create_index(op.f('ix_menu_item_id'), 'menu_item', ['id'], unique=False)
+ op.create_table('payment_event',
+ sa.Column('user_id', sa.Uuid(), nullable=False),
+ sa.Column('source_type', sqlmodel.sql.sqltypes.AutoString(), nullable=False),
+ sa.Column('gateway_transaction_id', sa.Uuid(), nullable=True),
+ sa.Column('payment_time', sa.DateTime(), nullable=False),
+ sa.Column('amount', sa.Float(), nullable=False),
+ sa.Column('status', sqlmodel.sql.sqltypes.AutoString(), nullable=False),
+ sa.Column('id', sa.Uuid(), nullable=False),
+ sa.Column('event_booking_id', sa.Uuid(), nullable=True),
+ sa.ForeignKeyConstraint(['event_booking_id'], ['event_booking.id'], ),
+ sa.ForeignKeyConstraint(['user_id'], ['user_public.id'], ),
+ sa.PrimaryKeyConstraint('id')
+ )
+ op.create_index(op.f('ix_payment_event_id'), 'payment_event', ['id'], unique=False)
+ op.create_table('orderitem',
+ sa.Column('id', sa.Uuid(), nullable=False),
+ sa.Column('nightclub_order_id', sa.Uuid(), nullable=True),
+ sa.Column('restaurant_order_id', sa.Uuid(), nullable=True),
+ sa.Column('qsr_order_id', sa.Uuid(), nullable=True),
+ sa.Column('item_id', sa.Uuid(), nullable=False),
+ sa.Column('quantity', sa.Integer(), nullable=False),
+ sa.ForeignKeyConstraint(['item_id'], ['menu_item.id'], ),
+ sa.ForeignKeyConstraint(['nightclub_order_id'], ['nightclub_order.id'], ),
+ sa.ForeignKeyConstraint(['qsr_order_id'], ['qsr_order.id'], ),
+ sa.ForeignKeyConstraint(['restaurant_order_id'], ['restaurant_order.id'], ),
+ sa.PrimaryKeyConstraint('id')
+ )
+ op.create_index(op.f('ix_orderitem_id'), 'orderitem', ['id'], unique=False)
+ # ### end Alembic commands ###
+
+
+def downgrade():
+ # ### commands auto generated by Alembic - please adjust! ###
+ op.drop_index(op.f('ix_orderitem_id'), table_name='orderitem')
+ op.drop_table('orderitem')
+ op.drop_index(op.f('ix_payment_event_id'), table_name='payment_event')
+ op.drop_table('payment_event')
+ op.drop_index(op.f('ix_menu_item_id'), table_name='menu_item')
+ op.drop_table('menu_item')
+ op.drop_index(op.f('ix_groupwallettopup_id'), table_name='groupwallettopup')
+ op.drop_table('groupwallettopup')
+ op.drop_index(op.f('ix_event_offering_id'), table_name='event_offering')
+ op.drop_table('event_offering')
+ op.drop_index(op.f('ix_qsr_order_id'), table_name='qsr_order')
+ op.drop_table('qsr_order')
+ op.drop_index(op.f('ix_menu_sub_category_id'), table_name='menu_sub_category')
+ op.drop_table('menu_sub_category')
+ op.drop_table('groupmembers')
+ op.drop_index(op.f('ix_group_wallet_id'), table_name='group_wallet')
+ op.drop_table('group_wallet')
+ op.drop_table('group_nightclub_order_link')
+ op.drop_table('event_booking')
+ op.drop_table('clubvisit')
+ op.drop_index(op.f('ix_restaurant_order_id'), table_name='restaurant_order')
+ op.drop_table('restaurant_order')
+ op.drop_index(op.f('ix_qsr_venue_id'), table_name='qsr')
+ op.drop_index(op.f('ix_qsr_id'), table_name='qsr')
+ op.drop_index(op.f('ix_qsr_foodcourt_id'), table_name='qsr')
+ op.drop_table('qsr')
+ op.drop_index(op.f('ix_nightclub_order_id'), table_name='nightclub_order')
+ op.drop_table('nightclub_order')
+ op.drop_index(op.f('ix_menu_category_id'), table_name='menu_category')
+ op.drop_table('menu_category')
+ op.drop_index(op.f('ix_group_id'), table_name='group')
+ op.drop_table('group')
+ op.drop_index(op.f('ix_event_id'), table_name='event')
+ op.drop_table('event')
+ op.drop_table('user_venue_association')
+ op.drop_index(op.f('ix_restaurant_venue_id'), table_name='restaurant')
+ op.drop_index(op.f('ix_restaurant_id'), table_name='restaurant')
+ op.drop_table('restaurant')
+ op.drop_index(op.f('ix_pickup_location_id'), table_name='pickup_location')
+ op.drop_table('pickup_location')
+ op.drop_index(op.f('ix_payment_source_restaurant_id'), table_name='payment_source_restaurant')
+ op.drop_table('payment_source_restaurant')
+ op.drop_index(op.f('ix_payment_source_qsr_id'), table_name='payment_source_qsr')
+ op.drop_table('payment_source_qsr')
+ op.drop_index(op.f('ix_payment_source_nightclub_id'), table_name='payment_source_nightclub')
+ op.drop_table('payment_source_nightclub')
+ op.drop_index(op.f('ix_nightclub_venue_id'), table_name='nightclub')
+ op.drop_index(op.f('ix_nightclub_id'), table_name='nightclub')
+ op.drop_table('nightclub')
+ op.drop_index(op.f('ix_menu_id'), table_name='menu')
+ op.drop_table('menu')
+ op.drop_index(op.f('ix_foodcourt_venue_id'), table_name='foodcourt')
+ op.drop_index(op.f('ix_foodcourt_id'), table_name='foodcourt')
+ op.drop_table('foodcourt')
+ op.drop_index(op.f('ix_venue_name'), table_name='venue')
+ op.drop_index(op.f('ix_venue_id'), table_name='venue')
+ op.drop_table('venue')
+ op.drop_index(op.f('ix_user_public_phone_number'), table_name='user_public')
+ op.drop_index(op.f('ix_user_public_id'), table_name='user_public')
+ op.drop_index(op.f('ix_user_public_email'), table_name='user_public')
+ op.drop_table('user_public')
+ op.drop_index(op.f('ix_user_business_phone_number'), table_name='user_business')
+ op.drop_index(op.f('ix_user_business_id'), table_name='user_business')
+ op.drop_index(op.f('ix_user_business_email'), table_name='user_business')
+ op.drop_table('user_business')
+ # ### end Alembic commands ###
diff --git a/backend/app/alembic/versions/8c693686becd_description_of_changes.py b/backend/app/alembic/versions/8c693686becd_description_of_changes.py
new file mode 100644
index 0000000000..3b39fcda0e
--- /dev/null
+++ b/backend/app/alembic/versions/8c693686becd_description_of_changes.py
@@ -0,0 +1,37 @@
+"""Description of changes
+
+Revision ID: 8c693686becd
+Revises: 9e690425af2e
+Create Date: 2024-10-23 23:16:40.093380
+
+"""
+from alembic import op
+import sqlalchemy as sa
+import sqlmodel.sql.sqltypes
+
+
+# revision identifiers, used by Alembic.
+revision = '8c693686becd'
+down_revision = '9e690425af2e'
+branch_labels = None
+depends_on = None
+
+
+def upgrade():
+ # ### commands auto generated by Alembic - please adjust! ###
+ op.add_column('nightclub', sa.Column('age_limit', sa.Integer(), nullable=True))
+ op.add_column('venue', sa.Column('zomato_link', sqlmodel.sql.sqltypes.AutoString(), nullable=True))
+ op.add_column('venue', sa.Column('swiggy_link', sqlmodel.sql.sqltypes.AutoString(), nullable=True))
+ op.drop_column('venue', 'swiggylink')
+ op.drop_column('venue', 'zomatolink')
+ # ### end Alembic commands ###
+
+
+def downgrade():
+ # ### commands auto generated by Alembic - please adjust! ###
+ op.add_column('venue', sa.Column('zomatolink', sa.VARCHAR(), autoincrement=False, nullable=True))
+ op.add_column('venue', sa.Column('swiggylink', sa.VARCHAR(), autoincrement=False, nullable=True))
+ op.drop_column('venue', 'swiggy_link')
+ op.drop_column('venue', 'zomato_link')
+ op.drop_column('nightclub', 'age_limit')
+ # ### end Alembic commands ###
diff --git a/backend/app/alembic/versions/91f6f2bc36a4_add_registration_date_to_user_public.py b/backend/app/alembic/versions/91f6f2bc36a4_add_registration_date_to_user_public.py
new file mode 100644
index 0000000000..701da3f637
--- /dev/null
+++ b/backend/app/alembic/versions/91f6f2bc36a4_add_registration_date_to_user_public.py
@@ -0,0 +1,63 @@
+"""Add registration_date to user_public
+
+Revision ID: 91f6f2bc36a4
+Revises: 4951a24acf4c
+Create Date: 2024-10-30 21:14:14.300480
+
+"""
+from alembic import op
+from sqlalchemy.dialects.postgresql import ENUM
+import sqlalchemy as sa
+import sqlmodel.sql.sqltypes
+from sqlalchemy.dialects import postgresql
+
+# revision identifiers, used by Alembic.
+revision = '91f6f2bc36a4'
+down_revision = '4951a24acf4c'
+branch_labels = None
+depends_on = None
+
+gender_enum = ENUM('male', 'female', 'others', name='gender')
+
+def upgrade():
+ # ### commands auto generated by Alembic - please adjust! ###
+ gender_enum.create(op.get_bind())
+ op.drop_table('carousel_poster')
+ op.drop_column('user_business', 'registration_date')
+ op.alter_column('user_public', 'gender',
+ type_=gender_enum,
+ existing_type=sa.String(), # Change to the existing type
+ existing_nullable=True,
+ postgresql_using='gender::gender') # If you need to convert existing data
+
+ op.drop_column('user_public', 'registration_date')
+ op.drop_column('venue', 'h3_index')
+ # ### end Alembic commands ###
+
+
+def downgrade():
+ # ### commands auto generated by Alembic - please adjust! ###
+ op.add_column('venue', sa.Column('h3_index', sa.VARCHAR(), autoincrement=False, nullable=True))
+ op.add_column('user_public', sa.Column('registration_date', postgresql.TIMESTAMP(), autoincrement=False, nullable=False))
+ op.drop_column('user_public', 'gender')
+ gender_enum.drop(op.get_bind())
+ op.add_column('user_business', sa.Column('registration_date', postgresql.TIMESTAMP(), autoincrement=False, nullable=False))
+ op.create_table('carousel_poster',
+ sa.Column('id', sa.UUID(), server_default=sa.text('gen_random_uuid()'), autoincrement=False, nullable=False),
+ sa.Column('image_url', sa.VARCHAR(length=255), autoincrement=False, nullable=False),
+ sa.Column('deep_link', sa.VARCHAR(length=255), autoincrement=False, nullable=False),
+ sa.Column('expires_at', postgresql.TIMESTAMP(), autoincrement=False, nullable=False),
+ sa.Column('event_id', sa.UUID(), autoincrement=False, nullable=True),
+ sa.Column('nightclub_id', sa.UUID(), autoincrement=False, nullable=True),
+ sa.Column('foodcourt_id', sa.UUID(), autoincrement=False, nullable=True),
+ sa.Column('qsr_id', sa.UUID(), autoincrement=False, nullable=True),
+ sa.Column('restaurant_id', sa.UUID(), autoincrement=False, nullable=True),
+ sa.Column('h3_index', sa.VARCHAR(length=255), autoincrement=False, nullable=False),
+ sa.ForeignKeyConstraint(['event_id'], ['event.id'], name='carousel_poster_event_id_fkey'),
+ sa.ForeignKeyConstraint(['foodcourt_id'], ['foodcourt.id'], name='carousel_poster_foodcourt_id_fkey'),
+ sa.ForeignKeyConstraint(['nightclub_id'], ['nightclub.id'], name='carousel_poster_nightclub_id_fkey'),
+ sa.ForeignKeyConstraint(['qsr_id'], ['qsr.id'], name='carousel_poster_qsr_id_fkey'),
+ sa.ForeignKeyConstraint(['restaurant_id'], ['restaurant.id'], name='carousel_poster_restaurant_id_fkey'),
+ sa.PrimaryKeyConstraint('id', name='carousel_poster_pkey')
+ )
+ # ### end Alembic commands ###
diff --git a/backend/app/alembic/versions/9c0a54914c78_add_max_length_for_string_varchar_.py b/backend/app/alembic/versions/9c0a54914c78_add_max_length_for_string_varchar_.py
deleted file mode 100755
index 78a41773b9..0000000000
--- a/backend/app/alembic/versions/9c0a54914c78_add_max_length_for_string_varchar_.py
+++ /dev/null
@@ -1,69 +0,0 @@
-"""Add max length for string(varchar) fields in User and Items models
-
-Revision ID: 9c0a54914c78
-Revises: e2412789c190
-Create Date: 2024-06-17 14:42:44.639457
-
-"""
-from alembic import op
-import sqlalchemy as sa
-import sqlmodel.sql.sqltypes
-
-
-# revision identifiers, used by Alembic.
-revision = '9c0a54914c78'
-down_revision = 'e2412789c190'
-branch_labels = None
-depends_on = None
-
-
-def upgrade():
- # Adjust the length of the email field in the User table
- op.alter_column('user', 'email',
- existing_type=sa.String(),
- type_=sa.String(length=255),
- existing_nullable=False)
-
- # Adjust the length of the full_name field in the User table
- op.alter_column('user', 'full_name',
- existing_type=sa.String(),
- type_=sa.String(length=255),
- existing_nullable=True)
-
- # Adjust the length of the title field in the Item table
- op.alter_column('item', 'title',
- existing_type=sa.String(),
- type_=sa.String(length=255),
- existing_nullable=False)
-
- # Adjust the length of the description field in the Item table
- op.alter_column('item', 'description',
- existing_type=sa.String(),
- type_=sa.String(length=255),
- existing_nullable=True)
-
-
-def downgrade():
- # Revert the length of the email field in the User table
- op.alter_column('user', 'email',
- existing_type=sa.String(length=255),
- type_=sa.String(),
- existing_nullable=False)
-
- # Revert the length of the full_name field in the User table
- op.alter_column('user', 'full_name',
- existing_type=sa.String(length=255),
- type_=sa.String(),
- existing_nullable=True)
-
- # Revert the length of the title field in the Item table
- op.alter_column('item', 'title',
- existing_type=sa.String(length=255),
- type_=sa.String(),
- existing_nullable=False)
-
- # Revert the length of the description field in the Item table
- op.alter_column('item', 'description',
- existing_type=sa.String(length=255),
- type_=sa.String(),
- existing_nullable=True)
diff --git a/backend/app/alembic/versions/9e690425af2e_add_zomato_link_to_venue.py b/backend/app/alembic/versions/9e690425af2e_add_zomato_link_to_venue.py
new file mode 100644
index 0000000000..0abc04de47
--- /dev/null
+++ b/backend/app/alembic/versions/9e690425af2e_add_zomato_link_to_venue.py
@@ -0,0 +1,25 @@
+"""add zomato_link to venue
+
+Revision ID: 9e690425af2e
+Revises: 7fd1b196213e
+Create Date: 2024-10-23 23:11:38.666505
+
+"""
+from alembic import op
+import sqlalchemy as sa
+import sqlmodel.sql.sqltypes
+
+
+# revision identifiers, used by Alembic.
+revision = '9e690425af2e'
+down_revision = '7fd1b196213e'
+branch_labels = None
+depends_on = None
+
+
+def upgrade():
+ pass
+
+
+def downgrade():
+ pass
diff --git a/backend/app/alembic/versions/a1c01df04ba4_create_qrcode_table.py b/backend/app/alembic/versions/a1c01df04ba4_create_qrcode_table.py
new file mode 100644
index 0000000000..c404b005de
--- /dev/null
+++ b/backend/app/alembic/versions/a1c01df04ba4_create_qrcode_table.py
@@ -0,0 +1,47 @@
+"""Create qrcode table
+
+Revision ID: a1c01df04ba4
+Revises: bef89635a1b9
+Create Date: 2024-11-01 13:29:27.200014
+
+"""
+from alembic import op
+import sqlalchemy as sa
+import sqlmodel.sql.sqltypes
+from sqlalchemy.dialects import postgresql
+
+# revision identifiers, used by Alembic.
+revision = 'a1c01df04ba4'
+down_revision = 'bef89635a1b9'
+branch_labels = None
+depends_on = None
+
+
+def upgrade():
+ # ### commands auto generated by Alembic - please adjust! ###
+ op.create_table('qrcode',
+ sa.Column('created_at', sa.DateTime(), nullable=False),
+ sa.Column('updated_at', sa.DateTime(), nullable=False),
+ sa.Column('id', sa.Uuid(), nullable=False),
+ sa.Column('venue_id', sa.Uuid(), nullable=True),
+ sa.Column('table_number', sqlmodel.sql.sqltypes.AutoString(), nullable=True),
+ sa.ForeignKeyConstraint(['venue_id'], ['venue.id'], ),
+ sa.PrimaryKeyConstraint('id')
+ )
+ op.drop_table('qr_codes')
+ # ### end Alembic commands ###
+
+
+def downgrade():
+ # ### commands auto generated by Alembic - please adjust! ###
+ op.create_table('qr_codes',
+ sa.Column('created_at', postgresql.TIMESTAMP(), autoincrement=False, nullable=False),
+ sa.Column('updated_at', postgresql.TIMESTAMP(), autoincrement=False, nullable=False),
+ sa.Column('id', sa.UUID(), autoincrement=False, nullable=False),
+ sa.Column('venue_id', sa.UUID(), autoincrement=False, nullable=True),
+ sa.Column('table_number', sa.VARCHAR(), autoincrement=False, nullable=True),
+ sa.ForeignKeyConstraint(['venue_id'], ['venue.id'], name='qr_codes_venue_id_fkey'),
+ sa.PrimaryKeyConstraint('id', name='qr_codes_pkey')
+ )
+ op.drop_table('qrcode')
+ # ### end Alembic commands ###
diff --git a/backend/app/alembic/versions/bc0a875f3f51_make_latitude_and_longitude_not_nullable.py b/backend/app/alembic/versions/bc0a875f3f51_make_latitude_and_longitude_not_nullable.py
new file mode 100644
index 0000000000..200aca7380
--- /dev/null
+++ b/backend/app/alembic/versions/bc0a875f3f51_make_latitude_and_longitude_not_nullable.py
@@ -0,0 +1,25 @@
+"""Make latitude and longitude not nullable
+
+Revision ID: bc0a875f3f51
+Revises: a1c01df04ba4
+Create Date: 2024-11-01 22:45:19.154522
+
+"""
+from alembic import op
+import sqlalchemy as sa
+import sqlmodel.sql.sqltypes
+
+
+# revision identifiers, used by Alembic.
+revision = 'bc0a875f3f51'
+down_revision = 'a1c01df04ba4'
+branch_labels = None
+depends_on = None
+
+
+def upgrade():
+ pass
+
+
+def downgrade():
+ pass
diff --git a/backend/app/alembic/versions/bef89635a1b9_create_qr_codes_table.py b/backend/app/alembic/versions/bef89635a1b9_create_qr_codes_table.py
new file mode 100644
index 0000000000..c412895ff4
--- /dev/null
+++ b/backend/app/alembic/versions/bef89635a1b9_create_qr_codes_table.py
@@ -0,0 +1,37 @@
+"""Create qrcode table
+
+Revision ID: bef89635a1b9
+Revises: 20cc9e9039fd
+Create Date: 2024-11-01 10:26:05.045546
+
+"""
+from alembic import op
+import sqlalchemy as sa
+import sqlmodel.sql.sqltypes
+
+
+# revision identifiers, used by Alembic.
+revision = 'bef89635a1b9'
+down_revision = '20cc9e9039fd'
+branch_labels = None
+depends_on = None
+
+
+def upgrade():
+ # ### commands auto generated by Alembic - please adjust! ###
+ op.create_table('qrcode',
+ sa.Column('created_at', sa.DateTime(), nullable=False),
+ sa.Column('updated_at', sa.DateTime(), nullable=False),
+ sa.Column('id', sa.Uuid(), nullable=False),
+ sa.Column('venue_id', sa.Uuid(), nullable=True),
+ sa.Column('table_number', sqlmodel.sql.sqltypes.AutoString(), nullable=True),
+ sa.ForeignKeyConstraint(['venue_id'], ['venue.id'], ),
+ sa.PrimaryKeyConstraint('id')
+ )
+ # ### end Alembic commands ###
+
+
+def downgrade():
+ # ### commands auto generated by Alembic - please adjust! ###
+ op.drop_table('qrcode')
+ # ### end Alembic commands ###
diff --git a/backend/app/alembic/versions/d98dd8ec85a3_edit_replace_id_integers_in_all_models_.py b/backend/app/alembic/versions/d98dd8ec85a3_edit_replace_id_integers_in_all_models_.py
deleted file mode 100755
index 37af1fa215..0000000000
--- a/backend/app/alembic/versions/d98dd8ec85a3_edit_replace_id_integers_in_all_models_.py
+++ /dev/null
@@ -1,90 +0,0 @@
-"""Edit replace id integers in all models to use UUID instead
-
-Revision ID: d98dd8ec85a3
-Revises: 9c0a54914c78
-Create Date: 2024-07-19 04:08:04.000976
-
-"""
-from alembic import op
-import sqlalchemy as sa
-import sqlmodel.sql.sqltypes
-from sqlalchemy.dialects import postgresql
-
-
-# revision identifiers, used by Alembic.
-revision = 'd98dd8ec85a3'
-down_revision = '9c0a54914c78'
-branch_labels = None
-depends_on = None
-
-
-def upgrade():
- # Ensure uuid-ossp extension is available
- op.execute('CREATE EXTENSION IF NOT EXISTS "uuid-ossp"')
-
- # Create a new UUID column with a default UUID value
- op.add_column('user', sa.Column('new_id', postgresql.UUID(as_uuid=True), default=sa.text('uuid_generate_v4()')))
- op.add_column('item', sa.Column('new_id', postgresql.UUID(as_uuid=True), default=sa.text('uuid_generate_v4()')))
- op.add_column('item', sa.Column('new_owner_id', postgresql.UUID(as_uuid=True), nullable=True))
-
- # Populate the new columns with UUIDs
- op.execute('UPDATE "user" SET new_id = uuid_generate_v4()')
- op.execute('UPDATE item SET new_id = uuid_generate_v4()')
- op.execute('UPDATE item SET new_owner_id = (SELECT new_id FROM "user" WHERE "user".id = item.owner_id)')
-
- # Set the new_id as not nullable
- op.alter_column('user', 'new_id', nullable=False)
- op.alter_column('item', 'new_id', nullable=False)
-
- # Drop old columns and rename new columns
- op.drop_constraint('item_owner_id_fkey', 'item', type_='foreignkey')
- op.drop_column('item', 'owner_id')
- op.alter_column('item', 'new_owner_id', new_column_name='owner_id')
-
- op.drop_column('user', 'id')
- op.alter_column('user', 'new_id', new_column_name='id')
-
- op.drop_column('item', 'id')
- op.alter_column('item', 'new_id', new_column_name='id')
-
- # Create primary key constraint
- op.create_primary_key('user_pkey', 'user', ['id'])
- op.create_primary_key('item_pkey', 'item', ['id'])
-
- # Recreate foreign key constraint
- op.create_foreign_key('item_owner_id_fkey', 'item', 'user', ['owner_id'], ['id'])
-
-def downgrade():
- # Reverse the upgrade process
- op.add_column('user', sa.Column('old_id', sa.Integer, autoincrement=True))
- op.add_column('item', sa.Column('old_id', sa.Integer, autoincrement=True))
- op.add_column('item', sa.Column('old_owner_id', sa.Integer, nullable=True))
-
- # Populate the old columns with default values
- # Generate sequences for the integer IDs if not exist
- op.execute('CREATE SEQUENCE IF NOT EXISTS user_id_seq AS INTEGER OWNED BY "user".old_id')
- op.execute('CREATE SEQUENCE IF NOT EXISTS item_id_seq AS INTEGER OWNED BY item.old_id')
-
- op.execute('SELECT setval(\'user_id_seq\', COALESCE((SELECT MAX(old_id) + 1 FROM "user"), 1), false)')
- op.execute('SELECT setval(\'item_id_seq\', COALESCE((SELECT MAX(old_id) + 1 FROM item), 1), false)')
-
- op.execute('UPDATE "user" SET old_id = nextval(\'user_id_seq\')')
- op.execute('UPDATE item SET old_id = nextval(\'item_id_seq\'), old_owner_id = (SELECT old_id FROM "user" WHERE "user".id = item.owner_id)')
-
- # Drop new columns and rename old columns back
- op.drop_constraint('item_owner_id_fkey', 'item', type_='foreignkey')
- op.drop_column('item', 'owner_id')
- op.alter_column('item', 'old_owner_id', new_column_name='owner_id')
-
- op.drop_column('user', 'id')
- op.alter_column('user', 'old_id', new_column_name='id')
-
- op.drop_column('item', 'id')
- op.alter_column('item', 'old_id', new_column_name='id')
-
- # Create primary key constraint
- op.create_primary_key('user_pkey', 'user', ['id'])
- op.create_primary_key('item_pkey', 'item', ['id'])
-
- # Recreate foreign key constraint
- op.create_foreign_key('item_owner_id_fkey', 'item', 'user', ['owner_id'], ['id'])
diff --git a/backend/app/alembic/versions/e2412789c190_initialize_models.py b/backend/app/alembic/versions/e2412789c190_initialize_models.py
deleted file mode 100644
index 7529ea91fa..0000000000
--- a/backend/app/alembic/versions/e2412789c190_initialize_models.py
+++ /dev/null
@@ -1,54 +0,0 @@
-"""Initialize models
-
-Revision ID: e2412789c190
-Revises:
-Create Date: 2023-11-24 22:55:43.195942
-
-"""
-import sqlalchemy as sa
-import sqlmodel.sql.sqltypes
-from alembic import op
-
-# revision identifiers, used by Alembic.
-revision = "e2412789c190"
-down_revision = None
-branch_labels = None
-depends_on = None
-
-
-def upgrade():
- # ### commands auto generated by Alembic - please adjust! ###
- op.create_table(
- "user",
- sa.Column("email", sqlmodel.sql.sqltypes.AutoString(), nullable=False),
- sa.Column("is_active", sa.Boolean(), nullable=False),
- sa.Column("is_superuser", sa.Boolean(), nullable=False),
- sa.Column("full_name", sqlmodel.sql.sqltypes.AutoString(), nullable=True),
- sa.Column("id", sa.Integer(), nullable=False),
- sa.Column(
- "hashed_password", sqlmodel.sql.sqltypes.AutoString(), nullable=False
- ),
- sa.PrimaryKeyConstraint("id"),
- )
- op.create_index(op.f("ix_user_email"), "user", ["email"], unique=True)
- op.create_table(
- "item",
- sa.Column("description", sqlmodel.sql.sqltypes.AutoString(), nullable=True),
- sa.Column("id", sa.Integer(), nullable=False),
- sa.Column("title", sqlmodel.sql.sqltypes.AutoString(), nullable=False),
- sa.Column("owner_id", sa.Integer(), nullable=False),
- sa.ForeignKeyConstraint(
- ["owner_id"],
- ["user.id"],
- ),
- sa.PrimaryKeyConstraint("id"),
- )
- # ### end Alembic commands ###
-
-
-def downgrade():
- # ### commands auto generated by Alembic - please adjust! ###
- op.drop_table("item")
- op.drop_index(op.f("ix_user_email"), table_name="user")
- op.drop_table("user")
- # ### end Alembic commands ###
diff --git a/backend/app/alembic/versions/e2955dcf9b00_updated_menu_and_category_models.py b/backend/app/alembic/versions/e2955dcf9b00_updated_menu_and_category_models.py
new file mode 100644
index 0000000000..288f13039b
--- /dev/null
+++ b/backend/app/alembic/versions/e2955dcf9b00_updated_menu_and_category_models.py
@@ -0,0 +1,29 @@
+"""Updated menu and category models
+
+Revision ID: e2955dcf9b00
+Revises: ea33a3e76248
+Create Date: 2024-10-24 23:47:03.184543
+
+"""
+from alembic import op
+import sqlalchemy as sa
+import sqlmodel.sql.sqltypes
+
+
+# revision identifiers, used by Alembic.
+revision = 'e2955dcf9b00'
+down_revision = 'ea33a3e76248'
+branch_labels = None
+depends_on = None
+
+
+def upgrade():
+ # ### commands auto generated by Alembic - please adjust! ###
+ pass
+ # ### end Alembic commands ###
+
+
+def downgrade():
+ # ### commands auto generated by Alembic - please adjust! ###
+ pass
+ # ### end Alembic commands ###
diff --git a/backend/app/alembic/versions/ea33a3e76248_create_menu_subcategory_table.py b/backend/app/alembic/versions/ea33a3e76248_create_menu_subcategory_table.py
new file mode 100644
index 0000000000..12c6f31eee
--- /dev/null
+++ b/backend/app/alembic/versions/ea33a3e76248_create_menu_subcategory_table.py
@@ -0,0 +1,57 @@
+"""create menu_subcategory table
+
+Revision ID: ea33a3e76248
+Revises: 3078d16ee962
+Create Date: 2024-10-24 23:25:14.486071
+
+"""
+from alembic import op
+import sqlalchemy as sa
+import sqlmodel.sql.sqltypes
+
+
+# revision identifiers, used by Alembic.
+revision = 'ea33a3e76248'
+down_revision = '3078d16ee962'
+branch_labels = None
+depends_on = None
+
+
+def upgrade():
+ # ### commands auto generated by Alembic - please adjust! ###
+ op.create_table('menu_subcategory',
+ sa.Column('id', sa.Uuid(), nullable=False),
+ sa.Column('category_id', sa.Uuid(), nullable=False),
+ sa.Column('name', sqlmodel.sql.sqltypes.AutoString(), nullable=False),
+ sa.Column('is_alcoholic', sa.Boolean(), nullable=False),
+ sa.ForeignKeyConstraint(['category_id'], ['menu_category.id'], ),
+ sa.PrimaryKeyConstraint('id')
+ )
+ op.create_index(op.f('ix_menu_subcategory_id'), 'menu_subcategory', ['id'], unique=False)
+ op.add_column('menu_item', sa.Column('subcategory_id', sa.Uuid(), nullable=False))
+ op.drop_constraint('menu_item_category_id_fkey', 'menu_item', type_='foreignkey')
+ op.create_foreign_key(None, 'menu_item', 'menu_subcategory', ['subcategory_id'], ['id'])
+ op.drop_column('menu_item', 'category_id')
+ op.drop_column('menu_item', 'sub_category_id')
+ # ### end Alembic commands ###
+
+
+def downgrade():
+ # ### commands auto generated by Alembic - please adjust! ###
+ op.add_column('menu_item', sa.Column('sub_category_id', sa.UUID(), autoincrement=False, nullable=True))
+ op.add_column('menu_item', sa.Column('category_id', sa.UUID(), autoincrement=False, nullable=False))
+ op.drop_constraint(None, 'menu_item', type_='foreignkey')
+ op.create_foreign_key('menu_item_category_id_fkey', 'menu_item', 'menu_category', ['category_id'], ['id'])
+ op.create_foreign_key('menu_item_sub_category_id_fkey', 'menu_item', 'menu_sub_category', ['sub_category_id'], ['id'])
+ op.drop_column('menu_item', 'subcategory_id')
+ op.create_table('menu_sub_category',
+ sa.Column('id', sa.UUID(), autoincrement=False, nullable=False),
+ sa.Column('category_id', sa.UUID(), autoincrement=False, nullable=False),
+ sa.Column('name', sa.VARCHAR(), autoincrement=False, nullable=False),
+ sa.ForeignKeyConstraint(['category_id'], ['menu_category.id'], name='menu_sub_category_category_id_fkey'),
+ sa.PrimaryKeyConstraint('id', name='menu_sub_category_pkey')
+ )
+ op.create_index('ix_menu_sub_category_id', 'menu_sub_category', ['id'], unique=False)
+ op.drop_index(op.f('ix_menu_subcategory_id'), table_name='menu_subcategory')
+ op.drop_table('menu_subcategory')
+ # ### end Alembic commands ###
diff --git a/backend/app/api/deps.py b/backend/app/api/deps.py
index c2b83c841d..57bfe25812 100644
--- a/backend/app/api/deps.py
+++ b/backend/app/api/deps.py
@@ -1,22 +1,19 @@
from collections.abc import Generator
from typing import Annotated
-import jwt
from fastapi import Depends, HTTPException, status
-from fastapi.security import OAuth2PasswordBearer
-from jwt.exceptions import InvalidTokenError
-from pydantic import ValidationError
-from sqlmodel import Session
+from fastapi.security import (
+ HTTPAuthorizationCredentials,
+ HTTPBearer,
+)
+from sqlalchemy.orm import Session
-from app.core import security
-from app.core.config import settings
from app.core.db import engine
-from app.models import TokenPayload, User
-
-reusable_oauth2 = OAuth2PasswordBearer(
- tokenUrl=f"{settings.API_V1_STR}/login/access-token"
-)
+from app.core.security import get_jwt_payload
+from app.models.user import UserBusiness, UserPublic
+# OAuth2PasswordBearer to extract the token from the request header
+bearer_scheme = HTTPBearer()
def get_db() -> Generator[Session, None, None]:
with Session(engine) as session:
@@ -24,34 +21,87 @@ def get_db() -> Generator[Session, None, None]:
SessionDep = Annotated[Session, Depends(get_db)]
-TokenDep = Annotated[str, Depends(reusable_oauth2)]
-
-def get_current_user(session: SessionDep, token: TokenDep) -> User:
+# Dependency to get the current user
+async def get_current_user(
+ credentials: Annotated[HTTPAuthorizationCredentials, Depends(bearer_scheme)],
+ session: SessionDep
+) -> UserPublic | UserBusiness:
+ # print('credentials.credentials ', credentials.credentials)
try:
- payload = jwt.decode(
- token, settings.SECRET_KEY, algorithms=[security.ALGORITHM]
- )
- token_data = TokenPayload(**payload)
- except (InvalidTokenError, ValidationError):
- raise HTTPException(
- status_code=status.HTTP_403_FORBIDDEN,
- detail="Could not validate credentials",
+ print('credentials.credentials ', credentials.credentials)
+ # Verify and decode the token (ensure this is as fast as possible)
+ token_data = get_jwt_payload(credentials.credentials)
+ user_id = token_data.sub
+ print('hhuuuser_id ', user_id)
+ # Query both user types in a single call, using a union if possible
+ user = (
+ session.query(UserPublic)
+ .filter(UserPublic.id == user_id)
+ .first()
+ ) or (
+ session.query(UserBusiness)
+ .filter(UserBusiness.id == user_id)
+ .first()
)
- user = session.get(User, token_data.sub)
- if not user:
- raise HTTPException(status_code=404, detail="User not found")
- if not user.is_active:
- raise HTTPException(status_code=400, detail="Inactive user")
- return user
+ # If no user is found or the user is inactive, raise an error
+ if not user:
+ raise HTTPException(
+ status_code=status.HTTP_404_NOT_FOUND,
+ detail="User not found",
+ )
+ if not user.is_active:
+ raise HTTPException(
+ status_code=status.HTTP_403_FORBIDDEN,
+ detail="Inactive user",
+ )
+
+ return user
-CurrentUser = Annotated[User, Depends(get_current_user)]
+ except Exception:
+ raise HTTPException(
+ status_code=status.HTTP_401_UNAUTHORIZED,
+ detail="Could not validate credentials"
+ )
+# Dependency to get the business user
+async def get_business_user(
+ credentials: Annotated[HTTPAuthorizationCredentials, Depends(bearer_scheme)],
+ session: SessionDep
+) -> UserBusiness:
+ current_user = await get_current_user(credentials, session)
+ if not isinstance(current_user, UserBusiness):
+ raise HTTPException(
+ status_code=status.HTTP_403_FORBIDDEN,
+ detail="Not a business user",
+ )
+ return current_user
-def get_current_active_superuser(current_user: CurrentUser) -> User:
+# Dependency to get the superuser
+async def get_super_user(
+ credentials: Annotated[HTTPAuthorizationCredentials, Depends(bearer_scheme)],
+ session: SessionDep
+) -> UserBusiness:
+ current_user = await get_business_user(credentials, session)
if not current_user.is_superuser:
raise HTTPException(
- status_code=403, detail="The user doesn't have enough privileges"
+ status_code=status.HTTP_403_FORBIDDEN,
+ detail="Not a superuser",
+ )
+ return current_user
+
+# Dependency to get any public user
+async def get_public_user(
+ credentials: Annotated[HTTPAuthorizationCredentials, Depends(bearer_scheme)],
+ session: SessionDep
+) -> UserPublic:
+ print('credentials.credentials ', credentials.credentials)
+ current_user = await get_current_user(credentials, session)
+ # print('current_user ', current_user)
+ if not isinstance(current_user, UserPublic):
+ raise HTTPException(
+ status_code=status.HTTP_403_FORBIDDEN,
+ detail="Not a public user",
)
return current_user
diff --git a/backend/app/api/main.py b/backend/app/api/main.py
index 09e0663fc3..e05e6f9d68 100644
--- a/backend/app/api/main.py
+++ b/backend/app/api/main.py
@@ -1,9 +1,12 @@
from fastapi import APIRouter
-from app.api.routes import items, login, users, utils
+from app.api.routes import carousel, login, menu, qrcode, users, venues, maps
api_router = APIRouter()
+api_router.include_router(venues.router, prefix="/venue", tags=["venue"])
+api_router.include_router(menu.router, prefix="/menu", tags=["menu"])
+api_router.include_router(users.router, prefix="/user", tags=["user"])
api_router.include_router(login.router, tags=["login"])
-api_router.include_router(users.router, prefix="/users", tags=["users"])
-api_router.include_router(utils.router, prefix="/utils", tags=["utils"])
-api_router.include_router(items.router, prefix="/items", tags=["items"])
+api_router.include_router(qrcode.router, tags=["qrcode"])
+api_router.include_router(carousel.router, prefix="/carousel", tags=["carousel"])
+api_router.include_router(maps.router, prefix="/maps", tags=["maps"])
\ No newline at end of file
diff --git a/backend/app/api/routes/carousel.py b/backend/app/api/routes/carousel.py
new file mode 100644
index 0000000000..994a7a0063
--- /dev/null
+++ b/backend/app/api/routes/carousel.py
@@ -0,0 +1,102 @@
+from datetime import datetime
+
+import h3
+from fastapi import APIRouter, Depends, HTTPException
+from sqlmodel import Session, select
+
+from app.api.deps import SessionDep, get_business_user, get_current_user, get_db
+from app.models.carousel_poster import CarouselPoster
+from app.models.event import Event
+from app.models.user import UserBusiness, UserPublic
+from app.models.venue import Venue
+from app.schema.carousel_poster import CarouselPosterCreate, CarouselPosterRead
+from app.util import check_user_permission, create_record, get_record_by_id
+from app.utils import get_h3_index
+
+router = APIRouter()
+
+
+@router.get("/poster/", response_model=list[CarouselPosterRead])
+async def get_carousel_posters(
+ latitude: float,
+ longitude: float,
+ session: SessionDep,
+ radius: int = 3000,
+ current_user: UserPublic = Depends(get_current_user), # noqa: ARG001
+):
+ user_h3_index = get_h3_index(latitude=latitude, longitude=longitude)
+
+ distance_in_km = radius / 1000
+ k_ring_size = int(distance_in_km / 1.2)
+
+ nearby_h3_indexes = h3.k_ring(user_h3_index, k_ring_size)
+
+ posters = (
+ session.execute(
+ select(CarouselPoster)
+ .where(CarouselPoster.h3_index.in_(nearby_h3_indexes))
+ .where(CarouselPoster.expires_at > datetime.now())
+ )
+ .scalars()
+ .all()
+ )
+
+ return [poster.to_read_schema() for poster in posters]
+
+
+@router.post("/poster/", response_model=CarouselPosterRead)
+async def create_carousel_poster(
+ poster: CarouselPosterCreate,
+ db: Session = Depends(get_db),
+ current_user: UserBusiness = Depends(get_business_user),
+):
+ print("Creating carousel poster, poster: ", poster)
+ poster_instance = CarouselPoster.from_create_schema(poster)
+ print("Creating carousel poster, poster_instance: ", poster_instance)
+ venue = None
+ if poster_instance.venue_id:
+ print("Creating carousel poster, venue_id: ", poster_instance.venue_id)
+ try:
+ venue = get_record_by_id(db, Venue, poster_instance.venue_id)
+ except Exception:
+ raise HTTPException(status_code=404, detail="Venue not found")
+ print("Creating carousel poster, venue: ", venue)
+ elif poster_instance.event_id:
+ try:
+ event = db.get(poster_instance.event_id, Event)
+ except Exception:
+ raise HTTPException(status_code=404, detail="Event not found")
+ venue = event.venue
+ else:
+ raise ValueError("Either event_id or venue_id must be provided")
+
+ check_user_permission(db, current_user, venue.id)
+
+ h3_index = get_h3_index(
+ latitude=venue.latitude, longitude=venue.longitude, resolution=9
+ )
+
+ poster_instance.h3_index = h3_index
+
+ created_poster = create_record(db, poster_instance)
+
+ assert isinstance(
+ created_poster, CarouselPoster
+ ), "The returned object is not of type CarouselPoster"
+
+ x = created_poster.to_read_schema()
+ return x
+
+
+# @router.put("/poster/{poster_id}", response_model=CarouselPosterRead)
+# async def update_carousel_poster(
+# poster_id: uuid.UUID, updated_data: CarouselPosterCreate, session: SessionDep
+# ):
+# return update_record(
+# session=session, record_id=poster_id, obj_in=updated_data, model=CarouselPoster
+# )
+
+
+# @router.delete("/poster/{poster_id}")
+# async def delete_carousel_poster(poster_id: uuid.UUID, session: SessionDep):
+# return delete_record(session=session, model=CarouselPoster, record_id=poster_id)
diff --git a/backend/app/api/routes/items.py b/backend/app/api/routes/items.py
deleted file mode 100644
index 67196c2366..0000000000
--- a/backend/app/api/routes/items.py
+++ /dev/null
@@ -1,109 +0,0 @@
-import uuid
-from typing import Any
-
-from fastapi import APIRouter, HTTPException
-from sqlmodel import func, select
-
-from app.api.deps import CurrentUser, SessionDep
-from app.models import Item, ItemCreate, ItemPublic, ItemsPublic, ItemUpdate, Message
-
-router = APIRouter()
-
-
-@router.get("/", response_model=ItemsPublic)
-def read_items(
- session: SessionDep, current_user: CurrentUser, skip: int = 0, limit: int = 100
-) -> Any:
- """
- Retrieve items.
- """
-
- if current_user.is_superuser:
- count_statement = select(func.count()).select_from(Item)
- count = session.exec(count_statement).one()
- statement = select(Item).offset(skip).limit(limit)
- items = session.exec(statement).all()
- else:
- count_statement = (
- select(func.count())
- .select_from(Item)
- .where(Item.owner_id == current_user.id)
- )
- count = session.exec(count_statement).one()
- statement = (
- select(Item)
- .where(Item.owner_id == current_user.id)
- .offset(skip)
- .limit(limit)
- )
- items = session.exec(statement).all()
-
- return ItemsPublic(data=items, count=count)
-
-
-@router.get("/{id}", response_model=ItemPublic)
-def read_item(session: SessionDep, current_user: CurrentUser, id: uuid.UUID) -> Any:
- """
- Get item by ID.
- """
- item = session.get(Item, id)
- if not item:
- raise HTTPException(status_code=404, detail="Item not found")
- if not current_user.is_superuser and (item.owner_id != current_user.id):
- raise HTTPException(status_code=400, detail="Not enough permissions")
- return item
-
-
-@router.post("/", response_model=ItemPublic)
-def create_item(
- *, session: SessionDep, current_user: CurrentUser, item_in: ItemCreate
-) -> Any:
- """
- Create new item.
- """
- item = Item.model_validate(item_in, update={"owner_id": current_user.id})
- session.add(item)
- session.commit()
- session.refresh(item)
- return item
-
-
-@router.put("/{id}", response_model=ItemPublic)
-def update_item(
- *,
- session: SessionDep,
- current_user: CurrentUser,
- id: uuid.UUID,
- item_in: ItemUpdate,
-) -> Any:
- """
- Update an item.
- """
- item = session.get(Item, id)
- if not item:
- raise HTTPException(status_code=404, detail="Item not found")
- if not current_user.is_superuser and (item.owner_id != current_user.id):
- raise HTTPException(status_code=400, detail="Not enough permissions")
- update_dict = item_in.model_dump(exclude_unset=True)
- item.sqlmodel_update(update_dict)
- session.add(item)
- session.commit()
- session.refresh(item)
- return item
-
-
-@router.delete("/{id}")
-def delete_item(
- session: SessionDep, current_user: CurrentUser, id: uuid.UUID
-) -> Message:
- """
- Delete an item.
- """
- item = session.get(Item, id)
- if not item:
- raise HTTPException(status_code=404, detail="Item not found")
- if not current_user.is_superuser and (item.owner_id != current_user.id):
- raise HTTPException(status_code=400, detail="Not enough permissions")
- session.delete(item)
- session.commit()
- return Message(message="Item deleted successfully")
diff --git a/backend/app/api/routes/login.py b/backend/app/api/routes/login.py
index fe7e94d5c1..643515827b 100644
--- a/backend/app/api/routes/login.py
+++ b/backend/app/api/routes/login.py
@@ -1,124 +1,182 @@
-from datetime import timedelta
-from typing import Annotated, Any
+import hashlib
+from datetime import datetime, timedelta, timezone
+from typing import Annotated
from fastapi import APIRouter, Depends, HTTPException
-from fastapi.responses import HTMLResponse
-from fastapi.security import OAuth2PasswordRequestForm
-
-from app import crud
-from app.api.deps import CurrentUser, SessionDep, get_current_active_superuser
-from app.core import security
-from app.core.config import settings
-from app.core.security import get_password_hash
-from app.models import Message, NewPassword, Token, UserPublic
-from app.utils import (
- generate_password_reset_token,
- generate_reset_password_email,
- send_email,
- verify_password_reset_token,
+
+from app.api.deps import SessionDep, get_current_user
+from app.core.security import ( # Adjust the import based on your structure
+ create_access_token,
+ create_refresh_token,
+ get_jwt_payload,
)
+from app.models.auth import OtplessToken, RefreshTokenPayload, UserAuthResponse
+from app.models.user import UserBusiness, UserPublic # Import your UserPublic model
router = APIRouter()
-@router.post("/login/access-token")
-def login_access_token(
- session: SessionDep, form_data: Annotated[OAuth2PasswordRequestForm, Depends()]
-) -> Token:
- """
- OAuth2 compatible token login, get an access token for future requests
- """
- user = crud.authenticate(
- session=session, email=form_data.username, password=form_data.password
- )
- if not user:
- raise HTTPException(status_code=400, detail="Incorrect email or password")
- elif not user.is_active:
- raise HTTPException(status_code=400, detail="Inactive user")
- access_token_expires = timedelta(minutes=settings.ACCESS_TOKEN_EXPIRE_MINUTES)
- return Token(
- access_token=security.create_access_token(
- user.id, expires_delta=access_token_expires
+def generate_number_from_string(s):
+ return int(hashlib.sha256(s.encode()).hexdigest(), 16) % 10 ** 8
+
+
+@router.post("/verify_token/business", response_model=UserAuthResponse)
+async def business_user_google_login(request: OtplessToken, session: SessionDep):
+ try:
+ # Verify the Google token with Google
+ # token_info_url = "https://oauth2.googleapis.com/tokeninfo"
+ # async with httpx.AsyncClient() as client:
+ # response = await client.get(f"{token_info_url}?id_token={request.google_token}")
+
+ # if response.status_code != 200:
+ # raise HTTPException(status_code=400, detail="Invalid Google token")
+
+ # # Parse user info
+ # user_info = response.json()
+ # email = user_info.get("email")
+ # user_id = user_info.get("sub") # Google user ID
+
+ # Check for user by Google ID or email
+ email = request.otpless_token+ "test@gmail.com" # Replace this with the actual email from the SDK response
+ user = session.query(UserBusiness).filter(UserBusiness.email == email).first()
+
+ if not user:
+ # Create new user if not found
+ user = UserBusiness(
+ email=email,
+ is_active=True
+ )
+ session.add(user)
+ session.commit()
+ session.refresh(user)
+
+ # Create tokens
+ access_token = create_access_token(subject=str(user.id), expires_delta=timedelta(minutes=30))
+ refresh_token = create_refresh_token(subject=str(user.id))
+
+ # Store refresh token
+ user.refresh_token = refresh_token.token
+ session.add(user)
+ session.commit()
+
+ return UserAuthResponse(
+ access_token=access_token,
+ refresh_token=refresh_token,
+ issued_at=datetime.now(timezone.utc)
+ )
+
+ except Exception as e:
+ raise HTTPException(status_code=400, detail=str(e))
+
+@router.post("/verify_token/public", response_model=UserAuthResponse)
+async def verify_token(request: OtplessToken, session: SessionDep):
+ try:
+ # Verify the token using OTPLess SDK
+ # Uncomment and implement token verification with OTPLess
+ # user_details = OTPLessAuthSDK.UserDetail.verify_token(
+ # request.otpless_token,
+ # settings.CLIENT_ID,
+ # settings.CLIENT_SECRET
+ # )
+
+ # Simulated user details for demonstration
+ # phone_number = "8130181469" # Replace this with the actual phone number from the SDK response
+ phone_number = str(generate_number_from_string(request.otpless_token))
+
+ # Check for the user by phone number
+ user = session.query(UserPublic).filter(UserPublic.phone_number == phone_number).first()
+
+ if not user:
+ # Create a new user if not found
+ user = UserPublic(
+ phone_number=phone_number,
+ is_active=True
+ )
+ session.add(user)
+ session.commit()
+ session.refresh(user)
+
+ # Create tokens
+ access_token = create_access_token(subject=str(user.id), expires_delta=timedelta(minutes=30))
+ refresh_token = create_refresh_token(subject=str(user.id))
+
+ # Store the new refresh token in the user's record
+ user.refresh_token = refresh_token.token
+ session.add(user) # Add the updated user to the session
+ session.commit() # Commit the changes to the database
+
+ return UserAuthResponse(
+ access_token=access_token,
+ refresh_token=refresh_token,
+ issued_at=datetime.now(timezone.utc)
)
- )
+ except Exception as e:
+ raise HTTPException(status_code=400, detail=str(e))
-@router.post("/login/test-token", response_model=UserPublic)
-def test_token(current_user: CurrentUser) -> Any:
- """
- Test access token
- """
- return current_user
+@router.post("/refresh_token", response_model=UserAuthResponse)
+async def refresh_token(request: RefreshTokenPayload, session: SessionDep):
+ try:
+ # Decode and validate the refresh token payload
+ payload = get_jwt_payload(request.refresh_token)
-@router.post("/password-recovery/{email}")
-def recover_password(email: str, session: SessionDep) -> Message:
- """
- Password Recovery
- """
- user = crud.get_user_by_email(session=session, email=email)
+ # Extract the user ID (sub) from the payload
+ user_id = payload.sub
- if not user:
- raise HTTPException(
- status_code=404,
- detail="The user with this email does not exist in the system.",
- )
- password_reset_token = generate_password_reset_token(email=email)
- email_data = generate_reset_password_email(
- email_to=user.email, email=email, token=password_reset_token
- )
- send_email(
- email_to=user.email,
- subject=email_data.subject,
- html_content=email_data.html_content,
- )
- return Message(message="Password recovery email sent")
-
-
-@router.post("/reset-password/")
-def reset_password(session: SessionDep, body: NewPassword) -> Message:
- """
- Reset password
- """
- email = verify_password_reset_token(token=body.token)
- if not email:
- raise HTTPException(status_code=400, detail="Invalid token")
- user = crud.get_user_by_email(session=session, email=email)
- if not user:
- raise HTTPException(
- status_code=404,
- detail="The user with this email does not exist in the system.",
+ # Fetch the user from the database using the user ID
+ user = (
+ session.query(UserPublic)
+ .filter(UserPublic.id == user_id)
+ .first()
+ ) or (
+ session.query(UserBusiness)
+ .filter(UserBusiness.id == user_id)
+ .first()
)
- elif not user.is_active:
- raise HTTPException(status_code=400, detail="Inactive user")
- hashed_password = get_password_hash(password=body.new_password)
- user.hashed_password = hashed_password
- session.add(user)
- session.commit()
- return Message(message="Password updated successfully")
-
-
-@router.post(
- "/password-recovery-html-content/{email}",
- dependencies=[Depends(get_current_active_superuser)],
- response_class=HTMLResponse,
-)
-def recover_password_html_content(email: str, session: SessionDep) -> Any:
- """
- HTML Content for Password Recovery
- """
- user = crud.get_user_by_email(session=session, email=email)
-
- if not user:
- raise HTTPException(
- status_code=404,
- detail="The user with this username does not exist in the system.",
+
+ if not user:
+ raise HTTPException(status_code=404, detail="User not found")
+
+ # Verify if the refresh token in the database matches the provided token
+ if user.refresh_token != request.refresh_token:
+ raise HTTPException(status_code=401, detail="Invalid refresh token")
+
+ # Check if the token has expired
+ current_time = datetime.now(timezone.utc)
+ if payload.exp and datetime.fromtimestamp(payload.exp, timezone.utc) < current_time:
+ raise HTTPException(status_code=401, detail="Refresh token expired")
+
+ # If valid, generate new tokens
+ new_access_token = create_access_token(subject=user_id, expires_delta=timedelta(minutes=30))
+ new_refresh_token = create_refresh_token(subject=user_id)
+
+ # Update the user's refresh token in the database
+ user.refresh_token = new_refresh_token.token
+ session.add(user)
+ session.commit()
+
+ # Return the new tokens and issue time
+ return UserAuthResponse(
+ access_token=new_access_token,
+ refresh_token=new_refresh_token,
+ issued_at=current_time
)
- password_reset_token = generate_password_reset_token(email=email)
- email_data = generate_reset_password_email(
- email_to=user.email, email=email, token=password_reset_token
- )
-
- return HTMLResponse(
- content=email_data.html_content, headers={"subject:": email_data.subject}
- )
+
+ except Exception as e:
+ raise HTTPException(status_code=400, detail=str(e))
+
+@router.get("/logout")
+async def logout(
+ session: SessionDep,
+ current_user: Annotated[UserPublic | UserBusiness, Depends(get_current_user)]):
+ try:
+ # Invalidate the refresh token by setting it to None or an empty string
+ current_user.refresh_token = None
+ session.add(current_user)
+ session.commit()
+
+ return {"message": "Logout successful, refresh token invalidated"}
+
+ except Exception as e:
+ raise HTTPException(status_code=400, detail=str(e))
diff --git a/backend/app/api/routes/maps.py b/backend/app/api/routes/maps.py
new file mode 100644
index 0000000000..a28f3ed1c6
--- /dev/null
+++ b/backend/app/api/routes/maps.py
@@ -0,0 +1,98 @@
+import requests
+from fastapi import APIRouter, HTTPException
+
+from app.core.config import settings
+from app.schema.maps import (
+ LocationByCoordinatesResponse,
+ LocationDetailsResponse,
+ SearchResponse,
+)
+
+router = APIRouter()
+GOOGLE_API_KEY = settings.GOOGLE_API_KEY
+GOOGLE_REVERSE_GEOCODE_URL = settings.GOOGLE_REVERSE_GEOCODE_URL
+GOOGLE_PLACES_URL = settings.GOOGLE_PLACES_URL
+GOOGLE_PLACE_DETAILS_URL = settings.GOOGLE_PLACE_DETAILS_URL
+
+@router.get("/search", response_model=SearchResponse)
+def search_places(query: str, radius: float = 1000):
+ params = {
+ "query": query,
+ "key": GOOGLE_API_KEY,
+ }
+
+ if radius:
+ params["radius"] = radius
+
+ response = requests.get(GOOGLE_PLACES_URL, params=params)
+ if response.status_code != 200:
+ raise HTTPException(
+ status_code=response.status_code,
+ detail="Error fetching data from Google Maps API",
+ )
+
+ data = response.json()
+
+ results = [
+ {
+ "name": place.get("name"),
+ "address": place.get("formatted_address"),
+ "location": place.get("geometry", {}).get("location"),
+ "place_id": place.get("place_id"),
+ }
+ for place in data.get("results", [])
+ ]
+
+ return {"results": results}
+
+
+@router.get("/location-details", response_model=LocationDetailsResponse)
+async def get_location_details(place_id: str):
+ GOOGLE_PLACE_DETAILS_URL = "https://maps.googleapis.com/maps/api/place/details/json"
+ params = {"place_id": place_id, "key": GOOGLE_API_KEY}
+
+ response = requests.get(GOOGLE_PLACE_DETAILS_URL, params=params)
+
+ if response.status_code != 200:
+ raise HTTPException(status_code=500, detail="Failed to fetch location details")
+
+ details = response.json().get("result", {})
+
+ location_details = {
+ "name": details.get("name"),
+ "address": details.get("formatted_address"),
+ "phone_number": details.get("formatted_phone_number"),
+ "location": details.get("geometry", {}).get("location"),
+ "place_id": place_id,
+ "url": details.get("url")
+ }
+
+ return location_details
+
+@router.get("/location-by-coordinates", response_model=LocationByCoordinatesResponse)
+async def get_location_by_coordinates(lat: float, lng: float):
+ reverse_geocode_params = {"latlng": f"{lat},{lng}", "key": GOOGLE_API_KEY}
+
+ reverse_response = requests.get(
+ GOOGLE_REVERSE_GEOCODE_URL, params=reverse_geocode_params
+ )
+
+ if reverse_response.status_code != 200:
+ raise HTTPException(
+ status_code=500, detail="Failed to fetch place ID from coordinates"
+ )
+
+ results = reverse_response.json().get("results", [])
+
+ if not results:
+ raise HTTPException(
+ status_code=404, detail="No location found for these coordinates"
+ )
+
+ place_id = results[0].get("place_id")
+ name = results[0].get("formatted_address")
+
+ return {
+ "name": name,
+ "place_id": place_id,
+ }
diff --git a/backend/app/api/routes/menu.py b/backend/app/api/routes/menu.py
new file mode 100644
index 0000000000..f845b7f7a1
--- /dev/null
+++ b/backend/app/api/routes/menu.py
@@ -0,0 +1,440 @@
+import uuid
+
+from fastapi import APIRouter, Depends, HTTPException
+from sqlmodel import Session, select
+
+from app.api.deps import get_current_user, get_db
+from app.models.menu import Menu, MenuCategory, MenuItem, MenuSubCategory
+from app.models.user import UserBusiness, UserPublic
+from app.models.venue import Venue
+from app.schema.menu import (
+ MenuCategoryCreate,
+ MenuCategoryRead,
+ MenuCategoryUpdate,
+ MenuCreate,
+ MenuItemCreate,
+ MenuItemRead,
+ MenuItemUpdate,
+ MenuRead,
+ MenuSubCategoryCreate,
+ MenuSubCategoryRead,
+ MenuSubCategoryUpdate,
+ MenuUpdate,
+)
+from app.util import (
+ check_user_permission,
+ create_record,
+ delete_record,
+ get_record_by_id,
+ update_record,
+)
+
+router = APIRouter()
+
+
+# Get all menus of a specific venue
+@router.get("/all/{venue_id}", response_model=list[MenuRead])
+async def read_menus(
+ venue_id: uuid.UUID,
+ db: Session = Depends(get_db),
+ current_user: UserPublic = Depends(get_current_user), # noqa: ARG001
+):
+ """
+ Retrieve all menus for a specific venue.
+ """
+ # Query the Menu table for menus associated with the specified venue
+ statement = select(Menu).where(Menu.venue_id == venue_id)
+ menus = db.execute(statement).scalars().all() # Execute the query
+
+ if not menus:
+ raise HTTPException(status_code=404, detail="No menus found for this venue.")
+
+ assert isinstance(menus[0], Menu), "Fetched Menu object is not of type Menu"
+
+ return [menu.to_read_schema() for menu in menus]
+
+
+@router.get("/menu/{menu_id}", response_model=MenuRead)
+async def read_menu(
+ menu_id: uuid.UUID,
+ db: Session = Depends(get_db),
+ current_user: UserPublic = Depends(get_current_user), # noqa: ARG001
+):
+ """
+ Retrieve a specific menu by its ID.
+ """
+ menu = get_record_by_id(db, Menu, menu_id)
+
+ assert isinstance(menu, Menu), "The returned object is not of type Menu"
+ return menu.to_read_schema()
+
+
+@router.post("/", response_model=MenuRead)
+async def create_menu(
+ menu_create: MenuCreate,
+ db: Session = Depends(get_db),
+ current_user: UserBusiness = Depends(get_current_user),
+):
+ """
+ Create a new menu for a specific venue.
+ """
+ # Check if the venue exists
+ venue = get_record_by_id(db, Venue, menu_create.venue_id)
+ if not venue:
+ raise HTTPException(status_code=404, detail="Venue not found.")
+
+ # Check if the user has permission to create a menu for this venue
+ check_user_permission(db, current_user, menu_create.venue_id)
+
+ try:
+ # Create the Menu object
+ menu_instance = Menu.from_create_schema(menu_create)
+
+ # Use the create_record helper to save the menu to the database
+ created_menu = create_record(db, menu_instance)
+
+ assert isinstance(created_menu, Menu), "The returned object is not of type Menu"
+ return created_menu.to_read_schema()
+
+ except Exception as e:
+ db.rollback() # Rollback in case of error
+ raise HTTPException(status_code=400, detail=f"Error creating menu: {str(e)}")
+
+
+@router.patch("/{menu_id}", response_model=MenuRead)
+async def update_menu(
+ menu_id: uuid.UUID,
+ menu_update: MenuUpdate,
+ db: Session = Depends(get_db),
+ current_user: UserBusiness = Depends(get_current_user),
+):
+ """
+ Update an existing menu's details using a partial update (PATCH).
+
+ :param menu_id: The ID of the menu to update.
+ :param menu_update: The fields to update, provided as a Pydantic model.
+ :param db: Active database session.
+ :return: The updated Menu as a response.
+ """
+ # Retrieve the menu by its ID
+ menu_instance = get_record_by_id(db, Menu, menu_id)
+ # Check if the user has permission to update a menu for this venue
+
+ if not menu_instance:
+ raise HTTPException(status_code=404, detail="Menu not found.")
+
+ check_user_permission(db, current_user, menu_instance.venue_id)
+
+ # Update the menu using the validated fields from MenuUpdate
+ updated_menu = update_record(db, menu_instance, menu_update)
+ assert isinstance(updated_menu, Menu), "The returned object is not of type Menu"
+ return updated_menu.to_read_schema()
+
+
+@router.delete("/{menu_id}", response_model=dict)
+async def delete_menu(
+ menu_id: uuid.UUID,
+ db: Session = Depends(get_db),
+ current_user: UserBusiness = Depends(get_current_user),
+):
+ """
+ Delete a menu by its ID.
+
+ :param menu_id: The ID of the menu to delete.
+ :param db: Active database session.
+ :return: Confirmation message on successful deletion.
+ """
+ menu_instance = get_record_by_id(db, Menu, menu_id)
+ if not menu_instance:
+ raise HTTPException(status_code=404, detail="Menu not found.")
+
+ # Check if the user has permission to delete a menu for this venue
+ check_user_permission(db, current_user, menu_instance.venue_id)
+
+ delete_record(db, menu_instance)
+
+ return {"detail": "Menu deleted successfully."}
+
+
+##############################################################################################################
+
+
+@router.post("/category", response_model=MenuCategoryRead)
+async def create_menu_category(
+ category_create: MenuCategoryCreate,
+ db: Session = Depends(get_db),
+ current_user: UserBusiness = Depends(get_current_user),
+):
+ """
+ Create a new menu category associated with a menu.
+
+ :param category_create: The details for the new menu category, provided as a Pydantic model.
+ :param db: Active database session.
+ :return: The created MenuCategory as a response.
+ """
+ # Check if the menu exists
+ menu = get_record_by_id(db, Menu, category_create.menu_id)
+
+ if not menu:
+ raise HTTPException(status_code=404, detail="Menu not found.")
+ # Check if the user has permission to update a menu for this venue
+ check_user_permission(db, current_user, menu.venue_id)
+
+ # Create a new MenuCategory instance from the provided data
+ category_instance = MenuCategory.from_create_schema(category_create)
+
+ # Persist the new category in the database
+ created_category = create_record(db, category_instance)
+
+ assert isinstance(
+ created_category, MenuCategory
+ ), "The returned object is not of type MenuCategory"
+ return created_category.to_read_schema()
+
+
+@router.patch("/category/{category_id}", response_model=MenuCategoryRead)
+async def update_menu_category(
+ category_id: uuid.UUID,
+ category_update: MenuCategoryUpdate,
+ db: Session = Depends(get_db),
+ current_user: UserBusiness = Depends(get_current_user),
+):
+ """
+ Update an existing menu category's details using a partial update (PATCH).
+
+ :param category_id: The ID of the menu category to update.
+ :param category_update: The fields to update, provided as a Pydantic model.
+ :param db: Active database session.
+ :return: The updated MenuCategory as a response.
+ """
+ # Retrieve the category by its ID
+ category_instance = get_record_by_id(db, MenuCategory, category_id)
+
+ if not category_instance:
+ raise HTTPException(status_code=404, detail="Menu category not found.")
+
+ check_user_permission(db, current_user, category_instance.menu.venue_id)
+
+ # Update the category using the validated fields from MenuCategoryUpdate
+ updated_category = update_record(db, category_instance, category_update)
+
+ assert isinstance(
+ updated_category, MenuCategory
+ ), "The returned object is not of type MenuCategory"
+ return updated_category.to_read_schema()
+
+
+@router.delete("/category/{category_id}", response_model=dict)
+async def delete_category(
+ category_id: uuid.UUID,
+ db: Session = Depends(get_db),
+ current_user: UserBusiness = Depends(get_current_user),
+):
+ """
+ Delete a menu category by its ID.
+
+ :param category_id: The ID of the category to delete.
+ :param db: Active database session.
+ :return: Confirmation message on successful deletion.
+ """
+ category = get_record_by_id(db, MenuCategory, category_id)
+
+ if not category:
+ raise HTTPException(status_code=404, detail="Category not found.")
+
+ check_user_permission(db, current_user, category.menu.venue_id)
+
+ delete_record(db, category)
+
+ return {"detail": "Category deleted successfully."}
+
+
+##############################################################################################################
+
+
+@router.post("/subcategory/", response_model=MenuSubCategoryRead)
+async def create_menu_subcategory(
+ subcategory_create: MenuSubCategoryCreate,
+ db: Session = Depends(get_db),
+ current_user: UserBusiness = Depends(get_current_user),
+):
+ """
+ Create a new menu subcategory associated with a category.
+
+ :param subcategory_create: The details for the new menu subcategory, provided as a Pydantic model.
+ :param db: Active database session.
+ :return: The created MenuSubCategory as a response.
+ """
+ # Check if the category exists
+ category = get_record_by_id(db, MenuCategory, subcategory_create.category_id)
+
+ if not category:
+ raise HTTPException(status_code=404, detail="Category not found.")
+
+ check_user_permission(db, current_user, category.menu.venue_id)
+
+ # Create a new MenuSubCategory instance from the provided data
+ subcategory_instance = MenuSubCategory.from_create_schema(subcategory_create)
+
+ # Persist the new subcategory in the database
+ created_subcategory = create_record(db, subcategory_instance)
+
+ assert isinstance(
+ created_subcategory, MenuSubCategory
+ ), "The returned object is not of type MenuSubCategory"
+ return created_subcategory.to_read_schema()
+
+
+@router.patch("/subcategory/{subcategory_id}", response_model=MenuSubCategoryRead)
+async def update_menu_subcategory(
+ subcategory_id: uuid.UUID,
+ subcategory_update: MenuSubCategoryUpdate,
+ db: Session = Depends(get_db),
+ current_user: UserBusiness = Depends(get_current_user),
+):
+ """
+ Update an existing menu subcategory.
+
+ :param subcategory_id: The unique identifier for the subcategory to update.
+ :param subcategory_update: The details to update, provided as a Pydantic model.
+ :param db: Active database session.
+ :return: The updated MenuSubCategory as a response.
+ """
+ # Check if the subcategory exists
+ subcategory = get_record_by_id(db, MenuSubCategory, subcategory_id)
+
+ if not subcategory:
+ raise HTTPException(status_code=404, detail="Subcategory not found.")
+
+ check_user_permission(db, current_user, subcategory.category.menu.venue_id)
+
+ # Update the subcategory with provided data
+ update_data = subcategory_update.dict(
+ exclude_unset=True
+ ) # Exclude unset fields for partial update
+ updated_subcategory = update_record(db, subcategory, update_data)
+
+ assert isinstance(
+ updated_subcategory, MenuSubCategory
+ ), "The returned object is not of type MenuSubCategory"
+ return updated_subcategory.to_read_schema()
+
+
+@router.delete("/subcategory/{subcategory_id}", response_model=dict)
+async def delete_subcategory(
+ subcategory_id: uuid.UUID,
+ db: Session = Depends(get_db),
+ current_user: UserBusiness = Depends(get_current_user),
+):
+ """
+ Delete a menu subcategory by its ID.
+
+ :param subcategory_id: The ID of the subcategory to delete.
+ :param db: Active database session.
+ :return: Confirmation message on successful deletion.
+ """
+ subcategory = get_record_by_id(db, MenuSubCategory, subcategory_id)
+
+ if not subcategory:
+ raise HTTPException(status_code=404, detail="Subcategory not found.")
+
+ check_user_permission(db, current_user, subcategory.category.menu.venue_id)
+
+ delete_record(db, subcategory)
+
+ return {"detail": "Subcategory deleted successfully."}
+
+
+##############################################################################################################
+
+
+@router.post("/item/", response_model=MenuItemRead)
+async def create_menu_item(
+ item_create: MenuItemCreate,
+ db: Session = Depends(get_db),
+ current_user: UserBusiness = Depends(get_current_user),
+):
+ """
+ Create a new menu item associated with a subcategory.
+
+ :param item_create: The details for the new menu item, provided as a Pydantic model.
+ :param db: Active database session.
+ :return: The created MenuItem as a response.
+ """
+ # Check if the subcategory exists
+ subcategory = get_record_by_id(db, MenuSubCategory, item_create.subcategory_id)
+
+ if not subcategory:
+ raise HTTPException(status_code=404, detail="Subcategory not found.")
+
+ check_user_permission(db, current_user, subcategory.category.menu.venue_id)
+
+ # Create a new MenuItem instance from the provided data
+ item_instance = MenuItem.from_create_schema(item_create)
+
+ # Persist the new item in the database
+ created_item = create_record(db, item_instance)
+
+ assert isinstance(
+ created_item, MenuItem
+ ), "The returned object is not of type MenuItem"
+ return created_item.to_read_schema()
+
+
+@router.patch("/item/{item_id}", response_model=MenuItemRead)
+async def update_menu_item(
+ item_id: uuid.UUID,
+ item_update: MenuItemUpdate,
+ db: Session = Depends(get_db),
+ current_user: UserBusiness = Depends(get_current_user),
+):
+ """
+ Update an existing menu item.
+
+ :param item_id: The unique identifier for the item to update.
+ :param item_update: The details to update, provided as a Pydantic model.
+ :param db: Active database session.
+ :return: The updated MenuItem as a response.
+ """
+ # Check if the item exists
+ item = get_record_by_id(db, MenuItem, item_id)
+
+ if not item:
+ raise HTTPException(status_code=404, detail="Menu item not found.")
+
+ check_user_permission(db, current_user, item.subcategory.category.menu.venue_id)
+
+ # Update the item with provided data
+ updated_item = update_record(db, item, item_update)
+ assert isinstance(
+ updated_item, MenuItem
+ ), "The returned object is not of type MenuItem"
+ return updated_item.to_read_schema()
+
+
+@router.delete("/item/{item_id}", response_model=dict)
+async def delete_menu_item(
+ item_id: uuid.UUID,
+ db: Session = Depends(get_db),
+ current_user: UserBusiness = Depends(get_current_user),
+):
+ """
+ Delete a menu item by its ID.
+
+ :param item_id: The ID of the item to delete.
+ :param db: Active database session.
+ :return: Confirmation message on successful deletion.
+ """
+ item = get_record_by_id(db, MenuItem, item_id)
+
+ if not item:
+ raise HTTPException(status_code=404, detail="Menu item not found.")
+
+ check_user_permission(db, current_user, item.subcategory.category.menu.venue_id)
+
+ delete_record(db, item)
+
+ return {"detail": "Menu item deleted successfully."}
+
+
+#########################################################################################################
diff --git a/backend/app/api/routes/qrcode.py b/backend/app/api/routes/qrcode.py
new file mode 100644
index 0000000000..1eb66b76c2
--- /dev/null
+++ b/backend/app/api/routes/qrcode.py
@@ -0,0 +1,179 @@
+import uuid
+from pathlib import Path
+
+from fastapi import APIRouter, Depends, HTTPException
+from fastapi.responses import HTMLResponse
+from jinja2 import Template
+from sqlmodel import Session, select
+
+from app.api.deps import SessionDep, get_current_user, get_db
+from app.models.qrcode import QRCode # Ensure you have this import for your model
+from app.models.user import UserBusiness
+from app.schema.qrcode import (
+ QRCodeCreate,
+ QRCodeRead,
+ QRCodeUpdate,
+)
+
+# Ensure you have these imports for your schemas
+from app.util import (
+ check_user_permission,
+ delete_record,
+ get_record_by_id,
+ update_record,
+)
+
+router = APIRouter()
+
+
+# Return all QR codes for a specific venue
+@router.get("/venue/{venue_id}", response_model=list[QRCodeRead])
+async def read_qrcode_by_venue(
+ venue_id: uuid.UUID,
+ db: Session = Depends(get_db),
+ current_user: UserBusiness = Depends(get_current_user),
+):
+ """
+ Retrieve all QR codes associated with a specific venue.
+ """
+ qrcodes = (
+ db.execute(select(QRCode).where(QRCode.venue_id == venue_id)).scalars().all()
+ )
+ # if qrcode is empty, raise an error
+ if not qrcodes:
+ raise HTTPException(status_code=404, detail="No QR codes found for this venue.")
+
+ check_user_permission(db, current_user, venue_id)
+ return [qr_code.to_read_schema() for qr_code in qrcodes]
+
+
+# Get a specific QR code
+@router.get("/{qr_code_id}", response_model=QRCodeRead)
+async def read_qr_code(
+ qr_code_id: uuid.UUID,
+ db: Session = Depends(get_db),
+ current_user: UserBusiness = Depends(get_current_user),
+):
+ """
+ Retrieve a specific QR code by ID.
+ """
+ qr_code_instance = get_record_by_id(db, QRCode, qr_code_id)
+ check_user_permission(db, current_user, qr_code_instance.venue_id)
+ assert isinstance(
+ qr_code_instance, QRCode
+ ), "The returned object is not of type QRCode"
+ return qr_code_instance.to_read_schema()
+
+
+# Create a new QR code
+@router.post("/", response_model=QRCodeRead)
+async def create_qr_code(
+ qr_code: QRCodeCreate,
+ db: Session = Depends(get_db),
+ current_user: UserBusiness = Depends(get_current_user),
+):
+ """
+ Create a new QR code.
+ """
+ check_user_permission(db, current_user, qr_code.venue_id)
+ try:
+ qr_code_instance = QRCode.from_create_schema(qr_code)
+ db.add(qr_code_instance) # Persist the new QR code
+ db.commit() # Commit the session
+ return (
+ qr_code_instance.to_read_schema()
+ ) # Call the instance method to convert to QRCodeRead
+ except Exception as e:
+ db.rollback() # Rollback the session in case of any error
+ raise HTTPException(status_code=500, detail=str(e)) from e
+
+
+# Patch a QR code for partial updates
+@router.patch("/{qr_code_id}", response_model=QRCodeRead)
+async def update_qr_code(
+ qr_code_id: uuid.UUID,
+ updated_qr_code: QRCodeUpdate,
+ db: Session = Depends(get_db),
+ current_user: UserBusiness = Depends(get_current_user),
+):
+ qr_code_instance = get_record_by_id(db, QRCode, qr_code_id)
+
+ if not qr_code_instance:
+ raise HTTPException(status_code=404, detail="QR code not found.")
+
+ # Check user permission before updating the QR code for this venue
+ check_user_permission(db, current_user, qr_code_instance.venue_id)
+
+ updated_qr_code = update_record(db, qr_code_instance, updated_qr_code)
+ assert isinstance(
+ updated_qr_code, QRCode
+ ), "The returned object is not of type QRCode"
+ return updated_qr_code.to_read_schema()
+
+
+# Delete a QR code
+@router.delete("/qrcode/{qr_code_id}", response_model=None)
+async def delete_qr_code(
+ qr_code_id: uuid.UUID,
+ db: Session = Depends(get_db),
+ current_user: UserBusiness = Depends(get_current_user),
+):
+ """
+ Delete a QR code by ID.
+ """
+ qr_code_instance = get_record_by_id(db, QRCode, qr_code_id)
+
+ if not qr_code_instance:
+ raise HTTPException(status_code=404, detail="QR code not found.")
+
+ check_user_permission(db, current_user, qr_code_instance.venue_id)
+ return delete_record(db, qr_code_instance)
+
+
+@router.get("/scan/{qr_id}", response_class=HTMLResponse)
+async def scan_qr_code(qr_id: uuid.UUID, session: SessionDep):
+ """
+ Scan a QR code to determine its associated venue and redirect appropriately.
+ """
+ # Retrieve the QR code from the database
+ qr_code_result = session.execute(
+ select(QRCode).where(QRCode.id == qr_id)
+ ).one_or_none()
+
+ if not qr_code_result:
+ raise HTTPException(status_code=404, detail="QR code not found.")
+
+ qr_code = qr_code_result[0]
+ # Determine the venue type and ID from the QR code
+ venue_type, venue_id = None, None
+
+ if qr_code.foodcourt_id:
+ venue_type = "foodcourt"
+ venue_id = qr_code.foodcourt_id
+ elif qr_code.qsr_id:
+ venue_type = "qsr"
+ venue_id = qr_code.qsr_id
+ elif qr_code.nightclub_id:
+ venue_type = "nightclub"
+ venue_id = qr_code.nightclub_id
+ elif qr_code.restaurant_id:
+ venue_type = "restaurant"
+ venue_id = qr_code.restaurant_id
+
+ if not venue_type or not venue_id:
+ raise HTTPException(status_code=400, detail="No associated venue found.")
+
+ # Load the landing page HTML template
+ template_path = Path(__file__).parent.parent.parent / "static" / "landing_page.html"
+ print("template_path:", template_path)
+ try:
+ template_str = template_path.read_text()
+ except FileNotFoundError as exc:
+ raise HTTPException(
+ status_code=500, detail="Landing page template not found."
+ ) from exc
+
+ # Use string.Template to replace placeholders in the HTML
+ template = Template(template_str)
+ html_content = template.render(venueId=venue_id, venueType=venue_type)
+ return HTMLResponse(content=html_content)
diff --git a/backend/app/api/routes/users.py b/backend/app/api/routes/users.py
index c636b094ee..6a757720c0 100644
--- a/backend/app/api/routes/users.py
+++ b/backend/app/api/routes/users.py
@@ -1,228 +1,214 @@
import uuid
-from typing import Any
-from fastapi import APIRouter, Depends, HTTPException
-from sqlmodel import col, delete, func, select
+from fastapi import APIRouter, Depends, HTTPException, Query
+from sqlmodel import Session
-from app import crud
from app.api.deps import (
- CurrentUser,
SessionDep,
- get_current_active_superuser,
+ get_business_user,
+ get_current_user,
+ get_db,
+ get_public_user,
+ get_super_user,
)
-from app.core.config import settings
-from app.core.security import get_password_hash, verify_password
-from app.models import (
- Item,
- Message,
- UpdatePassword,
- User,
- UserCreate,
- UserPublic,
- UserRegister,
- UsersPublic,
- UserUpdate,
- UserUpdateMe,
+from app.models import UserBusiness, UserPublic
+from app.schema.user import (
+ UserBusinessCreate,
+ UserBusinessRead,
+ UserBusinessUpdate,
+ UserPublicRead,
+ UserPublicUpdate,
+)
+from app.util import (
+ create_record,
+ delete_record,
+ get_all_records,
+ get_record_by_id,
+ update_record,
)
-from app.utils import generate_new_account_email, send_email
router = APIRouter()
-@router.get(
- "/",
- dependencies=[Depends(get_current_active_superuser)],
- response_model=UsersPublic,
-)
-def read_users(session: SessionDep, skip: int = 0, limit: int = 100) -> Any:
+@router.get("/me", response_model=UserPublicRead | UserBusinessRead)
+async def read_user_me(
+ current_user: UserPublic | UserBusiness = Depends(get_current_user),
+):
+ print("current_user", current_user)
"""
- Retrieve users.
+ Retrieve profile information of the currently authenticated user.
"""
+ x = current_user.to_read_schema()
+ print("hui", x)
+ return x
- count_statement = select(func.count()).select_from(User)
- count = session.exec(count_statement).one()
- statement = select(User).offset(skip).limit(limit)
- users = session.exec(statement).all()
+@router.get("/all-user-business/", response_model=list[UserBusinessRead])
+async def all_read_user_business(
+ db: Session = Depends(get_db),
+ skip: int = Query(0, alias="page", ge=0),
+ limit: int = Query(10, le=100),
+ current_user: UserBusiness = Depends(get_super_user), # noqa: ARG001
+):
+ """
+ Retrieve a paginated list of user businesses.
+ - **skip**: The page number (starting from 0)
+ - **limit**: The number of items per page
+ """
+ all_users = get_all_records(db, UserBusiness, skip=skip, limit=limit)
- return UsersPublic(data=users, count=count)
+ if all_users is None:
+ raise HTTPException(status_code=404, detail="No user businesses found.")
+ # Convert each record to its read schema
+ assert all(
+ hasattr(user, "to_read_schema") for user in all_users
+ ), "Each user must implement 'to_read_schema'"
-@router.post(
- "/", dependencies=[Depends(get_current_active_superuser)], response_model=UserPublic
-)
-def create_user(*, session: SessionDep, user_in: UserCreate) -> Any:
- """
- Create new user.
- """
- user = crud.get_user_by_email(session=session, email=user_in.email)
- if user:
- raise HTTPException(
- status_code=400,
- detail="The user with this email already exists in the system.",
- )
+ return [user.to_read_schema() for user in all_users]
- user = crud.create_user(session=session, user_create=user_in)
- if settings.emails_enabled and user_in.email:
- email_data = generate_new_account_email(
- email_to=user_in.email, username=user_in.email, password=user_in.password
- )
- send_email(
- email_to=user_in.email,
- subject=email_data.subject,
- html_content=email_data.html_content,
- )
- return user
-
-
-@router.patch("/me", response_model=UserPublic)
-def update_user_me(
- *, session: SessionDep, user_in: UserUpdateMe, current_user: CurrentUser
-) -> Any:
- """
- Update own user.
- """
-
- if user_in.email:
- existing_user = crud.get_user_by_email(session=session, email=user_in.email)
- if existing_user and existing_user.id != current_user.id:
- raise HTTPException(
- status_code=409, detail="User with this email already exists"
- )
- user_data = user_in.model_dump(exclude_unset=True)
- current_user.sqlmodel_update(user_data)
- session.add(current_user)
- session.commit()
- session.refresh(current_user)
- return current_user
-
-
-@router.patch("/me/password", response_model=Message)
-def update_password_me(
- *, session: SessionDep, body: UpdatePassword, current_user: CurrentUser
-) -> Any:
- """
- Update own password.
- """
- if not verify_password(body.current_password, current_user.hashed_password):
- raise HTTPException(status_code=400, detail="Incorrect password")
- if body.current_password == body.new_password:
- raise HTTPException(
- status_code=400, detail="New password cannot be the same as the current one"
- )
- hashed_password = get_password_hash(body.new_password)
- current_user.hashed_password = hashed_password
- session.add(current_user)
- session.commit()
- return Message(message="Password updated successfully")
-
-
-@router.get("/me", response_model=UserPublic)
-def read_user_me(current_user: CurrentUser) -> Any:
- """
- Get current user.
- """
- return current_user
-
-
-@router.delete("/me", response_model=Message)
-def delete_user_me(session: SessionDep, current_user: CurrentUser) -> Any:
- """
- Delete own user.
- """
- if current_user.is_superuser:
- raise HTTPException(
- status_code=403, detail="Super users are not allowed to delete themselves"
- )
- statement = delete(Item).where(col(Item.owner_id) == current_user.id)
- session.exec(statement) # type: ignore
- session.delete(current_user)
- session.commit()
- return Message(message="User deleted successfully")
-
-
-@router.post("/signup", response_model=UserPublic)
-def register_user(session: SessionDep, user_in: UserRegister) -> Any:
- """
- Create new user without the need to be logged in.
- """
- user = crud.get_user_by_email(session=session, email=user_in.email)
- if user:
- raise HTTPException(
- status_code=400,
- detail="The user with this email already exists in the system",
- )
- user_create = UserCreate.model_validate(user_in)
- user = crud.create_user(session=session, user_create=user_create)
- return user
-
-
-@router.get("/{user_id}", response_model=UserPublic)
-def read_user_by_id(
- user_id: uuid.UUID, session: SessionDep, current_user: CurrentUser
-) -> Any:
- """
- Get a specific user by id.
- """
- user = session.get(User, user_id)
- if user == current_user:
- return user
- if not current_user.is_superuser:
- raise HTTPException(
- status_code=403,
- detail="The user doesn't have enough privileges",
- )
- return user
+@router.get("/user-businesses/{user_business_id}", response_model=UserBusinessRead)
+async def read_user_business(
+ user_business_id: uuid.UUID,
+ db: Session = Depends(get_db),
+ current_user: UserBusiness = Depends(get_super_user), # noqa: ARG001
+):
+ """
+ Retrieve a single user business by ID.
+ - **user_business_id**: The ID of the user business to retrieve
+ """
+ user_instance = get_record_by_id(db, UserBusiness, user_business_id)
-@router.patch(
- "/{user_id}",
- dependencies=[Depends(get_current_active_superuser)],
- response_model=UserPublic,
-)
-def update_user(
- *,
+ return user_instance.to_read_schema()
+
+
+@router.post("/user-businesses/", response_model=UserBusinessRead)
+async def create_user_business(
+ user_business: UserBusinessCreate,
+ db: Session = Depends(get_db),
+ current_user: UserBusiness = Depends(get_super_user),
+):
+ """
+ Create a new user business.
+ - **user_business**: The user business data to create
+ """
+
+ try:
+ user_instance = UserBusiness.from_create_schema(user_business)
+ user_instance.id = current_user.id
+ return create_record(db, user_instance)
+ except Exception as e:
+ db.rollback()
+ raise HTTPException(status_code=400, detail=str(e))
+
+
+@router.patch("/user-businesses/{user_business_id}", response_model=UserBusinessRead)
+async def update_user_business(
+ user_business: UserBusinessUpdate,
+ db: Session = Depends(get_db),
+ current_user: UserBusiness = Depends(get_current_user),
+):
+ """
+ Update an existing user business.
+ - **user_business_id**: The ID of the user business to update
+ - **user_business**: The updated user business data
+ """
+ try:
+ user_instance = UserBusiness.from_create_schema(user_business)
+ return update_record(db, current_user, user_instance)
+ except Exception as e:
+ db.rollback()
+ raise HTTPException(status_code=400, detail=str(e))
+
+
+@router.delete("/user-businesses/{user_business_id}", response_model=dict)
+async def delete_user_business(
+ user_business_id: uuid.UUID,
+ session: SessionDep,
+ current_user: UserBusiness = Depends(get_super_user), # noqa: ARG001
+):
+ """
+ Delete a user business by ID.
+ - **user_business_id**: The ID of the user business to delete
+ """
+ user_instance = get_record_by_id(session, UserBusiness, user_business_id)
+
+ delete_record(session, user_instance)
+ return {"message": f"UserBusiness with ID {user_business_id} has been deleted."}
+
+
+@router.get("/all-user-public/", response_model=list[UserPublicRead])
+async def all_read_user_public(
+ db: Session = Depends(get_db),
+ skip: int = Query(0, alias="page", ge=0),
+ limit: int = Query(10, le=100),
+ current_user: UserBusiness = Depends(get_business_user), # noqa: ARG001
+):
+ """
+ Retrieve a paginated list of user public.
+ - **skip**: The page number (starting from 0)
+ - **limit**: The number of items per page
+ """
+ all_users = get_all_records(db, UserPublic, skip=skip, limit=limit)
+
+ if all_users is None:
+ raise HTTPException(status_code=404, detail="No user found.")
+
+ assert all(
+ hasattr(user, "to_read_schema") for user in all_users
+ ), "Each user must implement 'to_read_schema'"
+
+ # Convert each record to its read schema
+ return [user.to_read_schema() for user in all_users]
+
+
+@router.get("/user-public/{user_public_id}", response_model=UserPublicRead)
+async def read_user_public(
+ user_public_id: uuid.UUID,
+ db: Session = Depends(get_db),
+ current_user: UserBusiness = Depends(get_business_user), # noqa: ARG001
+):
+ """
+ Retrieve a single user public by ID.
+ - **user_public_id**: The ID of the user public to retrieve
+ """
+ user_instance = get_record_by_id(db, UserBusiness, user_public_id)
+
+ return user_instance.to_read_schema()
+
+
+@router.patch("/user-public/", response_model=UserPublicRead)
+async def update_user_public_me(
+ user_public: UserPublicUpdate,
+ db: Session = Depends(get_db),
+ current_user: UserPublic = Depends(get_public_user),
+):
+ """
+ Update an existing user public.
+ - **user_public_id**: The ID of the user public to update
+ - **user_public**: The updated user public data
+ """
+ print("user_public", user_public)
+ try:
+ user_instance = UserPublic.from_create_schema(user_public)
+ return update_record(db, current_user, user_instance)
+ except Exception as e:
+ db.rollback()
+ raise HTTPException(status_code=400, detail=str(e))
+
+
+@router.delete("/user-public/{user_public_id}", response_model=dict)
+async def delete_user_public(
+ user_public_id: uuid.UUID,
session: SessionDep,
- user_id: uuid.UUID,
- user_in: UserUpdate,
-) -> Any:
- """
- Update a user.
- """
-
- db_user = session.get(User, user_id)
- if not db_user:
- raise HTTPException(
- status_code=404,
- detail="The user with this id does not exist in the system",
- )
- if user_in.email:
- existing_user = crud.get_user_by_email(session=session, email=user_in.email)
- if existing_user and existing_user.id != user_id:
- raise HTTPException(
- status_code=409, detail="User with this email already exists"
- )
-
- db_user = crud.update_user(session=session, db_user=db_user, user_in=user_in)
- return db_user
-
-
-@router.delete("/{user_id}", dependencies=[Depends(get_current_active_superuser)])
-def delete_user(
- session: SessionDep, current_user: CurrentUser, user_id: uuid.UUID
-) -> Message:
- """
- Delete a user.
- """
- user = session.get(User, user_id)
- if not user:
- raise HTTPException(status_code=404, detail="User not found")
- if user == current_user:
- raise HTTPException(
- status_code=403, detail="Super users are not allowed to delete themselves"
- )
- statement = delete(Item).where(col(Item.owner_id) == user_id)
- session.exec(statement) # type: ignore
- session.delete(user)
- session.commit()
- return Message(message="User deleted successfully")
+ current_user: UserBusiness = Depends(get_super_user), # noqa: ARG001
+):
+ """
+ Delete a user public by ID.
+ - **user_public_id**: The ID of the user public to delete
+ """
+ user_instance = get_record_by_id(session, UserBusiness, user_public_id)
+
+ delete_record(session, user_instance)
+ return {"message": f"UserPublic with ID {user_public_id} has been deleted."}
diff --git a/backend/app/api/routes/utils.py b/backend/app/api/routes/utils.py
deleted file mode 100644
index 82f6d2b821..0000000000
--- a/backend/app/api/routes/utils.py
+++ /dev/null
@@ -1,26 +0,0 @@
-from fastapi import APIRouter, Depends
-from pydantic.networks import EmailStr
-
-from app.api.deps import get_current_active_superuser
-from app.models import Message
-from app.utils import generate_test_email, send_email
-
-router = APIRouter()
-
-
-@router.post(
- "/test-email/",
- dependencies=[Depends(get_current_active_superuser)],
- status_code=201,
-)
-def test_email(email_to: EmailStr) -> Message:
- """
- Test emails.
- """
- email_data = generate_test_email(email_to=email_to)
- send_email(
- email_to=email_to,
- subject=email_data.subject,
- html_content=email_data.html_content,
- )
- return Message(message="Test email sent")
diff --git a/backend/app/api/routes/venues.py b/backend/app/api/routes/venues.py
new file mode 100644
index 0000000000..ea2601a5bf
--- /dev/null
+++ b/backend/app/api/routes/venues.py
@@ -0,0 +1,218 @@
+from fastapi import APIRouter, Depends, FastAPI, HTTPException
+from sqlmodel import Session
+
+from app.api.deps import (
+ get_business_user,
+ get_db,
+)
+from app.models.user import UserBusiness, UserVenueAssociation
+from app.models.venue import QSR, Foodcourt, Nightclub, Restaurant, Venue
+from app.schema.venue import (
+ FoodcourtCreate,
+ FoodcourtRead,
+ NightclubCreate,
+ NightclubRead,
+ QSRCreate,
+ QSRRead,
+ RestaurantCreate,
+ RestaurantRead,
+ VenueListResponse,
+)
+
+# Assuming you have a dependency to get the database session
+from app.util import (
+ create_record,
+ get_all_records,
+)
+
+app = FastAPI()
+router = APIRouter()
+
+
+# POST endpoint for Foodcourt
+@router.post("/foodcourts/", response_model=FoodcourtRead)
+def create_foodcourt(
+ foodcourt: FoodcourtCreate,
+ db: Session = Depends(get_db),
+ current_user: UserBusiness = Depends(get_business_user),
+):
+ try:
+ # Check if the venue exists
+ venue_instance = Venue.from_create_schema(foodcourt.venue)
+ create_record(db, venue_instance) # Persist the new venue
+ # Use the newly created venue instance
+ foodcourt_instance = Foodcourt.from_create_schema(venue_instance.id, foodcourt)
+ # Create the new Foodcourt record in the database
+ create_record(db, foodcourt_instance)
+ association = UserVenueAssociation(
+ user_id=current_user.id, venue_id=venue_instance.id
+ )
+ create_record(db, association)
+
+ return foodcourt_instance.to_read_schema()
+
+ except Exception as e:
+ # Rollback the session in case of any error
+ db.rollback()
+ raise HTTPException(status_code=500, detail=str(e)) # Respond with a 500 error
+
+
+# GET endpoint for Foodcourt
+@router.get("/foodcourts/", response_model=list[FoodcourtRead])
+def read_foodcourts(skip: int = 0, limit: int = 10, db: Session = Depends(get_db)):
+ return get_all_records(db, Foodcourt, skip=skip, limit=limit)
+
+
+# POST endpoint for QSR
+@router.post("/qsrs/", response_model=QSRRead)
+def create_qsr(
+ qsr: QSRCreate,
+ db: Session = Depends(get_db),
+ current_user: UserBusiness = Depends(get_business_user),
+):
+ try:
+ # Check if the venue exists
+ venue_instance = Venue.from_create_schema(qsr.venue)
+ create_record(db, venue_instance) # Persist the new venue
+ # Use the newly created venue instance
+ qsr_instance = QSR.from_create_schema(venue_instance.id, qsr)
+ # Create the new Foodcourt record in the database
+ create_record(db, qsr_instance)
+ association = UserVenueAssociation(
+ user_id=current_user.id, venue_id=venue_instance.id
+ )
+ create_record(db, association)
+ return qsr_instance.to_read_schema()
+
+ except Exception as e:
+ # Rollback the session in case of any error
+ db.rollback()
+ raise HTTPException(status_code=500, detail=str(e)) # Respond with a 500 error
+
+
+# GET endpoint for QSR
+@router.get("/qsrs/", response_model=list[QSRRead])
+def read_qsrs(skip: int = 0, limit: int = 10, db: Session = Depends(get_db)):
+ return get_all_records(db, QSR, skip=skip, limit=limit)
+
+
+# POST endpoint for Restaurant
+@router.post("/restaurants/", response_model=RestaurantRead)
+def create_restaurant(
+ restaurant: RestaurantCreate,
+ db: Session = Depends(get_db),
+ current_user: UserBusiness = Depends(get_business_user),
+):
+ try:
+ # Check if the venue exists
+ venue_instance = Venue.from_create_schema(restaurant.venue)
+ create_record(db, venue_instance) # Persist the new venue
+ # Use the newly created venue instance
+ restaurant_instance = Restaurant.from_create_schema(
+ venue_instance.id, restaurant
+ )
+ # Create the new Foodcourt record in the database
+ create_record(db, restaurant_instance)
+ association = UserVenueAssociation(
+ user_id=current_user.id, venue_id=venue_instance.id
+ )
+ create_record(db, association)
+ return restaurant_instance.to_read_schema()
+
+ except Exception as e:
+ # Rollback the session in case of any error
+ db.rollback()
+ raise HTTPException(status_code=500, detail=str(e)) # Respond with a 500 error
+
+
+# GET endpoint for Restaurant
+@router.get("/restaurants/", response_model=list[RestaurantRead])
+def read_restaurants(skip: int = 0, limit: int = 10, db: Session = Depends(get_db)):
+ return get_all_records(db, Restaurant, skip=skip, limit=limit)
+
+
+# POST endpoint for Nightclub
+@router.post("/nightclubs/", response_model=NightclubRead)
+def create_nightclub(
+ nightclub: NightclubCreate,
+ db: Session = Depends(get_db),
+ current_user: UserBusiness = Depends(get_business_user),
+):
+ try:
+ # Check if the venue exists
+ venue_instance = Venue.from_create_schema(nightclub.venue)
+ create_record(db, venue_instance) # Persist the new venue
+ # Use the newly created venue instance
+ nightclub_instance = Nightclub.from_create_schema(venue_instance.id, nightclub)
+ # Create the new Foodcourt record in the database
+ create_record(db, nightclub_instance)
+ association = UserVenueAssociation(
+ user_id=current_user.id, venue_id=venue_instance.id
+ )
+ create_record(db, association)
+ return nightclub_instance.to_read_schema()
+
+ except Exception as e:
+ # Rollback the session in case of any error
+ db.rollback()
+ raise HTTPException(status_code=500, detail=str(e)) # Respond with a 500 error
+
+
+# GET endpoint for Nightclub
+@router.get("/nightclubs/", response_model=list[NightclubRead])
+def read_nightclubs(skip: int = 0, limit: int = 10, db: Session = Depends(get_db)):
+ return get_all_records(db, Nightclub, skip=skip, limit=limit)
+
+
+@router.get("/my-venues/", response_model=VenueListResponse)
+async def get_my_venues(
+ db: Session = Depends(get_db),
+ current_user: UserBusiness = Depends(get_business_user),
+):
+ """
+ Retrieve the venues managed by the current user, organized by venue type.
+
+ This method leverages SQLAlchemy's efficient querying capabilities to minimize database load
+ while ensuring data integrity through the association table, UserVenueAssociation.
+ """
+
+ # Fetch all venues managed by the current user
+ managed_venues = (
+ db.query(Venue)
+ .join(UserVenueAssociation)
+ .filter(UserVenueAssociation.user_id == current_user.id)
+ .all()
+ )
+
+ # Create a set for fast membership testing
+ managed_venue_ids = {venue.id for venue in managed_venues}
+
+ # Initialize lists for categorized venues
+ nightclubs, qsrs, foodcourts, restaurants = [], [], [], []
+
+ # Efficiently query and convert Nightclubs
+ for nightclub in (
+ db.query(Nightclub).filter(Nightclub.venue_id.in_(managed_venue_ids)).all()
+ ):
+ nightclubs.append(nightclub.to_read_schema())
+
+ # Efficiently query and convert QSRs
+ for qsr in db.query(QSR).filter(QSR.venue_id.in_(managed_venue_ids)).all():
+ qsrs.append(qsr.to_read_schema())
+
+ # Efficiently query and convert Foodcourts
+ for foodcourt in (
+ db.query(Foodcourt).filter(Foodcourt.venue_id.in_(managed_venue_ids)).all()
+ ):
+ foodcourts.append(foodcourt.to_read_schema())
+
+ # Efficiently query and convert Restaurants
+ for restaurant in (
+ db.query(Restaurant).filter(Restaurant.venue_id.in_(managed_venue_ids)).all()
+ ):
+ restaurants.append(restaurant.to_read_schema())
+
+ # Construct and return the response
+ return VenueListResponse(
+ nightclubs=nightclubs, qsrs=qsrs, foodcourts=foodcourts, restaurants=restaurants
+ )
diff --git a/backend/app/backend_pre_start.py b/backend/app/backend_pre_start.py
index c2f8e29ae1..9b9a3c3b81 100644
--- a/backend/app/backend_pre_start.py
+++ b/backend/app/backend_pre_start.py
@@ -28,7 +28,6 @@ def init(db_engine: Engine) -> None:
logger.error(e)
raise e
-
def main() -> None:
logger.info("Initializing service")
init(engine)
diff --git a/backend/app/constants.py b/backend/app/constants.py
new file mode 100644
index 0000000000..5ce00e9968
--- /dev/null
+++ b/backend/app/constants.py
@@ -0,0 +1,8 @@
+# constants.py
+from enum import Enum
+
+
+class Gender(Enum):
+ MALE = "male"
+ FEMALE = "female"
+ OTHERS = ("others",)
diff --git a/backend/app/core/config.py b/backend/app/core/config.py
index 1e3a440c1c..54e0b18a46 100644
--- a/backend/app/core/config.py
+++ b/backend/app/core/config.py
@@ -1,16 +1,14 @@
import secrets
import warnings
-from typing import Annotated, Any, Literal
+from typing import Annotated, Any, ClassVar, Literal
from pydantic import (
AnyUrl,
BeforeValidator,
HttpUrl,
- PostgresDsn,
computed_field,
model_validator,
)
-from pydantic_core import MultiHostUrl
from pydantic_settings import BaseSettings, SettingsConfigDict
from typing_extensions import Self
@@ -30,10 +28,15 @@ class Settings(BaseSettings):
API_V1_STR: str = "/api/v1"
SECRET_KEY: str = secrets.token_urlsafe(32)
# 60 minutes * 24 hours * 8 days = 8 days
- ACCESS_TOKEN_EXPIRE_MINUTES: int = 60 * 24 * 8
+ ACCESS_TOKEN_EXPIRE_MINUTES: int = 60
DOMAIN: str = "localhost"
ENVIRONMENT: Literal["local", "staging", "production"] = "local"
+ # Add CLIENT_ID and CLIENT_SECRET
+ CLIENT_ID: str
+ CLIENT_SECRET: str
+ REFRESH_TOKEN_EXPIRE_DAYS: ClassVar[int] = 365 # Use ClassVar if it's a constant
+ ALGORITHM: str = "HS256"
@computed_field # type: ignore[prop-decorator]
@property
def server_host(self) -> str:
@@ -53,18 +56,15 @@ def server_host(self) -> str:
POSTGRES_USER: str
POSTGRES_PASSWORD: str = ""
POSTGRES_DB: str = ""
+ GOOGLE_API_KEY: str
+ GOOGLE_REVERSE_GEOCODE_URL: str
+ GOOGLE_PLACES_URL: str
+ GOOGLE_PLACE_DETAILS_URL: str
@computed_field # type: ignore[prop-decorator]
@property
- def SQLALCHEMY_DATABASE_URI(self) -> PostgresDsn:
- return MultiHostUrl.build(
- scheme="postgresql+psycopg",
- username=self.POSTGRES_USER,
- password=self.POSTGRES_PASSWORD,
- host=self.POSTGRES_SERVER,
- port=self.POSTGRES_PORT,
- path=self.POSTGRES_DB,
- )
+ def SQLALCHEMY_DATABASE_URI(self) -> str:
+ return f"postgresql://{self.POSTGRES_USER}:{self.POSTGRES_PASSWORD}@{self.POSTGRES_SERVER}:{self.POSTGRES_PORT}/{self.POSTGRES_DB}"
SMTP_TLS: bool = True
SMTP_SSL: bool = False
@@ -113,8 +113,10 @@ def _enforce_non_default_secrets(self) -> Self:
self._check_default_secret(
"FIRST_SUPERUSER_PASSWORD", self.FIRST_SUPERUSER_PASSWORD
)
+ print(f"FIRST_SUPERUSER_PASSWORD: {self.FIRST_SUPERUSER_PASSWORD}")
return self
-
-settings = Settings() # type: ignore
+print("About to initialize Settings...")
+settings = Settings()
+print("Settings initialized.")
diff --git a/backend/app/core/db.py b/backend/app/core/db.py
index d260a856d2..6b8554581e 100644
--- a/backend/app/core/db.py
+++ b/backend/app/core/db.py
@@ -1,34 +1,49 @@
-from sqlmodel import Session, create_engine, select
+from sqlmodel import Session, create_engine
-from app import crud
from app.core.config import settings
-from app.models import User, UserCreate
+print("SQLALCHEMY_DATABASE_URI : ", str(settings.SQLALCHEMY_DATABASE_URI))
engine = create_engine(str(settings.SQLALCHEMY_DATABASE_URI))
-
-
-# make sure all SQLModel models are imported (app.models) before initializing DB
-# otherwise, SQLModel might fail to initialize relationships properly
-# for more details: https://github.com/fastapi/full-stack-fastapi-template/issues/28
-
-
-def init_db(session: Session) -> None:
- # Tables should be created with Alembic migrations
- # But if you don't want to use migrations, create
- # the tables un-commenting the next lines
- # from sqlmodel import SQLModel
-
- # from app.core.engine import engine
- # This works because the models are already imported and registered from app.models
- # SQLModel.metadata.create_all(engine)
-
- user = session.exec(
- select(User).where(User.email == settings.FIRST_SUPERUSER)
- ).first()
- if not user:
- user_in = UserCreate(
- email=settings.FIRST_SUPERUSER,
- password=settings.FIRST_SUPERUSER_PASSWORD,
- is_superuser=True,
- )
- user = crud.create_user(session=session, user_create=user_in)
+print("engine created : ")
+
+connection = engine.connect()
+print("Connection successful!")
+connection.close()
+
+def init_db() -> None:
+ """
+ Initialize the database with the necessary default data.
+ Assumes that database schema is up-to-date due to Alembic migrations.
+ """
+ # Example: Create the superuser if it does not exist
+ with Session(engine) as session:
+ print("here")
+ # Check for existing superuser
+ # superuser = session.exec(
+ # select(UserBusiness).where(UserBusiness.email == settings.FIRST_SUPERUSER)
+ # ).first()
+ # print("heree")
+
+ # if not superuser:
+ # print("here1")
+ # user_in = UserBusiness(
+ # email=settings.FIRST_SUPERUSER,
+ # phone_number=None,
+ # is_active=True,
+ # is_superuser=True,
+ # full_name="Superuser",
+ # registration_date=datetime.utcnow()
+ # )
+ # print("here2")
+ # # Create superuser in the database
+ # session.add(user_in)
+ # print("here3")
+ # session.commit()
+ # print("here4")
+ # session.refresh(user_in)
+
+ # Other initial setup tasks can go here
+ # Example: Create default Nightclub, Foodcourt, etc.
+ # ...
+
+ print("Database initialization complete.")
diff --git a/backend/app/core/security.py b/backend/app/core/security.py
index 7aff7cfb32..cdae82e4cd 100644
--- a/backend/app/core/security.py
+++ b/backend/app/core/security.py
@@ -1,27 +1,68 @@
+import uuid # For generating unique token ID
from datetime import datetime, timedelta, timezone
-from typing import Any
import jwt
-from passlib.context import CryptContext
+from fastapi import HTTPException, status
from app.core.config import settings
+from app.models.auth import AccessToken, RefreshToken, TokenModel
-pwd_context = CryptContext(schemes=["bcrypt"], deprecated="auto")
+ALGORITHM = "HS256"
+def get_jwt_payload(token: str) -> TokenModel:
+ try:
+ # Decode the token using the secret key and algorithm
+ payload = jwt.decode(token, settings.SECRET_KEY, algorithms=[settings.ALGORITHM])
+ print('payload ', payload)
+ # Create and return a TokenModel instance with the decoded payload
+ return TokenModel(sub=payload.get("sub"), exp=payload.get("exp"))
-ALGORITHM = "HS256"
+ except jwt.ExpiredSignatureError:
+ # Handle expired token
+ raise HTTPException(
+ status_code=status.HTTP_401_UNAUTHORIZED,
+ detail="Token has expired"
+ )
+ except jwt.InvalidTokenError:
+ raise HTTPException(
+ status_code=status.HTTP_401_UNAUTHORIZED,
+ detail="Invalid token"
+ )
+ except Exception as e:
+ raise HTTPException(
+ status_code=status.HTTP_400_BAD_REQUEST,
+ detail=f"Token validation failed: {str(e)}"
+ )
+def create_access_token(subject: str, expires_delta: timedelta | None = None) -> AccessToken:
+ if expires_delta:
+ expire = datetime.now(timezone.utc) + expires_delta
+ else:
+ expire = datetime.now(timezone.utc) + timedelta(minutes=settings.ACCESS_TOKEN_EXPIRE_MINUTES)
-def create_access_token(subject: str | Any, expires_delta: timedelta) -> str:
- expire = datetime.now(timezone.utc) + expires_delta
- to_encode = {"exp": expire, "sub": str(subject)}
+ jti = str(uuid.uuid4())
+ to_encode = {"exp": expire, "sub": str(subject), "jti": jti}
encoded_jwt = jwt.encode(to_encode, settings.SECRET_KEY, algorithm=ALGORITHM)
- return encoded_jwt
+ # Create an instance of AccessToken with the encoded JWT and expiration time
+ access_token = AccessToken(
+ token=encoded_jwt,
+ expires_at=expire,
+ token_type="Bearer"
+ )
-def verify_password(plain_password: str, hashed_password: str) -> bool:
- return pwd_context.verify(plain_password, hashed_password)
+ return access_token
+
+def create_refresh_token(subject: str) -> RefreshToken:
+ expire = datetime.now(timezone.utc) + timedelta(days=settings.REFRESH_TOKEN_EXPIRE_DAYS)
+ jti = str(uuid.uuid4())
+ to_encode = {"sub": str(subject), "exp": expire, "jti": jti}
+ encoded_jwt = jwt.encode(to_encode, settings.SECRET_KEY, algorithm=ALGORITHM)
+ # Create an instance of RefreshToken with the encoded JWT and expiration time
+ refresh_token = RefreshToken(
+ token=encoded_jwt,
+ expires_at=expire
+ )
-def get_password_hash(password: str) -> str:
- return pwd_context.hash(password)
+ return refresh_token
diff --git a/backend/app/crud.py b/backend/app/crud.py
deleted file mode 100644
index 905bf48724..0000000000
--- a/backend/app/crud.py
+++ /dev/null
@@ -1,54 +0,0 @@
-import uuid
-from typing import Any
-
-from sqlmodel import Session, select
-
-from app.core.security import get_password_hash, verify_password
-from app.models import Item, ItemCreate, User, UserCreate, UserUpdate
-
-
-def create_user(*, session: Session, user_create: UserCreate) -> User:
- db_obj = User.model_validate(
- user_create, update={"hashed_password": get_password_hash(user_create.password)}
- )
- session.add(db_obj)
- session.commit()
- session.refresh(db_obj)
- return db_obj
-
-
-def update_user(*, session: Session, db_user: User, user_in: UserUpdate) -> Any:
- user_data = user_in.model_dump(exclude_unset=True)
- extra_data = {}
- if "password" in user_data:
- password = user_data["password"]
- hashed_password = get_password_hash(password)
- extra_data["hashed_password"] = hashed_password
- db_user.sqlmodel_update(user_data, update=extra_data)
- session.add(db_user)
- session.commit()
- session.refresh(db_user)
- return db_user
-
-
-def get_user_by_email(*, session: Session, email: str) -> User | None:
- statement = select(User).where(User.email == email)
- session_user = session.exec(statement).first()
- return session_user
-
-
-def authenticate(*, session: Session, email: str, password: str) -> User | None:
- db_user = get_user_by_email(session=session, email=email)
- if not db_user:
- return None
- if not verify_password(password, db_user.hashed_password):
- return None
- return db_user
-
-
-def create_item(*, session: Session, item_in: ItemCreate, owner_id: uuid.UUID) -> Item:
- db_item = Item.model_validate(item_in, update={"owner_id": owner_id})
- session.add(db_item)
- session.commit()
- session.refresh(db_item)
- return db_item
diff --git a/backend/app/email-templates/build/new_account.html b/backend/app/email-templates/build/new_account.html
deleted file mode 100644
index 344505033b..0000000000
--- a/backend/app/email-templates/build/new_account.html
+++ /dev/null
@@ -1,25 +0,0 @@
-
{{ project_name }} - New Account | Welcome to your new account! | Here are your account details: | Username: {{ username }} | Password: {{ password }} | | |
|
\ No newline at end of file
diff --git a/backend/app/email-templates/build/reset_password.html b/backend/app/email-templates/build/reset_password.html
deleted file mode 100644
index 4148a5b773..0000000000
--- a/backend/app/email-templates/build/reset_password.html
+++ /dev/null
@@ -1,25 +0,0 @@
-{{ project_name }} - Password Recovery | Hello {{ username }} | We've received a request to reset your password. You can do it by clicking the button below: | | Or copy and paste the following link into your browser: | | This password will expire in {{ valid_hours }} hours. | | If you didn't request a password recovery you can disregard this email. |
|
\ No newline at end of file
diff --git a/backend/app/email-templates/build/test_email.html b/backend/app/email-templates/build/test_email.html
deleted file mode 100644
index 04d0d85092..0000000000
--- a/backend/app/email-templates/build/test_email.html
+++ /dev/null
@@ -1,25 +0,0 @@
-{{ project_name }} | Test email for: {{ email }} | |
|
\ No newline at end of file
diff --git a/backend/app/email-templates/src/new_account.mjml b/backend/app/email-templates/src/new_account.mjml
deleted file mode 100644
index f41a3e3cf1..0000000000
--- a/backend/app/email-templates/src/new_account.mjml
+++ /dev/null
@@ -1,15 +0,0 @@
-
-
-
-
- {{ project_name }} - New Account
- Welcome to your new account!
- Here are your account details:
- Username: {{ username }}
- Password: {{ password }}
- Go to Dashboard
-
-
-
-
-
diff --git a/backend/app/email-templates/src/reset_password.mjml b/backend/app/email-templates/src/reset_password.mjml
deleted file mode 100644
index 743f5d77f4..0000000000
--- a/backend/app/email-templates/src/reset_password.mjml
+++ /dev/null
@@ -1,17 +0,0 @@
-
-
-
-
- {{ project_name }} - Password Recovery
- Hello {{ username }}
- We've received a request to reset your password. You can do it by clicking the button below:
- Reset password
- Or copy and paste the following link into your browser:
- {{ link }}
- This password will expire in {{ valid_hours }} hours.
-
- If you didn't request a password recovery you can disregard this email.
-
-
-
-
diff --git a/backend/app/email-templates/src/test_email.mjml b/backend/app/email-templates/src/test_email.mjml
deleted file mode 100644
index 45d58d6bac..0000000000
--- a/backend/app/email-templates/src/test_email.mjml
+++ /dev/null
@@ -1,11 +0,0 @@
-
-
-
-
- {{ project_name }}
- Test email for: {{ email }}
-
-
-
-
-
diff --git a/backend/app/initial_data.py b/backend/app/initial_data.py
index d806c3d381..43552c5299 100644
--- a/backend/app/initial_data.py
+++ b/backend/app/initial_data.py
@@ -1,16 +1,13 @@
import logging
-from sqlmodel import Session
-
-from app.core.db import engine, init_db
+from app.core.db import init_db
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)
def init() -> None:
- with Session(engine) as session:
- init_db(session)
+ init_db()
def main() -> None:
diff --git a/backend/app/main.py b/backend/app/main.py
index 4c252a1722..29a97ad94d 100644
--- a/backend/app/main.py
+++ b/backend/app/main.py
@@ -20,6 +20,8 @@ def custom_generate_unique_id(route: APIRoute) -> str:
generate_unique_id_function=custom_generate_unique_id,
)
+# app.mount("/static", StaticFiles(directory="app/static"), name="static")
+
# Set all CORS enabled origins
if settings.BACKEND_CORS_ORIGINS:
app.add_middleware(
diff --git a/backend/app/models.py b/backend/app/models.py
deleted file mode 100644
index 90ef5559e3..0000000000
--- a/backend/app/models.py
+++ /dev/null
@@ -1,114 +0,0 @@
-import uuid
-
-from pydantic import EmailStr
-from sqlmodel import Field, Relationship, SQLModel
-
-
-# Shared properties
-class UserBase(SQLModel):
- email: EmailStr = Field(unique=True, index=True, max_length=255)
- is_active: bool = True
- is_superuser: bool = False
- full_name: str | None = Field(default=None, max_length=255)
-
-
-# Properties to receive via API on creation
-class UserCreate(UserBase):
- password: str = Field(min_length=8, max_length=40)
-
-
-class UserRegister(SQLModel):
- email: EmailStr = Field(max_length=255)
- password: str = Field(min_length=8, max_length=40)
- full_name: str | None = Field(default=None, max_length=255)
-
-
-# Properties to receive via API on update, all are optional
-class UserUpdate(UserBase):
- email: EmailStr | None = Field(default=None, max_length=255) # type: ignore
- password: str | None = Field(default=None, min_length=8, max_length=40)
-
-
-class UserUpdateMe(SQLModel):
- full_name: str | None = Field(default=None, max_length=255)
- email: EmailStr | None = Field(default=None, max_length=255)
-
-
-class UpdatePassword(SQLModel):
- current_password: str = Field(min_length=8, max_length=40)
- new_password: str = Field(min_length=8, max_length=40)
-
-
-# Database model, database table inferred from class name
-class User(UserBase, table=True):
- id: uuid.UUID = Field(default_factory=uuid.uuid4, primary_key=True)
- hashed_password: str
- items: list["Item"] = Relationship(back_populates="owner", cascade_delete=True)
-
-
-# Properties to return via API, id is always required
-class UserPublic(UserBase):
- id: uuid.UUID
-
-
-class UsersPublic(SQLModel):
- data: list[UserPublic]
- count: int
-
-
-# Shared properties
-class ItemBase(SQLModel):
- title: str = Field(min_length=1, max_length=255)
- description: str | None = Field(default=None, max_length=255)
-
-
-# Properties to receive on item creation
-class ItemCreate(ItemBase):
- pass
-
-
-# Properties to receive on item update
-class ItemUpdate(ItemBase):
- title: str | None = Field(default=None, min_length=1, max_length=255) # type: ignore
-
-
-# Database model, database table inferred from class name
-class Item(ItemBase, table=True):
- id: uuid.UUID = Field(default_factory=uuid.uuid4, primary_key=True)
- title: str = Field(max_length=255)
- owner_id: uuid.UUID = Field(
- foreign_key="user.id", nullable=False, ondelete="CASCADE"
- )
- owner: User | None = Relationship(back_populates="items")
-
-
-# Properties to return via API, id is always required
-class ItemPublic(ItemBase):
- id: uuid.UUID
- owner_id: uuid.UUID
-
-
-class ItemsPublic(SQLModel):
- data: list[ItemPublic]
- count: int
-
-
-# Generic message
-class Message(SQLModel):
- message: str
-
-
-# JSON payload containing access token
-class Token(SQLModel):
- access_token: str
- token_type: str = "bearer"
-
-
-# Contents of JWT token
-class TokenPayload(SQLModel):
- sub: str | None = None
-
-
-class NewPassword(SQLModel):
- token: str
- new_password: str = Field(min_length=8, max_length=40)
diff --git a/backend/app/models/__init__.py b/backend/app/models/__init__.py
new file mode 100644
index 0000000000..b20db96ec2
--- /dev/null
+++ b/backend/app/models/__init__.py
@@ -0,0 +1,57 @@
+from sqlmodel import SQLModel
+
+# Import all your models here
+from .carousel_poster import CarouselPoster
+from .club_visit import ClubVisit
+from .event import Event
+from .event_booking import EventBooking
+from .event_offering import EventOffering
+from .group import Group
+from .group_wallet import GroupWallet
+from .group_wallet_topup import GroupWalletTopup
+from .menu import Menu
+from .order import NightclubOrder, QSROrder, RestaurantOrder
+from .order_item import OrderItem
+from .payment import (
+ PaymentEvent,
+ PaymentOrderNightclub,
+ PaymentOrderQSR,
+ PaymentOrderRestaurant,
+)
+from .pickup_location import PickupLocation
+from .qrcode import QRCode
+from .user import UserBusiness, UserPublic
+from .venue import QSR, Foodcourt, Nightclub, Restaurant
+
+# Make all models accessible when importing app.models
+__all__ = [
+ "SQLModel",
+ "ClubVisit",
+ "Event",
+ "EventBooking",
+ "EventOffering",
+ "Group",
+ "GroupWallet",
+ "GroupWalletTopup",
+ "Menu",
+ "NightclubOrder",
+ "RestaurantOrder",
+ "QSROrder",
+ "OrderItem",
+ "PickupLocation",
+ "UserBusiness",
+ "UserPublic",
+ "Nightclub",
+ "QSR",
+ "Restaurant",
+ "Foodcourt",
+ "PaymentOrderNightclub",
+ "PaymentOrderRestaurant",
+ "PaymentOrderQSR",
+ "PaymentEvent",
+ "QRCode",
+ "CarouselPoster",
+]
+
+
+print("target_metadata in init:", SQLModel.metadata.tables)
diff --git a/backend/app/models/auth.py b/backend/app/models/auth.py
new file mode 100644
index 0000000000..39b3286a07
--- /dev/null
+++ b/backend/app/models/auth.py
@@ -0,0 +1,76 @@
+import uuid
+from datetime import datetime, timezone
+
+from pydantic import EmailStr
+from sqlmodel import Field, SQLModel
+
+
+class TokenBlacklist(SQLModel, table=True):
+ id: uuid.UUID = Field(default_factory=uuid.uuid4, primary_key=True)
+ jti: str = Field(index=True, unique=True) # Store JWT ID (jti)
+ user_id: uuid.UUID = Field(nullable=False)
+ created_at: datetime = Field(
+ default_factory=lambda: datetime.now(timezone.utc), nullable=False
+ )
+
+
+class OtplessPhoneAuthDetails(SQLModel):
+ mode: str
+ phone_number: str
+ country_code: str
+ auth_state: str
+
+
+class OtplessEmailAuthDetails(SQLModel):
+ email: EmailStr
+ mode: str
+ auth_state: str
+
+
+class AuthenticationDetails(SQLModel):
+ phone: OtplessPhoneAuthDetails
+ email: OtplessEmailAuthDetails
+
+
+class OtplessVerifyTokenResponse(SQLModel):
+ name: str
+ email: EmailStr
+ first_name: str
+ last_name: str
+ family_name: str
+ phone_number: str
+ national_phone_number: str
+ country_code: str
+ email_verified: bool
+ auth_time: str
+ authentication_details: AuthenticationDetails
+
+
+class OtplessToken(SQLModel):
+ otpless_token: str
+
+
+class TokenModel(SQLModel):
+ sub: str
+ exp: int | None = None
+
+
+class RefreshTokenPayload(SQLModel):
+ refresh_token: str
+
+
+class AccessToken(SQLModel):
+ token: str
+ expires_at: datetime
+ token_type: str = "Bearer"
+
+
+class RefreshToken(SQLModel):
+ token: str
+ expires_at: datetime
+
+
+class UserAuthResponse(SQLModel):
+ access_token: AccessToken
+ refresh_token: RefreshToken
+ issued_at: datetime
diff --git a/backend/app/models/base_model.py b/backend/app/models/base_model.py
new file mode 100644
index 0000000000..427e48519f
--- /dev/null
+++ b/backend/app/models/base_model.py
@@ -0,0 +1,34 @@
+"""
+Base model class for all models in the application.
+"""
+
+from abc import ABC, abstractmethod
+from datetime import datetime, timezone
+
+from sqlmodel import Field, SQLModel
+
+
+class BaseTimeModel(SQLModel, ABC):
+ """
+ Base class for models that require timestamp fields.
+
+ Attributes:
+ created_at (Optional[datetime]): The timestamp when the model instance was created.
+ updated_at (Optional[datetime]): The timestamp when the model instance was last updated.
+ """
+
+ created_at: datetime | None = Field(
+ default_factory=lambda: datetime.now(timezone.utc), nullable=False
+ )
+ updated_at: datetime | None = Field(
+ default_factory=lambda: datetime.now(timezone.utc), nullable=False
+ )
+
+ @abstractmethod
+ def to_read_schema(self):
+ """Convert the model instance to its read schema representation."""
+
+ @classmethod
+ @abstractmethod
+ def from_create_schema(cls, schema):
+ """Create a model instance from the provided create schema."""
diff --git a/backend/app/models/carousel_poster.py b/backend/app/models/carousel_poster.py
new file mode 100644
index 0000000000..fbc1627a7f
--- /dev/null
+++ b/backend/app/models/carousel_poster.py
@@ -0,0 +1,50 @@
+import uuid
+from datetime import datetime
+from typing import TYPE_CHECKING, Optional
+
+from sqlmodel import Field, Relationship
+
+from app.models.base_model import BaseTimeModel
+from app.schema.carousel_poster import CarouselPosterCreate, CarouselPosterRead
+
+if TYPE_CHECKING:
+ from app.models.event import Event
+ from app.models.venue import Venue
+
+
+class CarouselPoster(BaseTimeModel, table=True):
+ __tablename__ = "carousel_poster"
+ id: uuid.UUID = Field(default_factory=uuid.uuid4, primary_key=True, index=True)
+ h3_index: str = Field(nullable=True, index=True)
+ image_url: str = Field(nullable=False)
+ deep_link: str = Field(nullable=False)
+ expires_at: datetime = Field(nullable=False)
+
+ # Foreign keys [Optional]
+ event_id: uuid.UUID | None = Field(default=None, foreign_key="event.id")
+ venue_id: uuid.UUID | None = Field(default=None, foreign_key="venue.id")
+
+ # Relationships [Optional]
+ event: Optional["Event"] = Relationship(back_populates="carousel_posters")
+ venue: Optional["Venue"] = Relationship(back_populates="carousel_posters")
+
+ @classmethod
+ def from_create_schema(cls, carousel_poster_create: CarouselPosterCreate):
+ return cls(
+ image_url=carousel_poster_create.image_url,
+ deep_link=carousel_poster_create.deep_link,
+ expires_at=carousel_poster_create.expires_at,
+ event_id=carousel_poster_create.event_id,
+ venue_id=carousel_poster_create.venue_id,
+ )
+
+ def to_read_schema(self) -> CarouselPosterRead:
+ return CarouselPosterRead(
+ courosel_id=self.id,
+ h3_index=self.h3_index,
+ image_url=self.image_url,
+ deep_link=self.deep_link,
+ expires_at=self.expires_at,
+ event_id=self.event_id,
+ venue_id=self.venue_id,
+ )
diff --git a/backend/app/models/club_visit.py b/backend/app/models/club_visit.py
new file mode 100644
index 0000000000..7b9a6b5a86
--- /dev/null
+++ b/backend/app/models/club_visit.py
@@ -0,0 +1,26 @@
+import uuid
+from datetime import datetime
+from typing import TYPE_CHECKING, Optional
+
+from sqlmodel import Field, Relationship, SQLModel
+
+if TYPE_CHECKING:
+ from app.models.group import Group
+ from app.models.user import UserPublic
+ from app.models.venue import Nightclub
+
+
+class ClubVisit(SQLModel, table=True):
+ id: uuid.UUID = Field(default_factory=uuid.uuid4, primary_key=True)
+ user_id: uuid.UUID | None = Field(foreign_key="user_public.id", nullable=False)
+ group_id: uuid.UUID | None = Field(foreign_key="group.id", nullable=True)
+ nightclub_id: uuid.UUID | None = Field(foreign_key="nightclub.id", nullable=False)
+ entry_time: datetime = Field(nullable=False)
+ exit_time: datetime | None = Field(nullable=True)
+ cover_charge: float | None = Field(nullable=True)
+ total_bill: float | None = Field(nullable=True)
+
+ # Relationships
+ user: Optional["UserPublic"] = Relationship(back_populates="club_visits")
+ group: Optional["Group"] = Relationship(back_populates="club_visits")
+ nightclub: Optional["Nightclub"] = Relationship(back_populates="club_visits")
diff --git a/backend/app/models/event.py b/backend/app/models/event.py
new file mode 100644
index 0000000000..15e9a50a68
--- /dev/null
+++ b/backend/app/models/event.py
@@ -0,0 +1,30 @@
+import uuid
+from datetime import datetime
+from typing import TYPE_CHECKING, Optional
+
+from sqlmodel import Field, Relationship, SQLModel
+
+if TYPE_CHECKING:
+ from app.models.carousel_poster import CarouselPoster
+ from app.models.event_booking import EventBooking
+ from app.models.event_offering import EventOffering
+ from app.models.venue import Venue
+
+
+
+
+class Event(SQLModel, table=True):
+ id: uuid.UUID = Field(default_factory=uuid.uuid4, primary_key=True, index=True)
+ venue_id: uuid.UUID = Field(foreign_key="venue.id", nullable=False)
+ title: str = Field(nullable=False)
+ start_time: datetime = Field(nullable=False)
+ end_time: datetime = Field(nullable=False)
+ image_url: str | None = Field(nullable=True)
+ age_restriction: int | None = Field(nullable=True)
+ dress_code: str | None = Field(nullable=True)
+
+ # Relationships
+ venue: Optional["Venue"] = Relationship(back_populates="events")
+ offerings: list["EventOffering"] = Relationship(back_populates="event")
+ event_bookings: list["EventBooking"] = Relationship(back_populates="event")
+ carousel_posters: list["CarouselPoster"] | None = Relationship(back_populates="event")
\ No newline at end of file
diff --git a/backend/app/models/event_booking.py b/backend/app/models/event_booking.py
new file mode 100644
index 0000000000..24e456d232
--- /dev/null
+++ b/backend/app/models/event_booking.py
@@ -0,0 +1,32 @@
+import uuid
+from datetime import datetime
+from typing import TYPE_CHECKING, Optional
+
+from sqlmodel import Field, Relationship, SQLModel
+
+if TYPE_CHECKING:
+ from app.models.event import Event
+ from app.models.event_offering import EventOffering
+ from app.models.payment import PaymentEvent
+ from app.models.user import UserPublic
+
+
+class EventBooking(SQLModel, table=True):
+ __tablename__ = "event_booking"
+
+ id: uuid.UUID = Field(default_factory=uuid.uuid4, primary_key=True)
+ user_id: uuid.UUID | None = Field(foreign_key="user_public.id", nullable=False)
+ event_id: uuid.UUID | None = Field(foreign_key="event.id", nullable=False)
+ booking_time: datetime = Field(nullable=False)
+ total_amount: float = Field(nullable=False)
+ status: str = Field(nullable=False)
+
+ # Relationships
+ user: Optional["UserPublic"] = Relationship(back_populates="event_bookings")
+ event: Optional["Event"] = Relationship(back_populates="event_bookings")
+ payment: Optional["PaymentEvent"] = Relationship(
+ back_populates="event_booking", sa_relationship_kwargs={"uselist": False}
+ )
+ event_offerings: list["EventOffering"] = Relationship(
+ back_populates="event_booking"
+ )
diff --git a/backend/app/models/event_offering.py b/backend/app/models/event_offering.py
new file mode 100644
index 0000000000..f7466ba95f
--- /dev/null
+++ b/backend/app/models/event_offering.py
@@ -0,0 +1,29 @@
+import uuid
+from typing import TYPE_CHECKING, Optional
+
+from sqlmodel import Field, Relationship, SQLModel
+
+if TYPE_CHECKING:
+ from app.models.event import Event
+ from app.models.event_booking import EventBooking
+
+
+# Stag, couple etc
+class EventOffering(SQLModel, table=True):
+ __tablename__ = "event_offering"
+ id: uuid.UUID = Field(default_factory=uuid.uuid4, primary_key=True, index=True)
+ event_id: uuid.UUID = Field(foreign_key="event.id", nullable=False)
+ event_booking_id: uuid.UUID = Field(foreign_key="event_booking.id", nullable=False)
+ offering_type: str = Field(nullable=False)
+ description: str = Field(nullable=False)
+ price: float = Field(nullable=False)
+ total_guests_per_pass: int = Field(nullable=False)
+ cover_charge: float | None = Field(nullable=True)
+ additional_charges: float | None = Field(nullable=True)
+ availability: int = Field(nullable=False)
+
+ # Relationships
+ event: Optional["Event"] = Relationship(back_populates="offerings")
+ event_booking: Optional["EventBooking"] = Relationship(
+ back_populates="event_offerings"
+ )
diff --git a/backend/app/models/group.py b/backend/app/models/group.py
new file mode 100644
index 0000000000..8c19fbe24c
--- /dev/null
+++ b/backend/app/models/group.py
@@ -0,0 +1,48 @@
+import uuid
+from datetime import datetime, timezone
+from typing import TYPE_CHECKING, Optional
+
+from sqlmodel import Field, Relationship, SQLModel
+
+if TYPE_CHECKING:
+ from app.models.club_visit import ClubVisit
+ from app.models.group_wallet import GroupWallet
+ from app.models.order import NightclubOrder
+ from app.models.user import UserPublic
+ from app.models.venue import Nightclub
+
+
+class GroupMembers(SQLModel, table=True):
+ group_id: uuid.UUID = Field(foreign_key="group.id", primary_key=True)
+ user_id: uuid.UUID = Field(foreign_key="user_public.id", primary_key=True)
+
+
+class GroupNightclubOrderLink(SQLModel, table=True):
+ __tablename__ = "group_nightclub_order_link"
+
+ group_id: uuid.UUID = Field(foreign_key="group.id", primary_key=True)
+ nightclub_order_id: uuid.UUID = Field(
+ foreign_key="nightclub_order.id", primary_key=True
+ )
+
+
+class Group(SQLModel, table=True):
+ __tablename__ = "group"
+
+ id: uuid.UUID = Field(default_factory=uuid.uuid4, primary_key=True, index=True)
+ nightclub_id: uuid.UUID | None = Field(foreign_key="nightclub.id")
+ created_at: datetime = Field(default=datetime.now(timezone.utc))
+ admin_user_id: uuid.UUID = Field(foreign_key="user_public.id", nullable=False)
+ table_number: str | None = Field(default=None)
+
+ # Relationships
+ admin_user: Optional["UserPublic"] = Relationship(back_populates="managed_groups")
+ wallet: Optional["GroupWallet"] = Relationship(back_populates="group")
+ members: list["UserPublic"] = Relationship(
+ back_populates="groups", link_model=GroupMembers
+ )
+ club_visits: list["ClubVisit"] = Relationship(back_populates="group")
+ nightclub_orders: list["NightclubOrder"] = Relationship(
+ back_populates="groups", link_model=GroupNightclubOrderLink
+ )
+ nightclubs: list["Nightclub"] = Relationship(back_populates="group")
diff --git a/backend/app/models/group_wallet.py b/backend/app/models/group_wallet.py
new file mode 100644
index 0000000000..43565545fb
--- /dev/null
+++ b/backend/app/models/group_wallet.py
@@ -0,0 +1,20 @@
+import uuid
+from typing import TYPE_CHECKING, Optional
+
+from sqlmodel import Field, Relationship, SQLModel
+
+if TYPE_CHECKING:
+ from app.models.group import Group
+ from app.models.group_wallet_topup import GroupWalletTopup
+
+
+# (TODO) Added another model : Group wallet transactions
+class GroupWallet(SQLModel, table=True):
+ __tablename__ = "group_wallet"
+ id: uuid.UUID = Field(default_factory=uuid.uuid4, primary_key=True, index=True)
+ group_id: uuid.UUID = Field(foreign_key="group.id", nullable=False, unique=True)
+ balance: float = Field(default=0.0, nullable=False)
+
+ # Relationships
+ group: Optional["Group"] = Relationship(back_populates="wallet")
+ topups: list["GroupWalletTopup"] = Relationship(back_populates="group_wallet")
diff --git a/backend/app/models/group_wallet_topup.py b/backend/app/models/group_wallet_topup.py
new file mode 100644
index 0000000000..b73e697ba0
--- /dev/null
+++ b/backend/app/models/group_wallet_topup.py
@@ -0,0 +1,18 @@
+import uuid
+from datetime import datetime
+from typing import TYPE_CHECKING, Optional
+
+from sqlmodel import Field, Relationship, SQLModel
+
+if TYPE_CHECKING:
+ from app.models.group_wallet import GroupWallet
+
+
+class GroupWalletTopup(SQLModel, table=True):
+ id: uuid.UUID = Field(default_factory=uuid.uuid4, primary_key=True, index=True)
+ group_wallet_id: uuid.UUID = Field(foreign_key="group_wallet.id", nullable=False)
+ amount: float = Field(nullable=False)
+ topup_time: datetime = Field(nullable=False)
+
+ # Relationships
+ group_wallet: Optional["GroupWallet"] = Relationship(back_populates="topups")
diff --git a/backend/app/models/menu.py b/backend/app/models/menu.py
new file mode 100644
index 0000000000..f1017dce68
--- /dev/null
+++ b/backend/app/models/menu.py
@@ -0,0 +1,168 @@
+import uuid
+from typing import TYPE_CHECKING, Optional
+
+from sqlmodel import Field, Relationship
+
+from app.models.base_model import BaseTimeModel
+
+if TYPE_CHECKING:
+ from app.models.venue import Venue
+
+from app.schema.menu import (
+ MenuCategoryCreate,
+ MenuCategoryRead,
+ MenuCreate,
+ MenuItemCreate,
+ MenuItemRead,
+ MenuRead,
+ MenuSubCategoryCreate,
+ MenuSubCategoryRead,
+)
+
+
+class MenuItem(BaseTimeModel, table=True):
+ __tablename__ = "menu_item"
+ id: uuid.UUID = Field(default_factory=uuid.uuid4, primary_key=True, index=True)
+ subcategory_id: uuid.UUID = Field(foreign_key="menu_subcategory.id", nullable=False)
+ name: str = Field(nullable=False)
+ price: float = Field(nullable=False)
+ description: str | None = Field(default=None)
+ image_url: str | None = Field(default=None)
+ is_veg: bool | None = Field(default=None)
+ ingredients: str | None = Field(default=None)
+ abv: float | None = Field(default=None)
+ ibu: int | None = Field(default=None)
+
+ # Relationships
+ subcategory: Optional["MenuSubCategory"] = Relationship(back_populates="menu_items")
+
+ @classmethod
+ def from_create_schema(cls, schema: MenuItemCreate) -> "MenuItem":
+ return cls(
+ subcategory_id=schema.subcategory_id,
+ name=schema.name,
+ price=schema.price,
+ description=schema.description,
+ image_url=schema.image_url,
+ is_veg=schema.is_veg,
+ ingredients=schema.ingredients,
+ abv=schema.abv,
+ ibu=schema.ibu,
+ )
+
+ @classmethod
+ def to_read_schema(self) -> MenuItemRead:
+ return MenuItemRead(
+ item_id=self.id,
+ subcategory_id=self.subcategory_id,
+ name=self.name,
+ price=self.price,
+ description=self.description,
+ image_url=self.image_url,
+ is_veg=self.is_veg,
+ ingredients=self.ingredients,
+ abv=self.abv,
+ ibu=self.ibu,
+ )
+
+
+#########################################################################################################
+
+
+class MenuSubCategory(BaseTimeModel, table=True):
+ __tablename__ = "menu_subcategory"
+ id: uuid.UUID = Field(default_factory=uuid.uuid4, primary_key=True, index=True)
+ category_id: uuid.UUID = Field(foreign_key="menu_category.id", nullable=False)
+ name: str = Field(nullable=False)
+ is_alcoholic: bool = Field(default=False)
+
+ # Relationships
+ category: "MenuCategory" = Relationship(back_populates="sub_categories")
+ menu_items: list["MenuItem"] = Relationship(back_populates="subcategory")
+
+ @classmethod
+ def from_create_schema(cls, schema: "MenuSubCategoryCreate") -> "MenuSubCategory":
+ return cls(
+ name=schema.name,
+ category_id=schema.category_id,
+ is_alcoholic=schema.is_alcoholic, # Include is_alcoholic in the model
+ )
+
+ @classmethod
+ def to_read_schema(self) -> MenuSubCategoryRead:
+ return MenuSubCategoryRead(
+ subcategory_id=self.id,
+ category_id=self.category_id,
+ name=self.name,
+ is_alcoholic=self.is_alcoholic,
+ menu_items=[item.to_read_schema() for item in self.menu_items],
+ )
+
+
+#########################################################################################################
+
+
+class MenuCategory(BaseTimeModel, table=True):
+ __tablename__ = "menu_category"
+ id: uuid.UUID = Field(default_factory=uuid.uuid4, primary_key=True, index=True)
+ menu_id: uuid.UUID = Field(foreign_key="menu.id", nullable=False)
+ name: str = Field(nullable=False)
+
+ # Relationships
+ menu: "Menu" = Relationship(back_populates="categories")
+ sub_categories: list["MenuSubCategory"] = Relationship(back_populates="category")
+
+ @classmethod
+ def from_create_schema(cls, schema: MenuCategoryCreate) -> "MenuCategory":
+ return cls(name=schema.name, menu_id=schema.menu_id)
+
+ @classmethod
+ def to_read_schema(cls, self) -> MenuCategoryRead:
+ return MenuCategoryRead(
+ category_id=self.id,
+ menu_id=self.menu_id,
+ name=self.name,
+ sub_categories=[
+ subcategory.to_read_schema() for subcategory in self.sub_categories
+ ],
+ )
+
+
+#########################################################################################################
+
+
+class Menu(BaseTimeModel, table=True):
+ __tablename__ = "menu"
+ id: uuid.UUID = Field(default_factory=uuid.uuid4, primary_key=True, index=True)
+ name: str = Field(nullable=False)
+ description: str | None = Field(default=None)
+ menu_type: str | None = Field(default=None) # Type of menu (e.g., "Food", "Drink")
+ venue_id: uuid.UUID = Field(foreign_key="venue.id", nullable=False)
+
+ # Relationships
+ categories: list["MenuCategory"] = Relationship(back_populates="menu")
+ venue: "Venue" = Relationship(back_populates="menu")
+
+ @classmethod
+ def from_create_schema(cls, schema: MenuCreate) -> "Menu":
+ return cls(
+ name=schema.name,
+ description=schema.description,
+ menu_type=schema.menu_type,
+ venue_id=schema.venue_id,
+ )
+
+ def to_read_schema(self) -> MenuRead:
+ return MenuRead(
+ menu_id=self.id,
+ name=self.name,
+ description=self.description,
+ menu_type=self.menu_type,
+ venue_id=self.venue_id,
+ categories=[
+ MenuCategory.to_read_schema(category) for category in self.categories
+ ],
+ )
+
+
+#########################################################################################################
diff --git a/backend/app/models/order.py b/backend/app/models/order.py
new file mode 100644
index 0000000000..9a45eb4789
--- /dev/null
+++ b/backend/app/models/order.py
@@ -0,0 +1,91 @@
+import uuid
+from datetime import datetime
+from typing import TYPE_CHECKING, Optional
+
+from sqlmodel import Field, Relationship, SQLModel
+
+from app.models.group import GroupNightclubOrderLink
+
+if TYPE_CHECKING:
+ from app.models.group import Group
+ from app.models.order_item import OrderItem
+ from app.models.payment import (
+ PaymentOrderNightclub,
+ PaymentOrderQSR,
+ PaymentOrderRestaurant,
+ )
+ from app.models.pickup_location import PickupLocation
+ from app.models.user import UserPublic
+ from app.models.venue import QSR, Nightclub, Restaurant
+
+
+class OrderBase(SQLModel):
+ user_id: uuid.UUID = Field(foreign_key="user_public.id")
+ pickup_location_id: uuid.UUID | None = Field(
+ default=None, foreign_key="pickup_location.id"
+ )
+ note: str | None = Field(nullable=True)
+ order_time: datetime = Field(nullable=False)
+ total_amount: float = Field(nullable=False)
+ taxes_and_charges: float | None = Field(default=None)
+ cover_charge_used: float | None = Field(default=None)
+ status: str = Field(nullable=False)
+ service_type: str | None = Field(default=None)
+
+
+class NightclubOrder(OrderBase, table=True):
+ __tablename__ = "nightclub_order"
+
+ id: uuid.UUID = Field(default_factory=uuid.uuid4, primary_key=True, index=True)
+ venue_id: uuid.UUID | None = Field(default=None, foreign_key="nightclub.id")
+ payment_id: uuid.UUID | None = Field(
+ default=None, foreign_key="payment_source_nightclub.id"
+ )
+ pickup_location_id: uuid.UUID | None = Field(
+ default=None, foreign_key="pickup_location.id"
+ )
+ # Relationships
+ user: Optional["UserPublic"] = Relationship(back_populates="nightclub_orders")
+ nightclub: Optional["Nightclub"] = Relationship(back_populates="orders")
+ pickup_location: Optional["PickupLocation"] = Relationship(back_populates="orders")
+ payment: Optional["PaymentOrderNightclub"] = Relationship(
+ back_populates="order", sa_relationship_kwargs={"uselist": False}
+ )
+ groups: list["Group"] = Relationship(
+ back_populates="nightclub_orders", link_model=GroupNightclubOrderLink
+ ) # Many-to-many
+ order_items: list["OrderItem"] = Relationship(back_populates="nightclub_order")
+
+
+class RestaurantOrder(OrderBase, table=True):
+ __tablename__ = "restaurant_order"
+
+ id: uuid.UUID = Field(default_factory=uuid.uuid4, primary_key=True, index=True)
+ venue_id: uuid.UUID | None = Field(default=None, foreign_key="restaurant.id")
+ payment_id: uuid.UUID | None = Field(
+ default=None, foreign_key="payment_source_restaurant.id"
+ )
+
+ # Relationships
+ user: Optional["UserPublic"] = Relationship(back_populates="restaurant_orders")
+ restaurant: Optional["Restaurant"] = Relationship(back_populates="orders")
+ payment: Optional["PaymentOrderRestaurant"] = Relationship(
+ back_populates="order", sa_relationship_kwargs={"uselist": False}
+ )
+ order_items: list["OrderItem"] = Relationship(back_populates="restaurant_order")
+
+
+class QSROrder(OrderBase, table=True):
+ __tablename__ = "qsr_order"
+
+ id: uuid.UUID = Field(default_factory=uuid.uuid4, primary_key=True, index=True)
+ venue_id: uuid.UUID = Field(default=None, foreign_key="qsr.id")
+ payment_id: uuid.UUID = Field(default=None, foreign_key="payment_source_qsr.id")
+
+ # Relationships
+ user: Optional["UserPublic"] = Relationship(back_populates="qsr_orders")
+ qsr: Optional["QSR"] = Relationship(back_populates="orders")
+ payment: Optional["PaymentOrderQSR"] = Relationship(
+ back_populates="order", sa_relationship_kwargs={"uselist": False}
+ )
+ order_items: list["OrderItem"] = Relationship(back_populates="qsr_order")
diff --git a/backend/app/models/order_item.py b/backend/app/models/order_item.py
new file mode 100644
index 0000000000..56d9998487
--- /dev/null
+++ b/backend/app/models/order_item.py
@@ -0,0 +1,29 @@
+import uuid
+from typing import TYPE_CHECKING, Optional
+
+from sqlmodel import Field, Relationship, SQLModel
+
+if TYPE_CHECKING:
+ from app.models.order import NightclubOrder, QSROrder, RestaurantOrder
+
+
+class OrderItem(SQLModel, table=True):
+ id: uuid.UUID = Field(default_factory=uuid.uuid4, primary_key=True, index=True)
+ nightclub_order_id: uuid.UUID | None = Field(
+ default=None, foreign_key="nightclub_order.id"
+ )
+ restaurant_order_id: uuid.UUID | None = Field(
+ default=None, foreign_key="restaurant_order.id"
+ )
+ qsr_order_id: uuid.UUID | None = Field(default=None, foreign_key="qsr_order.id")
+ item_id: uuid.UUID = Field(foreign_key="menu_item.id", nullable=False)
+ quantity: int = Field(nullable=False)
+
+ # Relationships
+ nightclub_order: Optional["NightclubOrder"] = Relationship(
+ back_populates="order_items"
+ )
+ restaurant_order: Optional["RestaurantOrder"] = Relationship(
+ back_populates="order_items"
+ )
+ qsr_order: Optional["QSROrder"] = Relationship(back_populates="order_items")
diff --git a/backend/app/models/payment.py b/backend/app/models/payment.py
new file mode 100644
index 0000000000..341e1b5bea
--- /dev/null
+++ b/backend/app/models/payment.py
@@ -0,0 +1,64 @@
+import uuid
+from datetime import datetime
+from typing import TYPE_CHECKING, Optional
+
+if TYPE_CHECKING:
+ from app.models.event_booking import EventBooking
+ from app.models.order import NightclubOrder, QSROrder, RestaurantOrder
+ from app.models.user import UserPublic
+from sqlmodel import Field, Relationship, SQLModel
+
+
+class PaymentBase(SQLModel):
+ user_id: uuid.UUID = Field(foreign_key="user_public.id", nullable=False)
+ source_type: str = Field(nullable=False) # Changed to str
+ gateway_transaction_id: uuid.UUID | None = Field(default=None)
+ payment_time: datetime = Field(nullable=False)
+ amount: float = Field(nullable=False)
+ status: str = Field(nullable=False) # e.g., Paid, Pending, Failed
+ source_type: str = Field(nullable=False)
+
+
+class PaymentOrderNightclub(PaymentBase, table=True):
+ __tablename__ = "payment_source_nightclub"
+ id: uuid.UUID = Field(default_factory=uuid.uuid4, primary_key=True, index=True)
+ retry_count: int = Field(default=0)
+ last_attempt_time: datetime | None = Field(default=None)
+ order: "NightclubOrder" = Relationship(
+ back_populates="payment", sa_relationship_kwargs={"uselist": False}
+ )
+ user: Optional["UserPublic"] = Relationship(back_populates="nightclub_payments")
+
+
+class PaymentOrderQSR(PaymentBase, table=True):
+ __tablename__ = "payment_source_qsr"
+ id: uuid.UUID = Field(default_factory=uuid.uuid4, primary_key=True, index=True)
+ retry_count: int = Field(default=0)
+ last_attempt_time: datetime | None = Field(default=None)
+ order: "QSROrder" = Relationship(
+ back_populates="payment", sa_relationship_kwargs={"uselist": False}
+ )
+ user: Optional["UserPublic"] = Relationship(back_populates="qsr_payments")
+
+
+class PaymentOrderRestaurant(PaymentBase, table=True):
+ __tablename__ = "payment_source_restaurant"
+ id: uuid.UUID = Field(default_factory=uuid.uuid4, primary_key=True, index=True)
+ retry_count: int = Field(default=0)
+ last_attempt_time: datetime | None = Field(default=None)
+ order: "RestaurantOrder" = Relationship(
+ back_populates="payment", sa_relationship_kwargs={"uselist": False}
+ )
+ user: Optional["UserPublic"] = Relationship(back_populates="restaurant_payments")
+
+
+class PaymentEvent(PaymentBase, table=True):
+ __tablename__ = "payment_event"
+ id: uuid.UUID = Field(default_factory=uuid.uuid4, primary_key=True, index=True)
+ event_booking_id: uuid.UUID | None = Field(
+ default=None, foreign_key="event_booking.id"
+ )
+ event_booking: Optional["EventBooking"] = Relationship(
+ back_populates="payment", sa_relationship_kwargs={"uselist": False}
+ )
+ user: Optional["UserPublic"] = Relationship(back_populates="event_payments")
diff --git a/backend/app/models/pickup_location.py b/backend/app/models/pickup_location.py
new file mode 100644
index 0000000000..31b4ac345a
--- /dev/null
+++ b/backend/app/models/pickup_location.py
@@ -0,0 +1,22 @@
+import uuid
+from typing import TYPE_CHECKING, Optional
+
+if TYPE_CHECKING:
+ from app.models.order import NightclubOrder
+ from app.models.venue import Venue
+from sqlmodel import Field, Relationship, SQLModel
+
+
+class PickupLocation(SQLModel, table=True):
+ __tablename__ = "pickup_location"
+
+ id: uuid.UUID = Field(default_factory=uuid.uuid4, primary_key=True, index=True)
+ venue_id: uuid.UUID = Field(foreign_key="venue.id", nullable=False)
+ name: str = Field(nullable=False)
+ description: str | None = Field(default=None)
+
+ # Relationships
+ orders: list["NightclubOrder"] = Relationship(back_populates="pickup_location")
+
+ # Optionally, if you have a specific type of venue for PickupLocation
+ venue: Optional["Venue"] = Relationship(back_populates="pickup_locations")
diff --git a/backend/app/models/qrcode.py b/backend/app/models/qrcode.py
new file mode 100644
index 0000000000..4fca5dd7af
--- /dev/null
+++ b/backend/app/models/qrcode.py
@@ -0,0 +1,31 @@
+import uuid
+from typing import TYPE_CHECKING, Optional
+
+if TYPE_CHECKING:
+ from app.models.venue import Venue
+from sqlmodel import Field, Relationship
+
+from app.models.base_model import BaseTimeModel
+from app.schema.qrcode import QRCodeCreate, QRCodeRead
+
+
+class QRCode(BaseTimeModel, table=True):
+ __tablename__ = "qrcode"
+
+ id: uuid.UUID | None = Field(default_factory=uuid.uuid4, primary_key=True)
+ venue_id: uuid.UUID | None = Field(default=None, foreign_key="venue.id")
+ table_number: str | None = Field(default=None, nullable=True)
+
+ # Relationships
+ venue: Optional["Venue"] = Relationship(back_populates="qrcode")
+
+ @classmethod
+ def from_create_schema(cls, schema: "QRCodeCreate") -> "QRCode":
+ return cls(venue_id=schema.venue_id, table_number=schema.table_number)
+
+ def to_read_schema(self) -> "QRCodeRead":
+ return QRCodeRead(
+ id=self.id, # Access the actual value of the id
+ venue_id=self.venue_id, # Access the actual value of venue_id
+ table_number=self.table_number, # Provide a default empty string if None
+ )
diff --git a/backend/app/models/user.py b/backend/app/models/user.py
new file mode 100644
index 0000000000..0877cbd102
--- /dev/null
+++ b/backend/app/models/user.py
@@ -0,0 +1,131 @@
+import uuid
+from datetime import datetime
+from typing import TYPE_CHECKING
+
+from pydantic import EmailStr
+from sqlmodel import Field, Relationship, SQLModel
+
+from app.constants import Gender
+from app.models.base_model import BaseTimeModel
+from app.models.club_visit import ClubVisit
+from app.models.event_booking import EventBooking
+from app.models.group import GroupMembers
+from app.models.order import NightclubOrder, QSROrder, RestaurantOrder
+from app.models.payment import (
+ PaymentEvent,
+ PaymentOrderNightclub,
+ PaymentOrderQSR,
+ PaymentOrderRestaurant,
+)
+from app.models.venue import Venue
+
+if TYPE_CHECKING:
+ from app.models.group import Group
+
+from app.schema.user import (
+ UserBusinessCreate,
+ UserBusinessRead,
+ UserPublicCreate,
+ UserPublicRead,
+)
+
+
+# Shared properties
+class UserBase(BaseTimeModel):
+ is_active: bool = True
+ is_superuser: bool = False
+ full_name: str | None = Field(default=None, max_length=255)
+ refresh_token: str = Field(nullable=True)
+
+
+class UserPublic(UserBase, table=True):
+ __tablename__ = "user_public"
+ id: uuid.UUID = Field(default_factory=uuid.uuid4, primary_key=True, index=True)
+ phone_number: str | None = Field(
+ unique=True, nullable=False, index=True, default=None
+ )
+ email: EmailStr | None = Field(default=None)
+ date_of_birth: datetime | None = Field(default=None)
+ gender: Gender | None = Field(default=None)
+ profile_picture: str | None = Field(default=None)
+ preferences: str | None = Field(default=None)
+
+ # Relationships
+ nightclub_orders: list["NightclubOrder"] = Relationship(back_populates="user")
+ restaurant_orders: list["RestaurantOrder"] = Relationship(back_populates="user")
+ qsr_orders: list["QSROrder"] = Relationship(back_populates="user")
+
+ club_visits: list["ClubVisit"] = Relationship(back_populates="user")
+ event_bookings: list["EventBooking"] = Relationship(back_populates="user")
+ groups: list["Group"] = Relationship(
+ back_populates="members", link_model=GroupMembers
+ )
+ managed_groups: list["Group"] = Relationship(back_populates="admin_user")
+ nightclub_payments: list["PaymentOrderNightclub"] = Relationship(
+ back_populates="user"
+ )
+ qsr_payments: list["PaymentOrderQSR"] = Relationship(back_populates="user")
+ restaurant_payments: list["PaymentOrderRestaurant"] = Relationship(
+ back_populates="user"
+ )
+ event_payments: list["PaymentEvent"] = Relationship(back_populates="user")
+
+ @classmethod
+ def from_create_schema(cls, schema: UserPublicCreate) -> "UserPublic":
+ return cls(
+ full_name=schema.full_name,
+ phone_number=schema.phone_number,
+ email=schema.email,
+ date_of_birth=schema.date_of_birth,
+ gender=schema.gender,
+ profile_picture=schema.profile_picture,
+ preferences=schema.preferences,
+ )
+
+ def to_read_schema(self) -> UserPublicRead:
+ return UserPublicRead(
+ id=self.id,
+ full_name=self.full_name,
+ phone_number=self.phone_number,
+ email=self.email,
+ date_of_birth=self.date_of_birth,
+ gender=self.gender,
+ profile_picture=self.profile_picture,
+ preferences=self.preferences,
+ )
+
+
+class UserVenueAssociation(SQLModel, table=True):
+ __tablename__ = "user_venue_association"
+ id: uuid.UUID = Field(default_factory=uuid.uuid4, primary_key=True)
+ user_id: uuid.UUID = Field(foreign_key="user_business.id", primary_key=True)
+ venue_id: uuid.UUID = Field(foreign_key="venue.id", primary_key=True)
+ role: str | None = Field(default=None) # e.g., 'manager', 'owner'
+
+ user: "UserBusiness" = Relationship(back_populates="venues_association")
+ venue: "Venue" = Relationship(back_populates="managing_users")
+
+
+class UserBusiness(UserBase, table=True):
+ __tablename__ = "user_business"
+ id: uuid.UUID = Field(default_factory=uuid.uuid4, primary_key=True, index=True)
+ email: EmailStr = Field(unique=True, nullable=False, index=True, max_length=255)
+ phone_number: str | None = Field(default=None)
+ # Relationships
+ venues_association: list[UserVenueAssociation] = Relationship(back_populates="user")
+
+ @classmethod
+ def from_create_schema(cls, schema: UserBusinessCreate) -> "UserBusiness":
+ return cls(
+ full_name=schema.full_name,
+ email=schema.email,
+ phone_number=schema.phone_number,
+ )
+
+ def to_read_schema(self) -> UserBusinessRead:
+ return UserBusinessRead(
+ id=self.id,
+ full_name=self.full_name,
+ email=self.email,
+ phone_number=self.phone_number,
+ )
diff --git a/backend/app/models/venue.py b/backend/app/models/venue.py
new file mode 100644
index 0000000000..31c89195c0
--- /dev/null
+++ b/backend/app/models/venue.py
@@ -0,0 +1,233 @@
+import uuid
+from datetime import time
+from typing import TYPE_CHECKING, Optional
+
+from sqlmodel import Field, Relationship
+
+from app.models.base_model import BaseTimeModel
+
+if TYPE_CHECKING:
+ from app.models.carousel_poster import CarouselPoster
+ from app.models.club_visit import ClubVisit
+ from app.models.event import Event
+ from app.models.group import Group
+ from app.models.menu import Menu
+ from app.models.order import NightclubOrder, QSROrder, RestaurantOrder
+ from app.models.pickup_location import PickupLocation
+ from app.models.qrcode import QRCode
+ from app.models.user import UserVenueAssociation
+
+from app.schema.venue import (
+ FoodcourtCreate,
+ FoodcourtRead,
+ NightclubCreate,
+ NightclubRead,
+ QSRCreate,
+ QSRRead,
+ RestaurantCreate,
+ RestaurantRead,
+ VenueCreate,
+ VenueRead,
+)
+
+
+class Venue(BaseTimeModel, table=True):
+ __tablename__ = "venue"
+ id: uuid.UUID = Field(
+ default_factory=uuid.uuid4, primary_key=True, index=True
+ ) # Missing id field
+ name: str = Field(nullable=False, index=True)
+ address: str | None = Field(default=None)
+ latitude: float = Field(default=0)
+ longitude: float = Field(default=0)
+ capacity: int | None = Field(default=None)
+ description: str | None = Field(default=None)
+ google_rating: float | None = Field(default=None)
+ instagram_handle: str | None = Field(default=None)
+ instagram_token: str | None = Field(default=None)
+ google_map_link: str | None = Field(default=None)
+ mobile_number: str | None = Field(default=None)
+ email: str | None = Field(default=None)
+ opening_time: time | None = Field(default=None)
+ closing_time: time | None = Field(default=None)
+ avg_expense_for_two: float | None = Field(default=None)
+ zomato_link: str | None = Field(default=None)
+ swiggy_link: str | None = Field(default=None)
+
+ managing_users: list["UserVenueAssociation"] = Relationship(back_populates="venue")
+ qrcode: list["QRCode"] = Relationship(back_populates="venue")
+ menu: list["Menu"] = Relationship(back_populates="venue")
+ pickup_locations: list["PickupLocation"] = Relationship(back_populates="venue")
+ carousel_posters: list["CarouselPoster"] | None = Relationship(back_populates="venue")
+
+ # Back-references for specific venue types
+ foodcourt: Optional["Foodcourt"] = Relationship(back_populates="venue")
+ qsr: Optional["QSR"] = Relationship(back_populates="venue")
+ restaurant: Optional["Restaurant"] = Relationship(back_populates="venue")
+ nightclub: Optional["Nightclub"] = Relationship(back_populates="venue")
+ events: list["Event"] = Relationship(back_populates="venue")
+
+ @classmethod
+ def from_create_schema(cls, venue_create: VenueCreate) -> "Venue":
+ return cls(
+ name=venue_create.name,
+ capacity=venue_create.capacity,
+ description=venue_create.description,
+ instagram_handle=venue_create.instagram_handle,
+ instagram_token=venue_create.instagram_token,
+ google_map_link=venue_create.google_map_link,
+ mobile_number=venue_create.mobile_number,
+ email=venue_create.email,
+ opening_time=venue_create.opening_time,
+ closing_time=venue_create.closing_time,
+ avg_expense_for_two=venue_create.avg_expense_for_two,
+ zomato_link=venue_create.zomato_link,
+ swiggy_link=venue_create.swiggy_link,
+ )
+
+ def to_read_schema(self) -> VenueRead:
+ return VenueRead(
+ id=self.id,
+ name=self.name,
+ address=self.address,
+ latitude=self.latitude,
+ longitude=self.longitude,
+ capacity=self.capacity,
+ description=self.description,
+ google_rating=self.google_rating,
+ instagram_handle=self.instagram_handle,
+ google_map_link=self.google_map_link,
+ mobile_number=self.mobile_number,
+ email=self.email,
+ opening_time=self.opening_time,
+ closing_time=self.closing_time,
+ avg_expense_for_two=self.avg_expense_for_two,
+ zomato_link=self.zomato_link,
+ swiggy_link=self.swiggy_link,
+ )
+
+
+# Specific Venue Types
+class Foodcourt(BaseTimeModel, table=True):
+ __tablename__ = "foodcourt"
+
+ id: uuid.UUID = Field(default_factory=uuid.uuid4, primary_key=True, index=True)
+ total_qsrs: int | None = Field(default=None) # Example specific field for foodcourt
+ seating_capacity: int | None = Field(default=None) # Specific to foodcourts
+ venue_id: uuid.UUID = Field(foreign_key="venue.id", nullable=False, index=True)
+
+ # Relationships
+ venue: Venue = Relationship(back_populates="foodcourt")
+ qsrs: list["QSR"] = Relationship(back_populates="foodcourt")
+
+ @classmethod
+ def from_create_schema(
+ cls, venue_id: uuid, foodcourt_create: FoodcourtCreate
+ ) -> "Foodcourt":
+ return cls(
+ total_qsrs=foodcourt_create.total_qsrs,
+ seating_capacity=foodcourt_create.seating_capacity,
+ venue_id=venue_id,
+ )
+
+ def to_read_schema(self) -> FoodcourtRead:
+ venue_read = self.venue.to_read_schema()
+ return FoodcourtRead(
+ id=self.id,
+ total_qsrs=self.total_qsrs,
+ seating_capacity=self.seating_capacity,
+ venue=venue_read,
+ qsrs=[qsr.to_read_schema() for qsr in self.qsrs],
+ )
+
+
+class QSR(BaseTimeModel, table=True):
+ __tablename__ = "qsr"
+
+ id: uuid.UUID = Field(default_factory=uuid.uuid4, primary_key=True, index=True)
+ foodcourt_id: uuid.UUID | None = Field(
+ foreign_key="foodcourt.id", nullable=True, index=True
+ )
+
+ drive_thru: bool | None = Field(default=False) # Specific field for QSR
+ venue_id: uuid.UUID = Field(foreign_key="venue.id", nullable=False, index=True)
+
+ venue: Venue = Relationship(back_populates="qsr")
+ foodcourt: Foodcourt | None = Relationship(back_populates="qsrs")
+ orders: list["QSROrder"] = Relationship(back_populates="qsr")
+
+ @classmethod
+ def from_create_schema(cls, venue_id: uuid, qsr_create: QSRCreate) -> "QSR":
+ return cls(
+ foodcourt_id=qsr_create.foodcourt_id,
+ drive_thru=qsr_create.drive_thru,
+ venue_id=venue_id,
+ )
+
+ def to_read_schema(self) -> QSRRead:
+ venue_read = self.venue.to_read_schema()
+ return QSRRead(
+ id=self.id,
+ foodcourt_id=self.foodcourt_id,
+ drive_thru=self.drive_thru,
+ venue=venue_read,
+ )
+
+
+class Restaurant(BaseTimeModel, table=True):
+ __tablename__ = "restaurant"
+
+ id: uuid.UUID = Field(default_factory=uuid.uuid4, primary_key=True, index=True)
+ venue_id: uuid.UUID = Field(foreign_key="venue.id", nullable=False, index=True)
+ venue: Venue = Relationship(back_populates="restaurant")
+ cuisine_type: str | None = Field(
+ default=None
+ ) # Example specific field for restaurant
+ orders: list["RestaurantOrder"] = Relationship(back_populates="restaurant")
+
+ @classmethod
+ def from_create_schema(
+ cls, venue_id, restaurant_create: RestaurantCreate
+ ) -> "Restaurant":
+ return cls(
+ venue_id=venue_id,
+ cuisine_type=restaurant_create.cuisine_type,
+ )
+
+ def to_read_schema(self) -> RestaurantRead:
+ venue_read = self.venue.to_read_schema()
+ return RestaurantRead(
+ id=self.id,
+ venue=venue_read,
+ cuisine_type=self.cuisine_type,
+ )
+
+
+class Nightclub(BaseTimeModel, table=True):
+ __tablename__ = "nightclub"
+
+ id: uuid.UUID = Field(default_factory=uuid.uuid4, primary_key=True, index=True)
+ venue_id: uuid.UUID = Field(foreign_key="venue.id", nullable=False, index=True)
+ age_limit: int | None = Field(default=None)
+ # Relationships
+ club_visits: list["ClubVisit"] = Relationship(back_populates="nightclub")
+ orders: list["NightclubOrder"] = Relationship(back_populates="nightclub")
+ group: list["Group"] = Relationship(back_populates="nightclubs")
+ venue: Venue = Relationship(back_populates="nightclub")
+
+ @classmethod
+ def from_create_schema(
+ cls, venue_id, nightclub_create: NightclubCreate
+ ) -> "Nightclub":
+ return cls(
+ venue_id=venue_id,
+ age_limit=nightclub_create.age_limit,
+ )
+
+ def to_read_schema(self) -> NightclubRead:
+ venue_read = self.venue.to_read_schema()
+ return NightclubRead(
+ age_limit=self.age_limit,
+ id=self.id,
+ venue=venue_read,
+ )
diff --git a/backend/app/schema/__init__.py b/backend/app/schema/__init__.py
new file mode 100644
index 0000000000..e69de29bb2
diff --git a/backend/app/schema/carousel_poster.py b/backend/app/schema/carousel_poster.py
new file mode 100644
index 0000000000..9b2d963fb1
--- /dev/null
+++ b/backend/app/schema/carousel_poster.py
@@ -0,0 +1,34 @@
+import uuid
+from datetime import datetime
+
+from pydantic import BaseModel, model_validator
+
+
+class CarouselPosterCreate(BaseModel):
+ image_url: str
+ deep_link: str
+ expires_at: datetime
+ event_id: uuid.UUID | None = None
+ venue_id: uuid.UUID | None = None
+
+ class Config:
+ from_attributes = True
+
+ @model_validator(mode="before")
+ def validate_event_or_venue(cls, values):
+ event_id, venue_id = values.get('event_id'), values.get('venue_id')
+ if bool(event_id) == bool(venue_id): # Both or neither are set
+ raise ValueError("Exactly one of 'event_id' or 'venue_id' must be provided.")
+ return values
+
+class CarouselPosterRead(BaseModel):
+ courosel_id: uuid.UUID
+ image_url: str
+ deep_link: str
+ h3_index: str | None
+ expires_at: datetime
+ event_id: uuid.UUID | None
+ venue_id: uuid.UUID | None
+
+ class Config:
+ from_attributes = True
diff --git a/backend/app/schema/maps.py b/backend/app/schema/maps.py
new file mode 100644
index 0000000000..66e6cd7f50
--- /dev/null
+++ b/backend/app/schema/maps.py
@@ -0,0 +1,33 @@
+from typing import Optional
+
+from pydantic import BaseModel
+
+
+class Location(BaseModel):
+ lat: float
+ lng: float
+
+
+class Place(BaseModel):
+ name: Optional[str]
+ address: Optional[str]
+ location: Optional[Location]
+ place_id: Optional[str]
+
+
+class SearchResponse(BaseModel):
+ results: list[Place]
+
+
+class LocationDetailsResponse(BaseModel):
+ name: Optional[str]
+ address: Optional[str]
+ phone_number: Optional[str]
+ location: Optional[Location]
+ place_id: Optional[str]
+ url: Optional[str]
+
+
+class LocationByCoordinatesResponse(BaseModel):
+ name: Optional[str]
+ place_id: Optional[str]
diff --git a/backend/app/schema/menu.py b/backend/app/schema/menu.py
new file mode 100644
index 0000000000..1d3d45d609
--- /dev/null
+++ b/backend/app/schema/menu.py
@@ -0,0 +1,137 @@
+import uuid
+
+from pydantic import BaseModel, Field
+
+
+class MenuItemCreate(BaseModel):
+ subcategory_id: uuid.UUID
+ name: str
+ price: float
+ description: str | None = None
+ image_url: str | None = None
+ is_veg: bool | None = None
+ ingredients: str | None = None
+ abv: float | None = None
+ ibu: int | None = None
+
+ class Config:
+ from_attributes = True
+
+
+# Response schema for a menu item
+class MenuItemRead(BaseModel):
+ item_id: uuid.UUID
+ subcategory_id: uuid.UUID
+ name: str
+ price: float
+ description: str | None = None
+ image_url: str | None = None
+ is_veg: bool | None = None
+ ingredients: str | None = None
+ abv: float | None = None
+ ibu: int | None = None
+
+ class Config:
+ from_attributes = True
+
+
+class MenuItemUpdate(BaseModel):
+ name: str | None = None # Name can be updated
+ price: float | None = None # Price can be updated
+ description: str | None = None # Description is optional and updatable
+ image_url: str | None = None # Image URL is optional and updatable
+ is_veg: bool | None = None # Optionally update veg/non-veg status
+ ingredients: str | None = None # Ingredients list is optional and updatable
+ abv: float | None = None # Alcohol by volume can be updated
+ ibu: int | None = None # International Bitterness Units can be updated
+
+ class Config:
+ from_attributes = True
+
+
+#########################################################################################################
+
+
+class MenuSubCategoryCreate(BaseModel):
+ name: str # Name of the subcategory
+ is_alcoholic: bool = Field(default=False)
+ category_id: uuid.UUID # Foreign key to the parent category
+
+ class Config:
+ from_attributes = True
+
+
+class MenuSubCategoryRead(BaseModel):
+ subcategory_id: uuid.UUID # Unique identifier for the subcategory
+ category_id: uuid.UUID
+ is_alcoholic: bool
+ name: str # Name of the subcategory
+ menu_items: list[MenuItemRead] | None = [] # list of items under this subcategory
+
+
+class MenuSubCategoryUpdate(BaseModel):
+ name: str | None = None # Optional name for update
+ is_alcoholic: bool | None = None # Optional field for update
+ category_id: uuid.UUID | None = None # Optional foreign key to update category
+ menu_items: list["MenuItemUpdate"] | None = (
+ []
+ ) # Optional list for updating menu items
+
+ class Config:
+ from_attributes = True
+
+
+#########################################################################################################
+class MenuCategoryRead(BaseModel):
+ category_id: uuid.UUID # Unique identifier for the category
+ name: str # Name of the category
+ menu_id: uuid.UUID
+ sub_categories: list[MenuSubCategoryRead] = [] # list of subcategories
+
+
+class MenuCategoryCreate(BaseModel):
+ name: str # Name of the category
+ menu_id: uuid.UUID
+
+ class Config:
+ from_attributes = True
+
+
+class MenuCategoryUpdate(BaseModel):
+ name: str | None = None # Category name can be updated
+ menu_id: uuid.UUID | None = None # Menu ID can be updated
+
+ class Config:
+ from_attributes = True
+
+
+#########################################################################################################
+class MenuRead(BaseModel):
+ menu_id: uuid.UUID # Unique identifier for the menu
+ name: str # Name of the menu (could be a restaurant menu or type of menu)
+ description: str | None = None # Description of the menu
+ categories: list[MenuCategoryRead] = [] # Nested list of categories
+ venue_id: uuid.UUID # Foreign key to the venue
+ menu_type: str | None = None
+
+ class Config:
+ from_attributes = True
+
+
+class MenuCreate(BaseModel):
+ name: str # Name of the menu (could be a restaurant menu or type of menu)
+ description: str | None = None # Description of the menu
+ venue_id: uuid.UUID # Foreign key to the venue
+ menu_type: str | None = None # Type of menu (e.g., "Food", "Drink")
+
+ class Config:
+ from_attributes = True
+
+
+class MenuUpdate(BaseModel):
+ name: str # Name of the menu (could be a restaurant menu or type of menu)
+ description: str | None = None # Description of the menu
+ menu_type: str | None = None # Type of menu (e.g., "Food", "Drink")
+
+ class Config:
+ from_attributes = True
diff --git a/backend/app/schema/qrcode.py b/backend/app/schema/qrcode.py
new file mode 100644
index 0000000000..d0decde0b7
--- /dev/null
+++ b/backend/app/schema/qrcode.py
@@ -0,0 +1,32 @@
+from uuid import UUID
+
+from pydantic import BaseModel
+
+
+class QRCodeCreate(BaseModel):
+ """
+ Schema for creating a new QR code.
+ """
+
+ table_number: str | None = None
+ venue_id: UUID # Foreign key to the venue
+
+
+class QRCodeUpdate(BaseModel):
+ """
+ Schema for creating a new QR code.
+ """
+
+ table_number: str | None = None
+
+class QRCodeRead(BaseModel):
+ """
+ Schema for reading a QR code.
+ """
+
+ id: UUID # Automatically added by the model
+ venue_id: UUID
+ table_number: str | None
+
+ class Config:
+ from_attributes = True
diff --git a/backend/app/schema/user.py b/backend/app/schema/user.py
new file mode 100644
index 0000000000..6331cb9a6b
--- /dev/null
+++ b/backend/app/schema/user.py
@@ -0,0 +1,66 @@
+# Create and Read Schemas
+
+import uuid
+from datetime import datetime
+
+from pydantic import BaseModel, EmailStr
+
+from app.constants import Gender
+
+
+class UserPublicCreate(BaseModel):
+ full_name: str
+ phone_number: str | None = None
+ email: EmailStr | None = None
+ date_of_birth: datetime | None = None
+ gender: Gender | None = None
+ profile_picture: str | None = None
+ preferences: str | None = None
+
+
+class UserPublicUpdate(BaseModel):
+ full_name: str | None = None
+ phone_number: str | None = None
+ email: EmailStr | None = None
+ date_of_birth: datetime | None
+ gender: Gender | None = None
+ profile_picture: str | None = None
+ preferences: str | None = None
+
+
+class UserPublicRead(BaseModel):
+ id: uuid.UUID
+ phone_number: str
+ full_name: str | None = None
+ email: EmailStr | None = None
+ date_of_birth: datetime | None = None
+ gender: Gender | None = None
+ profile_picture: str | None = None
+ preferences: str | None = None
+
+ class Config:
+ from_attributes = True
+ extra = "forbid"
+
+
+class UserBusinessCreate(BaseModel):
+ full_name: str | None = None
+ email: EmailStr
+ phone_number: str | None = None
+
+
+class UserBusinessUpdate(BaseModel):
+ full_name: str | None = None
+ email: EmailStr | None = None
+ phone_number: str | None = None
+
+
+class UserBusinessRead(BaseModel):
+ id: uuid.UUID
+ full_name: str | None = None
+ email: EmailStr
+ phone_number: str | None = None
+
+ class Config:
+ from_attributes = True
+ extra = "forbid"
diff --git a/backend/app/schema/venue.py b/backend/app/schema/venue.py
new file mode 100644
index 0000000000..dd85e8276c
--- /dev/null
+++ b/backend/app/schema/venue.py
@@ -0,0 +1,124 @@
+import uuid
+from datetime import time
+
+from pydantic import BaseModel
+
+
+# Venue base details (composition)
+class VenueCreate(BaseModel):
+ name: str
+ capacity: int | None = None
+ description: str | None = None
+ instagram_handle: str | None = None
+ instagram_token: str | None = None
+ mobile_number: str | None = None
+ email: str | None = None
+ opening_time: time | None = None
+ closing_time: time | None = None
+ avg_expense_for_two: float | None = None
+ zomato_link: str | None = None
+ swiggy_link: str | None = None
+ google_map_link: str | None = None
+
+
+class FoodcourtCreate(BaseModel):
+ total_qsrs: int | None = None
+ seating_capacity: int | None = None
+ venue: VenueCreate
+
+ class Config:
+ from_attributes = True
+
+
+class QSRCreate(BaseModel):
+ drive_thru: bool | None = False
+ foodcourt_id: uuid.UUID | None = None
+ venue: VenueCreate
+
+ class Config:
+ from_attributes = True
+
+
+# Restaurant Schemas
+class RestaurantCreate(BaseModel):
+ cuisine_type: str | None = None
+ venue_id: uuid.UUID
+ venue: VenueCreate
+
+ class Config:
+ from_attributes = True
+
+
+# Nightclub Schemas
+class NightclubCreate(BaseModel):
+ venue: VenueCreate
+ age_limit: int | None = None
+
+ class Config:
+ from_attributes = True
+
+
+class VenueRead(BaseModel):
+ id: uuid.UUID
+ name: str
+ address: str | None
+ latitude: float
+ longitude: float
+ capacity: int | None
+ description: str | None
+ google_rating: float | None
+ instagram_handle: str | None
+ google_map_link: str | None
+ mobile_number: str | None
+ email: str | None
+ opening_time: time | None
+ closing_time: time | None
+ avg_expense_for_two: float | None
+ zomato_link: str | None
+ swiggy_link: str | None
+
+
+class FoodcourtRead(BaseModel):
+ id: uuid.UUID
+ total_qsrs: int | None = None # Specific field for foodcourt
+ seating_capacity: int | None = None # Specific to foodcourts
+ venue: VenueRead
+ qsrs: list["QSRRead"] = [] # list of QSRs in the foodcourt
+
+ class Config:
+ from_attributes = True
+
+
+class QSRRead(BaseModel):
+ id: uuid.UUID
+ # Add any specific fields for QSR if needed
+ foodcourt_id: uuid.UUID | None = None # Reference to the associated foodcourt
+ venue: VenueRead
+
+ class Config:
+ from_attributes = True
+
+
+class RestaurantRead(BaseModel):
+ id: uuid.UUID
+ cuisine_type: str | None = None
+ venue: VenueRead
+
+ class Config:
+ from_attributes = True
+
+
+class NightclubRead(BaseModel):
+ id: uuid.UUID
+ age_limit: int | None = None
+ venue: VenueRead
+
+ class Config:
+ from_attributes = True
+
+
+class VenueListResponse(BaseModel):
+ nightclubs: list[NightclubRead]
+ qsrs: list[QSRRead]
+ foodcourts: list[FoodcourtRead]
+ restaurants: list[RestaurantRead]
diff --git a/backend/app/static/landing_page.html b/backend/app/static/landing_page.html
new file mode 100644
index 0000000000..2bed0ef319
--- /dev/null
+++ b/backend/app/static/landing_page.html
@@ -0,0 +1,48 @@
+
+
+
+
+
+ Redirecting...
+
+
+
+ Redirecting...
+
+
\ No newline at end of file
diff --git a/backend/app/tests/api/routes/test_users.py b/backend/app/tests/api/routes/test_users.py
index ba9be65426..15d4a4d92e 100644
--- a/backend/app/tests/api/routes/test_users.py
+++ b/backend/app/tests/api/routes/test_users.py
@@ -4,7 +4,7 @@
from fastapi.testclient import TestClient
from sqlmodel import Session, select
-from app import crud
+from app import util
from app.core.config import settings
from app.core.security import verify_password
from app.models import User, UserCreate
@@ -51,7 +51,7 @@ def test_create_user_new_email(
)
assert 200 <= r.status_code < 300
created_user = r.json()
- user = crud.get_user_by_email(session=db, email=username)
+ user = util.get_user_by_email(session=db, email=username)
assert user
assert user.email == created_user["email"]
@@ -62,7 +62,7 @@ def test_get_existing_user(
username = random_email()
password = random_lower_string()
user_in = UserCreate(email=username, password=password)
- user = crud.create_user(session=db, user_create=user_in)
+ user = util.create_user(session=db, user_create=user_in)
user_id = user.id
r = client.get(
f"{settings.API_V1_STR}/users/{user_id}",
@@ -70,7 +70,7 @@ def test_get_existing_user(
)
assert 200 <= r.status_code < 300
api_user = r.json()
- existing_user = crud.get_user_by_email(session=db, email=username)
+ existing_user = util.get_user_by_email(session=db, email=username)
assert existing_user
assert existing_user.email == api_user["email"]
@@ -79,7 +79,7 @@ def test_get_existing_user_current_user(client: TestClient, db: Session) -> None
username = random_email()
password = random_lower_string()
user_in = UserCreate(email=username, password=password)
- user = crud.create_user(session=db, user_create=user_in)
+ user = util.create_user(session=db, user_create=user_in)
user_id = user.id
login_data = {
@@ -97,7 +97,7 @@ def test_get_existing_user_current_user(client: TestClient, db: Session) -> None
)
assert 200 <= r.status_code < 300
api_user = r.json()
- existing_user = crud.get_user_by_email(session=db, email=username)
+ existing_user = util.get_user_by_email(session=db, email=username)
assert existing_user
assert existing_user.email == api_user["email"]
@@ -120,7 +120,7 @@ def test_create_user_existing_username(
# username = email
password = random_lower_string()
user_in = UserCreate(email=username, password=password)
- crud.create_user(session=db, user_create=user_in)
+ util.create_user(session=db, user_create=user_in)
data = {"email": username, "password": password}
r = client.post(
f"{settings.API_V1_STR}/users/",
@@ -152,12 +152,12 @@ def test_retrieve_users(
username = random_email()
password = random_lower_string()
user_in = UserCreate(email=username, password=password)
- crud.create_user(session=db, user_create=user_in)
+ util.create_user(session=db, user_create=user_in)
username2 = random_email()
password2 = random_lower_string()
user_in2 = UserCreate(email=username2, password=password2)
- crud.create_user(session=db, user_create=user_in2)
+ util.create_user(session=db, user_create=user_in2)
r = client.get(f"{settings.API_V1_STR}/users/", headers=superuser_token_headers)
all_users = r.json()
@@ -251,7 +251,7 @@ def test_update_user_me_email_exists(
username = random_email()
password = random_lower_string()
user_in = UserCreate(email=username, password=password)
- user = crud.create_user(session=db, user_create=user_in)
+ user = util.create_user(session=db, user_create=user_in)
data = {"email": user.email}
r = client.patch(
@@ -326,7 +326,7 @@ def test_update_user(
username = random_email()
password = random_lower_string()
user_in = UserCreate(email=username, password=password)
- user = crud.create_user(session=db, user_create=user_in)
+ user = util.create_user(session=db, user_create=user_in)
data = {"full_name": "Updated_full_name"}
r = client.patch(
@@ -365,12 +365,12 @@ def test_update_user_email_exists(
username = random_email()
password = random_lower_string()
user_in = UserCreate(email=username, password=password)
- user = crud.create_user(session=db, user_create=user_in)
+ user = util.create_user(session=db, user_create=user_in)
username2 = random_email()
password2 = random_lower_string()
user_in2 = UserCreate(email=username2, password=password2)
- user2 = crud.create_user(session=db, user_create=user_in2)
+ user2 = util.create_user(session=db, user_create=user_in2)
data = {"email": user2.email}
r = client.patch(
@@ -386,7 +386,7 @@ def test_delete_user_me(client: TestClient, db: Session) -> None:
username = random_email()
password = random_lower_string()
user_in = UserCreate(email=username, password=password)
- user = crud.create_user(session=db, user_create=user_in)
+ user = util.create_user(session=db, user_create=user_in)
user_id = user.id
login_data = {
@@ -431,7 +431,7 @@ def test_delete_user_super_user(
username = random_email()
password = random_lower_string()
user_in = UserCreate(email=username, password=password)
- user = crud.create_user(session=db, user_create=user_in)
+ user = util.create_user(session=db, user_create=user_in)
user_id = user.id
r = client.delete(
f"{settings.API_V1_STR}/users/{user_id}",
@@ -458,7 +458,7 @@ def test_delete_user_not_found(
def test_delete_user_current_super_user_error(
client: TestClient, superuser_token_headers: dict[str, str], db: Session
) -> None:
- super_user = crud.get_user_by_email(session=db, email=settings.FIRST_SUPERUSER)
+ super_user = util.get_user_by_email(session=db, email=settings.FIRST_SUPERUSER)
assert super_user
user_id = super_user.id
@@ -476,7 +476,7 @@ def test_delete_user_without_privileges(
username = random_email()
password = random_lower_string()
user_in = UserCreate(email=username, password=password)
- user = crud.create_user(session=db, user_create=user_in)
+ user = util.create_user(session=db, user_create=user_in)
r = client.delete(
f"{settings.API_V1_STR}/users/{user.id}",
diff --git a/backend/app/tests/crud/test_user.py b/backend/app/tests/crud/test_user.py
index e9eb4a0391..3e974909af 100644
--- a/backend/app/tests/crud/test_user.py
+++ b/backend/app/tests/crud/test_user.py
@@ -1,7 +1,7 @@
from fastapi.encoders import jsonable_encoder
from sqlmodel import Session
-from app import crud
+from app import util
from app.core.security import verify_password
from app.models import User, UserCreate, UserUpdate
from app.tests.utils.utils import random_email, random_lower_string
@@ -11,7 +11,7 @@ def test_create_user(db: Session) -> None:
email = random_email()
password = random_lower_string()
user_in = UserCreate(email=email, password=password)
- user = crud.create_user(session=db, user_create=user_in)
+ user = util.create_user(session=db, user_create=user_in)
assert user.email == email
assert hasattr(user, "hashed_password")
@@ -20,8 +20,8 @@ def test_authenticate_user(db: Session) -> None:
email = random_email()
password = random_lower_string()
user_in = UserCreate(email=email, password=password)
- user = crud.create_user(session=db, user_create=user_in)
- authenticated_user = crud.authenticate(session=db, email=email, password=password)
+ user = util.create_user(session=db, user_create=user_in)
+ authenticated_user = util.authenticate(session=db, email=email, password=password)
assert authenticated_user
assert user.email == authenticated_user.email
@@ -29,7 +29,7 @@ def test_authenticate_user(db: Session) -> None:
def test_not_authenticate_user(db: Session) -> None:
email = random_email()
password = random_lower_string()
- user = crud.authenticate(session=db, email=email, password=password)
+ user = util.authenticate(session=db, email=email, password=password)
assert user is None
@@ -37,7 +37,7 @@ def test_check_if_user_is_active(db: Session) -> None:
email = random_email()
password = random_lower_string()
user_in = UserCreate(email=email, password=password)
- user = crud.create_user(session=db, user_create=user_in)
+ user = util.create_user(session=db, user_create=user_in)
assert user.is_active is True
@@ -45,7 +45,7 @@ def test_check_if_user_is_active_inactive(db: Session) -> None:
email = random_email()
password = random_lower_string()
user_in = UserCreate(email=email, password=password, disabled=True)
- user = crud.create_user(session=db, user_create=user_in)
+ user = util.create_user(session=db, user_create=user_in)
assert user.is_active
@@ -53,7 +53,7 @@ def test_check_if_user_is_superuser(db: Session) -> None:
email = random_email()
password = random_lower_string()
user_in = UserCreate(email=email, password=password, is_superuser=True)
- user = crud.create_user(session=db, user_create=user_in)
+ user = util.create_user(session=db, user_create=user_in)
assert user.is_superuser is True
@@ -61,7 +61,7 @@ def test_check_if_user_is_superuser_normal_user(db: Session) -> None:
username = random_email()
password = random_lower_string()
user_in = UserCreate(email=username, password=password)
- user = crud.create_user(session=db, user_create=user_in)
+ user = util.create_user(session=db, user_create=user_in)
assert user.is_superuser is False
@@ -69,7 +69,7 @@ def test_get_user(db: Session) -> None:
password = random_lower_string()
username = random_email()
user_in = UserCreate(email=username, password=password, is_superuser=True)
- user = crud.create_user(session=db, user_create=user_in)
+ user = util.create_user(session=db, user_create=user_in)
user_2 = db.get(User, user.id)
assert user_2
assert user.email == user_2.email
@@ -80,11 +80,11 @@ def test_update_user(db: Session) -> None:
password = random_lower_string()
email = random_email()
user_in = UserCreate(email=email, password=password, is_superuser=True)
- user = crud.create_user(session=db, user_create=user_in)
+ user = util.create_user(session=db, user_create=user_in)
new_password = random_lower_string()
user_in_update = UserUpdate(password=new_password, is_superuser=True)
if user.id is not None:
- crud.update_user(session=db, db_user=user, user_in=user_in_update)
+ util.update_user(session=db, db_user=user, user_in=user_in_update)
user_2 = db.get(User, user.id)
assert user_2
assert user.email == user_2.email
diff --git a/backend/app/tests/utils/item.py b/backend/app/tests/utils/item.py
index 6e32b3a84a..1016fd6072 100644
--- a/backend/app/tests/utils/item.py
+++ b/backend/app/tests/utils/item.py
@@ -1,6 +1,6 @@
from sqlmodel import Session
-from app import crud
+from app import util
from app.models import Item, ItemCreate
from app.tests.utils.user import create_random_user
from app.tests.utils.utils import random_lower_string
@@ -13,4 +13,4 @@ def create_random_item(db: Session) -> Item:
title = random_lower_string()
description = random_lower_string()
item_in = ItemCreate(title=title, description=description)
- return crud.create_item(session=db, item_in=item_in, owner_id=owner_id)
+ return util.create_item(session=db, item_in=item_in, owner_id=owner_id)
diff --git a/backend/app/tests/utils/user.py b/backend/app/tests/utils/user.py
index 9c1b073109..95fb061f8b 100644
--- a/backend/app/tests/utils/user.py
+++ b/backend/app/tests/utils/user.py
@@ -1,7 +1,7 @@
from fastapi.testclient import TestClient
from sqlmodel import Session
-from app import crud
+from app import util
from app.core.config import settings
from app.models import User, UserCreate, UserUpdate
from app.tests.utils.utils import random_email, random_lower_string
@@ -23,7 +23,7 @@ def create_random_user(db: Session) -> User:
email = random_email()
password = random_lower_string()
user_in = UserCreate(email=email, password=password)
- user = crud.create_user(session=db, user_create=user_in)
+ user = util.create_user(session=db, user_create=user_in)
return user
@@ -36,14 +36,14 @@ def authentication_token_from_email(
If the user doesn't exist it is created first.
"""
password = random_lower_string()
- user = crud.get_user_by_email(session=db, email=email)
+ user = util.get_user_by_email(session=db, email=email)
if not user:
user_in_create = UserCreate(email=email, password=password)
- user = crud.create_user(session=db, user_create=user_in_create)
+ user = util.create_user(session=db, user_create=user_in_create)
else:
user_in_update = UserUpdate(password=password)
if not user.id:
raise Exception("User id not set")
- user = crud.update_user(session=db, db_user=user, user_in=user_in_update)
+ user = util.update_user(session=db, db_user=user, user_in=user_in_update)
return user_authentication_headers(client=client, email=email, password=password)
diff --git a/backend/app/util.py b/backend/app/util.py
new file mode 100644
index 0000000000..717c23fb02
--- /dev/null
+++ b/backend/app/util.py
@@ -0,0 +1,171 @@
+import logging
+import uuid
+from typing import TypeVar
+
+from fastapi import HTTPException
+from pydantic import BaseModel
+from sqlmodel import Session, SQLModel, select
+
+from app.models.user import UserBusiness, UserVenueAssociation
+
+# Generic CRUD function to get all records with pagination
+T = TypeVar("T", bound=SQLModel)
+
+
+def get_all_records(
+ session: Session, model: type[T], skip: int = 0, limit: int = 10
+) -> list[SQLModel]:
+ """
+ Retrieve a paginated list of records as schemas.
+ - **session**: Database session
+ - **model**: SQLModel class (e.g., Nightclub, Restaurant, QSR, Foodcourt)
+ - **skip**: Number of records to skip
+ - **limit**: Number of records to return
+ """
+
+ try:
+ # Fetch raw model instances
+ statement = select(model).offset(skip).limit(limit)
+ result = session.execute(statement).scalars().all()
+
+ # Convert each record to its read schema
+ return result
+ except ValueError as ve:
+ # Handle errors (optional, add specific error handling as needed)
+ print(f"Error fetching records: {ve}")
+ return []
+
+
+def get_record_by_id(db: Session, model: type[T], record_id: uuid.UUID) -> T | None:
+ """
+ Generic function to retrieve a record by its ID.
+
+ Args:
+ db (Session): The database session.
+ model (Type[T]): The SQLModel class representing the table.
+ record_id (uuid.UUID): The ID of the record to retrieve.
+
+ Returns:
+ Optional[T]: The retrieved record if found, otherwise None.
+
+ Raises:
+ HTTPException: If the record is not found, raises a 404 error.
+ """
+ record = db.get(model, record_id)
+ if not record:
+ raise HTTPException(
+ status_code=404, detail=f"{model.__name__} with ID {record_id} not found."
+ )
+ return record
+
+
+# Function to create a new record
+# Create a new record
+def create_record(db: Session, instance: SQLModel) -> SQLModel:
+ """
+ Create a new record in the database with automatic timestamp management.
+
+ :param db: The active database session.
+ :param model: The SQLModel class representing the database model.
+ :param instance: An instance of the model to be persisted.
+ :return: The created instance with updated attributes.
+ """
+ # Current UTC time for timestamping
+ # current_time = datetime.now()
+
+ # # Setting timestamps for the record
+ # instance.created_at = current_time
+ # instance.updated_at = current_time
+
+ # Persisting the new record in the database
+ db.add(instance)
+ db.commit() # Commit the changes
+ db.refresh(instance) # Refresh to load any generated attributes
+
+ return instance # Return the created instance
+
+
+def update_record(db: Session, instance: SQLModel, update_data: BaseModel) -> SQLModel:
+ """
+ Update an existing record in the database, applying only the changes provided by a Pydantic model.
+ This approach ensures validation of input data and prevents partial updates with invalid fields.
+
+ :param db: Active database session.
+ :param instance: Existing model instance to be updated.
+ :param update_data: Pydantic model containing the fields to update.
+ :return: The updated model instance with changes committed.
+ """
+ try:
+ # Convert the Pydantic model to a dictionary, excluding unset fields
+ update_dict = update_data.dict(exclude_unset=True)
+
+ # Apply updates to only the fields that are set in the update_data Pydantic model
+ for key, value in update_dict.items():
+ if hasattr(instance, key):
+ setattr(instance, key, value)
+ else:
+ raise ValueError(f"Field '{key}' does not exist on the model.")
+
+ # Persist the changes in a single transaction
+ db.add(instance)
+ db.commit()
+ db.refresh(instance)
+
+ return instance
+
+ except ValueError as ve:
+ logging.error("Validation error: %s", ve)
+ db.rollback() # Undo any changes in case of failure
+ raise HTTPException(status_code=400, detail=str(ve))
+
+ except Exception as e:
+ logging.error("Unexpected error during record update: %s", e)
+ db.rollback() # Rollback any transaction in case of failure
+ raise HTTPException(
+ status_code=500,
+ detail="An internal error occurred while updating the record.",
+ ) from e
+
+
+# Function to delete a record
+def delete_record(db: Session, instance: SQLModel) -> None:
+ """
+ Delete a record from the database.
+
+ :param db: The active database session.
+ :param instance: The instance of the model to be deleted.
+ :return: None
+ """
+ db.delete(instance)
+ db.commit()
+
+
+def check_user_permission(db: Session, current_user: UserBusiness, venue_id: uuid.UUID):
+ """
+ Check if the user has permission to manage the specified venue.
+
+ Args:
+ db: Database session.
+ current_user: The current user object.
+ venue_id: The ID of the venue to check permissions for.
+
+ Raises:
+ HTTPException: If the user does not have permission.
+
+ Returns:
+ UserVenueAssociation: The association record if it exists.
+ """
+ statement = select(UserVenueAssociation).where(
+ UserVenueAssociation.user_id == current_user.id,
+ UserVenueAssociation.venue_id == venue_id,
+ )
+
+ user_venue_association = db.execute(statement).scalars().first()
+
+ if user_venue_association is None:
+ raise HTTPException(
+ status_code=403,
+ detail="User does not have permission to manage this venue.",
+ )
+
+ return user_venue_association
diff --git a/backend/app/utils.py b/backend/app/utils.py
index d5ccf3153f..34f7324bbc 100644
--- a/backend/app/utils.py
+++ b/backend/app/utils.py
@@ -1,117 +1,58 @@
-import logging
-from dataclasses import dataclass
-from datetime import datetime, timedelta, timezone
-from pathlib import Path
-from typing import Any
-
-import emails # type: ignore
-import jwt
-from jinja2 import Template
-from jwt.exceptions import InvalidTokenError
-
-from app.core.config import settings
-
-
-@dataclass
-class EmailData:
- html_content: str
- subject: str
-
-
-def render_email_template(*, template_name: str, context: dict[str, Any]) -> str:
- template_str = (
- Path(__file__).parent / "email-templates" / "build" / template_name
- ).read_text()
- html_content = Template(template_str).render(context)
- return html_content
-
-
-def send_email(
- *,
- email_to: str,
- subject: str = "",
- html_content: str = "",
-) -> None:
- assert settings.emails_enabled, "no provided configuration for email variables"
- message = emails.Message(
- subject=subject,
- html=html_content,
- mail_from=(settings.EMAILS_FROM_NAME, settings.EMAILS_FROM_EMAIL),
- )
- smtp_options = {"host": settings.SMTP_HOST, "port": settings.SMTP_PORT}
- if settings.SMTP_TLS:
- smtp_options["tls"] = True
- elif settings.SMTP_SSL:
- smtp_options["ssl"] = True
- if settings.SMTP_USER:
- smtp_options["user"] = settings.SMTP_USER
- if settings.SMTP_PASSWORD:
- smtp_options["password"] = settings.SMTP_PASSWORD
- response = message.send(to=email_to, smtp=smtp_options)
- logging.info(f"send email result: {response}")
-
-
-def generate_test_email(email_to: str) -> EmailData:
- project_name = settings.PROJECT_NAME
- subject = f"{project_name} - Test email"
- html_content = render_email_template(
- template_name="test_email.html",
- context={"project_name": settings.PROJECT_NAME, "email": email_to},
- )
- return EmailData(html_content=html_content, subject=subject)
-
-
-def generate_reset_password_email(email_to: str, email: str, token: str) -> EmailData:
- project_name = settings.PROJECT_NAME
- subject = f"{project_name} - Password recovery for user {email}"
- link = f"{settings.server_host}/reset-password?token={token}"
- html_content = render_email_template(
- template_name="reset_password.html",
- context={
- "project_name": settings.PROJECT_NAME,
- "username": email,
- "email": email_to,
- "valid_hours": settings.EMAIL_RESET_TOKEN_EXPIRE_HOURS,
- "link": link,
- },
- )
- return EmailData(html_content=html_content, subject=subject)
-
-
-def generate_new_account_email(
- email_to: str, username: str, password: str
-) -> EmailData:
- project_name = settings.PROJECT_NAME
- subject = f"{project_name} - New account for user {username}"
- html_content = render_email_template(
- template_name="new_account.html",
- context={
- "project_name": settings.PROJECT_NAME,
- "username": username,
- "password": password,
- "email": email_to,
- "link": settings.server_host,
- },
- )
- return EmailData(html_content=html_content, subject=subject)
-
-
-def generate_password_reset_token(email: str) -> str:
- delta = timedelta(hours=settings.EMAIL_RESET_TOKEN_EXPIRE_HOURS)
- now = datetime.now(timezone.utc)
- expires = now + delta
- exp = expires.timestamp()
- encoded_jwt = jwt.encode(
- {"exp": exp, "nbf": now, "sub": email},
- settings.SECRET_KEY,
- algorithm="HS256",
- )
- return encoded_jwt
-
-
-def verify_password_reset_token(token: str) -> str | None:
- try:
- decoded_token = jwt.decode(token, settings.SECRET_KEY, algorithms=["HS256"])
- return str(decoded_token["sub"])
- except InvalidTokenError:
- return None
+import re
+
+import unshortenit
+
+
+class CoordinatesNotFoundError(Exception):
+ """Custom exception raised when coordinates cannot be found in the Google Maps link."""
+
+ pass
+
+
+def extract_coordinates_from_full_link(link: str) -> tuple[float, float] | None:
+ """
+ Extracts latitude and longitude from a full Google Maps link.
+
+ Args:
+ link (str): The full Google Maps URL.
+
+ Returns:
+ Optional[Tuple[float, float]]: A tuple containing latitude and longitude, or None if not found.
+ """
+ lat_long_regex = r"@([-+]?\d*\.\d+),([-+]?\d*\.\d+)"
+ match = re.search(lat_long_regex, link)
+
+ if match:
+ latitude = float(match.group(1))
+ longitude = float(match.group(2))
+ return latitude, longitude
+
+ return None
+
+
+def get_coordinates_from_google_maps(
+ link: str,
+) -> list[tuple[str, float, float]] | None:
+ """
+ Retrieves latitude and longitude from a Google Maps link by unshortening it and extracting coordinates.
+
+ Args:
+ link (str): The Google Maps URL.
+
+ Raises:
+ CoordinatesNotFoundError: If coordinates are not found in the link.
+
+ Returns:
+ Optional[list[Tuple[str, float, float]]]: A list of tuples containing the link, latitude, and longitude,
+ or raises an exception if coordinates are not found.
+ """
+ # Unshorten the URL if it's a shortened link
+ unshortened_url = unshortenit.unshorten(link)
+
+ # Attempt to extract coordinates from the full link
+ coordinates = extract_coordinates_from_full_link(unshortened_url)
+ if coordinates:
+ latitude, longitude = coordinates
+ return [(unshortened_url, latitude, longitude)]
+
+ raise CoordinatesNotFoundError(f"No coordinates found in the provided link: {link}")
diff --git a/backend/app/utils/__init__.py b/backend/app/utils/__init__.py
new file mode 100644
index 0000000000..73b91aab81
--- /dev/null
+++ b/backend/app/utils/__init__.py
@@ -0,0 +1 @@
+from .h3_utils import get_h3_index, is_within_radius
\ No newline at end of file
diff --git a/backend/app/utils/h3_utils.py b/backend/app/utils/h3_utils.py
new file mode 100644
index 0000000000..3d6fc57817
--- /dev/null
+++ b/backend/app/utils/h3_utils.py
@@ -0,0 +1,8 @@
+import h3
+
+
+def get_h3_index(latitude: float, longitude: float, resolution: int = 9) -> str:
+ return h3.geo_to_h3(latitude, longitude, resolution)
+
+def is_within_radius(user_h3_index: str, poster_h3_index: str, radius: int) -> bool:
+ return h3.h3_distance(user_h3_index, poster_h3_index) <= radius
diff --git a/backend/poetry.lock b/backend/poetry.lock
index f56ce480f0..e9a85279f5 100644
--- a/backend/poetry.lock
+++ b/backend/poetry.lock
@@ -1,5 +1,182 @@
# This file is automatically @generated by Poetry 1.8.3 and should not be changed by hand.
+[[package]]
+name = "aiofiles"
+version = "24.1.0"
+description = "File support for asyncio."
+optional = false
+python-versions = ">=3.8"
+files = [
+ {file = "aiofiles-24.1.0-py3-none-any.whl", hash = "sha256:b4ec55f4195e3eb5d7abd1bf7e061763e864dd4954231fb8539a0ef8bb8260e5"},
+ {file = "aiofiles-24.1.0.tar.gz", hash = "sha256:22a075c9e5a3810f0c2e48f3008c94d68c65d763b9b03857924c99e57355166c"},
+]
+
+[[package]]
+name = "aiohappyeyeballs"
+version = "2.4.3"
+description = "Happy Eyeballs for asyncio"
+optional = false
+python-versions = ">=3.8"
+files = [
+ {file = "aiohappyeyeballs-2.4.3-py3-none-any.whl", hash = "sha256:8a7a83727b2756f394ab2895ea0765a0a8c475e3c71e98d43d76f22b4b435572"},
+ {file = "aiohappyeyeballs-2.4.3.tar.gz", hash = "sha256:75cf88a15106a5002a8eb1dab212525c00d1f4c0fa96e551c9fbe6f09a621586"},
+]
+
+[[package]]
+name = "aiohttp"
+version = "3.10.10"
+description = "Async http client/server framework (asyncio)"
+optional = false
+python-versions = ">=3.8"
+files = [
+ {file = "aiohttp-3.10.10-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:be7443669ae9c016b71f402e43208e13ddf00912f47f623ee5994e12fc7d4b3f"},
+ {file = "aiohttp-3.10.10-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:7b06b7843929e41a94ea09eb1ce3927865387e3e23ebe108e0d0d09b08d25be9"},
+ {file = "aiohttp-3.10.10-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:333cf6cf8e65f6a1e06e9eb3e643a0c515bb850d470902274239fea02033e9a8"},
+ {file = "aiohttp-3.10.10-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:274cfa632350225ce3fdeb318c23b4a10ec25c0e2c880eff951a3842cf358ac1"},
+ {file = "aiohttp-3.10.10-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d9e5e4a85bdb56d224f412d9c98ae4cbd032cc4f3161818f692cd81766eee65a"},
+ {file = "aiohttp-3.10.10-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2b606353da03edcc71130b52388d25f9a30a126e04caef1fd637e31683033abd"},
+ {file = "aiohttp-3.10.10-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ab5a5a0c7a7991d90446a198689c0535be89bbd6b410a1f9a66688f0880ec026"},
+ {file = "aiohttp-3.10.10-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:578a4b875af3e0daaf1ac6fa983d93e0bbfec3ead753b6d6f33d467100cdc67b"},
+ {file = "aiohttp-3.10.10-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:8105fd8a890df77b76dd3054cddf01a879fc13e8af576805d667e0fa0224c35d"},
+ {file = "aiohttp-3.10.10-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:3bcd391d083f636c06a68715e69467963d1f9600f85ef556ea82e9ef25f043f7"},
+ {file = "aiohttp-3.10.10-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:fbc6264158392bad9df19537e872d476f7c57adf718944cc1e4495cbabf38e2a"},
+ {file = "aiohttp-3.10.10-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:e48d5021a84d341bcaf95c8460b152cfbad770d28e5fe14a768988c461b821bc"},
+ {file = "aiohttp-3.10.10-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:2609e9ab08474702cc67b7702dbb8a80e392c54613ebe80db7e8dbdb79837c68"},
+ {file = "aiohttp-3.10.10-cp310-cp310-win32.whl", hash = "sha256:84afcdea18eda514c25bc68b9af2a2b1adea7c08899175a51fe7c4fb6d551257"},
+ {file = "aiohttp-3.10.10-cp310-cp310-win_amd64.whl", hash = "sha256:9c72109213eb9d3874f7ac8c0c5fa90e072d678e117d9061c06e30c85b4cf0e6"},
+ {file = "aiohttp-3.10.10-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:c30a0eafc89d28e7f959281b58198a9fa5e99405f716c0289b7892ca345fe45f"},
+ {file = "aiohttp-3.10.10-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:258c5dd01afc10015866114e210fb7365f0d02d9d059c3c3415382ab633fcbcb"},
+ {file = "aiohttp-3.10.10-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:15ecd889a709b0080f02721255b3f80bb261c2293d3c748151274dfea93ac871"},
+ {file = "aiohttp-3.10.10-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f3935f82f6f4a3820270842e90456ebad3af15810cf65932bd24da4463bc0a4c"},
+ {file = "aiohttp-3.10.10-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:413251f6fcf552a33c981c4709a6bba37b12710982fec8e558ae944bfb2abd38"},
+ {file = "aiohttp-3.10.10-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d1720b4f14c78a3089562b8875b53e36b51c97c51adc53325a69b79b4b48ebcb"},
+ {file = "aiohttp-3.10.10-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:679abe5d3858b33c2cf74faec299fda60ea9de62916e8b67e625d65bf069a3b7"},
+ {file = "aiohttp-3.10.10-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:79019094f87c9fb44f8d769e41dbb664d6e8fcfd62f665ccce36762deaa0e911"},
+ {file = "aiohttp-3.10.10-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:fe2fb38c2ed905a2582948e2de560675e9dfbee94c6d5ccdb1301c6d0a5bf092"},
+ {file = "aiohttp-3.10.10-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:a3f00003de6eba42d6e94fabb4125600d6e484846dbf90ea8e48a800430cc142"},
+ {file = "aiohttp-3.10.10-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:1bbb122c557a16fafc10354b9d99ebf2f2808a660d78202f10ba9d50786384b9"},
+ {file = "aiohttp-3.10.10-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:30ca7c3b94708a9d7ae76ff281b2f47d8eaf2579cd05971b5dc681db8caac6e1"},
+ {file = "aiohttp-3.10.10-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:df9270660711670e68803107d55c2b5949c2e0f2e4896da176e1ecfc068b974a"},
+ {file = "aiohttp-3.10.10-cp311-cp311-win32.whl", hash = "sha256:aafc8ee9b742ce75044ae9a4d3e60e3d918d15a4c2e08a6c3c3e38fa59b92d94"},
+ {file = "aiohttp-3.10.10-cp311-cp311-win_amd64.whl", hash = "sha256:362f641f9071e5f3ee6f8e7d37d5ed0d95aae656adf4ef578313ee585b585959"},
+ {file = "aiohttp-3.10.10-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:9294bbb581f92770e6ed5c19559e1e99255e4ca604a22c5c6397b2f9dd3ee42c"},
+ {file = "aiohttp-3.10.10-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:a8fa23fe62c436ccf23ff930149c047f060c7126eae3ccea005f0483f27b2e28"},
+ {file = "aiohttp-3.10.10-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:5c6a5b8c7926ba5d8545c7dd22961a107526562da31a7a32fa2456baf040939f"},
+ {file = "aiohttp-3.10.10-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:007ec22fbc573e5eb2fb7dec4198ef8f6bf2fe4ce20020798b2eb5d0abda6138"},
+ {file = "aiohttp-3.10.10-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9627cc1a10c8c409b5822a92d57a77f383b554463d1884008e051c32ab1b3742"},
+ {file = "aiohttp-3.10.10-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:50edbcad60d8f0e3eccc68da67f37268b5144ecc34d59f27a02f9611c1d4eec7"},
+ {file = "aiohttp-3.10.10-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a45d85cf20b5e0d0aa5a8dca27cce8eddef3292bc29d72dcad1641f4ed50aa16"},
+ {file = "aiohttp-3.10.10-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0b00807e2605f16e1e198f33a53ce3c4523114059b0c09c337209ae55e3823a8"},
+ {file = "aiohttp-3.10.10-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:f2d4324a98062be0525d16f768a03e0bbb3b9fe301ceee99611dc9a7953124e6"},
+ {file = "aiohttp-3.10.10-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:438cd072f75bb6612f2aca29f8bd7cdf6e35e8f160bc312e49fbecab77c99e3a"},
+ {file = "aiohttp-3.10.10-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:baa42524a82f75303f714108fea528ccacf0386af429b69fff141ffef1c534f9"},
+ {file = "aiohttp-3.10.10-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:a7d8d14fe962153fc681f6366bdec33d4356f98a3e3567782aac1b6e0e40109a"},
+ {file = "aiohttp-3.10.10-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:c1277cd707c465cd09572a774559a3cc7c7a28802eb3a2a9472588f062097205"},
+ {file = "aiohttp-3.10.10-cp312-cp312-win32.whl", hash = "sha256:59bb3c54aa420521dc4ce3cc2c3fe2ad82adf7b09403fa1f48ae45c0cbde6628"},
+ {file = "aiohttp-3.10.10-cp312-cp312-win_amd64.whl", hash = "sha256:0e1b370d8007c4ae31ee6db7f9a2fe801a42b146cec80a86766e7ad5c4a259cf"},
+ {file = "aiohttp-3.10.10-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:ad7593bb24b2ab09e65e8a1d385606f0f47c65b5a2ae6c551db67d6653e78c28"},
+ {file = "aiohttp-3.10.10-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:1eb89d3d29adaf533588f209768a9c02e44e4baf832b08118749c5fad191781d"},
+ {file = "aiohttp-3.10.10-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:3fe407bf93533a6fa82dece0e74dbcaaf5d684e5a51862887f9eaebe6372cd79"},
+ {file = "aiohttp-3.10.10-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:50aed5155f819873d23520919e16703fc8925e509abbb1a1491b0087d1cd969e"},
+ {file = "aiohttp-3.10.10-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4f05e9727ce409358baa615dbeb9b969db94324a79b5a5cea45d39bdb01d82e6"},
+ {file = "aiohttp-3.10.10-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3dffb610a30d643983aeb185ce134f97f290f8935f0abccdd32c77bed9388b42"},
+ {file = "aiohttp-3.10.10-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:aa6658732517ddabe22c9036479eabce6036655ba87a0224c612e1ae6af2087e"},
+ {file = "aiohttp-3.10.10-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:741a46d58677d8c733175d7e5aa618d277cd9d880301a380fd296975a9cdd7bc"},
+ {file = "aiohttp-3.10.10-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:e00e3505cd80440f6c98c6d69269dcc2a119f86ad0a9fd70bccc59504bebd68a"},
+ {file = "aiohttp-3.10.10-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:ffe595f10566f8276b76dc3a11ae4bb7eba1aac8ddd75811736a15b0d5311414"},
+ {file = "aiohttp-3.10.10-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:bdfcf6443637c148c4e1a20c48c566aa694fa5e288d34b20fcdc58507882fed3"},
+ {file = "aiohttp-3.10.10-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:d183cf9c797a5291e8301790ed6d053480ed94070637bfaad914dd38b0981f67"},
+ {file = "aiohttp-3.10.10-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:77abf6665ae54000b98b3c742bc6ea1d1fb31c394bcabf8b5d2c1ac3ebfe7f3b"},
+ {file = "aiohttp-3.10.10-cp313-cp313-win32.whl", hash = "sha256:4470c73c12cd9109db8277287d11f9dd98f77fc54155fc71a7738a83ffcc8ea8"},
+ {file = "aiohttp-3.10.10-cp313-cp313-win_amd64.whl", hash = "sha256:486f7aabfa292719a2753c016cc3a8f8172965cabb3ea2e7f7436c7f5a22a151"},
+ {file = "aiohttp-3.10.10-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:1b66ccafef7336a1e1f0e389901f60c1d920102315a56df85e49552308fc0486"},
+ {file = "aiohttp-3.10.10-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:acd48d5b80ee80f9432a165c0ac8cbf9253eaddb6113269a5e18699b33958dbb"},
+ {file = "aiohttp-3.10.10-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:3455522392fb15ff549d92fbf4b73b559d5e43dc522588f7eb3e54c3f38beee7"},
+ {file = "aiohttp-3.10.10-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:45c3b868724137f713a38376fef8120c166d1eadd50da1855c112fe97954aed8"},
+ {file = "aiohttp-3.10.10-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:da1dee8948d2137bb51fbb8a53cce6b1bcc86003c6b42565f008438b806cccd8"},
+ {file = "aiohttp-3.10.10-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c5ce2ce7c997e1971b7184ee37deb6ea9922ef5163c6ee5aa3c274b05f9e12fa"},
+ {file = "aiohttp-3.10.10-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:28529e08fde6f12eba8677f5a8608500ed33c086f974de68cc65ab218713a59d"},
+ {file = "aiohttp-3.10.10-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f7db54c7914cc99d901d93a34704833568d86c20925b2762f9fa779f9cd2e70f"},
+ {file = "aiohttp-3.10.10-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:03a42ac7895406220124c88911ebee31ba8b2d24c98507f4a8bf826b2937c7f2"},
+ {file = "aiohttp-3.10.10-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:7e338c0523d024fad378b376a79faff37fafb3c001872a618cde1d322400a572"},
+ {file = "aiohttp-3.10.10-cp38-cp38-musllinux_1_2_ppc64le.whl", hash = "sha256:038f514fe39e235e9fef6717fbf944057bfa24f9b3db9ee551a7ecf584b5b480"},
+ {file = "aiohttp-3.10.10-cp38-cp38-musllinux_1_2_s390x.whl", hash = "sha256:64f6c17757251e2b8d885d728b6433d9d970573586a78b78ba8929b0f41d045a"},
+ {file = "aiohttp-3.10.10-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:93429602396f3383a797a2a70e5f1de5df8e35535d7806c9f91df06f297e109b"},
+ {file = "aiohttp-3.10.10-cp38-cp38-win32.whl", hash = "sha256:c823bc3971c44ab93e611ab1a46b1eafeae474c0c844aff4b7474287b75fe49c"},
+ {file = "aiohttp-3.10.10-cp38-cp38-win_amd64.whl", hash = "sha256:54ca74df1be3c7ca1cf7f4c971c79c2daf48d9aa65dea1a662ae18926f5bc8ce"},
+ {file = "aiohttp-3.10.10-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:01948b1d570f83ee7bbf5a60ea2375a89dfb09fd419170e7f5af029510033d24"},
+ {file = "aiohttp-3.10.10-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:9fc1500fd2a952c5c8e3b29aaf7e3cc6e27e9cfc0a8819b3bce48cc1b849e4cc"},
+ {file = "aiohttp-3.10.10-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:f614ab0c76397661b90b6851a030004dac502e48260ea10f2441abd2207fbcc7"},
+ {file = "aiohttp-3.10.10-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:00819de9e45d42584bed046314c40ea7e9aea95411b38971082cad449392b08c"},
+ {file = "aiohttp-3.10.10-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:05646ebe6b94cc93407b3bf34b9eb26c20722384d068eb7339de802154d61bc5"},
+ {file = "aiohttp-3.10.10-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:998f3bd3cfc95e9424a6acd7840cbdd39e45bc09ef87533c006f94ac47296090"},
+ {file = "aiohttp-3.10.10-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d9010c31cd6fa59438da4e58a7f19e4753f7f264300cd152e7f90d4602449762"},
+ {file = "aiohttp-3.10.10-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7ea7ffc6d6d6f8a11e6f40091a1040995cdff02cfc9ba4c2f30a516cb2633554"},
+ {file = "aiohttp-3.10.10-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:ef9c33cc5cbca35808f6c74be11eb7f5f6b14d2311be84a15b594bd3e58b5527"},
+ {file = "aiohttp-3.10.10-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:ce0cdc074d540265bfeb31336e678b4e37316849d13b308607efa527e981f5c2"},
+ {file = "aiohttp-3.10.10-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:597a079284b7ee65ee102bc3a6ea226a37d2b96d0418cc9047490f231dc09fe8"},
+ {file = "aiohttp-3.10.10-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:7789050d9e5d0c309c706953e5e8876e38662d57d45f936902e176d19f1c58ab"},
+ {file = "aiohttp-3.10.10-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:e7f8b04d83483577fd9200461b057c9f14ced334dcb053090cea1da9c8321a91"},
+ {file = "aiohttp-3.10.10-cp39-cp39-win32.whl", hash = "sha256:c02a30b904282777d872266b87b20ed8cc0d1501855e27f831320f471d54d983"},
+ {file = "aiohttp-3.10.10-cp39-cp39-win_amd64.whl", hash = "sha256:edfe3341033a6b53a5c522c802deb2079eee5cbfbb0af032a55064bd65c73a23"},
+ {file = "aiohttp-3.10.10.tar.gz", hash = "sha256:0631dd7c9f0822cc61c88586ca76d5b5ada26538097d0f1df510b082bad3411a"},
+]
+
+[package.dependencies]
+aiohappyeyeballs = ">=2.3.0"
+aiosignal = ">=1.1.2"
+async-timeout = {version = ">=4.0,<5.0", markers = "python_version < \"3.11\""}
+attrs = ">=17.3.0"
+frozenlist = ">=1.1.1"
+multidict = ">=4.5,<7.0"
+yarl = ">=1.12.0,<2.0"
+
+[package.extras]
+speedups = ["Brotli", "aiodns (>=3.2.0)", "brotlicffi"]
+
+[[package]]
+name = "aioredis"
+version = "1.3.1"
+description = "asyncio (PEP 3156) Redis support"
+optional = false
+python-versions = "*"
+files = [
+ {file = "aioredis-1.3.1-py3-none-any.whl", hash = "sha256:b61808d7e97b7cd5a92ed574937a079c9387fdadd22bfbfa7ad2fd319ecc26e3"},
+ {file = "aioredis-1.3.1.tar.gz", hash = "sha256:15f8af30b044c771aee6787e5ec24694c048184c7b9e54c3b60c750a4b93273a"},
+]
+
+[package.dependencies]
+async-timeout = "*"
+hiredis = "*"
+
+[[package]]
+name = "aiosignal"
+version = "1.3.1"
+description = "aiosignal: a list of registered asynchronous callbacks"
+optional = false
+python-versions = ">=3.7"
+files = [
+ {file = "aiosignal-1.3.1-py3-none-any.whl", hash = "sha256:f8376fb07dd1e86a584e4fcdec80b36b7f81aac666ebc724e2c090300dd83b17"},
+ {file = "aiosignal-1.3.1.tar.gz", hash = "sha256:54cd96e15e1649b75d6c87526a6ff0b6c1b0dd3459f43d9ca11d48c339b68cfc"},
+]
+
+[package.dependencies]
+frozenlist = ">=1.1.0"
+
+[[package]]
+name = "aiosqlite"
+version = "0.17.0"
+description = "asyncio bridge to the standard sqlite3 module"
+optional = false
+python-versions = ">=3.6"
+files = [
+ {file = "aiosqlite-0.17.0-py3-none-any.whl", hash = "sha256:6c49dc6d3405929b1d08eeccc72306d3677503cc5e5e43771efc1e00232e8231"},
+ {file = "aiosqlite-0.17.0.tar.gz", hash = "sha256:f0e6acc24bc4864149267ac82fb46dfb3be4455f99fe21df82609cc6e6baee51"},
+]
+
+[package.dependencies]
+typing_extensions = ">=3.7.2"
+
[[package]]
name = "alembic"
version = "1.13.2"
@@ -52,40 +229,255 @@ doc = ["Sphinx (>=7)", "packaging", "sphinx-autodoc-typehints (>=1.2.0)", "sphin
test = ["anyio[trio]", "coverage[toml] (>=7)", "exceptiongroup (>=1.2.0)", "hypothesis (>=4.0)", "psutil (>=5.9)", "pytest (>=7.0)", "pytest-mock (>=3.6.1)", "trustme", "uvloop (>=0.17)"]
trio = ["trio (>=0.23)"]
+[[package]]
+name = "argon2-cffi"
+version = "23.1.0"
+description = "Argon2 for Python"
+optional = false
+python-versions = ">=3.7"
+files = [
+ {file = "argon2_cffi-23.1.0-py3-none-any.whl", hash = "sha256:c670642b78ba29641818ab2e68bd4e6a78ba53b7eff7b4c3815ae16abf91c7ea"},
+ {file = "argon2_cffi-23.1.0.tar.gz", hash = "sha256:879c3e79a2729ce768ebb7d36d4609e3a78a4ca2ec3a9f12286ca057e3d0db08"},
+]
+
+[package.dependencies]
+argon2-cffi-bindings = "*"
+
+[package.extras]
+dev = ["argon2-cffi[tests,typing]", "tox (>4)"]
+docs = ["furo", "myst-parser", "sphinx", "sphinx-copybutton", "sphinx-notfound-page"]
+tests = ["hypothesis", "pytest"]
+typing = ["mypy"]
+
+[[package]]
+name = "argon2-cffi-bindings"
+version = "21.2.0"
+description = "Low-level CFFI bindings for Argon2"
+optional = false
+python-versions = ">=3.6"
+files = [
+ {file = "argon2-cffi-bindings-21.2.0.tar.gz", hash = "sha256:bb89ceffa6c791807d1305ceb77dbfacc5aa499891d2c55661c6459651fc39e3"},
+ {file = "argon2_cffi_bindings-21.2.0-cp36-abi3-macosx_10_9_x86_64.whl", hash = "sha256:ccb949252cb2ab3a08c02024acb77cfb179492d5701c7cbdbfd776124d4d2367"},
+ {file = "argon2_cffi_bindings-21.2.0-cp36-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9524464572e12979364b7d600abf96181d3541da11e23ddf565a32e70bd4dc0d"},
+ {file = "argon2_cffi_bindings-21.2.0-cp36-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b746dba803a79238e925d9046a63aa26bf86ab2a2fe74ce6b009a1c3f5c8f2ae"},
+ {file = "argon2_cffi_bindings-21.2.0-cp36-abi3-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:58ed19212051f49a523abb1dbe954337dc82d947fb6e5a0da60f7c8471a8476c"},
+ {file = "argon2_cffi_bindings-21.2.0-cp36-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:bd46088725ef7f58b5a1ef7ca06647ebaf0eb4baff7d1d0d177c6cc8744abd86"},
+ {file = "argon2_cffi_bindings-21.2.0-cp36-abi3-musllinux_1_1_i686.whl", hash = "sha256:8cd69c07dd875537a824deec19f978e0f2078fdda07fd5c42ac29668dda5f40f"},
+ {file = "argon2_cffi_bindings-21.2.0-cp36-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:f1152ac548bd5b8bcecfb0b0371f082037e47128653df2e8ba6e914d384f3c3e"},
+ {file = "argon2_cffi_bindings-21.2.0-cp36-abi3-win32.whl", hash = "sha256:603ca0aba86b1349b147cab91ae970c63118a0f30444d4bc80355937c950c082"},
+ {file = "argon2_cffi_bindings-21.2.0-cp36-abi3-win_amd64.whl", hash = "sha256:b2ef1c30440dbbcba7a5dc3e319408b59676e2e039e2ae11a8775ecf482b192f"},
+ {file = "argon2_cffi_bindings-21.2.0-cp38-abi3-macosx_10_9_universal2.whl", hash = "sha256:e415e3f62c8d124ee16018e491a009937f8cf7ebf5eb430ffc5de21b900dad93"},
+ {file = "argon2_cffi_bindings-21.2.0-pp37-pypy37_pp73-macosx_10_9_x86_64.whl", hash = "sha256:3e385d1c39c520c08b53d63300c3ecc28622f076f4c2b0e6d7e796e9f6502194"},
+ {file = "argon2_cffi_bindings-21.2.0-pp37-pypy37_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2c3e3cc67fdb7d82c4718f19b4e7a87123caf8a93fde7e23cf66ac0337d3cb3f"},
+ {file = "argon2_cffi_bindings-21.2.0-pp37-pypy37_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6a22ad9800121b71099d0fb0a65323810a15f2e292f2ba450810a7316e128ee5"},
+ {file = "argon2_cffi_bindings-21.2.0-pp37-pypy37_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f9f8b450ed0547e3d473fdc8612083fd08dd2120d6ac8f73828df9b7d45bb351"},
+ {file = "argon2_cffi_bindings-21.2.0-pp37-pypy37_pp73-win_amd64.whl", hash = "sha256:93f9bf70084f97245ba10ee36575f0c3f1e7d7724d67d8e5b08e61787c320ed7"},
+ {file = "argon2_cffi_bindings-21.2.0-pp38-pypy38_pp73-macosx_10_9_x86_64.whl", hash = "sha256:3b9ef65804859d335dc6b31582cad2c5166f0c3e7975f324d9ffaa34ee7e6583"},
+ {file = "argon2_cffi_bindings-21.2.0-pp38-pypy38_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d4966ef5848d820776f5f562a7d45fdd70c2f330c961d0d745b784034bd9f48d"},
+ {file = "argon2_cffi_bindings-21.2.0-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:20ef543a89dee4db46a1a6e206cd015360e5a75822f76df533845c3cbaf72670"},
+ {file = "argon2_cffi_bindings-21.2.0-pp38-pypy38_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ed2937d286e2ad0cc79a7087d3c272832865f779430e0cc2b4f3718d3159b0cb"},
+ {file = "argon2_cffi_bindings-21.2.0-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:5e00316dabdaea0b2dd82d141cc66889ced0cdcbfa599e8b471cf22c620c329a"},
+]
+
+[package.dependencies]
+cffi = ">=1.0.1"
+
+[package.extras]
+dev = ["cogapp", "pre-commit", "pytest", "wheel"]
+tests = ["pytest"]
+
+[[package]]
+name = "async-timeout"
+version = "4.0.3"
+description = "Timeout context manager for asyncio programs"
+optional = false
+python-versions = ">=3.7"
+files = [
+ {file = "async-timeout-4.0.3.tar.gz", hash = "sha256:4640d96be84d82d02ed59ea2b7105a0f7b33abe8703703cd0ab0bf87c427522f"},
+ {file = "async_timeout-4.0.3-py3-none-any.whl", hash = "sha256:7405140ff1230c310e51dc27b3145b9092d659ce68ff733fb0cefe3ee42be028"},
+]
+
+[[package]]
+name = "asyncpg"
+version = "0.29.0"
+description = "An asyncio PostgreSQL driver"
+optional = false
+python-versions = ">=3.8.0"
+files = [
+ {file = "asyncpg-0.29.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:72fd0ef9f00aeed37179c62282a3d14262dbbafb74ec0ba16e1b1864d8a12169"},
+ {file = "asyncpg-0.29.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:52e8f8f9ff6e21f9b39ca9f8e3e33a5fcdceaf5667a8c5c32bee158e313be385"},
+ {file = "asyncpg-0.29.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a9e6823a7012be8b68301342ba33b4740e5a166f6bbda0aee32bc01638491a22"},
+ {file = "asyncpg-0.29.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:746e80d83ad5d5464cfbf94315eb6744222ab00aa4e522b704322fb182b83610"},
+ {file = "asyncpg-0.29.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:ff8e8109cd6a46ff852a5e6bab8b0a047d7ea42fcb7ca5ae6eaae97d8eacf397"},
+ {file = "asyncpg-0.29.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:97eb024685b1d7e72b1972863de527c11ff87960837919dac6e34754768098eb"},
+ {file = "asyncpg-0.29.0-cp310-cp310-win32.whl", hash = "sha256:5bbb7f2cafd8d1fa3e65431833de2642f4b2124be61a449fa064e1a08d27e449"},
+ {file = "asyncpg-0.29.0-cp310-cp310-win_amd64.whl", hash = "sha256:76c3ac6530904838a4b650b2880f8e7af938ee049e769ec2fba7cd66469d7772"},
+ {file = "asyncpg-0.29.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:d4900ee08e85af01adb207519bb4e14b1cae8fd21e0ccf80fac6aa60b6da37b4"},
+ {file = "asyncpg-0.29.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:a65c1dcd820d5aea7c7d82a3fdcb70e096f8f70d1a8bf93eb458e49bfad036ac"},
+ {file = "asyncpg-0.29.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5b52e46f165585fd6af4863f268566668407c76b2c72d366bb8b522fa66f1870"},
+ {file = "asyncpg-0.29.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dc600ee8ef3dd38b8d67421359779f8ccec30b463e7aec7ed481c8346decf99f"},
+ {file = "asyncpg-0.29.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:039a261af4f38f949095e1e780bae84a25ffe3e370175193174eb08d3cecab23"},
+ {file = "asyncpg-0.29.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:6feaf2d8f9138d190e5ec4390c1715c3e87b37715cd69b2c3dfca616134efd2b"},
+ {file = "asyncpg-0.29.0-cp311-cp311-win32.whl", hash = "sha256:1e186427c88225ef730555f5fdda6c1812daa884064bfe6bc462fd3a71c4b675"},
+ {file = "asyncpg-0.29.0-cp311-cp311-win_amd64.whl", hash = "sha256:cfe73ffae35f518cfd6e4e5f5abb2618ceb5ef02a2365ce64f132601000587d3"},
+ {file = "asyncpg-0.29.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:6011b0dc29886ab424dc042bf9eeb507670a3b40aece3439944006aafe023178"},
+ {file = "asyncpg-0.29.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:b544ffc66b039d5ec5a7454667f855f7fec08e0dfaf5a5490dfafbb7abbd2cfb"},
+ {file = "asyncpg-0.29.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d84156d5fb530b06c493f9e7635aa18f518fa1d1395ef240d211cb563c4e2364"},
+ {file = "asyncpg-0.29.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:54858bc25b49d1114178d65a88e48ad50cb2b6f3e475caa0f0c092d5f527c106"},
+ {file = "asyncpg-0.29.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:bde17a1861cf10d5afce80a36fca736a86769ab3579532c03e45f83ba8a09c59"},
+ {file = "asyncpg-0.29.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:37a2ec1b9ff88d8773d3eb6d3784dc7e3fee7756a5317b67f923172a4748a175"},
+ {file = "asyncpg-0.29.0-cp312-cp312-win32.whl", hash = "sha256:bb1292d9fad43112a85e98ecdc2e051602bce97c199920586be83254d9dafc02"},
+ {file = "asyncpg-0.29.0-cp312-cp312-win_amd64.whl", hash = "sha256:2245be8ec5047a605e0b454c894e54bf2ec787ac04b1cb7e0d3c67aa1e32f0fe"},
+ {file = "asyncpg-0.29.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:0009a300cae37b8c525e5b449233d59cd9868fd35431abc470a3e364d2b85cb9"},
+ {file = "asyncpg-0.29.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:5cad1324dbb33f3ca0cd2074d5114354ed3be2b94d48ddfd88af75ebda7c43cc"},
+ {file = "asyncpg-0.29.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:012d01df61e009015944ac7543d6ee30c2dc1eb2f6b10b62a3f598beb6531548"},
+ {file = "asyncpg-0.29.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:000c996c53c04770798053e1730d34e30cb645ad95a63265aec82da9093d88e7"},
+ {file = "asyncpg-0.29.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:e0bfe9c4d3429706cf70d3249089de14d6a01192d617e9093a8e941fea8ee775"},
+ {file = "asyncpg-0.29.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:642a36eb41b6313ffa328e8a5c5c2b5bea6ee138546c9c3cf1bffaad8ee36dd9"},
+ {file = "asyncpg-0.29.0-cp38-cp38-win32.whl", hash = "sha256:a921372bbd0aa3a5822dd0409da61b4cd50df89ae85150149f8c119f23e8c408"},
+ {file = "asyncpg-0.29.0-cp38-cp38-win_amd64.whl", hash = "sha256:103aad2b92d1506700cbf51cd8bb5441e7e72e87a7b3a2ca4e32c840f051a6a3"},
+ {file = "asyncpg-0.29.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:5340dd515d7e52f4c11ada32171d87c05570479dc01dc66d03ee3e150fb695da"},
+ {file = "asyncpg-0.29.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:e17b52c6cf83e170d3d865571ba574577ab8e533e7361a2b8ce6157d02c665d3"},
+ {file = "asyncpg-0.29.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f100d23f273555f4b19b74a96840aa27b85e99ba4b1f18d4ebff0734e78dc090"},
+ {file = "asyncpg-0.29.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:48e7c58b516057126b363cec8ca02b804644fd012ef8e6c7e23386b7d5e6ce83"},
+ {file = "asyncpg-0.29.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:f9ea3f24eb4c49a615573724d88a48bd1b7821c890c2effe04f05382ed9e8810"},
+ {file = "asyncpg-0.29.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:8d36c7f14a22ec9e928f15f92a48207546ffe68bc412f3be718eedccdf10dc5c"},
+ {file = "asyncpg-0.29.0-cp39-cp39-win32.whl", hash = "sha256:797ab8123ebaed304a1fad4d7576d5376c3a006a4100380fb9d517f0b59c1ab2"},
+ {file = "asyncpg-0.29.0-cp39-cp39-win_amd64.whl", hash = "sha256:cce08a178858b426ae1aa8409b5cc171def45d4293626e7aa6510696d46decd8"},
+ {file = "asyncpg-0.29.0.tar.gz", hash = "sha256:d1c49e1f44fffafd9a55e1a9b101590859d881d639ea2922516f5d9c512d354e"},
+]
+
+[package.dependencies]
+async-timeout = {version = ">=4.0.3", markers = "python_version < \"3.12.0\""}
+
+[package.extras]
+docs = ["Sphinx (>=5.3.0,<5.4.0)", "sphinx-rtd-theme (>=1.2.2)", "sphinxcontrib-asyncio (>=0.3.0,<0.4.0)"]
+test = ["flake8 (>=6.1,<7.0)", "uvloop (>=0.15.3)"]
+
+[[package]]
+name = "attrs"
+version = "24.2.0"
+description = "Classes Without Boilerplate"
+optional = false
+python-versions = ">=3.7"
+files = [
+ {file = "attrs-24.2.0-py3-none-any.whl", hash = "sha256:81921eb96de3191c8258c199618104dd27ac608d9366f5e35d011eae1867ede2"},
+ {file = "attrs-24.2.0.tar.gz", hash = "sha256:5cfb1b9148b5b086569baec03f20d7b6bf3bcacc9a42bebf87ffaaca362f6346"},
+]
+
+[package.extras]
+benchmark = ["cloudpickle", "hypothesis", "mypy (>=1.11.1)", "pympler", "pytest (>=4.3.0)", "pytest-codspeed", "pytest-mypy-plugins", "pytest-xdist[psutil]"]
+cov = ["cloudpickle", "coverage[toml] (>=5.3)", "hypothesis", "mypy (>=1.11.1)", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins", "pytest-xdist[psutil]"]
+dev = ["cloudpickle", "hypothesis", "mypy (>=1.11.1)", "pre-commit", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins", "pytest-xdist[psutil]"]
+docs = ["cogapp", "furo", "myst-parser", "sphinx", "sphinx-notfound-page", "sphinxcontrib-towncrier", "towncrier (<24.7)"]
+tests = ["cloudpickle", "hypothesis", "mypy (>=1.11.1)", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins", "pytest-xdist[psutil]"]
+tests-mypy = ["mypy (>=1.11.1)", "pytest-mypy-plugins"]
+
+[[package]]
+name = "autoscraper"
+version = "1.1.14"
+description = "A Smart, Automatic, Fast and Lightweight Web Scraper for Python"
+optional = false
+python-versions = ">=3.6"
+files = [
+ {file = "autoscraper-1.1.14-py3-none-any.whl", hash = "sha256:fc0265723f6bcc80ee908d7be8d5318129f3388d2830d049fc0bda6c25695cf9"},
+ {file = "autoscraper-1.1.14.tar.gz", hash = "sha256:281901477fb69aa09aa235abbd15bb38c46df1682c2cad504d0ac1ee0b6b81d0"},
+]
+
+[package.dependencies]
+bs4 = "*"
+lxml = "*"
+requests = "*"
+
+[[package]]
+name = "babel"
+version = "2.16.0"
+description = "Internationalization utilities"
+optional = false
+python-versions = ">=3.8"
+files = [
+ {file = "babel-2.16.0-py3-none-any.whl", hash = "sha256:368b5b98b37c06b7daf6696391c3240c938b37767d4584413e8438c5c435fa8b"},
+ {file = "babel-2.16.0.tar.gz", hash = "sha256:d1f3554ca26605fe173f3de0c65f750f5a42f924499bf134de6423582298e316"},
+]
+
+[package.extras]
+dev = ["freezegun (>=1.0,<2.0)", "pytest (>=6.0)", "pytest-cov"]
+
[[package]]
name = "bcrypt"
-version = "4.0.1"
+version = "4.1.2"
description = "Modern password hashing for your software and your servers"
optional = false
-python-versions = ">=3.6"
+python-versions = ">=3.7"
files = [
- {file = "bcrypt-4.0.1-cp36-abi3-macosx_10_10_universal2.whl", hash = "sha256:b1023030aec778185a6c16cf70f359cbb6e0c289fd564a7cfa29e727a1c38f8f"},
- {file = "bcrypt-4.0.1-cp36-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_24_aarch64.whl", hash = "sha256:08d2947c490093a11416df18043c27abe3921558d2c03e2076ccb28a116cb6d0"},
- {file = "bcrypt-4.0.1-cp36-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0eaa47d4661c326bfc9d08d16debbc4edf78778e6aaba29c1bc7ce67214d4410"},
- {file = "bcrypt-4.0.1-cp36-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ae88eca3024bb34bb3430f964beab71226e761f51b912de5133470b649d82344"},
- {file = "bcrypt-4.0.1-cp36-abi3-manylinux_2_24_x86_64.whl", hash = "sha256:a522427293d77e1c29e303fc282e2d71864579527a04ddcfda6d4f8396c6c36a"},
- {file = "bcrypt-4.0.1-cp36-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:fbdaec13c5105f0c4e5c52614d04f0bca5f5af007910daa8b6b12095edaa67b3"},
- {file = "bcrypt-4.0.1-cp36-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:ca3204d00d3cb2dfed07f2d74a25f12fc12f73e606fcaa6975d1f7ae69cacbb2"},
- {file = "bcrypt-4.0.1-cp36-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:089098effa1bc35dc055366740a067a2fc76987e8ec75349eb9484061c54f535"},
- {file = "bcrypt-4.0.1-cp36-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:e9a51bbfe7e9802b5f3508687758b564069ba937748ad7b9e890086290d2f79e"},
- {file = "bcrypt-4.0.1-cp36-abi3-win32.whl", hash = "sha256:2caffdae059e06ac23fce178d31b4a702f2a3264c20bfb5ff541b338194d8fab"},
- {file = "bcrypt-4.0.1-cp36-abi3-win_amd64.whl", hash = "sha256:8a68f4341daf7522fe8d73874de8906f3a339048ba406be6ddc1b3ccb16fc0d9"},
- {file = "bcrypt-4.0.1-pp37-pypy37_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bf4fa8b2ca74381bb5442c089350f09a3f17797829d958fad058d6e44d9eb83c"},
- {file = "bcrypt-4.0.1-pp37-pypy37_pp73-manylinux_2_24_x86_64.whl", hash = "sha256:67a97e1c405b24f19d08890e7ae0c4f7ce1e56a712a016746c8b2d7732d65d4b"},
- {file = "bcrypt-4.0.1-pp37-pypy37_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:b3b85202d95dd568efcb35b53936c5e3b3600c7cdcc6115ba461df3a8e89f38d"},
- {file = "bcrypt-4.0.1-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cbb03eec97496166b704ed663a53680ab57c5084b2fc98ef23291987b525cb7d"},
- {file = "bcrypt-4.0.1-pp38-pypy38_pp73-manylinux_2_24_x86_64.whl", hash = "sha256:5ad4d32a28b80c5fa6671ccfb43676e8c1cc232887759d1cd7b6f56ea4355215"},
- {file = "bcrypt-4.0.1-pp38-pypy38_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:b57adba8a1444faf784394de3436233728a1ecaeb6e07e8c22c8848f179b893c"},
- {file = "bcrypt-4.0.1-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:705b2cea8a9ed3d55b4491887ceadb0106acf7c6387699fca771af56b1cdeeda"},
- {file = "bcrypt-4.0.1-pp39-pypy39_pp73-manylinux_2_24_x86_64.whl", hash = "sha256:2b3ac11cf45161628f1f3733263e63194f22664bf4d0c0f3ab34099c02134665"},
- {file = "bcrypt-4.0.1-pp39-pypy39_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:3100851841186c25f127731b9fa11909ab7b1df6fc4b9f8353f4f1fd952fbf71"},
- {file = "bcrypt-4.0.1.tar.gz", hash = "sha256:27d375903ac8261cfe4047f6709d16f7d18d39b1ec92aaf72af989552a650ebd"},
+ {file = "bcrypt-4.1.2-cp37-abi3-macosx_10_12_universal2.whl", hash = "sha256:ac621c093edb28200728a9cca214d7e838529e557027ef0581685909acd28b5e"},
+ {file = "bcrypt-4.1.2-cp37-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ea505c97a5c465ab8c3ba75c0805a102ce526695cd6818c6de3b1a38f6f60da1"},
+ {file = "bcrypt-4.1.2-cp37-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:57fa9442758da926ed33a91644649d3e340a71e2d0a5a8de064fb621fd5a3326"},
+ {file = "bcrypt-4.1.2-cp37-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:eb3bd3321517916696233b5e0c67fd7d6281f0ef48e66812db35fc963a422a1c"},
+ {file = "bcrypt-4.1.2-cp37-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:6cad43d8c63f34b26aef462b6f5e44fdcf9860b723d2453b5d391258c4c8e966"},
+ {file = "bcrypt-4.1.2-cp37-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:44290ccc827d3a24604f2c8bcd00d0da349e336e6503656cb8192133e27335e2"},
+ {file = "bcrypt-4.1.2-cp37-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:732b3920a08eacf12f93e6b04ea276c489f1c8fb49344f564cca2adb663b3e4c"},
+ {file = "bcrypt-4.1.2-cp37-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:1c28973decf4e0e69cee78c68e30a523be441972c826703bb93099868a8ff5b5"},
+ {file = "bcrypt-4.1.2-cp37-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:b8df79979c5bae07f1db22dcc49cc5bccf08a0380ca5c6f391cbb5790355c0b0"},
+ {file = "bcrypt-4.1.2-cp37-abi3-win32.whl", hash = "sha256:fbe188b878313d01b7718390f31528be4010fed1faa798c5a1d0469c9c48c369"},
+ {file = "bcrypt-4.1.2-cp37-abi3-win_amd64.whl", hash = "sha256:9800ae5bd5077b13725e2e3934aa3c9c37e49d3ea3d06318010aa40f54c63551"},
+ {file = "bcrypt-4.1.2-cp39-abi3-macosx_10_12_universal2.whl", hash = "sha256:71b8be82bc46cedd61a9f4ccb6c1a493211d031415a34adde3669ee1b0afbb63"},
+ {file = "bcrypt-4.1.2-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:68e3c6642077b0c8092580c819c1684161262b2e30c4f45deb000c38947bf483"},
+ {file = "bcrypt-4.1.2-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:387e7e1af9a4dd636b9505a465032f2f5cb8e61ba1120e79a0e1cd0b512f3dfc"},
+ {file = "bcrypt-4.1.2-cp39-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:f70d9c61f9c4ca7d57f3bfe88a5ccf62546ffbadf3681bb1e268d9d2e41c91a7"},
+ {file = "bcrypt-4.1.2-cp39-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:2a298db2a8ab20056120b45e86c00a0a5eb50ec4075b6142db35f593b97cb3fb"},
+ {file = "bcrypt-4.1.2-cp39-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:ba55e40de38a24e2d78d34c2d36d6e864f93e0d79d0b6ce915e4335aa81d01b1"},
+ {file = "bcrypt-4.1.2-cp39-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:3566a88234e8de2ccae31968127b0ecccbb4cddb629da744165db72b58d88ca4"},
+ {file = "bcrypt-4.1.2-cp39-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:b90e216dc36864ae7132cb151ffe95155a37a14e0de3a8f64b49655dd959ff9c"},
+ {file = "bcrypt-4.1.2-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:69057b9fc5093ea1ab00dd24ede891f3e5e65bee040395fb1e66ee196f9c9b4a"},
+ {file = "bcrypt-4.1.2-cp39-abi3-win32.whl", hash = "sha256:02d9ef8915f72dd6daaef40e0baeef8a017ce624369f09754baf32bb32dba25f"},
+ {file = "bcrypt-4.1.2-cp39-abi3-win_amd64.whl", hash = "sha256:be3ab1071662f6065899fe08428e45c16aa36e28bc42921c4901a191fda6ee42"},
+ {file = "bcrypt-4.1.2-pp310-pypy310_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:d75fc8cd0ba23f97bae88a6ec04e9e5351ff3c6ad06f38fe32ba50cbd0d11946"},
+ {file = "bcrypt-4.1.2-pp310-pypy310_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:a97e07e83e3262599434816f631cc4c7ca2aa8e9c072c1b1a7fec2ae809a1d2d"},
+ {file = "bcrypt-4.1.2-pp39-pypy39_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:e51c42750b7585cee7892c2614be0d14107fad9581d1738d954a262556dd1aab"},
+ {file = "bcrypt-4.1.2-pp39-pypy39_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:ba4e4cc26610581a6329b3937e02d319f5ad4b85b074846bf4fef8a8cf51e7bb"},
+ {file = "bcrypt-4.1.2.tar.gz", hash = "sha256:33313a1200a3ae90b75587ceac502b048b840fc69e7f7a0905b5f87fac7a1258"},
]
[package.extras]
tests = ["pytest (>=3.2.1,!=3.3.0)"]
typecheck = ["mypy"]
+[[package]]
+name = "beautifulsoup4"
+version = "4.12.3"
+description = "Screen-scraping library"
+optional = false
+python-versions = ">=3.6.0"
+files = [
+ {file = "beautifulsoup4-4.12.3-py3-none-any.whl", hash = "sha256:b80878c9f40111313e55da8ba20bdba06d8fa3969fc68304167741bbf9e082ed"},
+ {file = "beautifulsoup4-4.12.3.tar.gz", hash = "sha256:74e3d1928edc070d21748185c46e3fb33490f22f52a3addee9aee0f4f7781051"},
+]
+
+[package.dependencies]
+soupsieve = ">1.2"
+
+[package.extras]
+cchardet = ["cchardet"]
+chardet = ["chardet"]
+charset-normalizer = ["charset-normalizer"]
+html5lib = ["html5lib"]
+lxml = ["lxml"]
+
+[[package]]
+name = "bs4"
+version = "0.0.2"
+description = "Dummy package for Beautiful Soup (beautifulsoup4)"
+optional = false
+python-versions = "*"
+files = [
+ {file = "bs4-0.0.2-py2.py3-none-any.whl", hash = "sha256:abf8742c0805ef7f662dce4b51cca104cffe52b835238afc169142ab9b3fbccc"},
+ {file = "bs4-0.0.2.tar.gz", hash = "sha256:a48685c58f50fe127722417bae83fe6badf500d54b55f7e39ffe43b798653925"},
+]
+
+[package.dependencies]
+beautifulsoup4 = "*"
+
[[package]]
name = "cachetools"
version = "5.4.0"
@@ -108,6 +500,85 @@ files = [
{file = "certifi-2024.7.4.tar.gz", hash = "sha256:5a1e7645bc0ec61a09e26c36f6106dd4cf40c6db3a1fb6352b0244e7fb057c7b"},
]
+[[package]]
+name = "cffi"
+version = "1.17.1"
+description = "Foreign Function Interface for Python calling C code."
+optional = false
+python-versions = ">=3.8"
+files = [
+ {file = "cffi-1.17.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:df8b1c11f177bc2313ec4b2d46baec87a5f3e71fc8b45dab2ee7cae86d9aba14"},
+ {file = "cffi-1.17.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:8f2cdc858323644ab277e9bb925ad72ae0e67f69e804f4898c070998d50b1a67"},
+ {file = "cffi-1.17.1-cp310-cp310-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:edae79245293e15384b51f88b00613ba9f7198016a5948b5dddf4917d4d26382"},
+ {file = "cffi-1.17.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:45398b671ac6d70e67da8e4224a065cec6a93541bb7aebe1b198a61b58c7b702"},
+ {file = "cffi-1.17.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ad9413ccdeda48c5afdae7e4fa2192157e991ff761e7ab8fdd8926f40b160cc3"},
+ {file = "cffi-1.17.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5da5719280082ac6bd9aa7becb3938dc9f9cbd57fac7d2871717b1feb0902ab6"},
+ {file = "cffi-1.17.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2bb1a08b8008b281856e5971307cc386a8e9c5b625ac297e853d36da6efe9c17"},
+ {file = "cffi-1.17.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:045d61c734659cc045141be4bae381a41d89b741f795af1dd018bfb532fd0df8"},
+ {file = "cffi-1.17.1-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:6883e737d7d9e4899a8a695e00ec36bd4e5e4f18fabe0aca0efe0a4b44cdb13e"},
+ {file = "cffi-1.17.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:6b8b4a92e1c65048ff98cfe1f735ef8f1ceb72e3d5f0c25fdb12087a23da22be"},
+ {file = "cffi-1.17.1-cp310-cp310-win32.whl", hash = "sha256:c9c3d058ebabb74db66e431095118094d06abf53284d9c81f27300d0e0d8bc7c"},
+ {file = "cffi-1.17.1-cp310-cp310-win_amd64.whl", hash = "sha256:0f048dcf80db46f0098ccac01132761580d28e28bc0f78ae0d58048063317e15"},
+ {file = "cffi-1.17.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:a45e3c6913c5b87b3ff120dcdc03f6131fa0065027d0ed7ee6190736a74cd401"},
+ {file = "cffi-1.17.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:30c5e0cb5ae493c04c8b42916e52ca38079f1b235c2f8ae5f4527b963c401caf"},
+ {file = "cffi-1.17.1-cp311-cp311-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f75c7ab1f9e4aca5414ed4d8e5c0e303a34f4421f8a0d47a4d019ceff0ab6af4"},
+ {file = "cffi-1.17.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a1ed2dd2972641495a3ec98445e09766f077aee98a1c896dcb4ad0d303628e41"},
+ {file = "cffi-1.17.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:46bf43160c1a35f7ec506d254e5c890f3c03648a4dbac12d624e4490a7046cd1"},
+ {file = "cffi-1.17.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a24ed04c8ffd54b0729c07cee15a81d964e6fee0e3d4d342a27b020d22959dc6"},
+ {file = "cffi-1.17.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:610faea79c43e44c71e1ec53a554553fa22321b65fae24889706c0a84d4ad86d"},
+ {file = "cffi-1.17.1-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:a9b15d491f3ad5d692e11f6b71f7857e7835eb677955c00cc0aefcd0669adaf6"},
+ {file = "cffi-1.17.1-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:de2ea4b5833625383e464549fec1bc395c1bdeeb5f25c4a3a82b5a8c756ec22f"},
+ {file = "cffi-1.17.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:fc48c783f9c87e60831201f2cce7f3b2e4846bf4d8728eabe54d60700b318a0b"},
+ {file = "cffi-1.17.1-cp311-cp311-win32.whl", hash = "sha256:85a950a4ac9c359340d5963966e3e0a94a676bd6245a4b55bc43949eee26a655"},
+ {file = "cffi-1.17.1-cp311-cp311-win_amd64.whl", hash = "sha256:caaf0640ef5f5517f49bc275eca1406b0ffa6aa184892812030f04c2abf589a0"},
+ {file = "cffi-1.17.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:805b4371bf7197c329fcb3ead37e710d1bca9da5d583f5073b799d5c5bd1eee4"},
+ {file = "cffi-1.17.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:733e99bc2df47476e3848417c5a4540522f234dfd4ef3ab7fafdf555b082ec0c"},
+ {file = "cffi-1.17.1-cp312-cp312-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1257bdabf294dceb59f5e70c64a3e2f462c30c7ad68092d01bbbfb1c16b1ba36"},
+ {file = "cffi-1.17.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:da95af8214998d77a98cc14e3a3bd00aa191526343078b530ceb0bd710fb48a5"},
+ {file = "cffi-1.17.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d63afe322132c194cf832bfec0dc69a99fb9bb6bbd550f161a49e9e855cc78ff"},
+ {file = "cffi-1.17.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f79fc4fc25f1c8698ff97788206bb3c2598949bfe0fef03d299eb1b5356ada99"},
+ {file = "cffi-1.17.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b62ce867176a75d03a665bad002af8e6d54644fad99a3c70905c543130e39d93"},
+ {file = "cffi-1.17.1-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:386c8bf53c502fff58903061338ce4f4950cbdcb23e2902d86c0f722b786bbe3"},
+ {file = "cffi-1.17.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:4ceb10419a9adf4460ea14cfd6bc43d08701f0835e979bf821052f1805850fe8"},
+ {file = "cffi-1.17.1-cp312-cp312-win32.whl", hash = "sha256:a08d7e755f8ed21095a310a693525137cfe756ce62d066e53f502a83dc550f65"},
+ {file = "cffi-1.17.1-cp312-cp312-win_amd64.whl", hash = "sha256:51392eae71afec0d0c8fb1a53b204dbb3bcabcb3c9b807eedf3e1e6ccf2de903"},
+ {file = "cffi-1.17.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:f3a2b4222ce6b60e2e8b337bb9596923045681d71e5a082783484d845390938e"},
+ {file = "cffi-1.17.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:0984a4925a435b1da406122d4d7968dd861c1385afe3b45ba82b750f229811e2"},
+ {file = "cffi-1.17.1-cp313-cp313-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d01b12eeeb4427d3110de311e1774046ad344f5b1a7403101878976ecd7a10f3"},
+ {file = "cffi-1.17.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:706510fe141c86a69c8ddc029c7910003a17353970cff3b904ff0686a5927683"},
+ {file = "cffi-1.17.1-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:de55b766c7aa2e2a3092c51e0483d700341182f08e67c63630d5b6f200bb28e5"},
+ {file = "cffi-1.17.1-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c59d6e989d07460165cc5ad3c61f9fd8f1b4796eacbd81cee78957842b834af4"},
+ {file = "cffi-1.17.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dd398dbc6773384a17fe0d3e7eeb8d1a21c2200473ee6806bb5e6a8e62bb73dd"},
+ {file = "cffi-1.17.1-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:3edc8d958eb099c634dace3c7e16560ae474aa3803a5df240542b305d14e14ed"},
+ {file = "cffi-1.17.1-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:72e72408cad3d5419375fc87d289076ee319835bdfa2caad331e377589aebba9"},
+ {file = "cffi-1.17.1-cp313-cp313-win32.whl", hash = "sha256:e03eab0a8677fa80d646b5ddece1cbeaf556c313dcfac435ba11f107ba117b5d"},
+ {file = "cffi-1.17.1-cp313-cp313-win_amd64.whl", hash = "sha256:f6a16c31041f09ead72d69f583767292f750d24913dadacf5756b966aacb3f1a"},
+ {file = "cffi-1.17.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:636062ea65bd0195bc012fea9321aca499c0504409f413dc88af450b57ffd03b"},
+ {file = "cffi-1.17.1-cp38-cp38-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c7eac2ef9b63c79431bc4b25f1cd649d7f061a28808cbc6c47b534bd789ef964"},
+ {file = "cffi-1.17.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e221cf152cff04059d011ee126477f0d9588303eb57e88923578ace7baad17f9"},
+ {file = "cffi-1.17.1-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:31000ec67d4221a71bd3f67df918b1f88f676f1c3b535a7eb473255fdc0b83fc"},
+ {file = "cffi-1.17.1-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:6f17be4345073b0a7b8ea599688f692ac3ef23ce28e5df79c04de519dbc4912c"},
+ {file = "cffi-1.17.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0e2b1fac190ae3ebfe37b979cc1ce69c81f4e4fe5746bb401dca63a9062cdaf1"},
+ {file = "cffi-1.17.1-cp38-cp38-win32.whl", hash = "sha256:7596d6620d3fa590f677e9ee430df2958d2d6d6de2feeae5b20e82c00b76fbf8"},
+ {file = "cffi-1.17.1-cp38-cp38-win_amd64.whl", hash = "sha256:78122be759c3f8a014ce010908ae03364d00a1f81ab5c7f4a7a5120607ea56e1"},
+ {file = "cffi-1.17.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:b2ab587605f4ba0bf81dc0cb08a41bd1c0a5906bd59243d56bad7668a6fc6c16"},
+ {file = "cffi-1.17.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:28b16024becceed8c6dfbc75629e27788d8a3f9030691a1dbf9821a128b22c36"},
+ {file = "cffi-1.17.1-cp39-cp39-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1d599671f396c4723d016dbddb72fe8e0397082b0a77a4fab8028923bec050e8"},
+ {file = "cffi-1.17.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ca74b8dbe6e8e8263c0ffd60277de77dcee6c837a3d0881d8c1ead7268c9e576"},
+ {file = "cffi-1.17.1-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f7f5baafcc48261359e14bcd6d9bff6d4b28d9103847c9e136694cb0501aef87"},
+ {file = "cffi-1.17.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:98e3969bcff97cae1b2def8ba499ea3d6f31ddfdb7635374834cf89a1a08ecf0"},
+ {file = "cffi-1.17.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cdf5ce3acdfd1661132f2a9c19cac174758dc2352bfe37d98aa7512c6b7178b3"},
+ {file = "cffi-1.17.1-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:9755e4345d1ec879e3849e62222a18c7174d65a6a92d5b346b1863912168b595"},
+ {file = "cffi-1.17.1-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:f1e22e8c4419538cb197e4dd60acc919d7696e5ef98ee4da4e01d3f8cfa4cc5a"},
+ {file = "cffi-1.17.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:c03e868a0b3bc35839ba98e74211ed2b05d2119be4e8a0f224fba9384f1fe02e"},
+ {file = "cffi-1.17.1-cp39-cp39-win32.whl", hash = "sha256:e31ae45bc2e29f6b2abd0de1cc3b9d5205aa847cafaecb8af1476a609a2f6eb7"},
+ {file = "cffi-1.17.1-cp39-cp39-win_amd64.whl", hash = "sha256:d016c76bdd850f3c626af19b0542c9677ba156e4ee4fccfdd7848803533ef662"},
+ {file = "cffi-1.17.1.tar.gz", hash = "sha256:1c39c6016c32bc48dd54561950ebd6836e1670f2ae46128f67cf49e789c52824"},
+]
+
+[package.dependencies]
+pycparser = "*"
+
[[package]]
name = "cfgv"
version = "3.4.0"
@@ -318,6 +789,55 @@ files = [
[package.extras]
toml = ["tomli"]
+[[package]]
+name = "cryptography"
+version = "43.0.1"
+description = "cryptography is a package which provides cryptographic recipes and primitives to Python developers."
+optional = false
+python-versions = ">=3.7"
+files = [
+ {file = "cryptography-43.0.1-cp37-abi3-macosx_10_9_universal2.whl", hash = "sha256:8385d98f6a3bf8bb2d65a73e17ed87a3ba84f6991c155691c51112075f9ffc5d"},
+ {file = "cryptography-43.0.1-cp37-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:27e613d7077ac613e399270253259d9d53872aaf657471473ebfc9a52935c062"},
+ {file = "cryptography-43.0.1-cp37-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:68aaecc4178e90719e95298515979814bda0cbada1256a4485414860bd7ab962"},
+ {file = "cryptography-43.0.1-cp37-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:de41fd81a41e53267cb020bb3a7212861da53a7d39f863585d13ea11049cf277"},
+ {file = "cryptography-43.0.1-cp37-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:f98bf604c82c416bc829e490c700ca1553eafdf2912a91e23a79d97d9801372a"},
+ {file = "cryptography-43.0.1-cp37-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:61ec41068b7b74268fa86e3e9e12b9f0c21fcf65434571dbb13d954bceb08042"},
+ {file = "cryptography-43.0.1-cp37-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:014f58110f53237ace6a408b5beb6c427b64e084eb451ef25a28308270086494"},
+ {file = "cryptography-43.0.1-cp37-abi3-win32.whl", hash = "sha256:2bd51274dcd59f09dd952afb696bf9c61a7a49dfc764c04dd33ef7a6b502a1e2"},
+ {file = "cryptography-43.0.1-cp37-abi3-win_amd64.whl", hash = "sha256:666ae11966643886c2987b3b721899d250855718d6d9ce41b521252a17985f4d"},
+ {file = "cryptography-43.0.1-cp39-abi3-macosx_10_9_universal2.whl", hash = "sha256:ac119bb76b9faa00f48128b7f5679e1d8d437365c5d26f1c2c3f0da4ce1b553d"},
+ {file = "cryptography-43.0.1-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1bbcce1a551e262dfbafb6e6252f1ae36a248e615ca44ba302df077a846a8806"},
+ {file = "cryptography-43.0.1-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:58d4e9129985185a06d849aa6df265bdd5a74ca6e1b736a77959b498e0505b85"},
+ {file = "cryptography-43.0.1-cp39-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:d03a475165f3134f773d1388aeb19c2d25ba88b6a9733c5c590b9ff7bbfa2e0c"},
+ {file = "cryptography-43.0.1-cp39-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:511f4273808ab590912a93ddb4e3914dfd8a388fed883361b02dea3791f292e1"},
+ {file = "cryptography-43.0.1-cp39-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:80eda8b3e173f0f247f711eef62be51b599b5d425c429b5d4ca6a05e9e856baa"},
+ {file = "cryptography-43.0.1-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:38926c50cff6f533f8a2dae3d7f19541432610d114a70808f0926d5aaa7121e4"},
+ {file = "cryptography-43.0.1-cp39-abi3-win32.whl", hash = "sha256:a575913fb06e05e6b4b814d7f7468c2c660e8bb16d8d5a1faf9b33ccc569dd47"},
+ {file = "cryptography-43.0.1-cp39-abi3-win_amd64.whl", hash = "sha256:d75601ad10b059ec832e78823b348bfa1a59f6b8d545db3a24fd44362a1564cb"},
+ {file = "cryptography-43.0.1-pp310-pypy310_pp73-macosx_10_9_x86_64.whl", hash = "sha256:ea25acb556320250756e53f9e20a4177515f012c9eaea17eb7587a8c4d8ae034"},
+ {file = "cryptography-43.0.1-pp310-pypy310_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:c1332724be35d23a854994ff0b66530119500b6053d0bd3363265f7e5e77288d"},
+ {file = "cryptography-43.0.1-pp310-pypy310_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:fba1007b3ef89946dbbb515aeeb41e30203b004f0b4b00e5e16078b518563289"},
+ {file = "cryptography-43.0.1-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:5b43d1ea6b378b54a1dc99dd8a2b5be47658fe9a7ce0a58ff0b55f4b43ef2b84"},
+ {file = "cryptography-43.0.1-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:88cce104c36870d70c49c7c8fd22885875d950d9ee6ab54df2745f83ba0dc365"},
+ {file = "cryptography-43.0.1-pp39-pypy39_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:9d3cdb25fa98afdd3d0892d132b8d7139e2c087da1712041f6b762e4f807cc96"},
+ {file = "cryptography-43.0.1-pp39-pypy39_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:e710bf40870f4db63c3d7d929aa9e09e4e7ee219e703f949ec4073b4294f6172"},
+ {file = "cryptography-43.0.1-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:7c05650fe8023c5ed0d46793d4b7d7e6cd9c04e68eabe5b0aeea836e37bdcec2"},
+ {file = "cryptography-43.0.1.tar.gz", hash = "sha256:203e92a75716d8cfb491dc47c79e17d0d9207ccffcbcb35f598fbe463ae3444d"},
+]
+
+[package.dependencies]
+cffi = {version = ">=1.12", markers = "platform_python_implementation != \"PyPy\""}
+
+[package.extras]
+docs = ["sphinx (>=5.3.0)", "sphinx-rtd-theme (>=1.1.1)"]
+docstest = ["pyenchant (>=1.6.11)", "readme-renderer", "sphinxcontrib-spelling (>=4.0.1)"]
+nox = ["nox"]
+pep8test = ["check-sdist", "click", "mypy", "ruff"]
+sdist = ["build"]
+ssh = ["bcrypt (>=3.1.5)"]
+test = ["certifi", "cryptography-vectors (==43.0.1)", "pretend", "pytest (>=6.2.0)", "pytest-benchmark", "pytest-cov", "pytest-xdist"]
+test-randomorder = ["pytest-randomly"]
+
[[package]]
name = "cssselect"
version = "1.2.0"
@@ -380,13 +900,13 @@ wmi = ["wmi (>=1.5.1)"]
[[package]]
name = "email-validator"
-version = "2.2.0"
+version = "2.1.2"
description = "A robust email address syntax and deliverability validation library."
optional = false
python-versions = ">=3.8"
files = [
- {file = "email_validator-2.2.0-py3-none-any.whl", hash = "sha256:561977c2d73ce3611850a06fa56b414621e0c8faa9d66f2611407d87465da631"},
- {file = "email_validator-2.2.0.tar.gz", hash = "sha256:cb690f344c617a714f22e66ae771445a1ceb46821152df8e165c5f9a364582b7"},
+ {file = "email_validator-2.1.2-py3-none-any.whl", hash = "sha256:d89f6324e13b1e39889eab7f9ca2f91dc9aebb6fa50a6d8bd4329ab50f251115"},
+ {file = "email_validator-2.1.2.tar.gz", hash = "sha256:14c0f3d343c4beda37400421b39fa411bbe33a75df20825df73ad53e06a9f04c"},
]
[package.dependencies]
@@ -445,6 +965,68 @@ typing-extensions = ">=4.8.0"
[package.extras]
all = ["email-validator (>=2.0.0)", "httpx (>=0.23.0)", "itsdangerous (>=1.1.0)", "jinja2 (>=2.11.2)", "orjson (>=3.2.1)", "pydantic-extra-types (>=2.0.0)", "pydantic-settings (>=2.0.0)", "python-multipart (>=0.0.7)", "pyyaml (>=5.3.1)", "ujson (>=4.0.1,!=4.0.2,!=4.1.0,!=4.2.0,!=4.3.0,!=5.0.0,!=5.1.0)", "uvicorn[standard] (>=0.12.0)"]
+[[package]]
+name = "fastapi-admin"
+version = "1.0.4"
+description = "A fast admin dashboard based on FastAPI and TortoiseORM with tabler ui, inspired by Django admin."
+optional = false
+python-versions = ">=3.7,<4.0"
+files = [
+ {file = "fastapi-admin-1.0.4.tar.gz", hash = "sha256:d45cb23a23fc2bbffb0e8adfb1362350a1fe808f47912253c1f067f7d7745d88"},
+ {file = "fastapi_admin-1.0.4-py3-none-any.whl", hash = "sha256:0b00dd2d72bbb5af50847cd4c49261aab999eeba7d8371786b822dce4b97ef0d"},
+]
+
+[package.dependencies]
+aiofiles = "*"
+aioredis = "*"
+Babel = "*"
+bcrypt = "*"
+fastapi = "*"
+jinja2 = "*"
+pendulum = "*"
+python-multipart = "*"
+tortoise-orm = "*"
+uvicorn = {version = "*", extras = ["standard"]}
+
+[[package]]
+name = "fastapi-cache"
+version = "0.1.0"
+description = "FastAPI simple cache"
+optional = false
+python-versions = "*"
+files = [
+ {file = "fastapi-cache-0.1.0.tar.gz", hash = "sha256:1f57e6e666672c84e3dd5d4141ec808d5339d158e10c87a87eb9ce11ff8b1735"},
+]
+
+[package.dependencies]
+aioredis = "1.3.1"
+
+[[package]]
+name = "fastapi-users"
+version = "13.0.0"
+description = "Ready-to-use and customizable users management for FastAPI"
+optional = false
+python-versions = ">=3.8"
+files = [
+ {file = "fastapi_users-13.0.0-py3-none-any.whl", hash = "sha256:e6246529e3080a5b50e5afeed1e996663b661f1dc791a1ac478925cb5bfc0fa0"},
+ {file = "fastapi_users-13.0.0.tar.gz", hash = "sha256:b397c815b7051c8fd4b560fbeee707acd28e00bd3e8f25c292ad158a1e47e884"},
+]
+
+[package.dependencies]
+email-validator = ">=1.1.0,<2.2"
+fastapi = ">=0.65.2"
+httpx-oauth = {version = ">=0.13", optional = true, markers = "extra == \"oauth\""}
+makefun = ">=1.11.2,<2.0.0"
+pwdlib = {version = "0.2.0", extras = ["argon2", "bcrypt"]}
+pyjwt = {version = "2.8.0", extras = ["crypto"]}
+python-multipart = "0.0.9"
+
+[package.extras]
+beanie = ["fastapi-users-db-beanie (>=3.0.0)"]
+oauth = ["httpx-oauth (>=0.13)"]
+redis = ["redis (>=4.3.3,<6.0.0)"]
+sqlalchemy = ["fastapi-users-db-sqlalchemy (>=6.0.0)"]
+
[[package]]
name = "filelock"
version = "3.15.4"
@@ -461,6 +1043,92 @@ docs = ["furo (>=2023.9.10)", "sphinx (>=7.2.6)", "sphinx-autodoc-typehints (>=1
testing = ["covdefaults (>=2.3)", "coverage (>=7.3.2)", "diff-cover (>=8.0.1)", "pytest (>=7.4.3)", "pytest-asyncio (>=0.21)", "pytest-cov (>=4.1)", "pytest-mock (>=3.12)", "pytest-timeout (>=2.2)", "virtualenv (>=20.26.2)"]
typing = ["typing-extensions (>=4.8)"]
+[[package]]
+name = "frozenlist"
+version = "1.4.1"
+description = "A list-like structure which implements collections.abc.MutableSequence"
+optional = false
+python-versions = ">=3.8"
+files = [
+ {file = "frozenlist-1.4.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:f9aa1878d1083b276b0196f2dfbe00c9b7e752475ed3b682025ff20c1c1f51ac"},
+ {file = "frozenlist-1.4.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:29acab3f66f0f24674b7dc4736477bcd4bc3ad4b896f5f45379a67bce8b96868"},
+ {file = "frozenlist-1.4.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:74fb4bee6880b529a0c6560885fce4dc95936920f9f20f53d99a213f7bf66776"},
+ {file = "frozenlist-1.4.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:590344787a90ae57d62511dd7c736ed56b428f04cd8c161fcc5e7232c130c69a"},
+ {file = "frozenlist-1.4.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:068b63f23b17df8569b7fdca5517edef76171cf3897eb68beb01341131fbd2ad"},
+ {file = "frozenlist-1.4.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5c849d495bf5154cd8da18a9eb15db127d4dba2968d88831aff6f0331ea9bd4c"},
+ {file = "frozenlist-1.4.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9750cc7fe1ae3b1611bb8cfc3f9ec11d532244235d75901fb6b8e42ce9229dfe"},
+ {file = "frozenlist-1.4.1-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a9b2de4cf0cdd5bd2dee4c4f63a653c61d2408055ab77b151c1957f221cabf2a"},
+ {file = "frozenlist-1.4.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:0633c8d5337cb5c77acbccc6357ac49a1770b8c487e5b3505c57b949b4b82e98"},
+ {file = "frozenlist-1.4.1-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:27657df69e8801be6c3638054e202a135c7f299267f1a55ed3a598934f6c0d75"},
+ {file = "frozenlist-1.4.1-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:f9a3ea26252bd92f570600098783d1371354d89d5f6b7dfd87359d669f2109b5"},
+ {file = "frozenlist-1.4.1-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:4f57dab5fe3407b6c0c1cc907ac98e8a189f9e418f3b6e54d65a718aaafe3950"},
+ {file = "frozenlist-1.4.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:e02a0e11cf6597299b9f3bbd3f93d79217cb90cfd1411aec33848b13f5c656cc"},
+ {file = "frozenlist-1.4.1-cp310-cp310-win32.whl", hash = "sha256:a828c57f00f729620a442881cc60e57cfcec6842ba38e1b19fd3e47ac0ff8dc1"},
+ {file = "frozenlist-1.4.1-cp310-cp310-win_amd64.whl", hash = "sha256:f56e2333dda1fe0f909e7cc59f021eba0d2307bc6f012a1ccf2beca6ba362439"},
+ {file = "frozenlist-1.4.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:a0cb6f11204443f27a1628b0e460f37fb30f624be6051d490fa7d7e26d4af3d0"},
+ {file = "frozenlist-1.4.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:b46c8ae3a8f1f41a0d2ef350c0b6e65822d80772fe46b653ab6b6274f61d4a49"},
+ {file = "frozenlist-1.4.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:fde5bd59ab5357e3853313127f4d3565fc7dad314a74d7b5d43c22c6a5ed2ced"},
+ {file = "frozenlist-1.4.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:722e1124aec435320ae01ee3ac7bec11a5d47f25d0ed6328f2273d287bc3abb0"},
+ {file = "frozenlist-1.4.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:2471c201b70d58a0f0c1f91261542a03d9a5e088ed3dc6c160d614c01649c106"},
+ {file = "frozenlist-1.4.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c757a9dd70d72b076d6f68efdbb9bc943665ae954dad2801b874c8c69e185068"},
+ {file = "frozenlist-1.4.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f146e0911cb2f1da549fc58fc7bcd2b836a44b79ef871980d605ec392ff6b0d2"},
+ {file = "frozenlist-1.4.1-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4f9c515e7914626b2a2e1e311794b4c35720a0be87af52b79ff8e1429fc25f19"},
+ {file = "frozenlist-1.4.1-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:c302220494f5c1ebeb0912ea782bcd5e2f8308037b3c7553fad0e48ebad6ad82"},
+ {file = "frozenlist-1.4.1-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:442acde1e068288a4ba7acfe05f5f343e19fac87bfc96d89eb886b0363e977ec"},
+ {file = "frozenlist-1.4.1-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:1b280e6507ea8a4fa0c0a7150b4e526a8d113989e28eaaef946cc77ffd7efc0a"},
+ {file = "frozenlist-1.4.1-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:fe1a06da377e3a1062ae5fe0926e12b84eceb8a50b350ddca72dc85015873f74"},
+ {file = "frozenlist-1.4.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:db9e724bebd621d9beca794f2a4ff1d26eed5965b004a97f1f1685a173b869c2"},
+ {file = "frozenlist-1.4.1-cp311-cp311-win32.whl", hash = "sha256:e774d53b1a477a67838a904131c4b0eef6b3d8a651f8b138b04f748fccfefe17"},
+ {file = "frozenlist-1.4.1-cp311-cp311-win_amd64.whl", hash = "sha256:fb3c2db03683b5767dedb5769b8a40ebb47d6f7f45b1b3e3b4b51ec8ad9d9825"},
+ {file = "frozenlist-1.4.1-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:1979bc0aeb89b33b588c51c54ab0161791149f2461ea7c7c946d95d5f93b56ae"},
+ {file = "frozenlist-1.4.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:cc7b01b3754ea68a62bd77ce6020afaffb44a590c2289089289363472d13aedb"},
+ {file = "frozenlist-1.4.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:c9c92be9fd329ac801cc420e08452b70e7aeab94ea4233a4804f0915c14eba9b"},
+ {file = "frozenlist-1.4.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5c3894db91f5a489fc8fa6a9991820f368f0b3cbdb9cd8849547ccfab3392d86"},
+ {file = "frozenlist-1.4.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ba60bb19387e13597fb059f32cd4d59445d7b18b69a745b8f8e5db0346f33480"},
+ {file = "frozenlist-1.4.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8aefbba5f69d42246543407ed2461db31006b0f76c4e32dfd6f42215a2c41d09"},
+ {file = "frozenlist-1.4.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:780d3a35680ced9ce682fbcf4cb9c2bad3136eeff760ab33707b71db84664e3a"},
+ {file = "frozenlist-1.4.1-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9acbb16f06fe7f52f441bb6f413ebae6c37baa6ef9edd49cdd567216da8600cd"},
+ {file = "frozenlist-1.4.1-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:23b701e65c7b36e4bf15546a89279bd4d8675faabc287d06bbcfac7d3c33e1e6"},
+ {file = "frozenlist-1.4.1-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:3e0153a805a98f5ada7e09826255ba99fb4f7524bb81bf6b47fb702666484ae1"},
+ {file = "frozenlist-1.4.1-cp312-cp312-musllinux_1_1_ppc64le.whl", hash = "sha256:dd9b1baec094d91bf36ec729445f7769d0d0cf6b64d04d86e45baf89e2b9059b"},
+ {file = "frozenlist-1.4.1-cp312-cp312-musllinux_1_1_s390x.whl", hash = "sha256:1a4471094e146b6790f61b98616ab8e44f72661879cc63fa1049d13ef711e71e"},
+ {file = "frozenlist-1.4.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:5667ed53d68d91920defdf4035d1cdaa3c3121dc0b113255124bcfada1cfa1b8"},
+ {file = "frozenlist-1.4.1-cp312-cp312-win32.whl", hash = "sha256:beee944ae828747fd7cb216a70f120767fc9f4f00bacae8543c14a6831673f89"},
+ {file = "frozenlist-1.4.1-cp312-cp312-win_amd64.whl", hash = "sha256:64536573d0a2cb6e625cf309984e2d873979709f2cf22839bf2d61790b448ad5"},
+ {file = "frozenlist-1.4.1-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:20b51fa3f588ff2fe658663db52a41a4f7aa6c04f6201449c6c7c476bd255c0d"},
+ {file = "frozenlist-1.4.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:410478a0c562d1a5bcc2f7ea448359fcb050ed48b3c6f6f4f18c313a9bdb1826"},
+ {file = "frozenlist-1.4.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:c6321c9efe29975232da3bd0af0ad216800a47e93d763ce64f291917a381b8eb"},
+ {file = "frozenlist-1.4.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:48f6a4533887e189dae092f1cf981f2e3885175f7a0f33c91fb5b7b682b6bab6"},
+ {file = "frozenlist-1.4.1-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:6eb73fa5426ea69ee0e012fb59cdc76a15b1283d6e32e4f8dc4482ec67d1194d"},
+ {file = "frozenlist-1.4.1-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:fbeb989b5cc29e8daf7f976b421c220f1b8c731cbf22b9130d8815418ea45887"},
+ {file = "frozenlist-1.4.1-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:32453c1de775c889eb4e22f1197fe3bdfe457d16476ea407472b9442e6295f7a"},
+ {file = "frozenlist-1.4.1-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:693945278a31f2086d9bf3df0fe8254bbeaef1fe71e1351c3bd730aa7d31c41b"},
+ {file = "frozenlist-1.4.1-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:1d0ce09d36d53bbbe566fe296965b23b961764c0bcf3ce2fa45f463745c04701"},
+ {file = "frozenlist-1.4.1-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:3a670dc61eb0d0eb7080890c13de3066790f9049b47b0de04007090807c776b0"},
+ {file = "frozenlist-1.4.1-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:dca69045298ce5c11fd539682cff879cc1e664c245d1c64da929813e54241d11"},
+ {file = "frozenlist-1.4.1-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:a06339f38e9ed3a64e4c4e43aec7f59084033647f908e4259d279a52d3757d09"},
+ {file = "frozenlist-1.4.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:b7f2f9f912dca3934c1baec2e4585a674ef16fe00218d833856408c48d5beee7"},
+ {file = "frozenlist-1.4.1-cp38-cp38-win32.whl", hash = "sha256:e7004be74cbb7d9f34553a5ce5fb08be14fb33bc86f332fb71cbe5216362a497"},
+ {file = "frozenlist-1.4.1-cp38-cp38-win_amd64.whl", hash = "sha256:5a7d70357e7cee13f470c7883a063aae5fe209a493c57d86eb7f5a6f910fae09"},
+ {file = "frozenlist-1.4.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:bfa4a17e17ce9abf47a74ae02f32d014c5e9404b6d9ac7f729e01562bbee601e"},
+ {file = "frozenlist-1.4.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:b7e3ed87d4138356775346e6845cccbe66cd9e207f3cd11d2f0b9fd13681359d"},
+ {file = "frozenlist-1.4.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:c99169d4ff810155ca50b4da3b075cbde79752443117d89429595c2e8e37fed8"},
+ {file = "frozenlist-1.4.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:edb678da49d9f72c9f6c609fbe41a5dfb9a9282f9e6a2253d5a91e0fc382d7c0"},
+ {file = "frozenlist-1.4.1-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:6db4667b187a6742b33afbbaf05a7bc551ffcf1ced0000a571aedbb4aa42fc7b"},
+ {file = "frozenlist-1.4.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:55fdc093b5a3cb41d420884cdaf37a1e74c3c37a31f46e66286d9145d2063bd0"},
+ {file = "frozenlist-1.4.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:82e8211d69a4f4bc360ea22cd6555f8e61a1bd211d1d5d39d3d228b48c83a897"},
+ {file = "frozenlist-1.4.1-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:89aa2c2eeb20957be2d950b85974b30a01a762f3308cd02bb15e1ad632e22dc7"},
+ {file = "frozenlist-1.4.1-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:9d3e0c25a2350080e9319724dede4f31f43a6c9779be48021a7f4ebde8b2d742"},
+ {file = "frozenlist-1.4.1-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:7268252af60904bf52c26173cbadc3a071cece75f873705419c8681f24d3edea"},
+ {file = "frozenlist-1.4.1-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:0c250a29735d4f15321007fb02865f0e6b6a41a6b88f1f523ca1596ab5f50bd5"},
+ {file = "frozenlist-1.4.1-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:96ec70beabbd3b10e8bfe52616a13561e58fe84c0101dd031dc78f250d5128b9"},
+ {file = "frozenlist-1.4.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:23b2d7679b73fe0e5a4560b672a39f98dfc6f60df63823b0a9970525325b95f6"},
+ {file = "frozenlist-1.4.1-cp39-cp39-win32.whl", hash = "sha256:a7496bfe1da7fb1a4e1cc23bb67c58fab69311cc7d32b5a99c2007b4b2a0e932"},
+ {file = "frozenlist-1.4.1-cp39-cp39-win_amd64.whl", hash = "sha256:e6a20a581f9ce92d389a8c7d7c3dd47c81fd5d6e655c8dddf341e14aa48659d0"},
+ {file = "frozenlist-1.4.1-py3-none-any.whl", hash = "sha256:04ced3e6a46b4cfffe20f9ae482818e34eba9b5fb0ce4056e4cc9b6e212d09b7"},
+ {file = "frozenlist-1.4.1.tar.gz", hash = "sha256:c037a86e8513059a2613aaba4d817bb90b9d9b6b69aace3ce9c877e8c8ed402b"},
+]
+
[[package]]
name = "greenlet"
version = "3.0.3"
@@ -564,6 +1232,163 @@ files = [
{file = "h11-0.14.0.tar.gz", hash = "sha256:8f19fbbe99e72420ff35c00b27a34cb9937e902a8b810e2c88300c6f0a3b699d"},
]
+[[package]]
+name = "h3"
+version = "3.7.7"
+description = "Hierarchical hexagonal geospatial indexing system"
+optional = false
+python-versions = "*"
+files = [
+ {file = "h3-3.7.7-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:951ecc9da0bcd5091670b13636928747bc98bc76891da0fa725524ec017cd9de"},
+ {file = "h3-3.7.7-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:26b9dd605541223ef927cc913deccb236cee024b16032f4a3e4387e2791479f2"},
+ {file = "h3-3.7.7-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:996ebb32dc26dd607af7493149f94ce316117be6f42971f7b33bbd326ec695d2"},
+ {file = "h3-3.7.7-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fa2a4aa888cd9476788b874b4e11e178293f5b86e8461c36596bf183c242d417"},
+ {file = "h3-3.7.7-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:0256e42687470c6f0044ca78fe375fe32a654be8b5a8313b4a68f52f513389c6"},
+ {file = "h3-3.7.7-cp310-cp310-win_amd64.whl", hash = "sha256:a3e2bc125490f900e0513c30480722f129bab1415f23040b6cd3a3f8d5a39336"},
+ {file = "h3-3.7.7-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:7d59018a50cd3b6d0ff0b18a54fdfcbaf2f79c13c831842f54fd2780c4b561ea"},
+ {file = "h3-3.7.7-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:9e74526d941c1656fe162cc63b459b61aa83a15e257e9477b1570f26c544b51a"},
+ {file = "h3-3.7.7-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7c7398dbab685fcf3fe92f7c4c5901ab258bc66f7fa05fd1da8693375a10a549"},
+ {file = "h3-3.7.7-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6d22ea488ab5fe01c94070e9a6b3222916905a4d3f7a9d33cb2298c93fa0ffd3"},
+ {file = "h3-3.7.7-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:c94836155e8169be393980fc059f06481a14dd1913bd9cba609f6f1e8864c171"},
+ {file = "h3-3.7.7-cp311-cp311-win_amd64.whl", hash = "sha256:836e74313ff55324485cd7e07783bc67df3191ec08a318035d7cd8ee0b0badab"},
+ {file = "h3-3.7.7-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:51c2f63ef5a57e4b18ebc9c0eb56656433e280ec45ab487a514127bb6e7d6a1f"},
+ {file = "h3-3.7.7-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:4d6e38dea47c220d9802af8e8bebc806f9f39358aee07b736191ff21e2c9921d"},
+ {file = "h3-3.7.7-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e408342e94f558802a97bfcbe1baae2af8b1fd926ad9041d970ff9dbd0502099"},
+ {file = "h3-3.7.7-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:644c3c84585aa4df62e81bc54fd305c4d6686324731de230b0ddbd7036ed172c"},
+ {file = "h3-3.7.7-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:bb4a3d5e82d0c89512dc71b4eac17976a29be29da250ba76bc94bc5b9e824f0e"},
+ {file = "h3-3.7.7-cp312-cp312-win_amd64.whl", hash = "sha256:2ccff5f02589e80202597ed0b9f61ebd114e262e7dd0fe88059298602898192f"},
+ {file = "h3-3.7.7-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:ef2e71b619f984e71c4bd9d128152e2c7e3e788e2d2ec571b32cef1d295ddf38"},
+ {file = "h3-3.7.7-cp36-cp36m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8cb13f0213ed6da80e739355e5b62cfc81b7b1469af997be3384a6cbc3a1a750"},
+ {file = "h3-3.7.7-cp36-cp36m-musllinux_1_1_x86_64.whl", hash = "sha256:701f72f703d892fb17e66b9fd7b6b2ad125e135b091eb7dd0ec11858b84d84d2"},
+ {file = "h3-3.7.7-cp36-cp36m-win_amd64.whl", hash = "sha256:796622be7cb052690404c0ac03768183e51ae22505ce4a424b4537b2b7609fba"},
+ {file = "h3-3.7.7-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:bcd88a72d6aa97d0f3b3b87b7bfd9725a8909501e6cb9d0057d5b690b6bb37b0"},
+ {file = "h3-3.7.7-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a7358ba3f91193a2551c4a8d7ad7fd348e567b3a3581c9c161630029dfb23e07"},
+ {file = "h3-3.7.7-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a8f34b204edc2e8f7d99a6db4ed1b5d202b7ea3ec6817d373ec432dee14efe04"},
+ {file = "h3-3.7.7-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:2aa0f8ce89b5e694815ee7a5172a782d58f2652267329de7008354b110b53955"},
+ {file = "h3-3.7.7-cp37-cp37m-win_amd64.whl", hash = "sha256:4c851baa1c2d4f29b01157ce2a4cdb1f3879fff5c36ff7861dad1526963a17a7"},
+ {file = "h3-3.7.7-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:6f3a9da5472820b0a4add342f96fe52f65fbb8f46984383885738517b38af69e"},
+ {file = "h3-3.7.7-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:1c57da776a3c1a01e2986b1f6a31d497ee0be8fcdbaaf9b23bb90f5a90eb8f0b"},
+ {file = "h3-3.7.7-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7a5c0c0ddd9c57694ecc3b9ba99cbef2842882f8943d6edc676a365e139dbc6d"},
+ {file = "h3-3.7.7-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0c1b5a0a652719b645387231bf6d7d4dd85150e4440a4ce72a804a10e86592ae"},
+ {file = "h3-3.7.7-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:64f76dc827fef94e9f43f95a1daea2e11f2ad2e8c55deac072f3d59bd62412d4"},
+ {file = "h3-3.7.7-cp38-cp38-win_amd64.whl", hash = "sha256:c993a36120d7f5607f24ba9e39caf715eaf9cd9d44f5d5660fd85e3f4e0c6bf7"},
+ {file = "h3-3.7.7-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:eb154d2af699870b888e10476e327c895078009d2d2a6ef2d053d7dcf0e2c270"},
+ {file = "h3-3.7.7-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:c96ad74e246bb7638d413efa8199dd4c58ee929424a4dcaadb16365195f77f87"},
+ {file = "h3-3.7.7-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:52901f14f8b6e2c82075fd52c0e70176b868f621d47b5dc93f468c510e963722"},
+ {file = "h3-3.7.7-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fa9d82a0fcc647e7bab36ab2e7a7392d141edc95d113ccf972e0fb7b0ddf80a0"},
+ {file = "h3-3.7.7-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:9f4417d09acb36f0452346052f576923d6e4334bff3459f217d6278d40397424"},
+ {file = "h3-3.7.7-cp39-cp39-win_amd64.whl", hash = "sha256:7ae774cd43b057f68dc10c99e4522fa40ed6b32ab90b2df0025595ffa15e77a0"},
+ {file = "h3-3.7.7.tar.gz", hash = "sha256:33d141c3cef0725a881771fd8cb80c06a0db84a6e4ca5c647ce095ae07c61e94"},
+]
+
+[package.extras]
+all = ["flake8", "numpy", "pylint", "pytest", "pytest-cov"]
+numpy = ["numpy"]
+test = ["flake8", "pylint", "pytest", "pytest-cov"]
+
+[[package]]
+name = "hiredis"
+version = "3.0.0"
+description = "Python wrapper for hiredis"
+optional = false
+python-versions = ">=3.8"
+files = [
+ {file = "hiredis-3.0.0-cp310-cp310-macosx_10_15_universal2.whl", hash = "sha256:4b182791c41c5eb1d9ed736f0ff81694b06937ca14b0d4dadde5dadba7ff6dae"},
+ {file = "hiredis-3.0.0-cp310-cp310-macosx_10_15_x86_64.whl", hash = "sha256:13c275b483a052dd645eb2cb60d6380f1f5215e4c22d6207e17b86be6dd87ffa"},
+ {file = "hiredis-3.0.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:c1018cc7f12824506f165027eabb302735b49e63af73eb4d5450c66c88f47026"},
+ {file = "hiredis-3.0.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:83a29cc7b21b746cb6a480189e49f49b2072812c445e66a9e38d2004d496b81c"},
+ {file = "hiredis-3.0.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e241fab6332e8fb5f14af00a4a9c6aefa22f19a336c069b7ddbf28ef8341e8d6"},
+ {file = "hiredis-3.0.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1fb8de899f0145d6c4d5d4bd0ee88a78eb980a7ffabd51e9889251b8f58f1785"},
+ {file = "hiredis-3.0.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b23291951959141173eec10f8573538e9349fa27f47a0c34323d1970bf891ee5"},
+ {file = "hiredis-3.0.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e421ac9e4b5efc11705a0d5149e641d4defdc07077f748667f359e60dc904420"},
+ {file = "hiredis-3.0.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:77c8006c12154c37691b24ff293c077300c22944018c3ff70094a33e10c1d795"},
+ {file = "hiredis-3.0.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:41afc0d3c18b59eb50970479a9c0e5544fb4b95e3a79cf2fbaece6ddefb926fe"},
+ {file = "hiredis-3.0.0-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:04ccae6dcd9647eae6025425ab64edb4d79fde8b9e6e115ebfabc6830170e3b2"},
+ {file = "hiredis-3.0.0-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:fe91d62b0594db5ea7d23fc2192182b1a7b6973f628a9b8b2e0a42a2be721ac6"},
+ {file = "hiredis-3.0.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:99516d99316062824a24d145d694f5b0d030c80da693ea6f8c4ecf71a251d8bb"},
+ {file = "hiredis-3.0.0-cp310-cp310-win32.whl", hash = "sha256:562eaf820de045eb487afaa37e6293fe7eceb5b25e158b5a1974b7e40bf04543"},
+ {file = "hiredis-3.0.0-cp310-cp310-win_amd64.whl", hash = "sha256:a1c81c89ed765198da27412aa21478f30d54ef69bf5e4480089d9c3f77b8f882"},
+ {file = "hiredis-3.0.0-cp311-cp311-macosx_10_15_universal2.whl", hash = "sha256:4664dedcd5933364756d7251a7ea86d60246ccf73a2e00912872dacbfcef8978"},
+ {file = "hiredis-3.0.0-cp311-cp311-macosx_10_15_x86_64.whl", hash = "sha256:47de0bbccf4c8a9f99d82d225f7672b9dd690d8fd872007b933ef51a302c9fa6"},
+ {file = "hiredis-3.0.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:e43679eca508ba8240d016d8cca9d27342d70184773c15bea78a23c87a1922f1"},
+ {file = "hiredis-3.0.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:13c345e7278c210317e77e1934b27b61394fee0dec2e8bd47e71570900f75823"},
+ {file = "hiredis-3.0.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:00018f22f38530768b73ea86c11f47e8d4df65facd4e562bd78773bd1baef35e"},
+ {file = "hiredis-3.0.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:4ea3a86405baa8eb0d3639ced6926ad03e07113de54cb00fd7510cb0db76a89d"},
+ {file = "hiredis-3.0.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c073848d2b1d5561f3903879ccf4e1a70c9b1e7566c7bdcc98d082fa3e7f0a1d"},
+ {file = "hiredis-3.0.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5a8dffb5f5b3415a4669d25de48b617fd9d44b0bccfc4c2ab24b06406ecc9ecb"},
+ {file = "hiredis-3.0.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:22c17c96143c2a62dfd61b13803bc5de2ac526b8768d2141c018b965d0333b66"},
+ {file = "hiredis-3.0.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:c3ece960008dab66c6b8bb3a1350764677ee7c74ccd6270aaf1b1caf9ccebb46"},
+ {file = "hiredis-3.0.0-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:f75999ae00a920f7dce6ecae76fa5e8674a3110e5a75f12c7a2c75ae1af53396"},
+ {file = "hiredis-3.0.0-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:e069967cbd5e1900aafc4b5943888f6d34937fc59bf8918a1a546cb729b4b1e4"},
+ {file = "hiredis-3.0.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:0aacc0a78e1d94d843a6d191f224a35893e6bdfeb77a4a89264155015c65f126"},
+ {file = "hiredis-3.0.0-cp311-cp311-win32.whl", hash = "sha256:719c32147ba29528cb451f037bf837dcdda4ff3ddb6cdb12c4216b0973174718"},
+ {file = "hiredis-3.0.0-cp311-cp311-win_amd64.whl", hash = "sha256:bdc144d56333c52c853c31b4e2e52cfbdb22d3da4374c00f5f3d67c42158970f"},
+ {file = "hiredis-3.0.0-cp312-cp312-macosx_10_15_universal2.whl", hash = "sha256:484025d2eb8f6348f7876fc5a2ee742f568915039fcb31b478fd5c242bb0fe3a"},
+ {file = "hiredis-3.0.0-cp312-cp312-macosx_10_15_x86_64.whl", hash = "sha256:fcdb552ffd97151dab8e7bc3ab556dfa1512556b48a367db94b5c20253a35ee1"},
+ {file = "hiredis-3.0.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:0bb6f9fd92f147ba11d338ef5c68af4fd2908739c09e51f186e1d90958c68cc1"},
+ {file = "hiredis-3.0.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fa86bf9a0ed339ec9e8a9a9d0ae4dccd8671625c83f9f9f2640729b15e07fbfd"},
+ {file = "hiredis-3.0.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e194a0d5df9456995d8f510eab9f529213e7326af6b94770abf8f8b7952ddcaa"},
+ {file = "hiredis-3.0.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c8a1df39d74ec507d79c7a82c8063eee60bf80537cdeee652f576059b9cdd15c"},
+ {file = "hiredis-3.0.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f91456507427ba36fd81b2ca11053a8e112c775325acc74e993201ea912d63e9"},
+ {file = "hiredis-3.0.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9862db92ef67a8a02e0d5370f07d380e14577ecb281b79720e0d7a89aedb9ee5"},
+ {file = "hiredis-3.0.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:d10fcd9e0eeab835f492832b2a6edb5940e2f1230155f33006a8dfd3bd2c94e4"},
+ {file = "hiredis-3.0.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:48727d7d405d03977d01885f317328dc21d639096308de126c2c4e9950cbd3c9"},
+ {file = "hiredis-3.0.0-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:8e0bb6102ebe2efecf8a3292c6660a0e6fac98176af6de67f020bea1c2343717"},
+ {file = "hiredis-3.0.0-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:df274e3abb4df40f4c7274dd3e587dfbb25691826c948bc98d5fead019dfb001"},
+ {file = "hiredis-3.0.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:034925b5fb514f7b11aac38cd55b3fd7e9d3af23bd6497f3f20aa5b8ba58e232"},
+ {file = "hiredis-3.0.0-cp312-cp312-win32.whl", hash = "sha256:120f2dda469b28d12ccff7c2230225162e174657b49cf4cd119db525414ae281"},
+ {file = "hiredis-3.0.0-cp312-cp312-win_amd64.whl", hash = "sha256:e584fe5f4e6681d8762982be055f1534e0170f6308a7a90f58d737bab12ff6a8"},
+ {file = "hiredis-3.0.0-cp38-cp38-macosx_10_15_universal2.whl", hash = "sha256:122171ff47d96ed8dd4bba6c0e41d8afaba3e8194949f7720431a62aa29d8895"},
+ {file = "hiredis-3.0.0-cp38-cp38-macosx_10_15_x86_64.whl", hash = "sha256:ba9fc605ac558f0de67463fb588722878641e6fa1dabcda979e8e69ff581d0bd"},
+ {file = "hiredis-3.0.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:a631e2990b8be23178f655cae8ac6c7422af478c420dd54e25f2e26c29e766f1"},
+ {file = "hiredis-3.0.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:63482db3fadebadc1d01ad33afa6045ebe2ea528eb77ccaabd33ee7d9c2bad48"},
+ {file = "hiredis-3.0.0-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1f669212c390eebfbe03c4e20181f5970b82c5d0a0ad1df1785f7ffbe7d61150"},
+ {file = "hiredis-3.0.0-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a6a49ef161739f8018c69b371528bdb47d7342edfdee9ddc75a4d8caddf45a6e"},
+ {file = "hiredis-3.0.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:98a152052b8878e5e43a2e3a14075218adafc759547c98668a21e9485882696c"},
+ {file = "hiredis-3.0.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:50a196af0ce657fcde9bf8a0bbe1032e22c64d8fcec2bc926a35e7ff68b3a166"},
+ {file = "hiredis-3.0.0-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:f2f312eef8aafc2255e3585dcf94d5da116c43ef837db91db9ecdc1bc930072d"},
+ {file = "hiredis-3.0.0-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:6ca41fa40fa019cde42c21add74aadd775e71458051a15a352eabeb12eb4d084"},
+ {file = "hiredis-3.0.0-cp38-cp38-musllinux_1_2_ppc64le.whl", hash = "sha256:6eecb343c70629f5af55a8b3e53264e44fa04e155ef7989de13668a0cb102a90"},
+ {file = "hiredis-3.0.0-cp38-cp38-musllinux_1_2_s390x.whl", hash = "sha256:c3fdad75e7837a475900a1d3a5cc09aa024293c3b0605155da2d42f41bc0e482"},
+ {file = "hiredis-3.0.0-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:8854969e7480e8d61ed7549eb232d95082a743e94138d98d7222ba4e9f7ecacd"},
+ {file = "hiredis-3.0.0-cp38-cp38-win32.whl", hash = "sha256:f114a6c86edbf17554672b050cce72abf489fe58d583c7921904d5f1c9691605"},
+ {file = "hiredis-3.0.0-cp38-cp38-win_amd64.whl", hash = "sha256:7d99b91e42217d7b4b63354b15b41ce960e27d216783e04c4a350224d55842a4"},
+ {file = "hiredis-3.0.0-cp39-cp39-macosx_10_15_universal2.whl", hash = "sha256:4c6efcbb5687cf8d2aedcc2c3ed4ac6feae90b8547427d417111194873b66b06"},
+ {file = "hiredis-3.0.0-cp39-cp39-macosx_10_15_x86_64.whl", hash = "sha256:5b5cff42a522a0d81c2ae7eae5e56d0ee7365e0c4ad50c4de467d8957aff4414"},
+ {file = "hiredis-3.0.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:82f794d564f4bc76b80c50b03267fe5d6589e93f08e66b7a2f674faa2fa76ebc"},
+ {file = "hiredis-3.0.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d7a4c1791d7aa7e192f60fe028ae409f18ccdd540f8b1e6aeb0df7816c77e4a4"},
+ {file = "hiredis-3.0.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a2537b2cd98192323fce4244c8edbf11f3cac548a9d633dbbb12b48702f379f4"},
+ {file = "hiredis-3.0.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8fed69bbaa307040c62195a269f82fc3edf46b510a17abb6b30a15d7dab548df"},
+ {file = "hiredis-3.0.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:869f6d5537d243080f44253491bb30aa1ec3c21754003b3bddeadedeb65842b0"},
+ {file = "hiredis-3.0.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d435ae89073d7cd51e6b6bf78369c412216261c9c01662e7008ff00978153729"},
+ {file = "hiredis-3.0.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:204b79b30a0e6be0dc2301a4d385bb61472809f09c49f400497f1cdd5a165c66"},
+ {file = "hiredis-3.0.0-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:3ea635101b739c12effd189cc19b2671c268abb03013fd1f6321ca29df3ca625"},
+ {file = "hiredis-3.0.0-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:f359175197fd833c8dd7a8c288f1516be45415bb5c939862ab60c2918e1e1943"},
+ {file = "hiredis-3.0.0-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:ac6d929cb33dd12ad3424b75725975f0a54b5b12dbff95f2a2d660c510aa106d"},
+ {file = "hiredis-3.0.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:100431e04d25a522ef2c3b94f294c4219c4de3bfc7d557b6253296145a144c11"},
+ {file = "hiredis-3.0.0-cp39-cp39-win32.whl", hash = "sha256:e1a9c14ae9573d172dc050a6f63a644457df5d01ec4d35a6a0f097f812930f83"},
+ {file = "hiredis-3.0.0-cp39-cp39-win_amd64.whl", hash = "sha256:54a6dd7b478e6eb01ce15b3bb5bf771e108c6c148315bf194eb2ab776a3cac4d"},
+ {file = "hiredis-3.0.0-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:50da7a9edf371441dfcc56288d790985ee9840d982750580710a9789b8f4a290"},
+ {file = "hiredis-3.0.0-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:9b285ef6bf1581310b0d5e8f6ce64f790a1c40e89c660e1320b35f7515433672"},
+ {file = "hiredis-3.0.0-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0dcfa684966f25b335072115de2f920228a3c2caf79d4bfa2b30f6e4f674a948"},
+ {file = "hiredis-3.0.0-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a41be8af1fd78ca97bc948d789a09b730d1e7587d07ca53af05758f31f4b985d"},
+ {file = "hiredis-3.0.0-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:038756db735e417ab36ee6fd7725ce412385ed2bd0767e8179a4755ea11b804f"},
+ {file = "hiredis-3.0.0-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:fcecbd39bd42cef905c0b51c9689c39d0cc8b88b1671e7f40d4fb213423aef3a"},
+ {file = "hiredis-3.0.0-pp38-pypy38_pp73-macosx_10_15_x86_64.whl", hash = "sha256:a131377493a59fb0f5eaeb2afd49c6540cafcfba5b0b3752bed707be9e7c4eaf"},
+ {file = "hiredis-3.0.0-pp38-pypy38_pp73-macosx_11_0_arm64.whl", hash = "sha256:3d22c53f0ec5c18ecb3d92aa9420563b1c5d657d53f01356114978107b00b860"},
+ {file = "hiredis-3.0.0-pp38-pypy38_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c8a91e9520fbc65a799943e5c970ffbcd67905744d8becf2e75f9f0a5e8414f0"},
+ {file = "hiredis-3.0.0-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3dc8043959b50141df58ab4f398e8ae84c6f9e673a2c9407be65fc789138f4a6"},
+ {file = "hiredis-3.0.0-pp38-pypy38_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:51b99cfac514173d7b8abdfe10338193e8a0eccdfe1870b646009d2fb7cbe4b5"},
+ {file = "hiredis-3.0.0-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:fa1fcad89d8a41d8dc10b1e54951ec1e161deabd84ed5a2c95c3c7213bdb3514"},
+ {file = "hiredis-3.0.0-pp39-pypy39_pp73-macosx_10_15_x86_64.whl", hash = "sha256:898636a06d9bf575d2c594129085ad6b713414038276a4bfc5db7646b8a5be78"},
+ {file = "hiredis-3.0.0-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:466f836dbcf86de3f9692097a7a01533dc9926986022c6617dc364a402b265c5"},
+ {file = "hiredis-3.0.0-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:23142a8af92a13fc1e3f2ca1d940df3dcf2af1d176be41fe8d89e30a837a0b60"},
+ {file = "hiredis-3.0.0-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:793c80a3d6b0b0e8196a2d5de37a08330125668c8012922685e17aa9108c33ac"},
+ {file = "hiredis-3.0.0-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:467d28112c7faa29b7db743f40803d927c8591e9da02b6ce3d5fadc170a542a2"},
+ {file = "hiredis-3.0.0-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:dc384874a719c767b50a30750f937af18842ee5e288afba95a5a3ed703b1515a"},
+ {file = "hiredis-3.0.0.tar.gz", hash = "sha256:fed8581ae26345dea1f1e0d1a96e05041a727a45e7d8d459164583e23c6ac441"},
+]
+
[[package]]
name = "httpcore"
version = "1.0.5"
@@ -657,6 +1482,20 @@ cli = ["click (==8.*)", "pygments (==2.*)", "rich (>=10,<14)"]
http2 = ["h2 (>=3,<5)"]
socks = ["socksio (==1.*)"]
+[[package]]
+name = "httpx-oauth"
+version = "0.15.1"
+description = "Async OAuth client using HTTPX"
+optional = false
+python-versions = ">=3.8"
+files = [
+ {file = "httpx_oauth-0.15.1-py3-none-any.whl", hash = "sha256:89b45f250e93e42bbe9631adf349cab0e3d3ced958c07e06651735198d1bdf00"},
+ {file = "httpx_oauth-0.15.1.tar.gz", hash = "sha256:4094cf0938fc7252b5f5dfd62cd1ab5aee2fcb6734e621942ee17d1af4806b74"},
+]
+
+[package.dependencies]
+httpx = ">=0.18,<1.0.0"
+
[[package]]
name = "identify"
version = "2.6.0"
@@ -693,6 +1532,28 @@ files = [
{file = "iniconfig-2.0.0.tar.gz", hash = "sha256:2d91e135bf72d31a410b17c16da610a82cb55f6b0477d1a902134b24a455b8b3"},
]
+[[package]]
+name = "iso8601"
+version = "1.1.0"
+description = "Simple module to parse ISO 8601 dates"
+optional = false
+python-versions = ">=3.6.2,<4.0"
+files = [
+ {file = "iso8601-1.1.0-py3-none-any.whl", hash = "sha256:8400e90141bf792bce2634df533dc57e3bee19ea120a87bebcd3da89a58ad73f"},
+ {file = "iso8601-1.1.0.tar.gz", hash = "sha256:32811e7b81deee2063ea6d2e94f8819a86d1f3811e49d23623a41fa832bef03f"},
+]
+
+[[package]]
+name = "itsdangerous"
+version = "2.2.0"
+description = "Safely pass data to untrusted environments and back."
+optional = false
+python-versions = ">=3.8"
+files = [
+ {file = "itsdangerous-2.2.0-py3-none-any.whl", hash = "sha256:c6242fc49e35958c8b15141343aa660db5fc54d4f13a1db01a3f5891b98700ef"},
+ {file = "itsdangerous-2.2.0.tar.gz", hash = "sha256:e0050c0b7da1eea53ffaf149c0cfbb5c6e2e2b69c4bef22c81fa6eb73e5f6173"},
+]
+
[[package]]
name = "jinja2"
version = "3.1.4"
@@ -868,6 +1729,17 @@ html5 = ["html5lib"]
htmlsoup = ["BeautifulSoup4"]
source = ["Cython (>=3.0.10)"]
+[[package]]
+name = "makefun"
+version = "1.15.4"
+description = "Small library to dynamically create python functions."
+optional = false
+python-versions = "*"
+files = [
+ {file = "makefun-1.15.4-py2.py3-none-any.whl", hash = "sha256:945d078a7e01a903f2cbef738b33e0ebc52b8d35fb7e20c528ed87b5c80db5b7"},
+ {file = "makefun-1.15.4.tar.gz", hash = "sha256:9f9b9904e7c397759374a88f4c57781fbab2a458dec78df4b3ee6272cd9fb010"},
+]
+
[[package]]
name = "mako"
version = "1.3.5"
@@ -967,6 +1839,110 @@ files = [
{file = "more_itertools-10.3.0-py3-none-any.whl", hash = "sha256:ea6a02e24a9161e51faad17a8782b92a0df82c12c1c8886fec7f0c3fa1a1b320"},
]
+[[package]]
+name = "multidict"
+version = "6.1.0"
+description = "multidict implementation"
+optional = false
+python-versions = ">=3.8"
+files = [
+ {file = "multidict-6.1.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:3380252550e372e8511d49481bd836264c009adb826b23fefcc5dd3c69692f60"},
+ {file = "multidict-6.1.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:99f826cbf970077383d7de805c0681799491cb939c25450b9b5b3ced03ca99f1"},
+ {file = "multidict-6.1.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:a114d03b938376557927ab23f1e950827c3b893ccb94b62fd95d430fd0e5cf53"},
+ {file = "multidict-6.1.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b1c416351ee6271b2f49b56ad7f308072f6f44b37118d69c2cad94f3fa8a40d5"},
+ {file = "multidict-6.1.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:6b5d83030255983181005e6cfbac1617ce9746b219bc2aad52201ad121226581"},
+ {file = "multidict-6.1.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3e97b5e938051226dc025ec80980c285b053ffb1e25a3db2a3aa3bc046bf7f56"},
+ {file = "multidict-6.1.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d618649d4e70ac6efcbba75be98b26ef5078faad23592f9b51ca492953012429"},
+ {file = "multidict-6.1.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:10524ebd769727ac77ef2278390fb0068d83f3acb7773792a5080f2b0abf7748"},
+ {file = "multidict-6.1.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:ff3827aef427c89a25cc96ded1759271a93603aba9fb977a6d264648ebf989db"},
+ {file = "multidict-6.1.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:06809f4f0f7ab7ea2cabf9caca7d79c22c0758b58a71f9d32943ae13c7ace056"},
+ {file = "multidict-6.1.0-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:f179dee3b863ab1c59580ff60f9d99f632f34ccb38bf67a33ec6b3ecadd0fd76"},
+ {file = "multidict-6.1.0-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:aaed8b0562be4a0876ee3b6946f6869b7bcdb571a5d1496683505944e268b160"},
+ {file = "multidict-6.1.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:3c8b88a2ccf5493b6c8da9076fb151ba106960a2df90c2633f342f120751a9e7"},
+ {file = "multidict-6.1.0-cp310-cp310-win32.whl", hash = "sha256:4a9cb68166a34117d6646c0023c7b759bf197bee5ad4272f420a0141d7eb03a0"},
+ {file = "multidict-6.1.0-cp310-cp310-win_amd64.whl", hash = "sha256:20b9b5fbe0b88d0bdef2012ef7dee867f874b72528cf1d08f1d59b0e3850129d"},
+ {file = "multidict-6.1.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:3efe2c2cb5763f2f1b275ad2bf7a287d3f7ebbef35648a9726e3b69284a4f3d6"},
+ {file = "multidict-6.1.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:c7053d3b0353a8b9de430a4f4b4268ac9a4fb3481af37dfe49825bf45ca24156"},
+ {file = "multidict-6.1.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:27e5fc84ccef8dfaabb09d82b7d179c7cf1a3fbc8a966f8274fcb4ab2eb4cadb"},
+ {file = "multidict-6.1.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0e2b90b43e696f25c62656389d32236e049568b39320e2735d51f08fd362761b"},
+ {file = "multidict-6.1.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d83a047959d38a7ff552ff94be767b7fd79b831ad1cd9920662db05fec24fe72"},
+ {file = "multidict-6.1.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d1a9dd711d0877a1ece3d2e4fea11a8e75741ca21954c919406b44e7cf971304"},
+ {file = "multidict-6.1.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ec2abea24d98246b94913b76a125e855eb5c434f7c46546046372fe60f666351"},
+ {file = "multidict-6.1.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4867cafcbc6585e4b678876c489b9273b13e9fff9f6d6d66add5e15d11d926cb"},
+ {file = "multidict-6.1.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:5b48204e8d955c47c55b72779802b219a39acc3ee3d0116d5080c388970b76e3"},
+ {file = "multidict-6.1.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:d8fff389528cad1618fb4b26b95550327495462cd745d879a8c7c2115248e399"},
+ {file = "multidict-6.1.0-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:a7a9541cd308eed5e30318430a9c74d2132e9a8cb46b901326272d780bf2d423"},
+ {file = "multidict-6.1.0-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:da1758c76f50c39a2efd5e9859ce7d776317eb1dd34317c8152ac9251fc574a3"},
+ {file = "multidict-6.1.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:c943a53e9186688b45b323602298ab727d8865d8c9ee0b17f8d62d14b56f0753"},
+ {file = "multidict-6.1.0-cp311-cp311-win32.whl", hash = "sha256:90f8717cb649eea3504091e640a1b8568faad18bd4b9fcd692853a04475a4b80"},
+ {file = "multidict-6.1.0-cp311-cp311-win_amd64.whl", hash = "sha256:82176036e65644a6cc5bd619f65f6f19781e8ec2e5330f51aa9ada7504cc1926"},
+ {file = "multidict-6.1.0-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:b04772ed465fa3cc947db808fa306d79b43e896beb677a56fb2347ca1a49c1fa"},
+ {file = "multidict-6.1.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:6180c0ae073bddeb5a97a38c03f30c233e0a4d39cd86166251617d1bbd0af436"},
+ {file = "multidict-6.1.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:071120490b47aa997cca00666923a83f02c7fbb44f71cf7f136df753f7fa8761"},
+ {file = "multidict-6.1.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:50b3a2710631848991d0bf7de077502e8994c804bb805aeb2925a981de58ec2e"},
+ {file = "multidict-6.1.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b58c621844d55e71c1b7f7c498ce5aa6985d743a1a59034c57a905b3f153c1ef"},
+ {file = "multidict-6.1.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:55b6d90641869892caa9ca42ff913f7ff1c5ece06474fbd32fb2cf6834726c95"},
+ {file = "multidict-6.1.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4b820514bfc0b98a30e3d85462084779900347e4d49267f747ff54060cc33925"},
+ {file = "multidict-6.1.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:10a9b09aba0c5b48c53761b7c720aaaf7cf236d5fe394cd399c7ba662d5f9966"},
+ {file = "multidict-6.1.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:1e16bf3e5fc9f44632affb159d30a437bfe286ce9e02754759be5536b169b305"},
+ {file = "multidict-6.1.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:76f364861c3bfc98cbbcbd402d83454ed9e01a5224bb3a28bf70002a230f73e2"},
+ {file = "multidict-6.1.0-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:820c661588bd01a0aa62a1283f20d2be4281b086f80dad9e955e690c75fb54a2"},
+ {file = "multidict-6.1.0-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:0e5f362e895bc5b9e67fe6e4ded2492d8124bdf817827f33c5b46c2fe3ffaca6"},
+ {file = "multidict-6.1.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:3ec660d19bbc671e3a6443325f07263be452c453ac9e512f5eb935e7d4ac28b3"},
+ {file = "multidict-6.1.0-cp312-cp312-win32.whl", hash = "sha256:58130ecf8f7b8112cdb841486404f1282b9c86ccb30d3519faf301b2e5659133"},
+ {file = "multidict-6.1.0-cp312-cp312-win_amd64.whl", hash = "sha256:188215fc0aafb8e03341995e7c4797860181562380f81ed0a87ff455b70bf1f1"},
+ {file = "multidict-6.1.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:d569388c381b24671589335a3be6e1d45546c2988c2ebe30fdcada8457a31008"},
+ {file = "multidict-6.1.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:052e10d2d37810b99cc170b785945421141bf7bb7d2f8799d431e7db229c385f"},
+ {file = "multidict-6.1.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:f90c822a402cb865e396a504f9fc8173ef34212a342d92e362ca498cad308e28"},
+ {file = "multidict-6.1.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b225d95519a5bf73860323e633a664b0d85ad3d5bede6d30d95b35d4dfe8805b"},
+ {file = "multidict-6.1.0-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:23bfd518810af7de1116313ebd9092cb9aa629beb12f6ed631ad53356ed6b86c"},
+ {file = "multidict-6.1.0-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5c09fcfdccdd0b57867577b719c69e347a436b86cd83747f179dbf0cc0d4c1f3"},
+ {file = "multidict-6.1.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bf6bea52ec97e95560af5ae576bdac3aa3aae0b6758c6efa115236d9e07dae44"},
+ {file = "multidict-6.1.0-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:57feec87371dbb3520da6192213c7d6fc892d5589a93db548331954de8248fd2"},
+ {file = "multidict-6.1.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:0c3f390dc53279cbc8ba976e5f8035eab997829066756d811616b652b00a23a3"},
+ {file = "multidict-6.1.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:59bfeae4b25ec05b34f1956eaa1cb38032282cd4dfabc5056d0a1ec4d696d3aa"},
+ {file = "multidict-6.1.0-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:b2f59caeaf7632cc633b5cf6fc449372b83bbdf0da4ae04d5be36118e46cc0aa"},
+ {file = "multidict-6.1.0-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:37bb93b2178e02b7b618893990941900fd25b6b9ac0fa49931a40aecdf083fe4"},
+ {file = "multidict-6.1.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:4e9f48f58c2c523d5a06faea47866cd35b32655c46b443f163d08c6d0ddb17d6"},
+ {file = "multidict-6.1.0-cp313-cp313-win32.whl", hash = "sha256:3a37ffb35399029b45c6cc33640a92bef403c9fd388acce75cdc88f58bd19a81"},
+ {file = "multidict-6.1.0-cp313-cp313-win_amd64.whl", hash = "sha256:e9aa71e15d9d9beaad2c6b9319edcdc0a49a43ef5c0a4c8265ca9ee7d6c67774"},
+ {file = "multidict-6.1.0-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:db7457bac39421addd0c8449933ac32d8042aae84a14911a757ae6ca3eef1392"},
+ {file = "multidict-6.1.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:d094ddec350a2fb899fec68d8353c78233debde9b7d8b4beeafa70825f1c281a"},
+ {file = "multidict-6.1.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:5845c1fd4866bb5dd3125d89b90e57ed3138241540897de748cdf19de8a2fca2"},
+ {file = "multidict-6.1.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9079dfc6a70abe341f521f78405b8949f96db48da98aeb43f9907f342f627cdc"},
+ {file = "multidict-6.1.0-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3914f5aaa0f36d5d60e8ece6a308ee1c9784cd75ec8151062614657a114c4478"},
+ {file = "multidict-6.1.0-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c08be4f460903e5a9d0f76818db3250f12e9c344e79314d1d570fc69d7f4eae4"},
+ {file = "multidict-6.1.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d093be959277cb7dee84b801eb1af388b6ad3ca6a6b6bf1ed7585895789d027d"},
+ {file = "multidict-6.1.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3702ea6872c5a2a4eeefa6ffd36b042e9773f05b1f37ae3ef7264b1163c2dcf6"},
+ {file = "multidict-6.1.0-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:2090f6a85cafc5b2db085124d752757c9d251548cedabe9bd31afe6363e0aff2"},
+ {file = "multidict-6.1.0-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:f67f217af4b1ff66c68a87318012de788dd95fcfeb24cc889011f4e1c7454dfd"},
+ {file = "multidict-6.1.0-cp38-cp38-musllinux_1_2_ppc64le.whl", hash = "sha256:189f652a87e876098bbc67b4da1049afb5f5dfbaa310dd67c594b01c10388db6"},
+ {file = "multidict-6.1.0-cp38-cp38-musllinux_1_2_s390x.whl", hash = "sha256:6bb5992037f7a9eff7991ebe4273ea7f51f1c1c511e6a2ce511d0e7bdb754492"},
+ {file = "multidict-6.1.0-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:ac10f4c2b9e770c4e393876e35a7046879d195cd123b4f116d299d442b335bcd"},
+ {file = "multidict-6.1.0-cp38-cp38-win32.whl", hash = "sha256:e27bbb6d14416713a8bd7aaa1313c0fc8d44ee48d74497a0ff4c3a1b6ccb5167"},
+ {file = "multidict-6.1.0-cp38-cp38-win_amd64.whl", hash = "sha256:22f3105d4fb15c8f57ff3959a58fcab6ce36814486500cd7485651230ad4d4ef"},
+ {file = "multidict-6.1.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:4e18b656c5e844539d506a0a06432274d7bd52a7487e6828c63a63d69185626c"},
+ {file = "multidict-6.1.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:a185f876e69897a6f3325c3f19f26a297fa058c5e456bfcff8015e9a27e83ae1"},
+ {file = "multidict-6.1.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:ab7c4ceb38d91570a650dba194e1ca87c2b543488fe9309b4212694174fd539c"},
+ {file = "multidict-6.1.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e617fb6b0b6953fffd762669610c1c4ffd05632c138d61ac7e14ad187870669c"},
+ {file = "multidict-6.1.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:16e5f4bf4e603eb1fdd5d8180f1a25f30056f22e55ce51fb3d6ad4ab29f7d96f"},
+ {file = "multidict-6.1.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f4c035da3f544b1882bac24115f3e2e8760f10a0107614fc9839fd232200b875"},
+ {file = "multidict-6.1.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:957cf8e4b6e123a9eea554fa7ebc85674674b713551de587eb318a2df3e00255"},
+ {file = "multidict-6.1.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:483a6aea59cb89904e1ceabd2b47368b5600fb7de78a6e4a2c2987b2d256cf30"},
+ {file = "multidict-6.1.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:87701f25a2352e5bf7454caa64757642734da9f6b11384c1f9d1a8e699758057"},
+ {file = "multidict-6.1.0-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:682b987361e5fd7a139ed565e30d81fd81e9629acc7d925a205366877d8c8657"},
+ {file = "multidict-6.1.0-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:ce2186a7df133a9c895dea3331ddc5ddad42cdd0d1ea2f0a51e5d161e4762f28"},
+ {file = "multidict-6.1.0-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:9f636b730f7e8cb19feb87094949ba54ee5357440b9658b2a32a5ce4bce53972"},
+ {file = "multidict-6.1.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:73eae06aa53af2ea5270cc066dcaf02cc60d2994bbb2c4ef5764949257d10f43"},
+ {file = "multidict-6.1.0-cp39-cp39-win32.whl", hash = "sha256:1ca0083e80e791cffc6efce7660ad24af66c8d4079d2a750b29001b53ff59ada"},
+ {file = "multidict-6.1.0-cp39-cp39-win_amd64.whl", hash = "sha256:aa466da5b15ccea564bdab9c89175c762bc12825f4659c11227f515cee76fa4a"},
+ {file = "multidict-6.1.0-py3-none-any.whl", hash = "sha256:48e171e52d1c4d33888e529b999e5900356b9ae588c2f09a52dcefb158b27506"},
+ {file = "multidict-6.1.0.tar.gz", hash = "sha256:22ae2ebf9b0c69d206c003e2f6a914ea33f0a932d4aa16f236afc049d9958f4a"},
+]
+
+[package.dependencies]
+typing-extensions = {version = ">=4.1.0", markers = "python_version < \"3.11\""}
+
[[package]]
name = "mypy"
version = "1.11.1"
@@ -1036,6 +2012,37 @@ files = [
{file = "nodeenv-1.9.1.tar.gz", hash = "sha256:6ec12890a2dab7946721edbfbcd91f3319c6ccc9aec47be7c7e6b7011ee6645f"},
]
+[[package]]
+name = "otplessauthsdk"
+version = "0.3.3"
+description = "otpless-auth-sdk"
+optional = false
+python-versions = ">=3"
+files = [
+ {file = "OTPLessAuthSDK-0.3.3-py3-none-any.whl", hash = "sha256:7f698e19a7af3501d69b1b8ca8cf952bcaaa6d3a783a7cfc390c7e0c65dfdd7f"},
+ {file = "OTPLessAuthSDK-0.3.3.tar.gz", hash = "sha256:46126c5f6a5009d44b7f7adaf7e67567930de5be530a2f0edb49d8f764cfae22"},
+]
+
+[package.dependencies]
+cryptography = "*"
+PyJWT = "*"
+requests = "*"
+rsa = "*"
+
+[[package]]
+name = "outcome"
+version = "1.3.0.post0"
+description = "Capture the outcome of Python function calls."
+optional = false
+python-versions = ">=3.7"
+files = [
+ {file = "outcome-1.3.0.post0-py2.py3-none-any.whl", hash = "sha256:e771c5ce06d1415e356078d3bdd68523f284b4ce5419828922b6871e65eda82b"},
+ {file = "outcome-1.3.0.post0.tar.gz", hash = "sha256:9dcf02e65f2971b80047b377468e72a268e15c0af3cf1238e6ff14f7f91143b8"},
+]
+
+[package.dependencies]
+attrs = ">=19.2.0"
+
[[package]]
name = "packaging"
version = "24.1"
@@ -1067,6 +2074,105 @@ bcrypt = ["bcrypt (>=3.1.0)"]
build-docs = ["cloud-sptheme (>=1.10.1)", "sphinx (>=1.6)", "sphinxcontrib-fulltoc (>=1.2.0)"]
totp = ["cryptography"]
+[[package]]
+name = "pendulum"
+version = "3.0.0"
+description = "Python datetimes made easy"
+optional = false
+python-versions = ">=3.8"
+files = [
+ {file = "pendulum-3.0.0-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:2cf9e53ef11668e07f73190c805dbdf07a1939c3298b78d5a9203a86775d1bfd"},
+ {file = "pendulum-3.0.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:fb551b9b5e6059377889d2d878d940fd0bbb80ae4810543db18e6f77b02c5ef6"},
+ {file = "pendulum-3.0.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6c58227ac260d5b01fc1025176d7b31858c9f62595737f350d22124a9a3ad82d"},
+ {file = "pendulum-3.0.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:60fb6f415fea93a11c52578eaa10594568a6716602be8430b167eb0d730f3332"},
+ {file = "pendulum-3.0.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b69f6b4dbcb86f2c2fe696ba991e67347bcf87fe601362a1aba6431454b46bde"},
+ {file = "pendulum-3.0.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:138afa9c373ee450ede206db5a5e9004fd3011b3c6bbe1e57015395cd076a09f"},
+ {file = "pendulum-3.0.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:83d9031f39c6da9677164241fd0d37fbfc9dc8ade7043b5d6d62f56e81af8ad2"},
+ {file = "pendulum-3.0.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:0c2308af4033fa534f089595bcd40a95a39988ce4059ccd3dc6acb9ef14ca44a"},
+ {file = "pendulum-3.0.0-cp310-none-win_amd64.whl", hash = "sha256:9a59637cdb8462bdf2dbcb9d389518c0263799189d773ad5c11db6b13064fa79"},
+ {file = "pendulum-3.0.0-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:3725245c0352c95d6ca297193192020d1b0c0f83d5ee6bb09964edc2b5a2d508"},
+ {file = "pendulum-3.0.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:6c035f03a3e565ed132927e2c1b691de0dbf4eb53b02a5a3c5a97e1a64e17bec"},
+ {file = "pendulum-3.0.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:597e66e63cbd68dd6d58ac46cb7a92363d2088d37ccde2dae4332ef23e95cd00"},
+ {file = "pendulum-3.0.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:99a0f8172e19f3f0c0e4ace0ad1595134d5243cf75985dc2233e8f9e8de263ca"},
+ {file = "pendulum-3.0.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:77d8839e20f54706aed425bec82a83b4aec74db07f26acd039905d1237a5e1d4"},
+ {file = "pendulum-3.0.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:afde30e8146292b059020fbc8b6f8fd4a60ae7c5e6f0afef937bbb24880bdf01"},
+ {file = "pendulum-3.0.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:660434a6fcf6303c4efd36713ca9212c753140107ee169a3fc6c49c4711c2a05"},
+ {file = "pendulum-3.0.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:dee9e5a48c6999dc1106eb7eea3e3a50e98a50651b72c08a87ee2154e544b33e"},
+ {file = "pendulum-3.0.0-cp311-none-win_amd64.whl", hash = "sha256:d4cdecde90aec2d67cebe4042fd2a87a4441cc02152ed7ed8fb3ebb110b94ec4"},
+ {file = "pendulum-3.0.0-cp311-none-win_arm64.whl", hash = "sha256:773c3bc4ddda2dda9f1b9d51fe06762f9200f3293d75c4660c19b2614b991d83"},
+ {file = "pendulum-3.0.0-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:409e64e41418c49f973d43a28afe5df1df4f1dd87c41c7c90f1a63f61ae0f1f7"},
+ {file = "pendulum-3.0.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:a38ad2121c5ec7c4c190c7334e789c3b4624798859156b138fcc4d92295835dc"},
+ {file = "pendulum-3.0.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fde4d0b2024b9785f66b7f30ed59281bd60d63d9213cda0eb0910ead777f6d37"},
+ {file = "pendulum-3.0.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4b2c5675769fb6d4c11238132962939b960fcb365436b6d623c5864287faa319"},
+ {file = "pendulum-3.0.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8af95e03e066826f0f4c65811cbee1b3123d4a45a1c3a2b4fc23c4b0dff893b5"},
+ {file = "pendulum-3.0.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2165a8f33cb15e06c67070b8afc87a62b85c5a273e3aaa6bc9d15c93a4920d6f"},
+ {file = "pendulum-3.0.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:ad5e65b874b5e56bd942546ea7ba9dd1d6a25121db1c517700f1c9de91b28518"},
+ {file = "pendulum-3.0.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:17fe4b2c844bbf5f0ece69cfd959fa02957c61317b2161763950d88fed8e13b9"},
+ {file = "pendulum-3.0.0-cp312-none-win_amd64.whl", hash = "sha256:78f8f4e7efe5066aca24a7a57511b9c2119f5c2b5eb81c46ff9222ce11e0a7a5"},
+ {file = "pendulum-3.0.0-cp312-none-win_arm64.whl", hash = "sha256:28f49d8d1e32aae9c284a90b6bb3873eee15ec6e1d9042edd611b22a94ac462f"},
+ {file = "pendulum-3.0.0-cp37-cp37m-macosx_10_12_x86_64.whl", hash = "sha256:d4e2512f4e1a4670284a153b214db9719eb5d14ac55ada5b76cbdb8c5c00399d"},
+ {file = "pendulum-3.0.0-cp37-cp37m-macosx_11_0_arm64.whl", hash = "sha256:3d897eb50883cc58d9b92f6405245f84b9286cd2de6e8694cb9ea5cb15195a32"},
+ {file = "pendulum-3.0.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2e169cc2ca419517f397811bbe4589cf3cd13fca6dc38bb352ba15ea90739ebb"},
+ {file = "pendulum-3.0.0-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f17c3084a4524ebefd9255513692f7e7360e23c8853dc6f10c64cc184e1217ab"},
+ {file = "pendulum-3.0.0-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:826d6e258052715f64d05ae0fc9040c0151e6a87aae7c109ba9a0ed930ce4000"},
+ {file = "pendulum-3.0.0-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d2aae97087872ef152a0c40e06100b3665d8cb86b59bc8471ca7c26132fccd0f"},
+ {file = "pendulum-3.0.0-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:ac65eeec2250d03106b5e81284ad47f0d417ca299a45e89ccc69e36130ca8bc7"},
+ {file = "pendulum-3.0.0-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:a5346d08f3f4a6e9e672187faa179c7bf9227897081d7121866358af369f44f9"},
+ {file = "pendulum-3.0.0-cp37-none-win_amd64.whl", hash = "sha256:235d64e87946d8f95c796af34818c76e0f88c94d624c268693c85b723b698aa9"},
+ {file = "pendulum-3.0.0-cp38-cp38-macosx_10_12_x86_64.whl", hash = "sha256:6a881d9c2a7f85bc9adafcfe671df5207f51f5715ae61f5d838b77a1356e8b7b"},
+ {file = "pendulum-3.0.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:d7762d2076b9b1cb718a6631ad6c16c23fc3fac76cbb8c454e81e80be98daa34"},
+ {file = "pendulum-3.0.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4e8e36a8130819d97a479a0e7bf379b66b3b1b520e5dc46bd7eb14634338df8c"},
+ {file = "pendulum-3.0.0-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:7dc843253ac373358ffc0711960e2dd5b94ab67530a3e204d85c6e8cb2c5fa10"},
+ {file = "pendulum-3.0.0-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:0a78ad3635d609ceb1e97d6aedef6a6a6f93433ddb2312888e668365908c7120"},
+ {file = "pendulum-3.0.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b30a137e9e0d1f751e60e67d11fc67781a572db76b2296f7b4d44554761049d6"},
+ {file = "pendulum-3.0.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:c95984037987f4a457bb760455d9ca80467be792236b69d0084f228a8ada0162"},
+ {file = "pendulum-3.0.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:d29c6e578fe0f893766c0d286adbf0b3c726a4e2341eba0917ec79c50274ec16"},
+ {file = "pendulum-3.0.0-cp38-none-win_amd64.whl", hash = "sha256:deaba8e16dbfcb3d7a6b5fabdd5a38b7c982809567479987b9c89572df62e027"},
+ {file = "pendulum-3.0.0-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:b11aceea5b20b4b5382962b321dbc354af0defe35daa84e9ff3aae3c230df694"},
+ {file = "pendulum-3.0.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:a90d4d504e82ad236afac9adca4d6a19e4865f717034fc69bafb112c320dcc8f"},
+ {file = "pendulum-3.0.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:825799c6b66e3734227756fa746cc34b3549c48693325b8b9f823cb7d21b19ac"},
+ {file = "pendulum-3.0.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ad769e98dc07972e24afe0cff8d365cb6f0ebc7e65620aa1976fcfbcadc4c6f3"},
+ {file = "pendulum-3.0.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a6fc26907eb5fb8cc6188cc620bc2075a6c534d981a2f045daa5f79dfe50d512"},
+ {file = "pendulum-3.0.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0c717eab1b6d898c00a3e0fa7781d615b5c5136bbd40abe82be100bb06df7a56"},
+ {file = "pendulum-3.0.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:3ddd1d66d1a714ce43acfe337190be055cdc221d911fc886d5a3aae28e14b76d"},
+ {file = "pendulum-3.0.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:822172853d7a9cf6da95d7b66a16c7160cb99ae6df55d44373888181d7a06edc"},
+ {file = "pendulum-3.0.0-cp39-none-win_amd64.whl", hash = "sha256:840de1b49cf1ec54c225a2a6f4f0784d50bd47f68e41dc005b7f67c7d5b5f3ae"},
+ {file = "pendulum-3.0.0-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:3b1f74d1e6ffe5d01d6023870e2ce5c2191486928823196f8575dcc786e107b1"},
+ {file = "pendulum-3.0.0-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:729e9f93756a2cdfa77d0fc82068346e9731c7e884097160603872686e570f07"},
+ {file = "pendulum-3.0.0-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e586acc0b450cd21cbf0db6bae386237011b75260a3adceddc4be15334689a9a"},
+ {file = "pendulum-3.0.0-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:22e7944ffc1f0099a79ff468ee9630c73f8c7835cd76fdb57ef7320e6a409df4"},
+ {file = "pendulum-3.0.0-pp310-pypy310_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:fa30af36bd8e50686846bdace37cf6707bdd044e5cb6e1109acbad3277232e04"},
+ {file = "pendulum-3.0.0-pp310-pypy310_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:440215347b11914ae707981b9a57ab9c7b6983ab0babde07063c6ee75c0dc6e7"},
+ {file = "pendulum-3.0.0-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:314c4038dc5e6a52991570f50edb2f08c339debdf8cea68ac355b32c4174e820"},
+ {file = "pendulum-3.0.0-pp37-pypy37_pp73-macosx_10_12_x86_64.whl", hash = "sha256:5acb1d386337415f74f4d1955c4ce8d0201978c162927d07df8eb0692b2d8533"},
+ {file = "pendulum-3.0.0-pp37-pypy37_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a789e12fbdefaffb7b8ac67f9d8f22ba17a3050ceaaa635cd1cc4645773a4b1e"},
+ {file = "pendulum-3.0.0-pp37-pypy37_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:860aa9b8a888e5913bd70d819306749e5eb488e6b99cd6c47beb701b22bdecf5"},
+ {file = "pendulum-3.0.0-pp37-pypy37_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:5ebc65ea033ef0281368217fbf59f5cb05b338ac4dd23d60959c7afcd79a60a0"},
+ {file = "pendulum-3.0.0-pp37-pypy37_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:d9fef18ab0386ef6a9ac7bad7e43ded42c83ff7ad412f950633854f90d59afa8"},
+ {file = "pendulum-3.0.0-pp38-pypy38_pp73-macosx_10_12_x86_64.whl", hash = "sha256:1c134ba2f0571d0b68b83f6972e2307a55a5a849e7dac8505c715c531d2a8795"},
+ {file = "pendulum-3.0.0-pp38-pypy38_pp73-macosx_11_0_arm64.whl", hash = "sha256:385680812e7e18af200bb9b4a49777418c32422d05ad5a8eb85144c4a285907b"},
+ {file = "pendulum-3.0.0-pp38-pypy38_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9eec91cd87c59fb32ec49eb722f375bd58f4be790cae11c1b70fac3ee4f00da0"},
+ {file = "pendulum-3.0.0-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4386bffeca23c4b69ad50a36211f75b35a4deb6210bdca112ac3043deb7e494a"},
+ {file = "pendulum-3.0.0-pp38-pypy38_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:dfbcf1661d7146d7698da4b86e7f04814221081e9fe154183e34f4c5f5fa3bf8"},
+ {file = "pendulum-3.0.0-pp38-pypy38_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:04a1094a5aa1daa34a6b57c865b25f691848c61583fb22722a4df5699f6bf74c"},
+ {file = "pendulum-3.0.0-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:5b0ec85b9045bd49dd3a3493a5e7ddfd31c36a2a60da387c419fa04abcaecb23"},
+ {file = "pendulum-3.0.0-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:0a15b90129765b705eb2039062a6daf4d22c4e28d1a54fa260892e8c3ae6e157"},
+ {file = "pendulum-3.0.0-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:bb8f6d7acd67a67d6fedd361ad2958ff0539445ef51cbe8cd288db4306503cd0"},
+ {file = "pendulum-3.0.0-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fd69b15374bef7e4b4440612915315cc42e8575fcda2a3d7586a0d88192d0c88"},
+ {file = "pendulum-3.0.0-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dc00f8110db6898360c53c812872662e077eaf9c75515d53ecc65d886eec209a"},
+ {file = "pendulum-3.0.0-pp39-pypy39_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:83a44e8b40655d0ba565a5c3d1365d27e3e6778ae2a05b69124db9e471255c4a"},
+ {file = "pendulum-3.0.0-pp39-pypy39_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:1a3604e9fbc06b788041b2a8b78f75c243021e0f512447806a6d37ee5214905d"},
+ {file = "pendulum-3.0.0-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:92c307ae7accebd06cbae4729f0ba9fa724df5f7d91a0964b1b972a22baa482b"},
+ {file = "pendulum-3.0.0.tar.gz", hash = "sha256:5d034998dea404ec31fae27af6b22cff1708f830a1ed7353be4d1019bb9f584e"},
+]
+
+[package.dependencies]
+python-dateutil = ">=2.6"
+tzdata = ">=2020.1"
+
+[package.extras]
+test = ["time-machine (>=2.6.0)"]
+
[[package]]
name = "platformdirs"
version = "4.2.2"
@@ -1138,6 +2244,113 @@ requests = "*"
dev = ["black", "flake8", "therapist", "tox", "twine", "wheel"]
test = ["mock", "nose"]
+[[package]]
+name = "propcache"
+version = "0.2.0"
+description = "Accelerated property cache"
+optional = false
+python-versions = ">=3.8"
+files = [
+ {file = "propcache-0.2.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:c5869b8fd70b81835a6f187c5fdbe67917a04d7e52b6e7cc4e5fe39d55c39d58"},
+ {file = "propcache-0.2.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:952e0d9d07609d9c5be361f33b0d6d650cd2bae393aabb11d9b719364521984b"},
+ {file = "propcache-0.2.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:33ac8f098df0585c0b53009f039dfd913b38c1d2edafed0cedcc0c32a05aa110"},
+ {file = "propcache-0.2.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:97e48e8875e6c13909c800fa344cd54cc4b2b0db1d5f911f840458a500fde2c2"},
+ {file = "propcache-0.2.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:388f3217649d6d59292b722d940d4d2e1e6a7003259eb835724092a1cca0203a"},
+ {file = "propcache-0.2.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f571aea50ba5623c308aa146eb650eebf7dbe0fd8c5d946e28343cb3b5aad577"},
+ {file = "propcache-0.2.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3dfafb44f7bb35c0c06eda6b2ab4bfd58f02729e7c4045e179f9a861b07c9850"},
+ {file = "propcache-0.2.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a3ebe9a75be7ab0b7da2464a77bb27febcb4fab46a34f9288f39d74833db7f61"},
+ {file = "propcache-0.2.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:d2f0d0f976985f85dfb5f3d685697ef769faa6b71993b46b295cdbbd6be8cc37"},
+ {file = "propcache-0.2.0-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:a3dc1a4b165283bd865e8f8cb5f0c64c05001e0718ed06250d8cac9bec115b48"},
+ {file = "propcache-0.2.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:9e0f07b42d2a50c7dd2d8675d50f7343d998c64008f1da5fef888396b7f84630"},
+ {file = "propcache-0.2.0-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:e63e3e1e0271f374ed489ff5ee73d4b6e7c60710e1f76af5f0e1a6117cd26394"},
+ {file = "propcache-0.2.0-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:56bb5c98f058a41bb58eead194b4db8c05b088c93d94d5161728515bd52b052b"},
+ {file = "propcache-0.2.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:7665f04d0c7f26ff8bb534e1c65068409bf4687aa2534faf7104d7182debb336"},
+ {file = "propcache-0.2.0-cp310-cp310-win32.whl", hash = "sha256:7cf18abf9764746b9c8704774d8b06714bcb0a63641518a3a89c7f85cc02c2ad"},
+ {file = "propcache-0.2.0-cp310-cp310-win_amd64.whl", hash = "sha256:cfac69017ef97db2438efb854edf24f5a29fd09a536ff3a992b75990720cdc99"},
+ {file = "propcache-0.2.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:63f13bf09cc3336eb04a837490b8f332e0db41da66995c9fd1ba04552e516354"},
+ {file = "propcache-0.2.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:608cce1da6f2672a56b24a015b42db4ac612ee709f3d29f27a00c943d9e851de"},
+ {file = "propcache-0.2.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:466c219deee4536fbc83c08d09115249db301550625c7fef1c5563a584c9bc87"},
+ {file = "propcache-0.2.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fc2db02409338bf36590aa985a461b2c96fce91f8e7e0f14c50c5fcc4f229016"},
+ {file = "propcache-0.2.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a6ed8db0a556343d566a5c124ee483ae113acc9a557a807d439bcecc44e7dfbb"},
+ {file = "propcache-0.2.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:91997d9cb4a325b60d4e3f20967f8eb08dfcb32b22554d5ef78e6fd1dda743a2"},
+ {file = "propcache-0.2.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4c7dde9e533c0a49d802b4f3f218fa9ad0a1ce21f2c2eb80d5216565202acab4"},
+ {file = "propcache-0.2.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ffcad6c564fe6b9b8916c1aefbb37a362deebf9394bd2974e9d84232e3e08504"},
+ {file = "propcache-0.2.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:97a58a28bcf63284e8b4d7b460cbee1edaab24634e82059c7b8c09e65284f178"},
+ {file = "propcache-0.2.0-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:945db8ee295d3af9dbdbb698cce9bbc5c59b5c3fe328bbc4387f59a8a35f998d"},
+ {file = "propcache-0.2.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:39e104da444a34830751715f45ef9fc537475ba21b7f1f5b0f4d71a3b60d7fe2"},
+ {file = "propcache-0.2.0-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:c5ecca8f9bab618340c8e848d340baf68bcd8ad90a8ecd7a4524a81c1764b3db"},
+ {file = "propcache-0.2.0-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:c436130cc779806bdf5d5fae0d848713105472b8566b75ff70048c47d3961c5b"},
+ {file = "propcache-0.2.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:191db28dc6dcd29d1a3e063c3be0b40688ed76434622c53a284e5427565bbd9b"},
+ {file = "propcache-0.2.0-cp311-cp311-win32.whl", hash = "sha256:5f2564ec89058ee7c7989a7b719115bdfe2a2fb8e7a4543b8d1c0cc4cf6478c1"},
+ {file = "propcache-0.2.0-cp311-cp311-win_amd64.whl", hash = "sha256:6e2e54267980349b723cff366d1e29b138b9a60fa376664a157a342689553f71"},
+ {file = "propcache-0.2.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:2ee7606193fb267be4b2e3b32714f2d58cad27217638db98a60f9efb5efeccc2"},
+ {file = "propcache-0.2.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:91ee8fc02ca52e24bcb77b234f22afc03288e1dafbb1f88fe24db308910c4ac7"},
+ {file = "propcache-0.2.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:2e900bad2a8456d00a113cad8c13343f3b1f327534e3589acc2219729237a2e8"},
+ {file = "propcache-0.2.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f52a68c21363c45297aca15561812d542f8fc683c85201df0bebe209e349f793"},
+ {file = "propcache-0.2.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1e41d67757ff4fbc8ef2af99b338bfb955010444b92929e9e55a6d4dcc3c4f09"},
+ {file = "propcache-0.2.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a64e32f8bd94c105cc27f42d3b658902b5bcc947ece3c8fe7bc1b05982f60e89"},
+ {file = "propcache-0.2.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:55346705687dbd7ef0d77883ab4f6fabc48232f587925bdaf95219bae072491e"},
+ {file = "propcache-0.2.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:00181262b17e517df2cd85656fcd6b4e70946fe62cd625b9d74ac9977b64d8d9"},
+ {file = "propcache-0.2.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:6994984550eaf25dd7fc7bd1b700ff45c894149341725bb4edc67f0ffa94efa4"},
+ {file = "propcache-0.2.0-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:56295eb1e5f3aecd516d91b00cfd8bf3a13991de5a479df9e27dd569ea23959c"},
+ {file = "propcache-0.2.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:439e76255daa0f8151d3cb325f6dd4a3e93043e6403e6491813bcaaaa8733887"},
+ {file = "propcache-0.2.0-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:f6475a1b2ecb310c98c28d271a30df74f9dd436ee46d09236a6b750a7599ce57"},
+ {file = "propcache-0.2.0-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:3444cdba6628accf384e349014084b1cacd866fbb88433cd9d279d90a54e0b23"},
+ {file = "propcache-0.2.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:4a9d9b4d0a9b38d1c391bb4ad24aa65f306c6f01b512e10a8a34a2dc5675d348"},
+ {file = "propcache-0.2.0-cp312-cp312-win32.whl", hash = "sha256:69d3a98eebae99a420d4b28756c8ce6ea5a29291baf2dc9ff9414b42676f61d5"},
+ {file = "propcache-0.2.0-cp312-cp312-win_amd64.whl", hash = "sha256:ad9c9b99b05f163109466638bd30ada1722abb01bbb85c739c50b6dc11f92dc3"},
+ {file = "propcache-0.2.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:ecddc221a077a8132cf7c747d5352a15ed763b674c0448d811f408bf803d9ad7"},
+ {file = "propcache-0.2.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:0e53cb83fdd61cbd67202735e6a6687a7b491c8742dfc39c9e01e80354956763"},
+ {file = "propcache-0.2.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:92fe151145a990c22cbccf9ae15cae8ae9eddabfc949a219c9f667877e40853d"},
+ {file = "propcache-0.2.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d6a21ef516d36909931a2967621eecb256018aeb11fc48656e3257e73e2e247a"},
+ {file = "propcache-0.2.0-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3f88a4095e913f98988f5b338c1d4d5d07dbb0b6bad19892fd447484e483ba6b"},
+ {file = "propcache-0.2.0-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5a5b3bb545ead161be780ee85a2b54fdf7092815995661947812dde94a40f6fb"},
+ {file = "propcache-0.2.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:67aeb72e0f482709991aa91345a831d0b707d16b0257e8ef88a2ad246a7280bf"},
+ {file = "propcache-0.2.0-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3c997f8c44ec9b9b0bcbf2d422cc00a1d9b9c681f56efa6ca149a941e5560da2"},
+ {file = "propcache-0.2.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:2a66df3d4992bc1d725b9aa803e8c5a66c010c65c741ad901e260ece77f58d2f"},
+ {file = "propcache-0.2.0-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:3ebbcf2a07621f29638799828b8d8668c421bfb94c6cb04269130d8de4fb7136"},
+ {file = "propcache-0.2.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:1235c01ddaa80da8235741e80815ce381c5267f96cc49b1477fdcf8c047ef325"},
+ {file = "propcache-0.2.0-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:3947483a381259c06921612550867b37d22e1df6d6d7e8361264b6d037595f44"},
+ {file = "propcache-0.2.0-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:d5bed7f9805cc29c780f3aee05de3262ee7ce1f47083cfe9f77471e9d6777e83"},
+ {file = "propcache-0.2.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:e4a91d44379f45f5e540971d41e4626dacd7f01004826a18cb048e7da7e96544"},
+ {file = "propcache-0.2.0-cp313-cp313-win32.whl", hash = "sha256:f902804113e032e2cdf8c71015651c97af6418363bea8d78dc0911d56c335032"},
+ {file = "propcache-0.2.0-cp313-cp313-win_amd64.whl", hash = "sha256:8f188cfcc64fb1266f4684206c9de0e80f54622c3f22a910cbd200478aeae61e"},
+ {file = "propcache-0.2.0-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:53d1bd3f979ed529f0805dd35ddaca330f80a9a6d90bc0121d2ff398f8ed8861"},
+ {file = "propcache-0.2.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:83928404adf8fb3d26793665633ea79b7361efa0287dfbd372a7e74311d51ee6"},
+ {file = "propcache-0.2.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:77a86c261679ea5f3896ec060be9dc8e365788248cc1e049632a1be682442063"},
+ {file = "propcache-0.2.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:218db2a3c297a3768c11a34812e63b3ac1c3234c3a086def9c0fee50d35add1f"},
+ {file = "propcache-0.2.0-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:7735e82e3498c27bcb2d17cb65d62c14f1100b71723b68362872bca7d0913d90"},
+ {file = "propcache-0.2.0-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:20a617c776f520c3875cf4511e0d1db847a076d720714ae35ffe0df3e440be68"},
+ {file = "propcache-0.2.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:67b69535c870670c9f9b14a75d28baa32221d06f6b6fa6f77a0a13c5a7b0a5b9"},
+ {file = "propcache-0.2.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4569158070180c3855e9c0791c56be3ceeb192defa2cdf6a3f39e54319e56b89"},
+ {file = "propcache-0.2.0-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:db47514ffdbd91ccdc7e6f8407aac4ee94cc871b15b577c1c324236b013ddd04"},
+ {file = "propcache-0.2.0-cp38-cp38-musllinux_1_2_armv7l.whl", hash = "sha256:2a60ad3e2553a74168d275a0ef35e8c0a965448ffbc3b300ab3a5bb9956c2162"},
+ {file = "propcache-0.2.0-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:662dd62358bdeaca0aee5761de8727cfd6861432e3bb828dc2a693aa0471a563"},
+ {file = "propcache-0.2.0-cp38-cp38-musllinux_1_2_ppc64le.whl", hash = "sha256:25a1f88b471b3bc911d18b935ecb7115dff3a192b6fef46f0bfaf71ff4f12418"},
+ {file = "propcache-0.2.0-cp38-cp38-musllinux_1_2_s390x.whl", hash = "sha256:f60f0ac7005b9f5a6091009b09a419ace1610e163fa5deaba5ce3484341840e7"},
+ {file = "propcache-0.2.0-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:74acd6e291f885678631b7ebc85d2d4aec458dd849b8c841b57ef04047833bed"},
+ {file = "propcache-0.2.0-cp38-cp38-win32.whl", hash = "sha256:d9b6ddac6408194e934002a69bcaadbc88c10b5f38fb9307779d1c629181815d"},
+ {file = "propcache-0.2.0-cp38-cp38-win_amd64.whl", hash = "sha256:676135dcf3262c9c5081cc8f19ad55c8a64e3f7282a21266d05544450bffc3a5"},
+ {file = "propcache-0.2.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:25c8d773a62ce0451b020c7b29a35cfbc05de8b291163a7a0f3b7904f27253e6"},
+ {file = "propcache-0.2.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:375a12d7556d462dc64d70475a9ee5982465fbb3d2b364f16b86ba9135793638"},
+ {file = "propcache-0.2.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:1ec43d76b9677637a89d6ab86e1fef70d739217fefa208c65352ecf0282be957"},
+ {file = "propcache-0.2.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f45eec587dafd4b2d41ac189c2156461ebd0c1082d2fe7013571598abb8505d1"},
+ {file = "propcache-0.2.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:bc092ba439d91df90aea38168e11f75c655880c12782facf5cf9c00f3d42b562"},
+ {file = "propcache-0.2.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:fa1076244f54bb76e65e22cb6910365779d5c3d71d1f18b275f1dfc7b0d71b4d"},
+ {file = "propcache-0.2.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:682a7c79a2fbf40f5dbb1eb6bfe2cd865376deeac65acf9beb607505dced9e12"},
+ {file = "propcache-0.2.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8e40876731f99b6f3c897b66b803c9e1c07a989b366c6b5b475fafd1f7ba3fb8"},
+ {file = "propcache-0.2.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:363ea8cd3c5cb6679f1c2f5f1f9669587361c062e4899fce56758efa928728f8"},
+ {file = "propcache-0.2.0-cp39-cp39-musllinux_1_2_armv7l.whl", hash = "sha256:140fbf08ab3588b3468932974a9331aff43c0ab8a2ec2c608b6d7d1756dbb6cb"},
+ {file = "propcache-0.2.0-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:e70fac33e8b4ac63dfc4c956fd7d85a0b1139adcfc0d964ce288b7c527537fea"},
+ {file = "propcache-0.2.0-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:b33d7a286c0dc1a15f5fc864cc48ae92a846df287ceac2dd499926c3801054a6"},
+ {file = "propcache-0.2.0-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:f6d5749fdd33d90e34c2efb174c7e236829147a2713334d708746e94c4bde40d"},
+ {file = "propcache-0.2.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:22aa8f2272d81d9317ff5756bb108021a056805ce63dd3630e27d042c8092798"},
+ {file = "propcache-0.2.0-cp39-cp39-win32.whl", hash = "sha256:73e4b40ea0eda421b115248d7e79b59214411109a5bc47d0d48e4c73e3b8fcf9"},
+ {file = "propcache-0.2.0-cp39-cp39-win_amd64.whl", hash = "sha256:9517d5e9e0731957468c29dbfd0f976736a0e55afaea843726e887f36fe017df"},
+ {file = "propcache-0.2.0-py3-none-any.whl", hash = "sha256:2ccc28197af5313706511fab3a8b66dcd6da067a1331372c82ea1cb74285e036"},
+ {file = "propcache-0.2.0.tar.gz", hash = "sha256:df81779732feb9d01e5d513fad0122efb3d53bbc75f61b2a4f29a020bc985e70"},
+]
+
[[package]]
name = "psycopg"
version = "3.2.1"
@@ -1224,20 +2437,83 @@ files = [
{file = "psycopg_binary-3.2.1-cp39-cp39-win_amd64.whl", hash = "sha256:921f0c7f39590763d64a619de84d1b142587acc70fd11cbb5ba8fa39786f3073"},
]
+[[package]]
+name = "psycopg2"
+version = "2.9.9"
+description = "psycopg2 - Python-PostgreSQL Database Adapter"
+optional = false
+python-versions = ">=3.7"
+files = [
+ {file = "psycopg2-2.9.9-cp310-cp310-win32.whl", hash = "sha256:38a8dcc6856f569068b47de286b472b7c473ac7977243593a288ebce0dc89516"},
+ {file = "psycopg2-2.9.9-cp310-cp310-win_amd64.whl", hash = "sha256:426f9f29bde126913a20a96ff8ce7d73fd8a216cfb323b1f04da402d452853c3"},
+ {file = "psycopg2-2.9.9-cp311-cp311-win32.whl", hash = "sha256:ade01303ccf7ae12c356a5e10911c9e1c51136003a9a1d92f7aa9d010fb98372"},
+ {file = "psycopg2-2.9.9-cp311-cp311-win_amd64.whl", hash = "sha256:121081ea2e76729acfb0673ff33755e8703d45e926e416cb59bae3a86c6a4981"},
+ {file = "psycopg2-2.9.9-cp312-cp312-win32.whl", hash = "sha256:d735786acc7dd25815e89cc4ad529a43af779db2e25aa7c626de864127e5a024"},
+ {file = "psycopg2-2.9.9-cp312-cp312-win_amd64.whl", hash = "sha256:a7653d00b732afb6fc597e29c50ad28087dcb4fbfb28e86092277a559ae4e693"},
+ {file = "psycopg2-2.9.9-cp37-cp37m-win32.whl", hash = "sha256:5e0d98cade4f0e0304d7d6f25bbfbc5bd186e07b38eac65379309c4ca3193efa"},
+ {file = "psycopg2-2.9.9-cp37-cp37m-win_amd64.whl", hash = "sha256:7e2dacf8b009a1c1e843b5213a87f7c544b2b042476ed7755be813eaf4e8347a"},
+ {file = "psycopg2-2.9.9-cp38-cp38-win32.whl", hash = "sha256:ff432630e510709564c01dafdbe996cb552e0b9f3f065eb89bdce5bd31fabf4c"},
+ {file = "psycopg2-2.9.9-cp38-cp38-win_amd64.whl", hash = "sha256:bac58c024c9922c23550af2a581998624d6e02350f4ae9c5f0bc642c633a2d5e"},
+ {file = "psycopg2-2.9.9-cp39-cp39-win32.whl", hash = "sha256:c92811b2d4c9b6ea0285942b2e7cac98a59e166d59c588fe5cfe1eda58e72d59"},
+ {file = "psycopg2-2.9.9-cp39-cp39-win_amd64.whl", hash = "sha256:de80739447af31525feddeb8effd640782cf5998e1a4e9192ebdf829717e3913"},
+ {file = "psycopg2-2.9.9.tar.gz", hash = "sha256:d1454bde93fb1e224166811694d600e746430c006fbb031ea06ecc2ea41bf156"},
+]
+
+[[package]]
+name = "pwdlib"
+version = "0.2.0"
+description = "Modern password hashing for Python"
+optional = false
+python-versions = ">=3.8"
+files = [
+ {file = "pwdlib-0.2.0-py3-none-any.whl", hash = "sha256:be53812012ab66795a57ac9393a59716ae7c2b60841ed453eb1262017fdec144"},
+ {file = "pwdlib-0.2.0.tar.gz", hash = "sha256:b1bdafc064310eb6d3d07144a210267063ab4f45ac73a97be948e6589f74e861"},
+]
+
+[package.dependencies]
+argon2-cffi = {version = "23.1.0", optional = true, markers = "extra == \"argon2\""}
+bcrypt = {version = "4.1.2", optional = true, markers = "extra == \"bcrypt\""}
+
+[package.extras]
+argon2 = ["argon2-cffi (==23.1.0)"]
+bcrypt = ["bcrypt (==4.1.2)"]
+
+[[package]]
+name = "pyasn1"
+version = "0.6.1"
+description = "Pure-Python implementation of ASN.1 types and DER/BER/CER codecs (X.208)"
+optional = false
+python-versions = ">=3.8"
+files = [
+ {file = "pyasn1-0.6.1-py3-none-any.whl", hash = "sha256:0d632f46f2ba09143da3a8afe9e33fb6f92fa2320ab7e886e2d0f7672af84629"},
+ {file = "pyasn1-0.6.1.tar.gz", hash = "sha256:6f580d2bdd84365380830acf45550f2511469f673cb4a5ae3857a3170128b034"},
+]
+
+[[package]]
+name = "pycparser"
+version = "2.22"
+description = "C parser in Python"
+optional = false
+python-versions = ">=3.8"
+files = [
+ {file = "pycparser-2.22-py3-none-any.whl", hash = "sha256:c3702b6d3dd8c7abc1afa565d7e63d53a1d0bd86cdc24edd75470f4de499cfcc"},
+ {file = "pycparser-2.22.tar.gz", hash = "sha256:491c8be9c040f5390f5bf44a5b07752bd07f56edf992381b05c701439eec10f6"},
+]
+
[[package]]
name = "pydantic"
-version = "2.8.2"
+version = "2.9.2"
description = "Data validation using Python type hints"
optional = false
python-versions = ">=3.8"
files = [
- {file = "pydantic-2.8.2-py3-none-any.whl", hash = "sha256:73ee9fddd406dc318b885c7a2eab8a6472b68b8fb5ba8150949fc3db939f23c8"},
- {file = "pydantic-2.8.2.tar.gz", hash = "sha256:6f62c13d067b0755ad1c21a34bdd06c0c12625a22b0fc09c6b149816604f7c2a"},
+ {file = "pydantic-2.9.2-py3-none-any.whl", hash = "sha256:f048cec7b26778210e28a0459867920654d48e5e62db0958433636cde4254f12"},
+ {file = "pydantic-2.9.2.tar.gz", hash = "sha256:d155cef71265d1e9807ed1c32b4c8deec042a44a50a4188b25ac67ecd81a9c0f"},
]
[package.dependencies]
-annotated-types = ">=0.4.0"
-pydantic-core = "2.20.1"
+annotated-types = ">=0.6.0"
+pydantic-core = "2.23.4"
typing-extensions = [
{version = ">=4.12.2", markers = "python_version >= \"3.13\""},
{version = ">=4.6.1", markers = "python_version < \"3.13\""},
@@ -1245,103 +2521,104 @@ typing-extensions = [
[package.extras]
email = ["email-validator (>=2.0.0)"]
+timezone = ["tzdata"]
[[package]]
name = "pydantic-core"
-version = "2.20.1"
+version = "2.23.4"
description = "Core functionality for Pydantic validation and serialization"
optional = false
python-versions = ">=3.8"
files = [
- {file = "pydantic_core-2.20.1-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:3acae97ffd19bf091c72df4d726d552c473f3576409b2a7ca36b2f535ffff4a3"},
- {file = "pydantic_core-2.20.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:41f4c96227a67a013e7de5ff8f20fb496ce573893b7f4f2707d065907bffdbd6"},
- {file = "pydantic_core-2.20.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5f239eb799a2081495ea659d8d4a43a8f42cd1fe9ff2e7e436295c38a10c286a"},
- {file = "pydantic_core-2.20.1-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:53e431da3fc53360db73eedf6f7124d1076e1b4ee4276b36fb25514544ceb4a3"},
- {file = "pydantic_core-2.20.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f1f62b2413c3a0e846c3b838b2ecd6c7a19ec6793b2a522745b0869e37ab5bc1"},
- {file = "pydantic_core-2.20.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5d41e6daee2813ecceea8eda38062d69e280b39df793f5a942fa515b8ed67953"},
- {file = "pydantic_core-2.20.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3d482efec8b7dc6bfaedc0f166b2ce349df0011f5d2f1f25537ced4cfc34fd98"},
- {file = "pydantic_core-2.20.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:e93e1a4b4b33daed65d781a57a522ff153dcf748dee70b40c7258c5861e1768a"},
- {file = "pydantic_core-2.20.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:e7c4ea22b6739b162c9ecaaa41d718dfad48a244909fe7ef4b54c0b530effc5a"},
- {file = "pydantic_core-2.20.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:4f2790949cf385d985a31984907fecb3896999329103df4e4983a4a41e13e840"},
- {file = "pydantic_core-2.20.1-cp310-none-win32.whl", hash = "sha256:5e999ba8dd90e93d57410c5e67ebb67ffcaadcea0ad973240fdfd3a135506250"},
- {file = "pydantic_core-2.20.1-cp310-none-win_amd64.whl", hash = "sha256:512ecfbefef6dac7bc5eaaf46177b2de58cdf7acac8793fe033b24ece0b9566c"},
- {file = "pydantic_core-2.20.1-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:d2a8fa9d6d6f891f3deec72f5cc668e6f66b188ab14bb1ab52422fe8e644f312"},
- {file = "pydantic_core-2.20.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:175873691124f3d0da55aeea1d90660a6ea7a3cfea137c38afa0a5ffabe37b88"},
- {file = "pydantic_core-2.20.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:37eee5b638f0e0dcd18d21f59b679686bbd18917b87db0193ae36f9c23c355fc"},
- {file = "pydantic_core-2.20.1-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:25e9185e2d06c16ee438ed39bf62935ec436474a6ac4f9358524220f1b236e43"},
- {file = "pydantic_core-2.20.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:150906b40ff188a3260cbee25380e7494ee85048584998c1e66df0c7a11c17a6"},
- {file = "pydantic_core-2.20.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8ad4aeb3e9a97286573c03df758fc7627aecdd02f1da04516a86dc159bf70121"},
- {file = "pydantic_core-2.20.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d3f3ed29cd9f978c604708511a1f9c2fdcb6c38b9aae36a51905b8811ee5cbf1"},
- {file = "pydantic_core-2.20.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:b0dae11d8f5ded51699c74d9548dcc5938e0804cc8298ec0aa0da95c21fff57b"},
- {file = "pydantic_core-2.20.1-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:faa6b09ee09433b87992fb5a2859efd1c264ddc37280d2dd5db502126d0e7f27"},
- {file = "pydantic_core-2.20.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:9dc1b507c12eb0481d071f3c1808f0529ad41dc415d0ca11f7ebfc666e66a18b"},
- {file = "pydantic_core-2.20.1-cp311-none-win32.whl", hash = "sha256:fa2fddcb7107e0d1808086ca306dcade7df60a13a6c347a7acf1ec139aa6789a"},
- {file = "pydantic_core-2.20.1-cp311-none-win_amd64.whl", hash = "sha256:40a783fb7ee353c50bd3853e626f15677ea527ae556429453685ae32280c19c2"},
- {file = "pydantic_core-2.20.1-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:595ba5be69b35777474fa07f80fc260ea71255656191adb22a8c53aba4479231"},
- {file = "pydantic_core-2.20.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:a4f55095ad087474999ee28d3398bae183a66be4823f753cd7d67dd0153427c9"},
- {file = "pydantic_core-2.20.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f9aa05d09ecf4c75157197f27cdc9cfaeb7c5f15021c6373932bf3e124af029f"},
- {file = "pydantic_core-2.20.1-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:e97fdf088d4b31ff4ba35db26d9cc472ac7ef4a2ff2badeabf8d727b3377fc52"},
- {file = "pydantic_core-2.20.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:bc633a9fe1eb87e250b5c57d389cf28998e4292336926b0b6cdaee353f89a237"},
- {file = "pydantic_core-2.20.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d573faf8eb7e6b1cbbcb4f5b247c60ca8be39fe2c674495df0eb4318303137fe"},
- {file = "pydantic_core-2.20.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:26dc97754b57d2fd00ac2b24dfa341abffc380b823211994c4efac7f13b9e90e"},
- {file = "pydantic_core-2.20.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:33499e85e739a4b60c9dac710c20a08dc73cb3240c9a0e22325e671b27b70d24"},
- {file = "pydantic_core-2.20.1-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:bebb4d6715c814597f85297c332297c6ce81e29436125ca59d1159b07f423eb1"},
- {file = "pydantic_core-2.20.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:516d9227919612425c8ef1c9b869bbbee249bc91912c8aaffb66116c0b447ebd"},
- {file = "pydantic_core-2.20.1-cp312-none-win32.whl", hash = "sha256:469f29f9093c9d834432034d33f5fe45699e664f12a13bf38c04967ce233d688"},
- {file = "pydantic_core-2.20.1-cp312-none-win_amd64.whl", hash = "sha256:035ede2e16da7281041f0e626459bcae33ed998cca6a0a007a5ebb73414ac72d"},
- {file = "pydantic_core-2.20.1-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:0827505a5c87e8aa285dc31e9ec7f4a17c81a813d45f70b1d9164e03a813a686"},
- {file = "pydantic_core-2.20.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:19c0fa39fa154e7e0b7f82f88ef85faa2a4c23cc65aae2f5aea625e3c13c735a"},
- {file = "pydantic_core-2.20.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4aa223cd1e36b642092c326d694d8bf59b71ddddc94cdb752bbbb1c5c91d833b"},
- {file = "pydantic_core-2.20.1-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:c336a6d235522a62fef872c6295a42ecb0c4e1d0f1a3e500fe949415761b8a19"},
- {file = "pydantic_core-2.20.1-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:7eb6a0587eded33aeefea9f916899d42b1799b7b14b8f8ff2753c0ac1741edac"},
- {file = "pydantic_core-2.20.1-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:70c8daf4faca8da5a6d655f9af86faf6ec2e1768f4b8b9d0226c02f3d6209703"},
- {file = "pydantic_core-2.20.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e9fa4c9bf273ca41f940bceb86922a7667cd5bf90e95dbb157cbb8441008482c"},
- {file = "pydantic_core-2.20.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:11b71d67b4725e7e2a9f6e9c0ac1239bbc0c48cce3dc59f98635efc57d6dac83"},
- {file = "pydantic_core-2.20.1-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:270755f15174fb983890c49881e93f8f1b80f0b5e3a3cc1394a255706cabd203"},
- {file = "pydantic_core-2.20.1-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:c81131869240e3e568916ef4c307f8b99583efaa60a8112ef27a366eefba8ef0"},
- {file = "pydantic_core-2.20.1-cp313-none-win32.whl", hash = "sha256:b91ced227c41aa29c672814f50dbb05ec93536abf8f43cd14ec9521ea09afe4e"},
- {file = "pydantic_core-2.20.1-cp313-none-win_amd64.whl", hash = "sha256:65db0f2eefcaad1a3950f498aabb4875c8890438bc80b19362cf633b87a8ab20"},
- {file = "pydantic_core-2.20.1-cp38-cp38-macosx_10_12_x86_64.whl", hash = "sha256:4745f4ac52cc6686390c40eaa01d48b18997cb130833154801a442323cc78f91"},
- {file = "pydantic_core-2.20.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:a8ad4c766d3f33ba8fd692f9aa297c9058970530a32c728a2c4bfd2616d3358b"},
- {file = "pydantic_core-2.20.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:41e81317dd6a0127cabce83c0c9c3fbecceae981c8391e6f1dec88a77c8a569a"},
- {file = "pydantic_core-2.20.1-cp38-cp38-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:04024d270cf63f586ad41fff13fde4311c4fc13ea74676962c876d9577bcc78f"},
- {file = "pydantic_core-2.20.1-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:eaad4ff2de1c3823fddf82f41121bdf453d922e9a238642b1dedb33c4e4f98ad"},
- {file = "pydantic_core-2.20.1-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:26ab812fa0c845df815e506be30337e2df27e88399b985d0bb4e3ecfe72df31c"},
- {file = "pydantic_core-2.20.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3c5ebac750d9d5f2706654c638c041635c385596caf68f81342011ddfa1e5598"},
- {file = "pydantic_core-2.20.1-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:2aafc5a503855ea5885559eae883978c9b6d8c8993d67766ee73d82e841300dd"},
- {file = "pydantic_core-2.20.1-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:4868f6bd7c9d98904b748a2653031fc9c2f85b6237009d475b1008bfaeb0a5aa"},
- {file = "pydantic_core-2.20.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:aa2f457b4af386254372dfa78a2eda2563680d982422641a85f271c859df1987"},
- {file = "pydantic_core-2.20.1-cp38-none-win32.whl", hash = "sha256:225b67a1f6d602de0ce7f6c1c3ae89a4aa25d3de9be857999e9124f15dab486a"},
- {file = "pydantic_core-2.20.1-cp38-none-win_amd64.whl", hash = "sha256:6b507132dcfc0dea440cce23ee2182c0ce7aba7054576efc65634f080dbe9434"},
- {file = "pydantic_core-2.20.1-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:b03f7941783b4c4a26051846dea594628b38f6940a2fdc0df00b221aed39314c"},
- {file = "pydantic_core-2.20.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:1eedfeb6089ed3fad42e81a67755846ad4dcc14d73698c120a82e4ccf0f1f9f6"},
- {file = "pydantic_core-2.20.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:635fee4e041ab9c479e31edda27fcf966ea9614fff1317e280d99eb3e5ab6fe2"},
- {file = "pydantic_core-2.20.1-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:77bf3ac639c1ff567ae3b47f8d4cc3dc20f9966a2a6dd2311dcc055d3d04fb8a"},
- {file = "pydantic_core-2.20.1-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:7ed1b0132f24beeec5a78b67d9388656d03e6a7c837394f99257e2d55b461611"},
- {file = "pydantic_core-2.20.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c6514f963b023aeee506678a1cf821fe31159b925c4b76fe2afa94cc70b3222b"},
- {file = "pydantic_core-2.20.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:10d4204d8ca33146e761c79f83cc861df20e7ae9f6487ca290a97702daf56006"},
- {file = "pydantic_core-2.20.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:2d036c7187b9422ae5b262badb87a20a49eb6c5238b2004e96d4da1231badef1"},
- {file = "pydantic_core-2.20.1-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:9ebfef07dbe1d93efb94b4700f2d278494e9162565a54f124c404a5656d7ff09"},
- {file = "pydantic_core-2.20.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:6b9d9bb600328a1ce523ab4f454859e9d439150abb0906c5a1983c146580ebab"},
- {file = "pydantic_core-2.20.1-cp39-none-win32.whl", hash = "sha256:784c1214cb6dd1e3b15dd8b91b9a53852aed16671cc3fbe4786f4f1db07089e2"},
- {file = "pydantic_core-2.20.1-cp39-none-win_amd64.whl", hash = "sha256:d2fe69c5434391727efa54b47a1e7986bb0186e72a41b203df8f5b0a19a4f669"},
- {file = "pydantic_core-2.20.1-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:a45f84b09ac9c3d35dfcf6a27fd0634d30d183205230a0ebe8373a0e8cfa0906"},
- {file = "pydantic_core-2.20.1-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:d02a72df14dfdbaf228424573a07af10637bd490f0901cee872c4f434a735b94"},
- {file = "pydantic_core-2.20.1-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d2b27e6af28f07e2f195552b37d7d66b150adbaa39a6d327766ffd695799780f"},
- {file = "pydantic_core-2.20.1-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:084659fac3c83fd674596612aeff6041a18402f1e1bc19ca39e417d554468482"},
- {file = "pydantic_core-2.20.1-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:242b8feb3c493ab78be289c034a1f659e8826e2233786e36f2893a950a719bb6"},
- {file = "pydantic_core-2.20.1-pp310-pypy310_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:38cf1c40a921d05c5edc61a785c0ddb4bed67827069f535d794ce6bcded919fc"},
- {file = "pydantic_core-2.20.1-pp310-pypy310_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:e0bbdd76ce9aa5d4209d65f2b27fc6e5ef1312ae6c5333c26db3f5ade53a1e99"},
- {file = "pydantic_core-2.20.1-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:254ec27fdb5b1ee60684f91683be95e5133c994cc54e86a0b0963afa25c8f8a6"},
- {file = "pydantic_core-2.20.1-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:407653af5617f0757261ae249d3fba09504d7a71ab36ac057c938572d1bc9331"},
- {file = "pydantic_core-2.20.1-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:c693e916709c2465b02ca0ad7b387c4f8423d1db7b4649c551f27a529181c5ad"},
- {file = "pydantic_core-2.20.1-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5b5ff4911aea936a47d9376fd3ab17e970cc543d1b68921886e7f64bd28308d1"},
- {file = "pydantic_core-2.20.1-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:177f55a886d74f1808763976ac4efd29b7ed15c69f4d838bbd74d9d09cf6fa86"},
- {file = "pydantic_core-2.20.1-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:964faa8a861d2664f0c7ab0c181af0bea66098b1919439815ca8803ef136fc4e"},
- {file = "pydantic_core-2.20.1-pp39-pypy39_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:4dd484681c15e6b9a977c785a345d3e378d72678fd5f1f3c0509608da24f2ac0"},
- {file = "pydantic_core-2.20.1-pp39-pypy39_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:f6d6cff3538391e8486a431569b77921adfcdef14eb18fbf19b7c0a5294d4e6a"},
- {file = "pydantic_core-2.20.1-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:a6d511cc297ff0883bc3708b465ff82d7560193169a8b93260f74ecb0a5e08a7"},
- {file = "pydantic_core-2.20.1.tar.gz", hash = "sha256:26ca695eeee5f9f1aeeb211ffc12f10bcb6f71e2989988fda61dabd65db878d4"},
+ {file = "pydantic_core-2.23.4-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:b10bd51f823d891193d4717448fab065733958bdb6a6b351967bd349d48d5c9b"},
+ {file = "pydantic_core-2.23.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:4fc714bdbfb534f94034efaa6eadd74e5b93c8fa6315565a222f7b6f42ca1166"},
+ {file = "pydantic_core-2.23.4-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:63e46b3169866bd62849936de036f901a9356e36376079b05efa83caeaa02ceb"},
+ {file = "pydantic_core-2.23.4-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:ed1a53de42fbe34853ba90513cea21673481cd81ed1be739f7f2efb931b24916"},
+ {file = "pydantic_core-2.23.4-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:cfdd16ab5e59fc31b5e906d1a3f666571abc367598e3e02c83403acabc092e07"},
+ {file = "pydantic_core-2.23.4-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:255a8ef062cbf6674450e668482456abac99a5583bbafb73f9ad469540a3a232"},
+ {file = "pydantic_core-2.23.4-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4a7cd62e831afe623fbb7aabbb4fe583212115b3ef38a9f6b71869ba644624a2"},
+ {file = "pydantic_core-2.23.4-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:f09e2ff1f17c2b51f2bc76d1cc33da96298f0a036a137f5440ab3ec5360b624f"},
+ {file = "pydantic_core-2.23.4-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:e38e63e6f3d1cec5a27e0afe90a085af8b6806ee208b33030e65b6516353f1a3"},
+ {file = "pydantic_core-2.23.4-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:0dbd8dbed2085ed23b5c04afa29d8fd2771674223135dc9bc937f3c09284d071"},
+ {file = "pydantic_core-2.23.4-cp310-none-win32.whl", hash = "sha256:6531b7ca5f951d663c339002e91aaebda765ec7d61b7d1e3991051906ddde119"},
+ {file = "pydantic_core-2.23.4-cp310-none-win_amd64.whl", hash = "sha256:7c9129eb40958b3d4500fa2467e6a83356b3b61bfff1b414c7361d9220f9ae8f"},
+ {file = "pydantic_core-2.23.4-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:77733e3892bb0a7fa797826361ce8a9184d25c8dffaec60b7ffe928153680ba8"},
+ {file = "pydantic_core-2.23.4-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:1b84d168f6c48fabd1f2027a3d1bdfe62f92cade1fb273a5d68e621da0e44e6d"},
+ {file = "pydantic_core-2.23.4-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:df49e7a0861a8c36d089c1ed57d308623d60416dab2647a4a17fe050ba85de0e"},
+ {file = "pydantic_core-2.23.4-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:ff02b6d461a6de369f07ec15e465a88895f3223eb75073ffea56b84d9331f607"},
+ {file = "pydantic_core-2.23.4-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:996a38a83508c54c78a5f41456b0103c30508fed9abcad0a59b876d7398f25fd"},
+ {file = "pydantic_core-2.23.4-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d97683ddee4723ae8c95d1eddac7c192e8c552da0c73a925a89fa8649bf13eea"},
+ {file = "pydantic_core-2.23.4-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:216f9b2d7713eb98cb83c80b9c794de1f6b7e3145eef40400c62e86cee5f4e1e"},
+ {file = "pydantic_core-2.23.4-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:6f783e0ec4803c787bcea93e13e9932edab72068f68ecffdf86a99fd5918878b"},
+ {file = "pydantic_core-2.23.4-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:d0776dea117cf5272382634bd2a5c1b6eb16767c223c6a5317cd3e2a757c61a0"},
+ {file = "pydantic_core-2.23.4-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:d5f7a395a8cf1621939692dba2a6b6a830efa6b3cee787d82c7de1ad2930de64"},
+ {file = "pydantic_core-2.23.4-cp311-none-win32.whl", hash = "sha256:74b9127ffea03643e998e0c5ad9bd3811d3dac8c676e47db17b0ee7c3c3bf35f"},
+ {file = "pydantic_core-2.23.4-cp311-none-win_amd64.whl", hash = "sha256:98d134c954828488b153d88ba1f34e14259284f256180ce659e8d83e9c05eaa3"},
+ {file = "pydantic_core-2.23.4-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:f3e0da4ebaef65158d4dfd7d3678aad692f7666877df0002b8a522cdf088f231"},
+ {file = "pydantic_core-2.23.4-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:f69a8e0b033b747bb3e36a44e7732f0c99f7edd5cea723d45bc0d6e95377ffee"},
+ {file = "pydantic_core-2.23.4-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:723314c1d51722ab28bfcd5240d858512ffd3116449c557a1336cbe3919beb87"},
+ {file = "pydantic_core-2.23.4-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:bb2802e667b7051a1bebbfe93684841cc9351004e2badbd6411bf357ab8d5ac8"},
+ {file = "pydantic_core-2.23.4-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d18ca8148bebe1b0a382a27a8ee60350091a6ddaf475fa05ef50dc35b5df6327"},
+ {file = "pydantic_core-2.23.4-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:33e3d65a85a2a4a0dc3b092b938a4062b1a05f3a9abde65ea93b233bca0e03f2"},
+ {file = "pydantic_core-2.23.4-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:128585782e5bfa515c590ccee4b727fb76925dd04a98864182b22e89a4e6ed36"},
+ {file = "pydantic_core-2.23.4-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:68665f4c17edcceecc112dfed5dbe6f92261fb9d6054b47d01bf6371a6196126"},
+ {file = "pydantic_core-2.23.4-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:20152074317d9bed6b7a95ade3b7d6054845d70584216160860425f4fbd5ee9e"},
+ {file = "pydantic_core-2.23.4-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:9261d3ce84fa1d38ed649c3638feefeae23d32ba9182963e465d58d62203bd24"},
+ {file = "pydantic_core-2.23.4-cp312-none-win32.whl", hash = "sha256:4ba762ed58e8d68657fc1281e9bb72e1c3e79cc5d464be146e260c541ec12d84"},
+ {file = "pydantic_core-2.23.4-cp312-none-win_amd64.whl", hash = "sha256:97df63000f4fea395b2824da80e169731088656d1818a11b95f3b173747b6cd9"},
+ {file = "pydantic_core-2.23.4-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:7530e201d10d7d14abce4fb54cfe5b94a0aefc87da539d0346a484ead376c3cc"},
+ {file = "pydantic_core-2.23.4-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:df933278128ea1cd77772673c73954e53a1c95a4fdf41eef97c2b779271bd0bd"},
+ {file = "pydantic_core-2.23.4-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0cb3da3fd1b6a5d0279a01877713dbda118a2a4fc6f0d821a57da2e464793f05"},
+ {file = "pydantic_core-2.23.4-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:42c6dcb030aefb668a2b7009c85b27f90e51e6a3b4d5c9bc4c57631292015b0d"},
+ {file = "pydantic_core-2.23.4-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:696dd8d674d6ce621ab9d45b205df149399e4bb9aa34102c970b721554828510"},
+ {file = "pydantic_core-2.23.4-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2971bb5ffe72cc0f555c13e19b23c85b654dd2a8f7ab493c262071377bfce9f6"},
+ {file = "pydantic_core-2.23.4-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8394d940e5d400d04cad4f75c0598665cbb81aecefaca82ca85bd28264af7f9b"},
+ {file = "pydantic_core-2.23.4-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:0dff76e0602ca7d4cdaacc1ac4c005e0ce0dcfe095d5b5259163a80d3a10d327"},
+ {file = "pydantic_core-2.23.4-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:7d32706badfe136888bdea71c0def994644e09fff0bfe47441deaed8e96fdbc6"},
+ {file = "pydantic_core-2.23.4-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:ed541d70698978a20eb63d8c5d72f2cc6d7079d9d90f6b50bad07826f1320f5f"},
+ {file = "pydantic_core-2.23.4-cp313-none-win32.whl", hash = "sha256:3d5639516376dce1940ea36edf408c554475369f5da2abd45d44621cb616f769"},
+ {file = "pydantic_core-2.23.4-cp313-none-win_amd64.whl", hash = "sha256:5a1504ad17ba4210df3a045132a7baeeba5a200e930f57512ee02909fc5c4cb5"},
+ {file = "pydantic_core-2.23.4-cp38-cp38-macosx_10_12_x86_64.whl", hash = "sha256:d4488a93b071c04dc20f5cecc3631fc78b9789dd72483ba15d423b5b3689b555"},
+ {file = "pydantic_core-2.23.4-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:81965a16b675b35e1d09dd14df53f190f9129c0202356ed44ab2728b1c905658"},
+ {file = "pydantic_core-2.23.4-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4ffa2ebd4c8530079140dd2d7f794a9d9a73cbb8e9d59ffe24c63436efa8f271"},
+ {file = "pydantic_core-2.23.4-cp38-cp38-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:61817945f2fe7d166e75fbfb28004034b48e44878177fc54d81688e7b85a3665"},
+ {file = "pydantic_core-2.23.4-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:29d2c342c4bc01b88402d60189f3df065fb0dda3654744d5a165a5288a657368"},
+ {file = "pydantic_core-2.23.4-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5e11661ce0fd30a6790e8bcdf263b9ec5988e95e63cf901972107efc49218b13"},
+ {file = "pydantic_core-2.23.4-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9d18368b137c6295db49ce7218b1a9ba15c5bc254c96d7c9f9e924a9bc7825ad"},
+ {file = "pydantic_core-2.23.4-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:ec4e55f79b1c4ffb2eecd8a0cfba9955a2588497d96851f4c8f99aa4a1d39b12"},
+ {file = "pydantic_core-2.23.4-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:374a5e5049eda9e0a44c696c7ade3ff355f06b1fe0bb945ea3cac2bc336478a2"},
+ {file = "pydantic_core-2.23.4-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:5c364564d17da23db1106787675fc7af45f2f7b58b4173bfdd105564e132e6fb"},
+ {file = "pydantic_core-2.23.4-cp38-none-win32.whl", hash = "sha256:d7a80d21d613eec45e3d41eb22f8f94ddc758a6c4720842dc74c0581f54993d6"},
+ {file = "pydantic_core-2.23.4-cp38-none-win_amd64.whl", hash = "sha256:5f5ff8d839f4566a474a969508fe1c5e59c31c80d9e140566f9a37bba7b8d556"},
+ {file = "pydantic_core-2.23.4-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:a4fa4fc04dff799089689f4fd502ce7d59de529fc2f40a2c8836886c03e0175a"},
+ {file = "pydantic_core-2.23.4-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:0a7df63886be5e270da67e0966cf4afbae86069501d35c8c1b3b6c168f42cb36"},
+ {file = "pydantic_core-2.23.4-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:dcedcd19a557e182628afa1d553c3895a9f825b936415d0dbd3cd0bbcfd29b4b"},
+ {file = "pydantic_core-2.23.4-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:5f54b118ce5de9ac21c363d9b3caa6c800341e8c47a508787e5868c6b79c9323"},
+ {file = "pydantic_core-2.23.4-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:86d2f57d3e1379a9525c5ab067b27dbb8a0642fb5d454e17a9ac434f9ce523e3"},
+ {file = "pydantic_core-2.23.4-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:de6d1d1b9e5101508cb37ab0d972357cac5235f5c6533d1071964c47139257df"},
+ {file = "pydantic_core-2.23.4-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1278e0d324f6908e872730c9102b0112477a7f7cf88b308e4fc36ce1bdb6d58c"},
+ {file = "pydantic_core-2.23.4-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:9a6b5099eeec78827553827f4c6b8615978bb4b6a88e5d9b93eddf8bb6790f55"},
+ {file = "pydantic_core-2.23.4-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:e55541f756f9b3ee346b840103f32779c695a19826a4c442b7954550a0972040"},
+ {file = "pydantic_core-2.23.4-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:a5c7ba8ffb6d6f8f2ab08743be203654bb1aaa8c9dcb09f82ddd34eadb695605"},
+ {file = "pydantic_core-2.23.4-cp39-none-win32.whl", hash = "sha256:37b0fe330e4a58d3c58b24d91d1eb102aeec675a3db4c292ec3928ecd892a9a6"},
+ {file = "pydantic_core-2.23.4-cp39-none-win_amd64.whl", hash = "sha256:1498bec4c05c9c787bde9125cfdcc63a41004ff167f495063191b863399b1a29"},
+ {file = "pydantic_core-2.23.4-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:f455ee30a9d61d3e1a15abd5068827773d6e4dc513e795f380cdd59932c782d5"},
+ {file = "pydantic_core-2.23.4-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:1e90d2e3bd2c3863d48525d297cd143fe541be8bbf6f579504b9712cb6b643ec"},
+ {file = "pydantic_core-2.23.4-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2e203fdf807ac7e12ab59ca2bfcabb38c7cf0b33c41efeb00f8e5da1d86af480"},
+ {file = "pydantic_core-2.23.4-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e08277a400de01bc72436a0ccd02bdf596631411f592ad985dcee21445bd0068"},
+ {file = "pydantic_core-2.23.4-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:f220b0eea5965dec25480b6333c788fb72ce5f9129e8759ef876a1d805d00801"},
+ {file = "pydantic_core-2.23.4-pp310-pypy310_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:d06b0c8da4f16d1d1e352134427cb194a0a6e19ad5db9161bf32b2113409e728"},
+ {file = "pydantic_core-2.23.4-pp310-pypy310_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:ba1a0996f6c2773bd83e63f18914c1de3c9dd26d55f4ac302a7efe93fb8e7433"},
+ {file = "pydantic_core-2.23.4-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:9a5bce9d23aac8f0cf0836ecfc033896aa8443b501c58d0602dbfd5bd5b37753"},
+ {file = "pydantic_core-2.23.4-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:78ddaaa81421a29574a682b3179d4cf9e6d405a09b99d93ddcf7e5239c742e21"},
+ {file = "pydantic_core-2.23.4-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:883a91b5dd7d26492ff2f04f40fbb652de40fcc0afe07e8129e8ae779c2110eb"},
+ {file = "pydantic_core-2.23.4-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:88ad334a15b32a791ea935af224b9de1bf99bcd62fabf745d5f3442199d86d59"},
+ {file = "pydantic_core-2.23.4-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:233710f069d251feb12a56da21e14cca67994eab08362207785cf8c598e74577"},
+ {file = "pydantic_core-2.23.4-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:19442362866a753485ba5e4be408964644dd6a09123d9416c54cd49171f50744"},
+ {file = "pydantic_core-2.23.4-pp39-pypy39_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:624e278a7d29b6445e4e813af92af37820fafb6dcc55c012c834f9e26f9aaaef"},
+ {file = "pydantic_core-2.23.4-pp39-pypy39_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:f5ef8f42bec47f21d07668a043f077d507e5bf4e668d5c6dfe6aaba89de1a5b8"},
+ {file = "pydantic_core-2.23.4-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:aea443fffa9fbe3af1a9ba721a87f926fe548d32cab71d188a6ede77d0ff244e"},
+ {file = "pydantic_core-2.23.4.tar.gz", hash = "sha256:2584f7cf844ac4d970fba483a717dbe10c1c1c96a969bf65d61ffe94df1b2863"},
]
[package.dependencies]
@@ -1378,12 +2655,52 @@ files = [
{file = "PyJWT-2.8.0.tar.gz", hash = "sha256:57e28d156e3d5c10088e0c68abb90bfac3df82b40a71bd0daa20c65ccd5c23de"},
]
+[package.dependencies]
+cryptography = {version = ">=3.4.0", optional = true, markers = "extra == \"crypto\""}
+
[package.extras]
crypto = ["cryptography (>=3.4.0)"]
dev = ["coverage[toml] (==5.0.4)", "cryptography (>=3.4.0)", "pre-commit", "pytest (>=6.0.0,<7.0.0)", "sphinx (>=4.5.0,<5.0.0)", "sphinx-rtd-theme", "zope.interface"]
docs = ["sphinx (>=4.5.0,<5.0.0)", "sphinx-rtd-theme", "zope.interface"]
tests = ["coverage[toml] (==5.0.4)", "pytest (>=6.0.0,<7.0.0)"]
+[[package]]
+name = "pyotp"
+version = "2.9.0"
+description = "Python One Time Password Library"
+optional = false
+python-versions = ">=3.7"
+files = [
+ {file = "pyotp-2.9.0-py3-none-any.whl", hash = "sha256:81c2e5865b8ac55e825b0358e496e1d9387c811e85bb40e71a3b29b288963612"},
+ {file = "pyotp-2.9.0.tar.gz", hash = "sha256:346b6642e0dbdde3b4ff5a930b664ca82abfa116356ed48cc42c7d6590d36f63"},
+]
+
+[package.extras]
+test = ["coverage", "mypy", "ruff", "wheel"]
+
+[[package]]
+name = "pypika-tortoise"
+version = "0.1.6"
+description = "Forked from pypika and streamline just for tortoise-orm"
+optional = false
+python-versions = ">=3.7,<4.0"
+files = [
+ {file = "pypika-tortoise-0.1.6.tar.gz", hash = "sha256:d802868f479a708e3263724c7b5719a26ad79399b2a70cea065f4a4cadbebf36"},
+ {file = "pypika_tortoise-0.1.6-py3-none-any.whl", hash = "sha256:2d68bbb7e377673743cff42aa1059f3a80228d411fbcae591e4465e173109fd8"},
+]
+
+[[package]]
+name = "pysocks"
+version = "1.7.1"
+description = "A Python SOCKS client module. See https://github.com/Anorov/PySocks for more information."
+optional = false
+python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"
+files = [
+ {file = "PySocks-1.7.1-py27-none-any.whl", hash = "sha256:08e69f092cc6dbe92a0fdd16eeb9b9ffbc13cadfe5ca4c7bd92ffb078b293299"},
+ {file = "PySocks-1.7.1-py3-none-any.whl", hash = "sha256:2725bd0a9925919b9b51739eea5f9e2bae91e83288108a9ad338b2e3a4435ee5"},
+ {file = "PySocks-1.7.1.tar.gz", hash = "sha256:3f8804571ebe159c380ac6de37643bb4685970655d3bba243530d6558b799aa0"},
+]
+
[[package]]
name = "pytest"
version = "7.4.4"
@@ -1436,17 +2753,28 @@ cli = ["click (>=5.0)"]
[[package]]
name = "python-multipart"
-version = "0.0.7"
+version = "0.0.9"
description = "A streaming multipart parser for Python"
optional = false
-python-versions = ">=3.7"
+python-versions = ">=3.8"
files = [
- {file = "python_multipart-0.0.7-py3-none-any.whl", hash = "sha256:b1fef9a53b74c795e2347daac8c54b252d9e0df9c619712691c1cc8021bd3c49"},
- {file = "python_multipart-0.0.7.tar.gz", hash = "sha256:288a6c39b06596c1b988bb6794c6fbc80e6c369e35e5062637df256bee0c9af9"},
+ {file = "python_multipart-0.0.9-py3-none-any.whl", hash = "sha256:97ca7b8ea7b05f977dc3849c3ba99d51689822fab725c3703af7c866a0c2b215"},
+ {file = "python_multipart-0.0.9.tar.gz", hash = "sha256:03f54688c663f1b7977105f021043b0793151e4cb1c1a9d4a11fc13d622c4026"},
]
[package.extras]
-dev = ["atomicwrites (==1.2.1)", "attrs (==19.2.0)", "coverage (==6.5.0)", "hatch", "invoke (==2.2.0)", "more-itertools (==4.3.0)", "pbr (==4.3.0)", "pluggy (==1.0.0)", "py (==1.11.0)", "pytest (==7.2.0)", "pytest-cov (==4.0.0)", "pytest-timeout (==2.1.0)", "pyyaml (==5.1)"]
+dev = ["atomicwrites (==1.4.1)", "attrs (==23.2.0)", "coverage (==7.4.1)", "hatch", "invoke (==2.2.0)", "more-itertools (==10.2.0)", "pbr (==6.0.0)", "pluggy (==1.4.0)", "py (==1.11.0)", "pytest (==8.0.0)", "pytest-cov (==4.1.0)", "pytest-timeout (==2.2.0)", "pyyaml (==6.0.1)", "ruff (==0.2.1)"]
+
+[[package]]
+name = "pytz"
+version = "2024.2"
+description = "World timezone definitions, modern and historical"
+optional = false
+python-versions = "*"
+files = [
+ {file = "pytz-2024.2-py2.py3-none-any.whl", hash = "sha256:31c7c1817eb7fae7ca4b8c7ee50c72f93aa2dd863de768e1ef4245d426aa0725"},
+ {file = "pytz-2024.2.tar.gz", hash = "sha256:2aa355083c50a0f93fa581709deac0c9ad65cca8a9e9beac660adcbd493c798a"},
+]
[[package]]
name = "pyyaml"
@@ -1508,6 +2836,24 @@ files = [
{file = "PyYAML-6.0.1.tar.gz", hash = "sha256:bfdf460b1736c775f2ba9f6a92bca30bc2095067b8a9d77876d1fad6cc3b4a43"},
]
+[[package]]
+name = "redis"
+version = "5.0.8"
+description = "Python client for Redis database and key-value store"
+optional = false
+python-versions = ">=3.7"
+files = [
+ {file = "redis-5.0.8-py3-none-any.whl", hash = "sha256:56134ee08ea909106090934adc36f65c9bcbbaecea5b21ba704ba6fb561f8eb4"},
+ {file = "redis-5.0.8.tar.gz", hash = "sha256:0c5b10d387568dfe0698c6fad6615750c24170e548ca2deac10c649d463e9870"},
+]
+
+[package.dependencies]
+async-timeout = {version = ">=4.0.3", markers = "python_full_version < \"3.11.3\""}
+
+[package.extras]
+hiredis = ["hiredis (>1.0.0)"]
+ocsp = ["cryptography (>=36.0.1)", "pyopenssl (==20.0.1)", "requests (>=2.26.0)"]
+
[[package]]
name = "requests"
version = "2.32.3"
@@ -1529,6 +2875,20 @@ urllib3 = ">=1.21.1,<3"
socks = ["PySocks (>=1.5.6,!=1.5.7)"]
use-chardet-on-py3 = ["chardet (>=3.0.2,<6)"]
+[[package]]
+name = "rsa"
+version = "4.9"
+description = "Pure-Python RSA implementation"
+optional = false
+python-versions = ">=3.6,<4"
+files = [
+ {file = "rsa-4.9-py3-none-any.whl", hash = "sha256:90260d9058e514786967344d0ef75fa8727eed8a7d2e43ce9f4bcf1b536174f7"},
+ {file = "rsa-4.9.tar.gz", hash = "sha256:e38464a49c6c85d7f1351b0126661487a7e0a14a50f1675ec50eb34d4f20ef21"},
+]
+
+[package.dependencies]
+pyasn1 = ">=0.1.3"
+
[[package]]
name = "ruff"
version = "0.2.2"
@@ -1555,6 +2915,25 @@ files = [
{file = "ruff-0.2.2.tar.gz", hash = "sha256:e62ed7f36b3068a30ba39193a14274cd706bc486fad521276458022f7bccb31d"},
]
+[[package]]
+name = "selenium"
+version = "4.25.0"
+description = "Official Python bindings for Selenium WebDriver"
+optional = false
+python-versions = ">=3.8"
+files = [
+ {file = "selenium-4.25.0-py3-none-any.whl", hash = "sha256:3798d2d12b4a570bc5790163ba57fef10b2afee958bf1d80f2a3cf07c4141f33"},
+ {file = "selenium-4.25.0.tar.gz", hash = "sha256:95d08d3b82fb353f3c474895154516604c7f0e6a9a565ae6498ef36c9bac6921"},
+]
+
+[package.dependencies]
+certifi = ">=2021.10.8"
+trio = ">=0.17,<1.0"
+trio-websocket = ">=0.9,<1.0"
+typing_extensions = ">=4.9,<5.0"
+urllib3 = {version = ">=1.26,<3", extras = ["socks"]}
+websocket-client = ">=1.8,<2.0"
+
[[package]]
name = "sentry-sdk"
version = "1.45.1"
@@ -1625,6 +3004,50 @@ files = [
{file = "sniffio-1.3.1.tar.gz", hash = "sha256:f4324edc670a0f49750a81b895f35c3adb843cca46f0530f79fc1babb23789dc"},
]
+[[package]]
+name = "sortedcontainers"
+version = "2.4.0"
+description = "Sorted Containers -- Sorted List, Sorted Dict, Sorted Set"
+optional = false
+python-versions = "*"
+files = [
+ {file = "sortedcontainers-2.4.0-py2.py3-none-any.whl", hash = "sha256:a163dcaede0f1c021485e957a39245190e74249897e2ae4b2aa38595db237ee0"},
+ {file = "sortedcontainers-2.4.0.tar.gz", hash = "sha256:25caa5a06cc30b6b83d11423433f65d1f9d76c4c6a0c90e3379eaa43b9bfdb88"},
+]
+
+[[package]]
+name = "soupsieve"
+version = "2.6"
+description = "A modern CSS selector implementation for Beautiful Soup."
+optional = false
+python-versions = ">=3.8"
+files = [
+ {file = "soupsieve-2.6-py3-none-any.whl", hash = "sha256:e72c4ff06e4fb6e4b5a9f0f55fe6e81514581fca1515028625d0f299c602ccc9"},
+ {file = "soupsieve-2.6.tar.gz", hash = "sha256:e2e68417777af359ec65daac1057404a3c8a5455bb8abc36f1a9866ab1a51abb"},
+]
+
+[[package]]
+name = "sqladmin"
+version = "0.19.0"
+description = "SQLAlchemy admin for FastAPI and Starlette"
+optional = false
+python-versions = ">=3.8"
+files = [
+ {file = "sqladmin-0.19.0-py3-none-any.whl", hash = "sha256:7549df159c1a9e65d2a9bf66ddca321e4c2aafef824faa23b881fa0aa08b88bf"},
+ {file = "sqladmin-0.19.0.tar.gz", hash = "sha256:edd7d1a16e61fc4edb428dc92a99e9f5b41252127a9d93637ce1d9b3eaa20877"},
+]
+
+[package.dependencies]
+itsdangerous = {version = "*", optional = true, markers = "extra == \"full\""}
+jinja2 = "*"
+python-multipart = "*"
+sqlalchemy = ">=1.4"
+starlette = "*"
+wtforms = ">=3.1,<3.2"
+
+[package.extras]
+full = ["itsdangerous"]
+
[[package]]
name = "sqlalchemy"
version = "2.0.31"
@@ -1770,6 +3193,68 @@ files = [
{file = "tomli-2.0.1.tar.gz", hash = "sha256:de526c12914f0c550d15924c62d72abc48d6fe7364aa87328337a31007fe8a4f"},
]
+[[package]]
+name = "tortoise-orm"
+version = "0.21.6"
+description = "Easy async ORM for python, built with relations in mind"
+optional = false
+python-versions = "<4.0,>=3.8"
+files = [
+ {file = "tortoise_orm-0.21.6-py3-none-any.whl", hash = "sha256:98fcf07dce3396075eac36b0d2b14d2267ff875d32455e03ee15e38de2f138df"},
+ {file = "tortoise_orm-0.21.6.tar.gz", hash = "sha256:0fbc718001647bf282c01eaaa360f94f1432c9281701244180703d48d58a88ec"},
+]
+
+[package.dependencies]
+aiosqlite = ">=0.16.0,<0.18.0"
+iso8601 = ">=1.0.2,<2.0.0"
+pydantic = ">=2.0,<2.7.0 || >2.7.0,<3.0"
+pypika-tortoise = ">=0.1.6,<0.2.0"
+pytz = "*"
+
+[package.extras]
+accel = ["ciso8601", "orjson", "uvloop"]
+aiomysql = ["aiomysql"]
+asyncmy = ["asyncmy (>=0.2.8,<0.3.0)"]
+asyncodbc = ["asyncodbc (>=0.1.1,<0.2.0)"]
+asyncpg = ["asyncpg"]
+psycopg = ["psycopg[binary,pool] (>=3.0.12,<4.0.0)"]
+
+[[package]]
+name = "trio"
+version = "0.27.0"
+description = "A friendly Python library for async concurrency and I/O"
+optional = false
+python-versions = ">=3.8"
+files = [
+ {file = "trio-0.27.0-py3-none-any.whl", hash = "sha256:68eabbcf8f457d925df62da780eff15ff5dc68fd6b367e2dde59f7aaf2a0b884"},
+ {file = "trio-0.27.0.tar.gz", hash = "sha256:1dcc95ab1726b2da054afea8fd761af74bad79bd52381b84eae408e983c76831"},
+]
+
+[package.dependencies]
+attrs = ">=23.2.0"
+cffi = {version = ">=1.14", markers = "os_name == \"nt\" and implementation_name != \"pypy\""}
+exceptiongroup = {version = "*", markers = "python_version < \"3.11\""}
+idna = "*"
+outcome = "*"
+sniffio = ">=1.3.0"
+sortedcontainers = "*"
+
+[[package]]
+name = "trio-websocket"
+version = "0.11.1"
+description = "WebSocket library for Trio"
+optional = false
+python-versions = ">=3.7"
+files = [
+ {file = "trio-websocket-0.11.1.tar.gz", hash = "sha256:18c11793647703c158b1f6e62de638acada927344d534e3c7628eedcb746839f"},
+ {file = "trio_websocket-0.11.1-py3-none-any.whl", hash = "sha256:520d046b0d030cf970b8b2b2e00c4c2245b3807853ecd44214acd33d74581638"},
+]
+
+[package.dependencies]
+exceptiongroup = {version = "*", markers = "python_version < \"3.11\""}
+trio = ">=0.11"
+wsproto = ">=0.14"
+
[[package]]
name = "types-passlib"
version = "1.7.7.20240327"
@@ -1803,6 +3288,21 @@ files = [
{file = "tzdata-2024.1.tar.gz", hash = "sha256:2674120f8d891909751c38abcdfd386ac0a5a1127954fbc332af6b5ceae07efd"},
]
+[[package]]
+name = "unshortenit"
+version = "0.4.0"
+description = "Unshortens ad-based shorteners and any 301 redirected urls."
+optional = false
+python-versions = "*"
+files = [
+ {file = "unshortenit-0.4.0.tar.gz", hash = "sha256:ffe218acb22cd743b152e90ca3ac547a99d642333824048b6567a3e1688cf703"},
+]
+
+[package.dependencies]
+click = ">=6.7"
+lxml = ">=4.1.1"
+requests = ">=2.18.4"
+
[[package]]
name = "urllib3"
version = "2.2.2"
@@ -1814,6 +3314,9 @@ files = [
{file = "urllib3-2.2.2.tar.gz", hash = "sha256:dd505485549a7a552833da5e6063639d0d177c04f23bc3864e41e5dc5f612168"},
]
+[package.dependencies]
+pysocks = {version = ">=1.5.6,<1.5.7 || >1.5.7,<2.0", optional = true, markers = "extra == \"socks\""}
+
[package.extras]
brotli = ["brotli (>=1.0.9)", "brotlicffi (>=0.8.0)"]
h2 = ["h2 (>=4,<5)"]
@@ -1997,6 +3500,38 @@ files = [
[package.dependencies]
anyio = ">=3.0.0"
+[[package]]
+name = "webdriver-manager"
+version = "4.0.2"
+description = "Library provides the way to automatically manage drivers for different browsers"
+optional = false
+python-versions = ">=3.7"
+files = [
+ {file = "webdriver_manager-4.0.2-py2.py3-none-any.whl", hash = "sha256:75908d92ecc45ff2b9953614459c633db8f9aa1ff30181cefe8696e312908129"},
+ {file = "webdriver_manager-4.0.2.tar.gz", hash = "sha256:efedf428f92fd6d5c924a0d054e6d1322dd77aab790e834ee767af392b35590f"},
+]
+
+[package.dependencies]
+packaging = "*"
+python-dotenv = "*"
+requests = "*"
+
+[[package]]
+name = "websocket-client"
+version = "1.8.0"
+description = "WebSocket client for Python with low level API options"
+optional = false
+python-versions = ">=3.8"
+files = [
+ {file = "websocket_client-1.8.0-py3-none-any.whl", hash = "sha256:17b44cc997f5c498e809b22cdf2d9c7a9e71c02c8cc2b6c56e7c2d1239bfa526"},
+ {file = "websocket_client-1.8.0.tar.gz", hash = "sha256:3239df9f44da632f96012472805d40a23281a991027ce11d2f45a6f24ac4c3da"},
+]
+
+[package.extras]
+docs = ["Sphinx (>=6.0)", "myst-parser (>=2.0.0)", "sphinx-rtd-theme (>=1.1.0)"]
+optional = ["python-socks", "wsaccel"]
+test = ["websockets"]
+
[[package]]
name = "websockets"
version = "12.0"
@@ -2078,7 +3613,134 @@ files = [
{file = "websockets-12.0.tar.gz", hash = "sha256:81df9cbcbb6c260de1e007e58c011bfebe2dafc8435107b0537f393dd38c8b1b"},
]
+[[package]]
+name = "wsproto"
+version = "1.2.0"
+description = "WebSockets state-machine based protocol implementation"
+optional = false
+python-versions = ">=3.7.0"
+files = [
+ {file = "wsproto-1.2.0-py3-none-any.whl", hash = "sha256:b9acddd652b585d75b20477888c56642fdade28bdfd3579aa24a4d2c037dd736"},
+ {file = "wsproto-1.2.0.tar.gz", hash = "sha256:ad565f26ecb92588a3e43bc3d96164de84cd9902482b130d0ddbaa9664a85065"},
+]
+
+[package.dependencies]
+h11 = ">=0.9.0,<1"
+
+[[package]]
+name = "wtforms"
+version = "3.1.2"
+description = "Form validation and rendering for Python web development."
+optional = false
+python-versions = ">=3.8"
+files = [
+ {file = "wtforms-3.1.2-py3-none-any.whl", hash = "sha256:bf831c042829c8cdbad74c27575098d541d039b1faa74c771545ecac916f2c07"},
+ {file = "wtforms-3.1.2.tar.gz", hash = "sha256:f8d76180d7239c94c6322f7990ae1216dae3659b7aa1cee94b6318bdffb474b9"},
+]
+
+[package.dependencies]
+markupsafe = "*"
+
+[package.extras]
+email = ["email-validator"]
+
+[[package]]
+name = "yarl"
+version = "1.15.5"
+description = "Yet another URL library"
+optional = false
+python-versions = ">=3.9"
+files = [
+ {file = "yarl-1.15.5-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:b6c57972a406ea0f61e3f28f2b3a780fb71fbe1d82d267afe5a2f889a83ee7e7"},
+ {file = "yarl-1.15.5-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:5c3ac5bdcc1375c8ee52784adf94edbce37c471dd2100a117cfef56fe8dbc2b4"},
+ {file = "yarl-1.15.5-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:68d21d0563d82aaf46163eac529adac301b20be3181b8a2811f7bd5615466055"},
+ {file = "yarl-1.15.5-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a7d317fb80bc17ed4b34a9aad8b80cef34bea0993654f3e8566daf323def7ef9"},
+ {file = "yarl-1.15.5-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ed9c72d5361cfd5af5ccadffa8f8077f4929640e1f938aa0f4b92c5a24996ac5"},
+ {file = "yarl-1.15.5-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:bb707859218e8335447b210f41a755e7b1367c33e87add884128bba144694a7f"},
+ {file = "yarl-1.15.5-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6563394492c96cb57f4dff0c69c63d2b28b5469c59c66f35a1e6451583cd0ab4"},
+ {file = "yarl-1.15.5-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9c2d1109c8d92059314cc34dd8f0a31f74b720dc140744923ed7ca228bf9b491"},
+ {file = "yarl-1.15.5-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:8fc727f0fb388debc771eaa7091c092bd2e8b6b4741b73354b8efadcf96d6031"},
+ {file = "yarl-1.15.5-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:94189746c5ad62e1014a16298130e696fe593d031d442ef135fb7787b7a1f820"},
+ {file = "yarl-1.15.5-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:b06d8b05d0fafef204d635a4711283ddbf19c7c0facdc61b4b775f6e47e2d4be"},
+ {file = "yarl-1.15.5-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:de6917946dc6bc237d4b354e38aa13a232e0c7948fdbdb160edee3862e9d735f"},
+ {file = "yarl-1.15.5-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:34816f1d833433a16c4832562a050b0a60eac53dcb71b2032e6ebff82d74b6a7"},
+ {file = "yarl-1.15.5-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:19e2a4b2935f95fad0949f420514c5d862f5f18058fbbfd8854f496a97d9fd87"},
+ {file = "yarl-1.15.5-cp310-cp310-win32.whl", hash = "sha256:30ca64521f1a96b72886dd9e8652f16eab11891b4572dcfcfc1ad6d6ccb27abd"},
+ {file = "yarl-1.15.5-cp310-cp310-win_amd64.whl", hash = "sha256:86648c53b10c53db8b967a75fb41e0c89dbec7398f6525e34af2b6c456bb0ac0"},
+ {file = "yarl-1.15.5-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:e652aa9f8dfa808bc5b2da4d1f4e286cf1d640570fdfa72ffc0c1d16ba114651"},
+ {file = "yarl-1.15.5-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:21050b6cd569980fe20ceeab4baeb900d3f7247270475e42bafe117416a5496c"},
+ {file = "yarl-1.15.5-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:18940191ec9a83bbfe63eea61c3e9d12474bb910d5613bce8fa46e84a80b75b2"},
+ {file = "yarl-1.15.5-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a082dc948045606f62dca0228ab24f13737180b253378d6443f5b2b9ef8beefe"},
+ {file = "yarl-1.15.5-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:0a843e692f9d5402b3455653f4607dc521de2385f01c5cad7ba4a87c46e2ea8d"},
+ {file = "yarl-1.15.5-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5093a453176a4fad4f9c3006f507cf300546190bb3e27944275a37cfd6323a65"},
+ {file = "yarl-1.15.5-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2597a589859b94d0a5e2f5d30fee95081867926e57cb751f8b44a7dd92da4e79"},
+ {file = "yarl-1.15.5-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1f5a1ca6eaabfe62718b87eac06d9a47b30cf92ffa065fee9196d3ecd24a3cf1"},
+ {file = "yarl-1.15.5-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:4ac83b307cc4b8907345b52994055c6c3c2601ceb6fcb94c5ed6a93c6b4e8257"},
+ {file = "yarl-1.15.5-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:325e2beb2cd8654b276e7686a3cd203628dd3fe32d5c616e632bc35a2901fb16"},
+ {file = "yarl-1.15.5-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:75d04ba8ed335042328086e643e01165e0c24598216f72da709b375930ae3bdb"},
+ {file = "yarl-1.15.5-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:7abd7d15aedb3961a967cc65f8144dbbca42e3626a21c5f4f29919cf43eeafb9"},
+ {file = "yarl-1.15.5-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:294c742a273f44511f14b03a9e06b66094dcdf4bbb75a5e23fead548fd5310ae"},
+ {file = "yarl-1.15.5-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:63d46606b20f80a6476f1044bab78e1a69c2e0747f174583e2f12fc70bad2170"},
+ {file = "yarl-1.15.5-cp311-cp311-win32.whl", hash = "sha256:b1217102a455e3ac9ac293081093f21f0183e978c7692171ff669fee5296fa28"},
+ {file = "yarl-1.15.5-cp311-cp311-win_amd64.whl", hash = "sha256:5848500b6a01497560969e8c3a7eb1b2570853c74a0ca6f67ebaf6064106c49b"},
+ {file = "yarl-1.15.5-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:d3309ee667f2d9c7ac9ecf44620d6b274bfdd8065b8c5019ff6795dd887b8fed"},
+ {file = "yarl-1.15.5-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:96ce879799fee124d241ea3b84448378f638e290c49493d00b706f3fd57ec22b"},
+ {file = "yarl-1.15.5-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:c884dfa56b050f718ea3cbbfd972e29a6f07f63a7449b10d9a20d64f7eec92e2"},
+ {file = "yarl-1.15.5-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0327081978fe186c3390dd4f73f95f825d0bb9c74967e22c2a1a87735974d8f5"},
+ {file = "yarl-1.15.5-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:524b3bb7dff320e305bc979c65eddc0342548c56ea9241502f907853fe53c408"},
+ {file = "yarl-1.15.5-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:fd56de8b645421ff09c993fdb0ee9c5a3b50d290a8f55793b500d99b34d0c1ce"},
+ {file = "yarl-1.15.5-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c166ad987265bb343be58cdf4fbc4478cc1d81f2246d2be9a15f94393b269faa"},
+ {file = "yarl-1.15.5-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d56980374a10c74255fcea6ebcfb0aeca7166d212ee9fd7e823ddef35fb62ad0"},
+ {file = "yarl-1.15.5-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:cbf36099a9b407e1456dbf55844743a98603fcba32d2a46fb3a698d926facf1b"},
+ {file = "yarl-1.15.5-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:d7fa4b033e2f267e37aabcc36949fa89f9f1716a723395912147f9cf3fb437c7"},
+ {file = "yarl-1.15.5-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:bb129f77ddaea2d8e6e00417b8d907448de3407af4eddacca0a515574ad71493"},
+ {file = "yarl-1.15.5-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:68e837b3edfcd037f9706157e7cb8efda832de6248c7d9e893e2638356dfae5d"},
+ {file = "yarl-1.15.5-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:5b8af4165e097ff84d9bbb97bb4f4d7f71b9c1c9565a2d0e27d93e5f92dae220"},
+ {file = "yarl-1.15.5-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:70d074d5a96e0954fe6db81ff356f4361397da1cda3f7c127fc0902f671a087e"},
+ {file = "yarl-1.15.5-cp312-cp312-win32.whl", hash = "sha256:362da97ad4360e4ef1dd24ccdd3bceb18332da7f40026a42f49b7edd686e31c3"},
+ {file = "yarl-1.15.5-cp312-cp312-win_amd64.whl", hash = "sha256:9aa054d97033beac9cb9b19b7c0b8784b85b12cd17879087ca6bffba57884e02"},
+ {file = "yarl-1.15.5-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:5fadcf532fd9f6cbad71485ef8c2462dd9a91d3efc72ca01eb0970792c92552a"},
+ {file = "yarl-1.15.5-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:8b7dd6983c81523f9de0ae6334c3b7a3cb33283936e0525f80c4f713f54a9bb6"},
+ {file = "yarl-1.15.5-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:fcfd663dc88465ebe41c7c938bdc91c4b01cda96a0d64bf38fd66c1877323771"},
+ {file = "yarl-1.15.5-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cd529e637cd23204bd82072f6637cff7af2516ad2c132e8f3342cbc84871f7d1"},
+ {file = "yarl-1.15.5-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3b30f13fac56598474071a4f1ecd66c78fdaf2f8619042d7ca135f72dbb348cf"},
+ {file = "yarl-1.15.5-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:44088ec0be82fba118ed29b6b429f80bf295297727adae4c257ac297e01e8bcd"},
+ {file = "yarl-1.15.5-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:607683991bab8607e5158cd290dd8fdaa613442aeab802fe1c237d3a3eee7358"},
+ {file = "yarl-1.15.5-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:da48cdff56b01ea4282a6d04b83b07a2088351a4a3ff7aacc1e7e9b6b04b90b9"},
+ {file = "yarl-1.15.5-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:9162ea117ce8bad8ebc95b7376b4135988acd888d2cf4702f8281e3c11f8b81f"},
+ {file = "yarl-1.15.5-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:e8aa19c39cb20bfb16f0266df175a6004943122cf20707fbf0cacc21f6468a25"},
+ {file = "yarl-1.15.5-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:5d6be369488d503c8edc14e2f63d71ab2a607041ad216a8ad444fa18e8dea792"},
+ {file = "yarl-1.15.5-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:6e2c674cfe4c03ad7a4d536b1f808221f0d11a360486b4b032d2557c0bd633ad"},
+ {file = "yarl-1.15.5-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:041bafaa82b77fd4ec2826d42a55461ec86d999adf7ed9644eef7e8a9febb366"},
+ {file = "yarl-1.15.5-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:2eeb9ba53c055740cd282ae9d34eb7970d65e73a46f15adec4b0c1b0f2e55cc2"},
+ {file = "yarl-1.15.5-cp313-cp313-win32.whl", hash = "sha256:73143dd279e641543da52c55652ad7b4c7c5f79e797f124f58f04cc060f14271"},
+ {file = "yarl-1.15.5-cp313-cp313-win_amd64.whl", hash = "sha256:94ab1185900f43760d5487c8e49f5f1a66f864e36092f282f1813597479b9dfa"},
+ {file = "yarl-1.15.5-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:6b3d2767bd64c62909ea33525b954ba05c8f9726bfdf2141d175da4e344f19ae"},
+ {file = "yarl-1.15.5-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:44359c52af9c383e5107f3b6301446fc8269599721fa42fafb2afb5f31a42dcb"},
+ {file = "yarl-1.15.5-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:6493da9ba5c551978c679ab04856c2cf8f79c316e8ec8c503460a135705edc3b"},
+ {file = "yarl-1.15.5-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1a6b6e95bc621c11cf9ff21012173337e789f2461ebc3b4e5bf65c74ef69adb8"},
+ {file = "yarl-1.15.5-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:7983290ede3aaa2c9620879530849532529b4dcbf5b12a0b6a91163a773eadb9"},
+ {file = "yarl-1.15.5-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:07a4b53abe85813c538b9cdbb02909ebe3734e3af466a587df516e960d500cc8"},
+ {file = "yarl-1.15.5-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5882faa2a6e684f65ee44f18c701768749a950cbd5e72db452fc07805f6bdec0"},
+ {file = "yarl-1.15.5-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e27861251d9c094f641d39a8a78dd2371fb9a252ea2f689d1ad353a31d46a0bc"},
+ {file = "yarl-1.15.5-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:8669a110f655c9eb22f16fb68a7d4942020aeaa09f1def584a80183e3e89953c"},
+ {file = "yarl-1.15.5-cp39-cp39-musllinux_1_2_armv7l.whl", hash = "sha256:10bfe0bef4cf5ea0383886beda004071faadedf2647048b9f876664284c5b60d"},
+ {file = "yarl-1.15.5-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:f7de0d4b6b4d8a77e422eb54d765255c0ec6883ee03b8fd537101633948619d7"},
+ {file = "yarl-1.15.5-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:00bb3a559d7bd006a5302ecd7e409916939106a8cdbe31f4eb5e5b9ffcca57ea"},
+ {file = "yarl-1.15.5-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:06ec070a2d71415f90dbe9d70af3158e7da97a128519dba2d1581156ee27fb92"},
+ {file = "yarl-1.15.5-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:b997a806846c00d1f41d6a251803732837771b2091bead7566f68820e317bfe7"},
+ {file = "yarl-1.15.5-cp39-cp39-win32.whl", hash = "sha256:7825506fbee4055265528ec3532a8197ff26fc53d4978917a4c8ddbb4c1667d7"},
+ {file = "yarl-1.15.5-cp39-cp39-win_amd64.whl", hash = "sha256:71730658be0b5de7c570a9795d7404c577b2313c1db370407092c66f70e04ccb"},
+ {file = "yarl-1.15.5-py3-none-any.whl", hash = "sha256:625f31d6650829fba4030b4e7bdb2d69e41510dddfa29a1da27076c199521757"},
+ {file = "yarl-1.15.5.tar.gz", hash = "sha256:8249147ee81c1cf4d1dc6f26ba28a1b9d92751529f83c308ad02164bb93abd0d"},
+]
+
+[package.dependencies]
+idna = ">=2.0"
+multidict = ">=4.0"
+propcache = ">=0.2.0"
+
[metadata]
lock-version = "2.0"
python-versions = "^3.10"
-content-hash = "7ec220bee66b5bc207f9a8b2f4ca9100da0213bb9d0a407b51cac3dc8201e97c"
+content-hash = "d337d313741d3aa3b36d4dd7d2761cdc6165ff5b0ce547bf479e9fc76aac03d4"
diff --git a/backend/pyproject.toml b/backend/pyproject.toml
index 671a864645..d8724b02eb 100644
--- a/backend/pyproject.toml
+++ b/backend/pyproject.toml
@@ -8,12 +8,14 @@ authors = ["Admin "]
python = "^3.10"
uvicorn = {extras = ["standard"], version = "^0.24.0.post1"}
fastapi = "^0.109.1"
-python-multipart = "^0.0.7"
+python-multipart = "0.0.9"
email-validator = "^2.1.0.post1"
passlib = {extras = ["bcrypt"], version = "^1.7.4"}
tenacity = "^8.2.3"
-pydantic = ">2.0"
+pydantic = "^2.9.2"
emails = "^0.6"
+psycopg2 = "^2.9.6"
+h3 = "^3.7.7"
gunicorn = "^22.0.0"
jinja2 = "^3.1.4"
@@ -22,10 +24,24 @@ httpx = "^0.25.1"
psycopg = {extras = ["binary"], version = "^3.1.13"}
sqlmodel = "^0.0.21"
# Pin bcrypt until passlib supports the latest
-bcrypt = "4.0.1"
+bcrypt = "4.1.2"
pydantic-settings = "^2.2.1"
sentry-sdk = {extras = ["fastapi"], version = "^1.40.6"}
pyjwt = "^2.8.0"
+fastapi-cache = {extras = ["redis"], version = "^0.1.0"}
+pyotp = "^2.9.0"
+redis = "^5.0.8"
+fastapi-users = {extras = ["oauth"], version = "^13.0.0"}
+asyncpg = "^0.29.0"
+otplessauthsdk = "^0.3.3"
+fastapi-admin = "^1.0.4"
+sqladmin = {extras = ["full"], version = "^0.19.0"}
+unshortenit = "^0.4.0"
+aiohttp = "^3.10.10"
+bs4 = "^0.0.2"
+selenium = "^4.25.0"
+webdriver-manager = "^4.0.2"
+autoscraper = "^1.1.14"
[tool.poetry.group.dev.dependencies]
pytest = "^7.4.3"
diff --git a/copier.yml b/copier.yml
index 5db3891c8d..f53c66bb2f 100644
--- a/copier.yml
+++ b/copier.yml
@@ -74,7 +74,6 @@ _exclude:
- poetry.lock
- .cache
- .venv
- # Frontend
# Logs
- logs
- "*.log"
diff --git a/deployment.md b/deployment.md
index 6bcbe40259..a0b8b0db17 100644
--- a/deployment.md
+++ b/deployment.md
@@ -284,8 +284,6 @@ Traefik UI: `https://traefik.fastapi-project.example.com`
### Production
-Frontend: `https://fastapi-project.example.com`
-
Backend API docs: `https://fastapi-project.example.com/docs`
Backend API base URL: `https://fastapi-project.example.com/api/`
@@ -294,8 +292,6 @@ Adminer: `https://adminer.fastapi-project.example.com`
### Staging
-Frontend: `https://staging.fastapi-project.example.com`
-
Backend API docs: `https://staging.fastapi-project.example.com/docs`
Backend API base URL: `https://staging.fastapi-project.example.com/api/`
diff --git a/development.md b/development.md
index 857a4e0a38..b4753b3902 100644
--- a/development.md
+++ b/development.md
@@ -146,13 +146,9 @@ The production or staging URLs would use these same paths, but with your own dom
Development URLs, for local development.
-Frontend: http://localhost
-
-Backend: http://localhost/api/
-
Automatic Interactive Docs (Swagger UI): http://localhost/docs
-Automatic Alternative Docs (ReDoc): http://localhost/redoc
+Automatic Alternative Docs (ReDoc): http://localhost/redoc
Adminer: http://localhost:8080
@@ -162,8 +158,6 @@ Traefik UI: http://localhost:8090
Development URLs, for local development.
-Frontend: http://localhost.tiangolo.com
-
Backend: http://localhost.tiangolo.com/api/
Automatic Interactive Docs (Swagger UI): http://localhost.tiangolo.com/docs
diff --git a/docker-compose.override.yml b/docker-compose.override.yml
index 418b535ab6..15f669bb68 100644
--- a/docker-compose.override.yml
+++ b/docker-compose.override.yml
@@ -74,14 +74,6 @@ services:
- "1080:1080"
- "1025:1025"
- frontend:
- restart: "no"
- build:
- context: ./frontend
- args:
- - VITE_API_URL=http://${DOMAIN?Variable not set}
- - NODE_ENV=development
-
networks:
traefik-public:
# For local dev, don't expect an external Traefik network
diff --git a/docker-compose.yml b/docker-compose.yml
index d614942cbd..42a4754b23 100644
--- a/docker-compose.yml
+++ b/docker-compose.yml
@@ -56,7 +56,7 @@ services:
- SMTP_USER=${SMTP_USER}
- SMTP_PASSWORD=${SMTP_PASSWORD}
- EMAILS_FROM_EMAIL=${EMAILS_FROM_EMAIL}
- - POSTGRES_SERVER=db
+ - POSTGRES_SERVER=${POSTGRES_SERVER}
- POSTGRES_PORT=${POSTGRES_PORT}
- POSTGRES_DB=${POSTGRES_DB}
- POSTGRES_USER=${POSTGRES_USER?Variable not set}
@@ -72,59 +72,21 @@ services:
- traefik.enable=true
- traefik.docker.network=traefik-public
- traefik.constraint-label=traefik-public
-
- traefik.http.services.${STACK_NAME?Variable not set}-backend.loadbalancer.server.port=80
-
- traefik.http.routers.${STACK_NAME?Variable not set}-backend-http.rule=(Host(`${DOMAIN?Variable not set}`) || Host(`www.${DOMAIN?Variable not set}`)) && (PathPrefix(`/api`) || PathPrefix(`/docs`) || PathPrefix(`/redoc`))
- traefik.http.routers.${STACK_NAME?Variable not set}-backend-http.entrypoints=http
-
- traefik.http.routers.${STACK_NAME?Variable not set}-backend-https.rule=(Host(`${DOMAIN?Variable not set}`) || Host(`www.${DOMAIN?Variable not set}`)) && (PathPrefix(`/api`) || PathPrefix(`/docs`) || PathPrefix(`/redoc`))
- traefik.http.routers.${STACK_NAME?Variable not set}-backend-https.entrypoints=https
- traefik.http.routers.${STACK_NAME?Variable not set}-backend-https.tls=true
- traefik.http.routers.${STACK_NAME?Variable not set}-backend-https.tls.certresolver=le
-
- # Define Traefik Middleware to handle domain with and without "www" to redirect to only one
- traefik.http.middlewares.${STACK_NAME?Variable not set}-www-redirect.redirectregex.regex=^http(s)?://www.(${DOMAIN?Variable not set})/(.*)
- # Redirect a domain with www to non-www
- traefik.http.middlewares.${STACK_NAME?Variable not set}-www-redirect.redirectregex.replacement=http$${1}://${DOMAIN?Variable not set}/$${3}
-
- # Enable www redirection for HTTP and HTTPS
- traefik.http.routers.${STACK_NAME?Variable not set}-backend-http.middlewares=https-redirect,${STACK_NAME?Variable not set}-www-redirect
- traefik.http.routers.${STACK_NAME?Variable not set}-backend-https.middlewares=${STACK_NAME?Variable not set}-www-redirect
- frontend:
- image: '${DOCKER_IMAGE_FRONTEND?Variable not set}:${TAG-latest}'
- restart: always
- networks:
- - traefik-public
- - default
- build:
- context: ./frontend
- args:
- - VITE_API_URL=https://${DOMAIN?Variable not set}
- - NODE_ENV=production
- labels:
- - traefik.enable=true
- - traefik.docker.network=traefik-public
- - traefik.constraint-label=traefik-public
-
- - traefik.http.services.${STACK_NAME?Variable not set}-frontend.loadbalancer.server.port=80
-
- - traefik.http.routers.${STACK_NAME?Variable not set}-frontend-http.rule=Host(`${DOMAIN?Variable not set}`) || Host(`www.${DOMAIN?Variable not set}`)
- - traefik.http.routers.${STACK_NAME?Variable not set}-frontend-http.entrypoints=http
-
- - traefik.http.routers.${STACK_NAME?Variable not set}-frontend-https.rule=Host(`${DOMAIN?Variable not set}`) || Host(`www.${DOMAIN?Variable not set}`)
- - traefik.http.routers.${STACK_NAME?Variable not set}-frontend-https.entrypoints=https
- - traefik.http.routers.${STACK_NAME?Variable not set}-frontend-https.tls=true
- - traefik.http.routers.${STACK_NAME?Variable not set}-frontend-https.tls.certresolver=le
-
- # Enable www redirection for HTTP and HTTPS
- - traefik.http.routers.${STACK_NAME?Variable not set}-frontend-https.middlewares=${STACK_NAME?Variable not set}-www-redirect
- - traefik.http.routers.${STACK_NAME?Variable not set}-frontend-http.middlewares=https-redirect,${STACK_NAME?Variable not set}-www-redirect
volumes:
app-db-data:
networks:
traefik-public:
- # Allow setting it to false for testing
- external: true
+ external: true
\ No newline at end of file
diff --git a/frontend/.dockerignore b/frontend/.dockerignore
deleted file mode 100644
index f06235c460..0000000000
--- a/frontend/.dockerignore
+++ /dev/null
@@ -1,2 +0,0 @@
-node_modules
-dist
diff --git a/frontend/.env b/frontend/.env
deleted file mode 100644
index f829bd1979..0000000000
--- a/frontend/.env
+++ /dev/null
@@ -1 +0,0 @@
-VITE_API_URL=http://localhost
diff --git a/frontend/.gitignore b/frontend/.gitignore
deleted file mode 100644
index dfc4015cce..0000000000
--- a/frontend/.gitignore
+++ /dev/null
@@ -1,29 +0,0 @@
-# Logs
-logs
-*.log
-npm-debug.log*
-yarn-debug.log*
-yarn-error.log*
-pnpm-debug.log*
-lerna-debug.log*
-
-node_modules
-dist
-dist-ssr
-*.local
-openapi.json
-
-# Editor directories and files
-.vscode/*
-!.vscode/extensions.json
-.idea
-.DS_Store
-*.suo
-*.ntvs*
-*.njsproj
-*.sln
-*.sw?
-/test-results/
-/playwright-report/
-/blob-report/
-/playwright/.cache/
diff --git a/frontend/.nvmrc b/frontend/.nvmrc
deleted file mode 100644
index 209e3ef4b6..0000000000
--- a/frontend/.nvmrc
+++ /dev/null
@@ -1 +0,0 @@
-20
diff --git a/frontend/Dockerfile b/frontend/Dockerfile
deleted file mode 100644
index 8728c7b029..0000000000
--- a/frontend/Dockerfile
+++ /dev/null
@@ -1,23 +0,0 @@
-# Stage 0, "build-stage", based on Node.js, to build and compile the frontend
-FROM node:20 AS build-stage
-
-WORKDIR /app
-
-COPY package*.json /app/
-
-RUN npm install
-
-COPY ./ /app/
-
-ARG VITE_API_URL=${VITE_API_URL}
-
-RUN npm run build
-
-
-# Stage 1, based on Nginx, to have only the compiled app, ready for production with Nginx
-FROM nginx:1
-
-COPY --from=build-stage /app/dist/ /usr/share/nginx/html
-
-COPY ./nginx.conf /etc/nginx/conf.d/default.conf
-COPY ./nginx-backend-not-found.conf /etc/nginx/extra-conf.d/backend-not-found.conf
diff --git a/frontend/README.md b/frontend/README.md
deleted file mode 100644
index 9a01970fda..0000000000
--- a/frontend/README.md
+++ /dev/null
@@ -1,160 +0,0 @@
-# FastAPI Project - Frontend
-
-The frontend is built with [Vite](https://vitejs.dev/), [React](https://reactjs.org/), [TypeScript](https://www.typescriptlang.org/), [TanStack Query](https://tanstack.com/query), [TanStack Router](https://tanstack.com/router) and [Chakra UI](https://chakra-ui.com/).
-
-## Frontend development
-
-Before you begin, ensure that you have either the Node Version Manager (nvm) or Fast Node Manager (fnm) installed on your system.
-
-* To install fnm follow the [official fnm guide](https://github.com/Schniz/fnm#installation). If you prefer nvm, you can install it using the [official nvm guide](https://github.com/nvm-sh/nvm#installing-and-updating).
-
-* After installing either nvm or fnm, proceed to the `frontend` directory:
-
-```bash
-cd frontend
-```
-* If the Node.js version specified in the `.nvmrc` file isn't installed on your system, you can install it using the appropriate command:
-
-```bash
-# If using fnm
-fnm install
-
-# If using nvm
-nvm install
-```
-
-* Once the installation is complete, switch to the installed version:
-
-```bash
-# If using fnm
-fnm use
-
-# If using nvm
-nvm use
-```
-
-* Within the `frontend` directory, install the necessary NPM packages:
-
-```bash
-npm install
-```
-
-* And start the live server with the following `npm` script:
-
-```bash
-npm run dev
-```
-
-* Then open your browser at http://localhost:5173/.
-
-Notice that this live server is not running inside Docker, it's for local development, and that is the recommended workflow. Once you are happy with your frontend, you can build the frontend Docker image and start it, to test it in a production-like environment. But building the image at every change will not be as productive as running the local development server with live reload.
-
-Check the file `package.json` to see other available options.
-
-### Removing the frontend
-
-If you are developing an API-only app and want to remove the frontend, you can do it easily:
-
-* Remove the `./frontend` directory.
-
-* In the `docker-compose.yml` file, remove the whole service / section `frontend`.
-
-* In the `docker-compose.override.yml` file, remove the whole service / section `frontend`.
-
-Done, you have a frontend-less (api-only) app. 🤓
-
----
-
-If you want, you can also remove the `FRONTEND` environment variables from:
-
-* `.env`
-* `./scripts/*.sh`
-
-But it would be only to clean them up, leaving them won't really have any effect either way.
-
-## Generate Client
-
-### Automatically
-
-* Activate the backend virtual environment.
-* From the top level project directory, run the script:
-
-```bash
-./scripts/generate-frontend-client.sh
-```
-
-* Commit the changes.
-
-### Manually
-
-* Start the Docker Compose stack.
-
-* Download the OpenAPI JSON file from `http://localhost/api/v1/openapi.json` and copy it to a new file `openapi.json` at the root of the `frontend` directory.
-
-* To simplify the names in the generated frontend client code, modify the `openapi.json` file by running the following script:
-
-```bash
-node modify-openapi-operationids.js
-```
-
-* To generate the frontend client, run:
-
-```bash
-npm run generate-client
-```
-
-* Commit the changes.
-
-Notice that everytime the backend changes (changing the OpenAPI schema), you should follow these steps again to update the frontend client.
-
-## Using a Remote API
-
-If you want to use a remote API, you can set the environment variable `VITE_API_URL` to the URL of the remote API. For example, you can set it in the `frontend/.env` file:
-
-```env
-VITE_API_URL=https://my-remote-api.example.com
-```
-
-Then, when you run the frontend, it will use that URL as the base URL for the API.
-
-## Code Structure
-
-The frontend code is structured as follows:
-
-* `frontend/src` - The main frontend code.
-* `frontend/src/assets` - Static assets.
-* `frontend/src/client` - The generated OpenAPI client.
-* `frontend/src/components` - The different components of the frontend.
-* `frontend/src/hooks` - Custom hooks.
-* `frontend/src/routes` - The different routes of the frontend which include the pages.
-* `theme.tsx` - The Chakra UI custom theme.
-
-## End-to-End Testing with Playwright
-
-The frontend includes initial end-to-end tests using Playwright. To run the tests, you need to have the Docker Compose stack running. Start the stack with the following command:
-
-```bash
-docker compose up -d
-```
-
-Then, you can run the tests with the following command:
-
-```bash
-npx playwright test
-```
-
-You can also run your tests in UI mode to see the browser and interact with it running:
-
-```bash
-npx playwright test --ui
-```
-
-To stop and remove the Docker Compose stack and clean the data created in tests, use the following command:
-
-```bash
-docker compose down -v
-```
-
-To update the tests, navigate to the tests directory and modify the existing test files or add new ones as needed.
-
-For more information on writing and running Playwright tests, refer to the official [Playwright documentation](https://playwright.dev/docs/intro).
\ No newline at end of file
diff --git a/frontend/biome.json b/frontend/biome.json
deleted file mode 100644
index a06315dc2a..0000000000
--- a/frontend/biome.json
+++ /dev/null
@@ -1,36 +0,0 @@
-{
- "$schema": "https://biomejs.dev/schemas/1.6.1/schema.json",
- "organizeImports": {
- "enabled": true
- },
- "files": {
- "ignore": [
- "node_modules",
- "src/routeTree.gen.ts",
- "playwright.config.ts",
- "playwright-report"
- ]
- },
- "linter": {
- "enabled": true,
- "rules": {
- "recommended": true,
- "suspicious": {
- "noExplicitAny": "off",
- "noArrayIndexKey": "off"
- },
- "style": {
- "noNonNullAssertion": "off"
- }
- }
- },
- "formatter": {
- "indentStyle": "space"
- },
- "javascript": {
- "formatter": {
- "quoteStyle": "double",
- "semicolons": "asNeeded"
- }
- }
-}
diff --git a/frontend/index.html b/frontend/index.html
deleted file mode 100644
index 57621a268b..0000000000
--- a/frontend/index.html
+++ /dev/null
@@ -1,14 +0,0 @@
-
-
-
-
-
-
- Full Stack FastAPI Project
-
-
-
-
-
-
-
diff --git a/frontend/modify-openapi-operationids.js b/frontend/modify-openapi-operationids.js
deleted file mode 100644
index b22fd17f9e..0000000000
--- a/frontend/modify-openapi-operationids.js
+++ /dev/null
@@ -1,36 +0,0 @@
-import * as fs from "node:fs"
-
-async function modifyOpenAPIFile(filePath) {
- try {
- const data = await fs.promises.readFile(filePath)
- const openapiContent = JSON.parse(data)
-
- const paths = openapiContent.paths
- for (const pathKey of Object.keys(paths)) {
- const pathData = paths[pathKey]
- for (const method of Object.keys(pathData)) {
- const operation = pathData[method]
- if (operation.tags && operation.tags.length > 0) {
- const tag = operation.tags[0]
- const operationId = operation.operationId
- const toRemove = `${tag}-`
- if (operationId.startsWith(toRemove)) {
- const newOperationId = operationId.substring(toRemove.length)
- operation.operationId = newOperationId
- }
- }
- }
- }
-
- await fs.promises.writeFile(
- filePath,
- JSON.stringify(openapiContent, null, 2),
- )
- console.log("File successfully modified")
- } catch (err) {
- console.error("Error:", err)
- }
-}
-
-const filePath = "./openapi.json"
-modifyOpenAPIFile(filePath)
diff --git a/frontend/nginx-backend-not-found.conf b/frontend/nginx-backend-not-found.conf
deleted file mode 100644
index f6fea66358..0000000000
--- a/frontend/nginx-backend-not-found.conf
+++ /dev/null
@@ -1,9 +0,0 @@
-location /api {
- return 404;
-}
-location /docs {
- return 404;
-}
-location /redoc {
- return 404;
-}
diff --git a/frontend/nginx.conf b/frontend/nginx.conf
deleted file mode 100644
index ba4d9aad6c..0000000000
--- a/frontend/nginx.conf
+++ /dev/null
@@ -1,11 +0,0 @@
-server {
- listen 80;
-
- location / {
- root /usr/share/nginx/html;
- index index.html index.htm;
- try_files $uri /index.html =404;
- }
-
- include /etc/nginx/extra-conf.d/*.conf;
-}
diff --git a/frontend/package-lock.json b/frontend/package-lock.json
deleted file mode 100644
index 9560ff9f69..0000000000
--- a/frontend/package-lock.json
+++ /dev/null
@@ -1,6478 +0,0 @@
-{
- "name": "frontend",
- "version": "0.0.0",
- "lockfileVersion": 2,
- "requires": true,
- "packages": {
- "": {
- "name": "frontend",
- "version": "0.0.0",
- "dependencies": {
- "@chakra-ui/icons": "2.1.1",
- "@chakra-ui/react": "2.8.2",
- "@emotion/react": "11.11.3",
- "@emotion/styled": "11.11.0",
- "@tanstack/react-query": "^5.28.14",
- "@tanstack/react-query-devtools": "^5.28.14",
- "@tanstack/react-router": "1.19.1",
- "axios": "1.7.4",
- "form-data": "4.0.0",
- "framer-motion": "10.16.16",
- "react": "^18.2.0",
- "react-dom": "^18.2.0",
- "react-error-boundary": "^4.0.13",
- "react-hook-form": "7.49.3",
- "react-icons": "5.0.1"
- },
- "devDependencies": {
- "@biomejs/biome": "1.6.1",
- "@hey-api/openapi-ts": "^0.34.1",
- "@playwright/test": "^1.45.2",
- "@tanstack/router-devtools": "1.19.1",
- "@tanstack/router-vite-plugin": "1.19.0",
- "@types/node": "^20.10.5",
- "@types/react": "^18.2.37",
- "@types/react-dom": "^18.2.15",
- "@vitejs/plugin-react-swc": "^3.5.0",
- "dotenv": "^16.4.5",
- "typescript": "^5.2.2",
- "vite": "^5.0.13"
- }
- },
- "node_modules/@babel/code-frame": {
- "version": "7.23.5",
- "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.23.5.tgz",
- "integrity": "sha512-CgH3s1a96LipHCmSUmYFPwY7MNx8C3avkq7i4Wl3cfa662ldtUe4VM1TPXX70pfmrlWTb6jLqTYrZyT2ZTJBgA==",
- "dependencies": {
- "@babel/highlight": "^7.23.4",
- "chalk": "^2.4.2"
- },
- "engines": {
- "node": ">=6.9.0"
- }
- },
- "node_modules/@babel/code-frame/node_modules/ansi-styles": {
- "version": "3.2.1",
- "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz",
- "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==",
- "dependencies": {
- "color-convert": "^1.9.0"
- },
- "engines": {
- "node": ">=4"
- }
- },
- "node_modules/@babel/code-frame/node_modules/chalk": {
- "version": "2.4.2",
- "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz",
- "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==",
- "dependencies": {
- "ansi-styles": "^3.2.1",
- "escape-string-regexp": "^1.0.5",
- "supports-color": "^5.3.0"
- },
- "engines": {
- "node": ">=4"
- }
- },
- "node_modules/@babel/code-frame/node_modules/color-convert": {
- "version": "1.9.3",
- "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz",
- "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==",
- "dependencies": {
- "color-name": "1.1.3"
- }
- },
- "node_modules/@babel/code-frame/node_modules/color-name": {
- "version": "1.1.3",
- "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz",
- "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw=="
- },
- "node_modules/@babel/code-frame/node_modules/escape-string-regexp": {
- "version": "1.0.5",
- "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz",
- "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==",
- "engines": {
- "node": ">=0.8.0"
- }
- },
- "node_modules/@babel/code-frame/node_modules/has-flag": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz",
- "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==",
- "engines": {
- "node": ">=4"
- }
- },
- "node_modules/@babel/code-frame/node_modules/supports-color": {
- "version": "5.5.0",
- "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz",
- "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==",
- "dependencies": {
- "has-flag": "^3.0.0"
- },
- "engines": {
- "node": ">=4"
- }
- },
- "node_modules/@babel/helper-module-imports": {
- "version": "7.22.15",
- "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.22.15.tgz",
- "integrity": "sha512-0pYVBnDKZO2fnSPCrgM/6WMc7eS20Fbok+0r88fp+YtWVLZrp4CkafFGIp+W0VKw4a22sgebPT99y+FDNMdP4w==",
- "dependencies": {
- "@babel/types": "^7.22.15"
- },
- "engines": {
- "node": ">=6.9.0"
- }
- },
- "node_modules/@babel/helper-string-parser": {
- "version": "7.23.4",
- "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.23.4.tgz",
- "integrity": "sha512-803gmbQdqwdf4olxrX4AJyFBV/RTr3rSmOj0rKwesmzlfhYNDEs+/iOcznzpNWlJlIlTJC2QfPFcHB6DlzdVLQ==",
- "engines": {
- "node": ">=6.9.0"
- }
- },
- "node_modules/@babel/helper-validator-identifier": {
- "version": "7.22.20",
- "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.20.tgz",
- "integrity": "sha512-Y4OZ+ytlatR8AI+8KZfKuL5urKp7qey08ha31L8b3BwewJAoJamTzyvxPR/5D+KkdJCGPq/+8TukHBlY10FX9A==",
- "engines": {
- "node": ">=6.9.0"
- }
- },
- "node_modules/@babel/highlight": {
- "version": "7.23.4",
- "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.23.4.tgz",
- "integrity": "sha512-acGdbYSfp2WheJoJm/EBBBLh/ID8KDc64ISZ9DYtBmC8/Q204PZJLHyzeB5qMzJ5trcOkybd78M4x2KWsUq++A==",
- "dependencies": {
- "@babel/helper-validator-identifier": "^7.22.20",
- "chalk": "^2.4.2",
- "js-tokens": "^4.0.0"
- },
- "engines": {
- "node": ">=6.9.0"
- }
- },
- "node_modules/@babel/highlight/node_modules/ansi-styles": {
- "version": "3.2.1",
- "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz",
- "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==",
- "dependencies": {
- "color-convert": "^1.9.0"
- },
- "engines": {
- "node": ">=4"
- }
- },
- "node_modules/@babel/highlight/node_modules/chalk": {
- "version": "2.4.2",
- "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz",
- "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==",
- "dependencies": {
- "ansi-styles": "^3.2.1",
- "escape-string-regexp": "^1.0.5",
- "supports-color": "^5.3.0"
- },
- "engines": {
- "node": ">=4"
- }
- },
- "node_modules/@babel/highlight/node_modules/color-convert": {
- "version": "1.9.3",
- "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz",
- "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==",
- "dependencies": {
- "color-name": "1.1.3"
- }
- },
- "node_modules/@babel/highlight/node_modules/color-name": {
- "version": "1.1.3",
- "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz",
- "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw=="
- },
- "node_modules/@babel/highlight/node_modules/escape-string-regexp": {
- "version": "1.0.5",
- "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz",
- "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==",
- "engines": {
- "node": ">=0.8.0"
- }
- },
- "node_modules/@babel/highlight/node_modules/has-flag": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz",
- "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==",
- "engines": {
- "node": ">=4"
- }
- },
- "node_modules/@babel/highlight/node_modules/supports-color": {
- "version": "5.5.0",
- "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz",
- "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==",
- "dependencies": {
- "has-flag": "^3.0.0"
- },
- "engines": {
- "node": ">=4"
- }
- },
- "node_modules/@babel/runtime": {
- "version": "7.23.9",
- "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.23.9.tgz",
- "integrity": "sha512-0CX6F+BI2s9dkUqr08KFrAIZgNFj75rdBU/DjCyYLIaV/quFjkk6T+EJ2LkZHyZTbEV4L5p97mNkUsHl2wLFAw==",
- "dependencies": {
- "regenerator-runtime": "^0.14.0"
- },
- "engines": {
- "node": ">=6.9.0"
- }
- },
- "node_modules/@babel/types": {
- "version": "7.23.9",
- "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.23.9.tgz",
- "integrity": "sha512-dQjSq/7HaSjRM43FFGnv5keM2HsxpmyV1PfaSVm0nzzjwwTmjOe6J4bC8e3+pTEIgHaHj+1ZlLThRJ2auc/w1Q==",
- "dependencies": {
- "@babel/helper-string-parser": "^7.23.4",
- "@babel/helper-validator-identifier": "^7.22.20",
- "to-fast-properties": "^2.0.0"
- },
- "engines": {
- "node": ">=6.9.0"
- }
- },
- "node_modules/@biomejs/biome": {
- "version": "1.6.1",
- "resolved": "https://registry.npmjs.org/@biomejs/biome/-/biome-1.6.1.tgz",
- "integrity": "sha512-SILQvA2S0XeaOuu1bivv6fQmMo7zMfr2xqDEN+Sz78pGbAKZnGmg0emsXjQWoBY/RVm9kPCgX+aGEpZZTYaM7w==",
- "dev": true,
- "hasInstallScript": true,
- "bin": {
- "biome": "bin/biome"
- },
- "engines": {
- "node": ">=14.*"
- },
- "funding": {
- "type": "opencollective",
- "url": "https://opencollective.com/biome"
- },
- "optionalDependencies": {
- "@biomejs/cli-darwin-arm64": "1.6.1",
- "@biomejs/cli-darwin-x64": "1.6.1",
- "@biomejs/cli-linux-arm64": "1.6.1",
- "@biomejs/cli-linux-arm64-musl": "1.6.1",
- "@biomejs/cli-linux-x64": "1.6.1",
- "@biomejs/cli-linux-x64-musl": "1.6.1",
- "@biomejs/cli-win32-arm64": "1.6.1",
- "@biomejs/cli-win32-x64": "1.6.1"
- }
- },
- "node_modules/@biomejs/cli-darwin-arm64": {
- "version": "1.6.1",
- "resolved": "https://registry.npmjs.org/@biomejs/cli-darwin-arm64/-/cli-darwin-arm64-1.6.1.tgz",
- "integrity": "sha512-KlvY00iB9T/vFi4m/GXxEyYkYnYy6aw06uapzUIIdiMMj7I/pmZu7CsZlzWdekVD0j+SsQbxdZMsb0wPhnRSsg==",
- "cpu": [
- "arm64"
- ],
- "dev": true,
- "optional": true,
- "os": [
- "darwin"
- ],
- "engines": {
- "node": ">=14.*"
- }
- },
- "node_modules/@biomejs/cli-darwin-x64": {
- "version": "1.6.1",
- "resolved": "https://registry.npmjs.org/@biomejs/cli-darwin-x64/-/cli-darwin-x64-1.6.1.tgz",
- "integrity": "sha512-jP4E8TXaQX5e3nvRJSzB+qicZrdIDCrjR0sSb1DaDTx4JPZH5WXq/BlTqAyWi3IijM+IYMjWqAAK4kOHsSCzxw==",
- "cpu": [
- "x64"
- ],
- "dev": true,
- "optional": true,
- "os": [
- "darwin"
- ],
- "engines": {
- "node": ">=14.*"
- }
- },
- "node_modules/@biomejs/cli-linux-arm64": {
- "version": "1.6.1",
- "resolved": "https://registry.npmjs.org/@biomejs/cli-linux-arm64/-/cli-linux-arm64-1.6.1.tgz",
- "integrity": "sha512-nxD1UyX3bWSl/RSKlib/JsOmt+652/9yieogdSC/UTLgVCZYOF7u8L/LK7kAa0Y4nA8zSPavAQTgko7mHC2ObA==",
- "cpu": [
- "arm64"
- ],
- "dev": true,
- "optional": true,
- "os": [
- "linux"
- ],
- "engines": {
- "node": ">=14.*"
- }
- },
- "node_modules/@biomejs/cli-linux-arm64-musl": {
- "version": "1.6.1",
- "resolved": "https://registry.npmjs.org/@biomejs/cli-linux-arm64-musl/-/cli-linux-arm64-musl-1.6.1.tgz",
- "integrity": "sha512-YdkDgFecdHJg7PJxAMaZIixVWGB6St4yH08BHagO0fEhNNiY8cAKEVo2mcXlsnEiTMpeSEAY9VxLUrVT3IVxpw==",
- "cpu": [
- "arm64"
- ],
- "dev": true,
- "optional": true,
- "os": [
- "linux"
- ],
- "engines": {
- "node": ">=14.*"
- }
- },
- "node_modules/@biomejs/cli-linux-x64": {
- "version": "1.6.1",
- "resolved": "https://registry.npmjs.org/@biomejs/cli-linux-x64/-/cli-linux-x64-1.6.1.tgz",
- "integrity": "sha512-BYAzenlMF3QdngjNFw9QVBXKGNzeecqwF3pwDgUGEvU7OJpn1/lyVkJVxYPtVGRNdjQ9e6l/s8NjKuBpW/ZR4Q==",
- "cpu": [
- "x64"
- ],
- "dev": true,
- "optional": true,
- "os": [
- "linux"
- ],
- "engines": {
- "node": ">=14.*"
- }
- },
- "node_modules/@biomejs/cli-linux-x64-musl": {
- "version": "1.6.1",
- "resolved": "https://registry.npmjs.org/@biomejs/cli-linux-x64-musl/-/cli-linux-x64-musl-1.6.1.tgz",
- "integrity": "sha512-aSISIDmxq04NNy7tm4x9rBk2vH0ub2VDIE4outEmdC2LBtEJoINiphlZagx/FvjbsqUfygent9QUSn0oREnAXg==",
- "cpu": [
- "x64"
- ],
- "dev": true,
- "optional": true,
- "os": [
- "linux"
- ],
- "engines": {
- "node": ">=14.*"
- }
- },
- "node_modules/@biomejs/cli-win32-arm64": {
- "version": "1.6.1",
- "resolved": "https://registry.npmjs.org/@biomejs/cli-win32-arm64/-/cli-win32-arm64-1.6.1.tgz",
- "integrity": "sha512-/eCHQKZ1kEawUpkSuXq4urtxMsD1P1678OPG3zNKt3ru16AqqspLdO3jzBe3k74xCPYnQ36e9Yqc97Mo0qgPtg==",
- "cpu": [
- "arm64"
- ],
- "dev": true,
- "optional": true,
- "os": [
- "win32"
- ],
- "engines": {
- "node": ">=14.*"
- }
- },
- "node_modules/@biomejs/cli-win32-x64": {
- "version": "1.6.1",
- "resolved": "https://registry.npmjs.org/@biomejs/cli-win32-x64/-/cli-win32-x64-1.6.1.tgz",
- "integrity": "sha512-5TUZbzBwnDLFxLVGEPsorNi6eC2Gt+z4Oei9Qvq0M/4c4/mjZ96ABgwao/tMxf4ZBr/qyy2YdvF+gX9Rc+xC0A==",
- "cpu": [
- "x64"
- ],
- "dev": true,
- "optional": true,
- "os": [
- "win32"
- ],
- "engines": {
- "node": ">=14.*"
- }
- },
- "node_modules/@chakra-ui/accordion": {
- "version": "2.3.1",
- "resolved": "https://registry.npmjs.org/@chakra-ui/accordion/-/accordion-2.3.1.tgz",
- "integrity": "sha512-FSXRm8iClFyU+gVaXisOSEw0/4Q+qZbFRiuhIAkVU6Boj0FxAMrlo9a8AV5TuF77rgaHytCdHk0Ng+cyUijrag==",
- "dependencies": {
- "@chakra-ui/descendant": "3.1.0",
- "@chakra-ui/icon": "3.2.0",
- "@chakra-ui/react-context": "2.1.0",
- "@chakra-ui/react-use-controllable-state": "2.1.0",
- "@chakra-ui/react-use-merge-refs": "2.1.0",
- "@chakra-ui/shared-utils": "2.0.5",
- "@chakra-ui/transition": "2.1.0"
- },
- "peerDependencies": {
- "@chakra-ui/system": ">=2.0.0",
- "framer-motion": ">=4.0.0",
- "react": ">=18"
- }
- },
- "node_modules/@chakra-ui/alert": {
- "version": "2.2.2",
- "resolved": "https://registry.npmjs.org/@chakra-ui/alert/-/alert-2.2.2.tgz",
- "integrity": "sha512-jHg4LYMRNOJH830ViLuicjb3F+v6iriE/2G5T+Sd0Hna04nukNJ1MxUmBPE+vI22me2dIflfelu2v9wdB6Pojw==",
- "dependencies": {
- "@chakra-ui/icon": "3.2.0",
- "@chakra-ui/react-context": "2.1.0",
- "@chakra-ui/shared-utils": "2.0.5",
- "@chakra-ui/spinner": "2.1.0"
- },
- "peerDependencies": {
- "@chakra-ui/system": ">=2.0.0",
- "react": ">=18"
- }
- },
- "node_modules/@chakra-ui/anatomy": {
- "version": "2.2.2",
- "resolved": "https://registry.npmjs.org/@chakra-ui/anatomy/-/anatomy-2.2.2.tgz",
- "integrity": "sha512-MV6D4VLRIHr4PkW4zMyqfrNS1mPlCTiCXwvYGtDFQYr+xHFfonhAuf9WjsSc0nyp2m0OdkSLnzmVKkZFLo25Tg=="
- },
- "node_modules/@chakra-ui/avatar": {
- "version": "2.3.0",
- "resolved": "https://registry.npmjs.org/@chakra-ui/avatar/-/avatar-2.3.0.tgz",
- "integrity": "sha512-8gKSyLfygnaotbJbDMHDiJoF38OHXUYVme4gGxZ1fLnQEdPVEaIWfH+NndIjOM0z8S+YEFnT9KyGMUtvPrBk3g==",
- "dependencies": {
- "@chakra-ui/image": "2.1.0",
- "@chakra-ui/react-children-utils": "2.0.6",
- "@chakra-ui/react-context": "2.1.0",
- "@chakra-ui/shared-utils": "2.0.5"
- },
- "peerDependencies": {
- "@chakra-ui/system": ">=2.0.0",
- "react": ">=18"
- }
- },
- "node_modules/@chakra-ui/breadcrumb": {
- "version": "2.2.0",
- "resolved": "https://registry.npmjs.org/@chakra-ui/breadcrumb/-/breadcrumb-2.2.0.tgz",
- "integrity": "sha512-4cWCG24flYBxjruRi4RJREWTGF74L/KzI2CognAW/d/zWR0CjiScuJhf37Am3LFbCySP6WSoyBOtTIoTA4yLEA==",
- "dependencies": {
- "@chakra-ui/react-children-utils": "2.0.6",
- "@chakra-ui/react-context": "2.1.0",
- "@chakra-ui/shared-utils": "2.0.5"
- },
- "peerDependencies": {
- "@chakra-ui/system": ">=2.0.0",
- "react": ">=18"
- }
- },
- "node_modules/@chakra-ui/breakpoint-utils": {
- "version": "2.0.8",
- "resolved": "https://registry.npmjs.org/@chakra-ui/breakpoint-utils/-/breakpoint-utils-2.0.8.tgz",
- "integrity": "sha512-Pq32MlEX9fwb5j5xx8s18zJMARNHlQZH2VH1RZgfgRDpp7DcEgtRW5AInfN5CfqdHLO1dGxA7I3MqEuL5JnIsA==",
- "dependencies": {
- "@chakra-ui/shared-utils": "2.0.5"
- }
- },
- "node_modules/@chakra-ui/button": {
- "version": "2.1.0",
- "resolved": "https://registry.npmjs.org/@chakra-ui/button/-/button-2.1.0.tgz",
- "integrity": "sha512-95CplwlRKmmUXkdEp/21VkEWgnwcx2TOBG6NfYlsuLBDHSLlo5FKIiE2oSi4zXc4TLcopGcWPNcm/NDaSC5pvA==",
- "dependencies": {
- "@chakra-ui/react-context": "2.1.0",
- "@chakra-ui/react-use-merge-refs": "2.1.0",
- "@chakra-ui/shared-utils": "2.0.5",
- "@chakra-ui/spinner": "2.1.0"
- },
- "peerDependencies": {
- "@chakra-ui/system": ">=2.0.0",
- "react": ">=18"
- }
- },
- "node_modules/@chakra-ui/card": {
- "version": "2.2.0",
- "resolved": "https://registry.npmjs.org/@chakra-ui/card/-/card-2.2.0.tgz",
- "integrity": "sha512-xUB/k5MURj4CtPAhdSoXZidUbm8j3hci9vnc+eZJVDqhDOShNlD6QeniQNRPRys4lWAQLCbFcrwL29C8naDi6g==",
- "dependencies": {
- "@chakra-ui/shared-utils": "2.0.5"
- },
- "peerDependencies": {
- "@chakra-ui/system": ">=2.0.0",
- "react": ">=18"
- }
- },
- "node_modules/@chakra-ui/checkbox": {
- "version": "2.3.2",
- "resolved": "https://registry.npmjs.org/@chakra-ui/checkbox/-/checkbox-2.3.2.tgz",
- "integrity": "sha512-85g38JIXMEv6M+AcyIGLh7igNtfpAN6KGQFYxY9tBj0eWvWk4NKQxvqqyVta0bSAyIl1rixNIIezNpNWk2iO4g==",
- "dependencies": {
- "@chakra-ui/form-control": "2.2.0",
- "@chakra-ui/react-context": "2.1.0",
- "@chakra-ui/react-types": "2.0.7",
- "@chakra-ui/react-use-callback-ref": "2.1.0",
- "@chakra-ui/react-use-controllable-state": "2.1.0",
- "@chakra-ui/react-use-merge-refs": "2.1.0",
- "@chakra-ui/react-use-safe-layout-effect": "2.1.0",
- "@chakra-ui/react-use-update-effect": "2.1.0",
- "@chakra-ui/shared-utils": "2.0.5",
- "@chakra-ui/visually-hidden": "2.2.0",
- "@zag-js/focus-visible": "0.16.0"
- },
- "peerDependencies": {
- "@chakra-ui/system": ">=2.0.0",
- "react": ">=18"
- }
- },
- "node_modules/@chakra-ui/clickable": {
- "version": "2.1.0",
- "resolved": "https://registry.npmjs.org/@chakra-ui/clickable/-/clickable-2.1.0.tgz",
- "integrity": "sha512-flRA/ClPUGPYabu+/GLREZVZr9j2uyyazCAUHAdrTUEdDYCr31SVGhgh7dgKdtq23bOvAQJpIJjw/0Bs0WvbXw==",
- "dependencies": {
- "@chakra-ui/react-use-merge-refs": "2.1.0",
- "@chakra-ui/shared-utils": "2.0.5"
- },
- "peerDependencies": {
- "react": ">=18"
- }
- },
- "node_modules/@chakra-ui/close-button": {
- "version": "2.1.1",
- "resolved": "https://registry.npmjs.org/@chakra-ui/close-button/-/close-button-2.1.1.tgz",
- "integrity": "sha512-gnpENKOanKexswSVpVz7ojZEALl2x5qjLYNqSQGbxz+aP9sOXPfUS56ebyBrre7T7exuWGiFeRwnM0oVeGPaiw==",
- "dependencies": {
- "@chakra-ui/icon": "3.2.0"
- },
- "peerDependencies": {
- "@chakra-ui/system": ">=2.0.0",
- "react": ">=18"
- }
- },
- "node_modules/@chakra-ui/color-mode": {
- "version": "2.2.0",
- "resolved": "https://registry.npmjs.org/@chakra-ui/color-mode/-/color-mode-2.2.0.tgz",
- "integrity": "sha512-niTEA8PALtMWRI9wJ4LL0CSBDo8NBfLNp4GD6/0hstcm3IlbBHTVKxN6HwSaoNYfphDQLxCjT4yG+0BJA5tFpg==",
- "dependencies": {
- "@chakra-ui/react-use-safe-layout-effect": "2.1.0"
- },
- "peerDependencies": {
- "react": ">=18"
- }
- },
- "node_modules/@chakra-ui/control-box": {
- "version": "2.1.0",
- "resolved": "https://registry.npmjs.org/@chakra-ui/control-box/-/control-box-2.1.0.tgz",
- "integrity": "sha512-gVrRDyXFdMd8E7rulL0SKeoljkLQiPITFnsyMO8EFHNZ+AHt5wK4LIguYVEq88APqAGZGfHFWXr79RYrNiE3Mg==",
- "peerDependencies": {
- "@chakra-ui/system": ">=2.0.0",
- "react": ">=18"
- }
- },
- "node_modules/@chakra-ui/counter": {
- "version": "2.1.0",
- "resolved": "https://registry.npmjs.org/@chakra-ui/counter/-/counter-2.1.0.tgz",
- "integrity": "sha512-s6hZAEcWT5zzjNz2JIWUBzRubo9la/oof1W7EKZVVfPYHERnl5e16FmBC79Yfq8p09LQ+aqFKm/etYoJMMgghw==",
- "dependencies": {
- "@chakra-ui/number-utils": "2.0.7",
- "@chakra-ui/react-use-callback-ref": "2.1.0",
- "@chakra-ui/shared-utils": "2.0.5"
- },
- "peerDependencies": {
- "react": ">=18"
- }
- },
- "node_modules/@chakra-ui/css-reset": {
- "version": "2.3.0",
- "resolved": "https://registry.npmjs.org/@chakra-ui/css-reset/-/css-reset-2.3.0.tgz",
- "integrity": "sha512-cQwwBy5O0jzvl0K7PLTLgp8ijqLPKyuEMiDXwYzl95seD3AoeuoCLyzZcJtVqaUZ573PiBdAbY/IlZcwDOItWg==",
- "peerDependencies": {
- "@emotion/react": ">=10.0.35",
- "react": ">=18"
- }
- },
- "node_modules/@chakra-ui/descendant": {
- "version": "3.1.0",
- "resolved": "https://registry.npmjs.org/@chakra-ui/descendant/-/descendant-3.1.0.tgz",
- "integrity": "sha512-VxCIAir08g5w27klLyi7PVo8BxhW4tgU/lxQyujkmi4zx7hT9ZdrcQLAted/dAa+aSIZ14S1oV0Q9lGjsAdxUQ==",
- "dependencies": {
- "@chakra-ui/react-context": "2.1.0",
- "@chakra-ui/react-use-merge-refs": "2.1.0"
- },
- "peerDependencies": {
- "react": ">=18"
- }
- },
- "node_modules/@chakra-ui/dom-utils": {
- "version": "2.1.0",
- "resolved": "https://registry.npmjs.org/@chakra-ui/dom-utils/-/dom-utils-2.1.0.tgz",
- "integrity": "sha512-ZmF2qRa1QZ0CMLU8M1zCfmw29DmPNtfjR9iTo74U5FPr3i1aoAh7fbJ4qAlZ197Xw9eAW28tvzQuoVWeL5C7fQ=="
- },
- "node_modules/@chakra-ui/editable": {
- "version": "3.1.0",
- "resolved": "https://registry.npmjs.org/@chakra-ui/editable/-/editable-3.1.0.tgz",
- "integrity": "sha512-j2JLrUL9wgg4YA6jLlbU88370eCRyor7DZQD9lzpY95tSOXpTljeg3uF9eOmDnCs6fxp3zDWIfkgMm/ExhcGTg==",
- "dependencies": {
- "@chakra-ui/react-context": "2.1.0",
- "@chakra-ui/react-types": "2.0.7",
- "@chakra-ui/react-use-callback-ref": "2.1.0",
- "@chakra-ui/react-use-controllable-state": "2.1.0",
- "@chakra-ui/react-use-focus-on-pointer-down": "2.1.0",
- "@chakra-ui/react-use-merge-refs": "2.1.0",
- "@chakra-ui/react-use-safe-layout-effect": "2.1.0",
- "@chakra-ui/react-use-update-effect": "2.1.0",
- "@chakra-ui/shared-utils": "2.0.5"
- },
- "peerDependencies": {
- "@chakra-ui/system": ">=2.0.0",
- "react": ">=18"
- }
- },
- "node_modules/@chakra-ui/event-utils": {
- "version": "2.0.8",
- "resolved": "https://registry.npmjs.org/@chakra-ui/event-utils/-/event-utils-2.0.8.tgz",
- "integrity": "sha512-IGM/yGUHS+8TOQrZGpAKOJl/xGBrmRYJrmbHfUE7zrG3PpQyXvbLDP1M+RggkCFVgHlJi2wpYIf0QtQlU0XZfw=="
- },
- "node_modules/@chakra-ui/focus-lock": {
- "version": "2.1.0",
- "resolved": "https://registry.npmjs.org/@chakra-ui/focus-lock/-/focus-lock-2.1.0.tgz",
- "integrity": "sha512-EmGx4PhWGjm4dpjRqM4Aa+rCWBxP+Rq8Uc/nAVnD4YVqkEhBkrPTpui2lnjsuxqNaZ24fIAZ10cF1hlpemte/w==",
- "dependencies": {
- "@chakra-ui/dom-utils": "2.1.0",
- "react-focus-lock": "^2.9.4"
- },
- "peerDependencies": {
- "react": ">=18"
- }
- },
- "node_modules/@chakra-ui/form-control": {
- "version": "2.2.0",
- "resolved": "https://registry.npmjs.org/@chakra-ui/form-control/-/form-control-2.2.0.tgz",
- "integrity": "sha512-wehLC1t4fafCVJ2RvJQT2jyqsAwX7KymmiGqBu7nQoQz8ApTkGABWpo/QwDh3F/dBLrouHDoOvGmYTqft3Mirw==",
- "dependencies": {
- "@chakra-ui/icon": "3.2.0",
- "@chakra-ui/react-context": "2.1.0",
- "@chakra-ui/react-types": "2.0.7",
- "@chakra-ui/react-use-merge-refs": "2.1.0",
- "@chakra-ui/shared-utils": "2.0.5"
- },
- "peerDependencies": {
- "@chakra-ui/system": ">=2.0.0",
- "react": ">=18"
- }
- },
- "node_modules/@chakra-ui/hooks": {
- "version": "2.2.1",
- "resolved": "https://registry.npmjs.org/@chakra-ui/hooks/-/hooks-2.2.1.tgz",
- "integrity": "sha512-RQbTnzl6b1tBjbDPf9zGRo9rf/pQMholsOudTxjy4i9GfTfz6kgp5ValGjQm2z7ng6Z31N1cnjZ1AlSzQ//ZfQ==",
- "dependencies": {
- "@chakra-ui/react-utils": "2.0.12",
- "@chakra-ui/utils": "2.0.15",
- "compute-scroll-into-view": "3.0.3",
- "copy-to-clipboard": "3.3.3"
- },
- "peerDependencies": {
- "react": ">=18"
- }
- },
- "node_modules/@chakra-ui/icon": {
- "version": "3.2.0",
- "resolved": "https://registry.npmjs.org/@chakra-ui/icon/-/icon-3.2.0.tgz",
- "integrity": "sha512-xxjGLvlX2Ys4H0iHrI16t74rG9EBcpFvJ3Y3B7KMQTrnW34Kf7Da/UC8J67Gtx85mTHW020ml85SVPKORWNNKQ==",
- "dependencies": {
- "@chakra-ui/shared-utils": "2.0.5"
- },
- "peerDependencies": {
- "@chakra-ui/system": ">=2.0.0",
- "react": ">=18"
- }
- },
- "node_modules/@chakra-ui/icons": {
- "version": "2.1.1",
- "resolved": "https://registry.npmjs.org/@chakra-ui/icons/-/icons-2.1.1.tgz",
- "integrity": "sha512-3p30hdo4LlRZTT5CwoAJq3G9fHI0wDc0pBaMHj4SUn0yomO+RcDRlzhdXqdr5cVnzax44sqXJVnf3oQG0eI+4g==",
- "dependencies": {
- "@chakra-ui/icon": "3.2.0"
- },
- "peerDependencies": {
- "@chakra-ui/system": ">=2.0.0",
- "react": ">=18"
- }
- },
- "node_modules/@chakra-ui/image": {
- "version": "2.1.0",
- "resolved": "https://registry.npmjs.org/@chakra-ui/image/-/image-2.1.0.tgz",
- "integrity": "sha512-bskumBYKLiLMySIWDGcz0+D9Th0jPvmX6xnRMs4o92tT3Od/bW26lahmV2a2Op2ItXeCmRMY+XxJH5Gy1i46VA==",
- "dependencies": {
- "@chakra-ui/react-use-safe-layout-effect": "2.1.0",
- "@chakra-ui/shared-utils": "2.0.5"
- },
- "peerDependencies": {
- "@chakra-ui/system": ">=2.0.0",
- "react": ">=18"
- }
- },
- "node_modules/@chakra-ui/input": {
- "version": "2.1.2",
- "resolved": "https://registry.npmjs.org/@chakra-ui/input/-/input-2.1.2.tgz",
- "integrity": "sha512-GiBbb3EqAA8Ph43yGa6Mc+kUPjh4Spmxp1Pkelr8qtudpc3p2PJOOebLpd90mcqw8UePPa+l6YhhPtp6o0irhw==",
- "dependencies": {
- "@chakra-ui/form-control": "2.2.0",
- "@chakra-ui/object-utils": "2.1.0",
- "@chakra-ui/react-children-utils": "2.0.6",
- "@chakra-ui/react-context": "2.1.0",
- "@chakra-ui/shared-utils": "2.0.5"
- },
- "peerDependencies": {
- "@chakra-ui/system": ">=2.0.0",
- "react": ">=18"
- }
- },
- "node_modules/@chakra-ui/layout": {
- "version": "2.3.1",
- "resolved": "https://registry.npmjs.org/@chakra-ui/layout/-/layout-2.3.1.tgz",
- "integrity": "sha512-nXuZ6WRbq0WdgnRgLw+QuxWAHuhDtVX8ElWqcTK+cSMFg/52eVP47czYBE5F35YhnoW2XBwfNoNgZ7+e8Z01Rg==",
- "dependencies": {
- "@chakra-ui/breakpoint-utils": "2.0.8",
- "@chakra-ui/icon": "3.2.0",
- "@chakra-ui/object-utils": "2.1.0",
- "@chakra-ui/react-children-utils": "2.0.6",
- "@chakra-ui/react-context": "2.1.0",
- "@chakra-ui/shared-utils": "2.0.5"
- },
- "peerDependencies": {
- "@chakra-ui/system": ">=2.0.0",
- "react": ">=18"
- }
- },
- "node_modules/@chakra-ui/lazy-utils": {
- "version": "2.0.5",
- "resolved": "https://registry.npmjs.org/@chakra-ui/lazy-utils/-/lazy-utils-2.0.5.tgz",
- "integrity": "sha512-UULqw7FBvcckQk2n3iPO56TMJvDsNv0FKZI6PlUNJVaGsPbsYxK/8IQ60vZgaTVPtVcjY6BE+y6zg8u9HOqpyg=="
- },
- "node_modules/@chakra-ui/live-region": {
- "version": "2.1.0",
- "resolved": "https://registry.npmjs.org/@chakra-ui/live-region/-/live-region-2.1.0.tgz",
- "integrity": "sha512-ZOxFXwtaLIsXjqnszYYrVuswBhnIHHP+XIgK1vC6DePKtyK590Wg+0J0slDwThUAd4MSSIUa/nNX84x1GMphWw==",
- "peerDependencies": {
- "react": ">=18"
- }
- },
- "node_modules/@chakra-ui/media-query": {
- "version": "3.3.0",
- "resolved": "https://registry.npmjs.org/@chakra-ui/media-query/-/media-query-3.3.0.tgz",
- "integrity": "sha512-IsTGgFLoICVoPRp9ykOgqmdMotJG0CnPsKvGQeSFOB/dZfIujdVb14TYxDU4+MURXry1MhJ7LzZhv+Ml7cr8/g==",
- "dependencies": {
- "@chakra-ui/breakpoint-utils": "2.0.8",
- "@chakra-ui/react-env": "3.1.0",
- "@chakra-ui/shared-utils": "2.0.5"
- },
- "peerDependencies": {
- "@chakra-ui/system": ">=2.0.0",
- "react": ">=18"
- }
- },
- "node_modules/@chakra-ui/menu": {
- "version": "2.2.1",
- "resolved": "https://registry.npmjs.org/@chakra-ui/menu/-/menu-2.2.1.tgz",
- "integrity": "sha512-lJS7XEObzJxsOwWQh7yfG4H8FzFPRP5hVPN/CL+JzytEINCSBvsCDHrYPQGp7jzpCi8vnTqQQGQe0f8dwnXd2g==",
- "dependencies": {
- "@chakra-ui/clickable": "2.1.0",
- "@chakra-ui/descendant": "3.1.0",
- "@chakra-ui/lazy-utils": "2.0.5",
- "@chakra-ui/popper": "3.1.0",
- "@chakra-ui/react-children-utils": "2.0.6",
- "@chakra-ui/react-context": "2.1.0",
- "@chakra-ui/react-use-animation-state": "2.1.0",
- "@chakra-ui/react-use-controllable-state": "2.1.0",
- "@chakra-ui/react-use-disclosure": "2.1.0",
- "@chakra-ui/react-use-focus-effect": "2.1.0",
- "@chakra-ui/react-use-merge-refs": "2.1.0",
- "@chakra-ui/react-use-outside-click": "2.2.0",
- "@chakra-ui/react-use-update-effect": "2.1.0",
- "@chakra-ui/shared-utils": "2.0.5",
- "@chakra-ui/transition": "2.1.0"
- },
- "peerDependencies": {
- "@chakra-ui/system": ">=2.0.0",
- "framer-motion": ">=4.0.0",
- "react": ">=18"
- }
- },
- "node_modules/@chakra-ui/modal": {
- "version": "2.3.1",
- "resolved": "https://registry.npmjs.org/@chakra-ui/modal/-/modal-2.3.1.tgz",
- "integrity": "sha512-TQv1ZaiJMZN+rR9DK0snx/OPwmtaGH1HbZtlYt4W4s6CzyK541fxLRTjIXfEzIGpvNW+b6VFuFjbcR78p4DEoQ==",
- "dependencies": {
- "@chakra-ui/close-button": "2.1.1",
- "@chakra-ui/focus-lock": "2.1.0",
- "@chakra-ui/portal": "2.1.0",
- "@chakra-ui/react-context": "2.1.0",
- "@chakra-ui/react-types": "2.0.7",
- "@chakra-ui/react-use-merge-refs": "2.1.0",
- "@chakra-ui/shared-utils": "2.0.5",
- "@chakra-ui/transition": "2.1.0",
- "aria-hidden": "^1.2.3",
- "react-remove-scroll": "^2.5.6"
- },
- "peerDependencies": {
- "@chakra-ui/system": ">=2.0.0",
- "framer-motion": ">=4.0.0",
- "react": ">=18",
- "react-dom": ">=18"
- }
- },
- "node_modules/@chakra-ui/number-input": {
- "version": "2.1.2",
- "resolved": "https://registry.npmjs.org/@chakra-ui/number-input/-/number-input-2.1.2.tgz",
- "integrity": "sha512-pfOdX02sqUN0qC2ysuvgVDiws7xZ20XDIlcNhva55Jgm095xjm8eVdIBfNm3SFbSUNxyXvLTW/YQanX74tKmuA==",
- "dependencies": {
- "@chakra-ui/counter": "2.1.0",
- "@chakra-ui/form-control": "2.2.0",
- "@chakra-ui/icon": "3.2.0",
- "@chakra-ui/react-context": "2.1.0",
- "@chakra-ui/react-types": "2.0.7",
- "@chakra-ui/react-use-callback-ref": "2.1.0",
- "@chakra-ui/react-use-event-listener": "2.1.0",
- "@chakra-ui/react-use-interval": "2.1.0",
- "@chakra-ui/react-use-merge-refs": "2.1.0",
- "@chakra-ui/react-use-safe-layout-effect": "2.1.0",
- "@chakra-ui/react-use-update-effect": "2.1.0",
- "@chakra-ui/shared-utils": "2.0.5"
- },
- "peerDependencies": {
- "@chakra-ui/system": ">=2.0.0",
- "react": ">=18"
- }
- },
- "node_modules/@chakra-ui/number-utils": {
- "version": "2.0.7",
- "resolved": "https://registry.npmjs.org/@chakra-ui/number-utils/-/number-utils-2.0.7.tgz",
- "integrity": "sha512-yOGxBjXNvLTBvQyhMDqGU0Oj26s91mbAlqKHiuw737AXHt0aPllOthVUqQMeaYLwLCjGMg0jtI7JReRzyi94Dg=="
- },
- "node_modules/@chakra-ui/object-utils": {
- "version": "2.1.0",
- "resolved": "https://registry.npmjs.org/@chakra-ui/object-utils/-/object-utils-2.1.0.tgz",
- "integrity": "sha512-tgIZOgLHaoti5PYGPTwK3t/cqtcycW0owaiOXoZOcpwwX/vlVb+H1jFsQyWiiwQVPt9RkoSLtxzXamx+aHH+bQ=="
- },
- "node_modules/@chakra-ui/pin-input": {
- "version": "2.1.0",
- "resolved": "https://registry.npmjs.org/@chakra-ui/pin-input/-/pin-input-2.1.0.tgz",
- "integrity": "sha512-x4vBqLStDxJFMt+jdAHHS8jbh294O53CPQJoL4g228P513rHylV/uPscYUHrVJXRxsHfRztQO9k45jjTYaPRMw==",
- "dependencies": {
- "@chakra-ui/descendant": "3.1.0",
- "@chakra-ui/react-children-utils": "2.0.6",
- "@chakra-ui/react-context": "2.1.0",
- "@chakra-ui/react-use-controllable-state": "2.1.0",
- "@chakra-ui/react-use-merge-refs": "2.1.0",
- "@chakra-ui/shared-utils": "2.0.5"
- },
- "peerDependencies": {
- "@chakra-ui/system": ">=2.0.0",
- "react": ">=18"
- }
- },
- "node_modules/@chakra-ui/popover": {
- "version": "2.2.1",
- "resolved": "https://registry.npmjs.org/@chakra-ui/popover/-/popover-2.2.1.tgz",
- "integrity": "sha512-K+2ai2dD0ljvJnlrzesCDT9mNzLifE3noGKZ3QwLqd/K34Ym1W/0aL1ERSynrcG78NKoXS54SdEzkhCZ4Gn/Zg==",
- "dependencies": {
- "@chakra-ui/close-button": "2.1.1",
- "@chakra-ui/lazy-utils": "2.0.5",
- "@chakra-ui/popper": "3.1.0",
- "@chakra-ui/react-context": "2.1.0",
- "@chakra-ui/react-types": "2.0.7",
- "@chakra-ui/react-use-animation-state": "2.1.0",
- "@chakra-ui/react-use-disclosure": "2.1.0",
- "@chakra-ui/react-use-focus-effect": "2.1.0",
- "@chakra-ui/react-use-focus-on-pointer-down": "2.1.0",
- "@chakra-ui/react-use-merge-refs": "2.1.0",
- "@chakra-ui/shared-utils": "2.0.5"
- },
- "peerDependencies": {
- "@chakra-ui/system": ">=2.0.0",
- "framer-motion": ">=4.0.0",
- "react": ">=18"
- }
- },
- "node_modules/@chakra-ui/popper": {
- "version": "3.1.0",
- "resolved": "https://registry.npmjs.org/@chakra-ui/popper/-/popper-3.1.0.tgz",
- "integrity": "sha512-ciDdpdYbeFG7og6/6J8lkTFxsSvwTdMLFkpVylAF6VNC22jssiWfquj2eyD4rJnzkRFPvIWJq8hvbfhsm+AjSg==",
- "dependencies": {
- "@chakra-ui/react-types": "2.0.7",
- "@chakra-ui/react-use-merge-refs": "2.1.0",
- "@popperjs/core": "^2.9.3"
- },
- "peerDependencies": {
- "react": ">=18"
- }
- },
- "node_modules/@chakra-ui/portal": {
- "version": "2.1.0",
- "resolved": "https://registry.npmjs.org/@chakra-ui/portal/-/portal-2.1.0.tgz",
- "integrity": "sha512-9q9KWf6SArEcIq1gGofNcFPSWEyl+MfJjEUg/un1SMlQjaROOh3zYr+6JAwvcORiX7tyHosnmWC3d3wI2aPSQg==",
- "dependencies": {
- "@chakra-ui/react-context": "2.1.0",
- "@chakra-ui/react-use-safe-layout-effect": "2.1.0"
- },
- "peerDependencies": {
- "react": ">=18",
- "react-dom": ">=18"
- }
- },
- "node_modules/@chakra-ui/progress": {
- "version": "2.2.0",
- "resolved": "https://registry.npmjs.org/@chakra-ui/progress/-/progress-2.2.0.tgz",
- "integrity": "sha512-qUXuKbuhN60EzDD9mHR7B67D7p/ZqNS2Aze4Pbl1qGGZfulPW0PY8Rof32qDtttDQBkzQIzFGE8d9QpAemToIQ==",
- "dependencies": {
- "@chakra-ui/react-context": "2.1.0"
- },
- "peerDependencies": {
- "@chakra-ui/system": ">=2.0.0",
- "react": ">=18"
- }
- },
- "node_modules/@chakra-ui/provider": {
- "version": "2.4.2",
- "resolved": "https://registry.npmjs.org/@chakra-ui/provider/-/provider-2.4.2.tgz",
- "integrity": "sha512-w0Tef5ZCJK1mlJorcSjItCSbyvVuqpvyWdxZiVQmE6fvSJR83wZof42ux0+sfWD+I7rHSfj+f9nzhNaEWClysw==",
- "dependencies": {
- "@chakra-ui/css-reset": "2.3.0",
- "@chakra-ui/portal": "2.1.0",
- "@chakra-ui/react-env": "3.1.0",
- "@chakra-ui/system": "2.6.2",
- "@chakra-ui/utils": "2.0.15"
- },
- "peerDependencies": {
- "@emotion/react": "^11.0.0",
- "@emotion/styled": "^11.0.0",
- "react": ">=18",
- "react-dom": ">=18"
- }
- },
- "node_modules/@chakra-ui/radio": {
- "version": "2.1.2",
- "resolved": "https://registry.npmjs.org/@chakra-ui/radio/-/radio-2.1.2.tgz",
- "integrity": "sha512-n10M46wJrMGbonaghvSRnZ9ToTv/q76Szz284gv4QUWvyljQACcGrXIONUnQ3BIwbOfkRqSk7Xl/JgZtVfll+w==",
- "dependencies": {
- "@chakra-ui/form-control": "2.2.0",
- "@chakra-ui/react-context": "2.1.0",
- "@chakra-ui/react-types": "2.0.7",
- "@chakra-ui/react-use-merge-refs": "2.1.0",
- "@chakra-ui/shared-utils": "2.0.5",
- "@zag-js/focus-visible": "0.16.0"
- },
- "peerDependencies": {
- "@chakra-ui/system": ">=2.0.0",
- "react": ">=18"
- }
- },
- "node_modules/@chakra-ui/react": {
- "version": "2.8.2",
- "resolved": "https://registry.npmjs.org/@chakra-ui/react/-/react-2.8.2.tgz",
- "integrity": "sha512-Hn0moyxxyCDKuR9ywYpqgX8dvjqwu9ArwpIb9wHNYjnODETjLwazgNIliCVBRcJvysGRiV51U2/JtJVrpeCjUQ==",
- "dependencies": {
- "@chakra-ui/accordion": "2.3.1",
- "@chakra-ui/alert": "2.2.2",
- "@chakra-ui/avatar": "2.3.0",
- "@chakra-ui/breadcrumb": "2.2.0",
- "@chakra-ui/button": "2.1.0",
- "@chakra-ui/card": "2.2.0",
- "@chakra-ui/checkbox": "2.3.2",
- "@chakra-ui/close-button": "2.1.1",
- "@chakra-ui/control-box": "2.1.0",
- "@chakra-ui/counter": "2.1.0",
- "@chakra-ui/css-reset": "2.3.0",
- "@chakra-ui/editable": "3.1.0",
- "@chakra-ui/focus-lock": "2.1.0",
- "@chakra-ui/form-control": "2.2.0",
- "@chakra-ui/hooks": "2.2.1",
- "@chakra-ui/icon": "3.2.0",
- "@chakra-ui/image": "2.1.0",
- "@chakra-ui/input": "2.1.2",
- "@chakra-ui/layout": "2.3.1",
- "@chakra-ui/live-region": "2.1.0",
- "@chakra-ui/media-query": "3.3.0",
- "@chakra-ui/menu": "2.2.1",
- "@chakra-ui/modal": "2.3.1",
- "@chakra-ui/number-input": "2.1.2",
- "@chakra-ui/pin-input": "2.1.0",
- "@chakra-ui/popover": "2.2.1",
- "@chakra-ui/popper": "3.1.0",
- "@chakra-ui/portal": "2.1.0",
- "@chakra-ui/progress": "2.2.0",
- "@chakra-ui/provider": "2.4.2",
- "@chakra-ui/radio": "2.1.2",
- "@chakra-ui/react-env": "3.1.0",
- "@chakra-ui/select": "2.1.2",
- "@chakra-ui/skeleton": "2.1.0",
- "@chakra-ui/skip-nav": "2.1.0",
- "@chakra-ui/slider": "2.1.0",
- "@chakra-ui/spinner": "2.1.0",
- "@chakra-ui/stat": "2.1.1",
- "@chakra-ui/stepper": "2.3.1",
- "@chakra-ui/styled-system": "2.9.2",
- "@chakra-ui/switch": "2.1.2",
- "@chakra-ui/system": "2.6.2",
- "@chakra-ui/table": "2.1.0",
- "@chakra-ui/tabs": "3.0.0",
- "@chakra-ui/tag": "3.1.1",
- "@chakra-ui/textarea": "2.1.2",
- "@chakra-ui/theme": "3.3.1",
- "@chakra-ui/theme-utils": "2.0.21",
- "@chakra-ui/toast": "7.0.2",
- "@chakra-ui/tooltip": "2.3.1",
- "@chakra-ui/transition": "2.1.0",
- "@chakra-ui/utils": "2.0.15",
- "@chakra-ui/visually-hidden": "2.2.0"
- },
- "peerDependencies": {
- "@emotion/react": "^11.0.0",
- "@emotion/styled": "^11.0.0",
- "framer-motion": ">=4.0.0",
- "react": ">=18",
- "react-dom": ">=18"
- }
- },
- "node_modules/@chakra-ui/react-children-utils": {
- "version": "2.0.6",
- "resolved": "https://registry.npmjs.org/@chakra-ui/react-children-utils/-/react-children-utils-2.0.6.tgz",
- "integrity": "sha512-QVR2RC7QsOsbWwEnq9YduhpqSFnZGvjjGREV8ygKi8ADhXh93C8azLECCUVgRJF2Wc+So1fgxmjLcbZfY2VmBA==",
- "peerDependencies": {
- "react": ">=18"
- }
- },
- "node_modules/@chakra-ui/react-context": {
- "version": "2.1.0",
- "resolved": "https://registry.npmjs.org/@chakra-ui/react-context/-/react-context-2.1.0.tgz",
- "integrity": "sha512-iahyStvzQ4AOwKwdPReLGfDesGG+vWJfEsn0X/NoGph/SkN+HXtv2sCfYFFR9k7bb+Kvc6YfpLlSuLvKMHi2+w==",
- "peerDependencies": {
- "react": ">=18"
- }
- },
- "node_modules/@chakra-ui/react-env": {
- "version": "3.1.0",
- "resolved": "https://registry.npmjs.org/@chakra-ui/react-env/-/react-env-3.1.0.tgz",
- "integrity": "sha512-Vr96GV2LNBth3+IKzr/rq1IcnkXv+MLmwjQH6C8BRtn3sNskgDFD5vLkVXcEhagzZMCh8FR3V/bzZPojBOyNhw==",
- "dependencies": {
- "@chakra-ui/react-use-safe-layout-effect": "2.1.0"
- },
- "peerDependencies": {
- "react": ">=18"
- }
- },
- "node_modules/@chakra-ui/react-types": {
- "version": "2.0.7",
- "resolved": "https://registry.npmjs.org/@chakra-ui/react-types/-/react-types-2.0.7.tgz",
- "integrity": "sha512-12zv2qIZ8EHwiytggtGvo4iLT0APris7T0qaAWqzpUGS0cdUtR8W+V1BJ5Ocq+7tA6dzQ/7+w5hmXih61TuhWQ==",
- "peerDependencies": {
- "react": ">=18"
- }
- },
- "node_modules/@chakra-ui/react-use-animation-state": {
- "version": "2.1.0",
- "resolved": "https://registry.npmjs.org/@chakra-ui/react-use-animation-state/-/react-use-animation-state-2.1.0.tgz",
- "integrity": "sha512-CFZkQU3gmDBwhqy0vC1ryf90BVHxVN8cTLpSyCpdmExUEtSEInSCGMydj2fvn7QXsz/za8JNdO2xxgJwxpLMtg==",
- "dependencies": {
- "@chakra-ui/dom-utils": "2.1.0",
- "@chakra-ui/react-use-event-listener": "2.1.0"
- },
- "peerDependencies": {
- "react": ">=18"
- }
- },
- "node_modules/@chakra-ui/react-use-callback-ref": {
- "version": "2.1.0",
- "resolved": "https://registry.npmjs.org/@chakra-ui/react-use-callback-ref/-/react-use-callback-ref-2.1.0.tgz",
- "integrity": "sha512-efnJrBtGDa4YaxDzDE90EnKD3Vkh5a1t3w7PhnRQmsphLy3g2UieasoKTlT2Hn118TwDjIv5ZjHJW6HbzXA9wQ==",
- "peerDependencies": {
- "react": ">=18"
- }
- },
- "node_modules/@chakra-ui/react-use-controllable-state": {
- "version": "2.1.0",
- "resolved": "https://registry.npmjs.org/@chakra-ui/react-use-controllable-state/-/react-use-controllable-state-2.1.0.tgz",
- "integrity": "sha512-QR/8fKNokxZUs4PfxjXuwl0fj/d71WPrmLJvEpCTkHjnzu7LnYvzoe2wB867IdooQJL0G1zBxl0Dq+6W1P3jpg==",
- "dependencies": {
- "@chakra-ui/react-use-callback-ref": "2.1.0"
- },
- "peerDependencies": {
- "react": ">=18"
- }
- },
- "node_modules/@chakra-ui/react-use-disclosure": {
- "version": "2.1.0",
- "resolved": "https://registry.npmjs.org/@chakra-ui/react-use-disclosure/-/react-use-disclosure-2.1.0.tgz",
- "integrity": "sha512-Ax4pmxA9LBGMyEZJhhUZobg9C0t3qFE4jVF1tGBsrLDcdBeLR9fwOogIPY9Hf0/wqSlAryAimICbr5hkpa5GSw==",
- "dependencies": {
- "@chakra-ui/react-use-callback-ref": "2.1.0"
- },
- "peerDependencies": {
- "react": ">=18"
- }
- },
- "node_modules/@chakra-ui/react-use-event-listener": {
- "version": "2.1.0",
- "resolved": "https://registry.npmjs.org/@chakra-ui/react-use-event-listener/-/react-use-event-listener-2.1.0.tgz",
- "integrity": "sha512-U5greryDLS8ISP69DKDsYcsXRtAdnTQT+jjIlRYZ49K/XhUR/AqVZCK5BkR1spTDmO9H8SPhgeNKI70ODuDU/Q==",
- "dependencies": {
- "@chakra-ui/react-use-callback-ref": "2.1.0"
- },
- "peerDependencies": {
- "react": ">=18"
- }
- },
- "node_modules/@chakra-ui/react-use-focus-effect": {
- "version": "2.1.0",
- "resolved": "https://registry.npmjs.org/@chakra-ui/react-use-focus-effect/-/react-use-focus-effect-2.1.0.tgz",
- "integrity": "sha512-xzVboNy7J64xveLcxTIJ3jv+lUJKDwRM7Szwn9tNzUIPD94O3qwjV7DDCUzN2490nSYDF4OBMt/wuDBtaR3kUQ==",
- "dependencies": {
- "@chakra-ui/dom-utils": "2.1.0",
- "@chakra-ui/react-use-event-listener": "2.1.0",
- "@chakra-ui/react-use-safe-layout-effect": "2.1.0",
- "@chakra-ui/react-use-update-effect": "2.1.0"
- },
- "peerDependencies": {
- "react": ">=18"
- }
- },
- "node_modules/@chakra-ui/react-use-focus-on-pointer-down": {
- "version": "2.1.0",
- "resolved": "https://registry.npmjs.org/@chakra-ui/react-use-focus-on-pointer-down/-/react-use-focus-on-pointer-down-2.1.0.tgz",
- "integrity": "sha512-2jzrUZ+aiCG/cfanrolsnSMDykCAbv9EK/4iUyZno6BYb3vziucmvgKuoXbMPAzWNtwUwtuMhkby8rc61Ue+Lg==",
- "dependencies": {
- "@chakra-ui/react-use-event-listener": "2.1.0"
- },
- "peerDependencies": {
- "react": ">=18"
- }
- },
- "node_modules/@chakra-ui/react-use-interval": {
- "version": "2.1.0",
- "resolved": "https://registry.npmjs.org/@chakra-ui/react-use-interval/-/react-use-interval-2.1.0.tgz",
- "integrity": "sha512-8iWj+I/+A0J08pgEXP1J1flcvhLBHkk0ln7ZvGIyXiEyM6XagOTJpwNhiu+Bmk59t3HoV/VyvyJTa+44sEApuw==",
- "dependencies": {
- "@chakra-ui/react-use-callback-ref": "2.1.0"
- },
- "peerDependencies": {
- "react": ">=18"
- }
- },
- "node_modules/@chakra-ui/react-use-latest-ref": {
- "version": "2.1.0",
- "resolved": "https://registry.npmjs.org/@chakra-ui/react-use-latest-ref/-/react-use-latest-ref-2.1.0.tgz",
- "integrity": "sha512-m0kxuIYqoYB0va9Z2aW4xP/5b7BzlDeWwyXCH6QpT2PpW3/281L3hLCm1G0eOUcdVlayqrQqOeD6Mglq+5/xoQ==",
- "peerDependencies": {
- "react": ">=18"
- }
- },
- "node_modules/@chakra-ui/react-use-merge-refs": {
- "version": "2.1.0",
- "resolved": "https://registry.npmjs.org/@chakra-ui/react-use-merge-refs/-/react-use-merge-refs-2.1.0.tgz",
- "integrity": "sha512-lERa6AWF1cjEtWSGjxWTaSMvneccnAVH4V4ozh8SYiN9fSPZLlSG3kNxfNzdFvMEhM7dnP60vynF7WjGdTgQbQ==",
- "peerDependencies": {
- "react": ">=18"
- }
- },
- "node_modules/@chakra-ui/react-use-outside-click": {
- "version": "2.2.0",
- "resolved": "https://registry.npmjs.org/@chakra-ui/react-use-outside-click/-/react-use-outside-click-2.2.0.tgz",
- "integrity": "sha512-PNX+s/JEaMneijbgAM4iFL+f3m1ga9+6QK0E5Yh4s8KZJQ/bLwZzdhMz8J/+mL+XEXQ5J0N8ivZN28B82N1kNw==",
- "dependencies": {
- "@chakra-ui/react-use-callback-ref": "2.1.0"
- },
- "peerDependencies": {
- "react": ">=18"
- }
- },
- "node_modules/@chakra-ui/react-use-pan-event": {
- "version": "2.1.0",
- "resolved": "https://registry.npmjs.org/@chakra-ui/react-use-pan-event/-/react-use-pan-event-2.1.0.tgz",
- "integrity": "sha512-xmL2qOHiXqfcj0q7ZK5s9UjTh4Gz0/gL9jcWPA6GVf+A0Od5imEDa/Vz+533yQKWiNSm1QGrIj0eJAokc7O4fg==",
- "dependencies": {
- "@chakra-ui/event-utils": "2.0.8",
- "@chakra-ui/react-use-latest-ref": "2.1.0",
- "framesync": "6.1.2"
- },
- "peerDependencies": {
- "react": ">=18"
- }
- },
- "node_modules/@chakra-ui/react-use-previous": {
- "version": "2.1.0",
- "resolved": "https://registry.npmjs.org/@chakra-ui/react-use-previous/-/react-use-previous-2.1.0.tgz",
- "integrity": "sha512-pjxGwue1hX8AFcmjZ2XfrQtIJgqbTF3Qs1Dy3d1krC77dEsiCUbQ9GzOBfDc8pfd60DrB5N2tg5JyHbypqh0Sg==",
- "peerDependencies": {
- "react": ">=18"
- }
- },
- "node_modules/@chakra-ui/react-use-safe-layout-effect": {
- "version": "2.1.0",
- "resolved": "https://registry.npmjs.org/@chakra-ui/react-use-safe-layout-effect/-/react-use-safe-layout-effect-2.1.0.tgz",
- "integrity": "sha512-Knbrrx/bcPwVS1TorFdzrK/zWA8yuU/eaXDkNj24IrKoRlQrSBFarcgAEzlCHtzuhufP3OULPkELTzz91b0tCw==",
- "peerDependencies": {
- "react": ">=18"
- }
- },
- "node_modules/@chakra-ui/react-use-size": {
- "version": "2.1.0",
- "resolved": "https://registry.npmjs.org/@chakra-ui/react-use-size/-/react-use-size-2.1.0.tgz",
- "integrity": "sha512-tbLqrQhbnqOjzTaMlYytp7wY8BW1JpL78iG7Ru1DlV4EWGiAmXFGvtnEt9HftU0NJ0aJyjgymkxfVGI55/1Z4A==",
- "dependencies": {
- "@zag-js/element-size": "0.10.5"
- },
- "peerDependencies": {
- "react": ">=18"
- }
- },
- "node_modules/@chakra-ui/react-use-timeout": {
- "version": "2.1.0",
- "resolved": "https://registry.npmjs.org/@chakra-ui/react-use-timeout/-/react-use-timeout-2.1.0.tgz",
- "integrity": "sha512-cFN0sobKMM9hXUhyCofx3/Mjlzah6ADaEl/AXl5Y+GawB5rgedgAcu2ErAgarEkwvsKdP6c68CKjQ9dmTQlJxQ==",
- "dependencies": {
- "@chakra-ui/react-use-callback-ref": "2.1.0"
- },
- "peerDependencies": {
- "react": ">=18"
- }
- },
- "node_modules/@chakra-ui/react-use-update-effect": {
- "version": "2.1.0",
- "resolved": "https://registry.npmjs.org/@chakra-ui/react-use-update-effect/-/react-use-update-effect-2.1.0.tgz",
- "integrity": "sha512-ND4Q23tETaR2Qd3zwCKYOOS1dfssojPLJMLvUtUbW5M9uW1ejYWgGUobeAiOVfSplownG8QYMmHTP86p/v0lbA==",
- "peerDependencies": {
- "react": ">=18"
- }
- },
- "node_modules/@chakra-ui/react-utils": {
- "version": "2.0.12",
- "resolved": "https://registry.npmjs.org/@chakra-ui/react-utils/-/react-utils-2.0.12.tgz",
- "integrity": "sha512-GbSfVb283+YA3kA8w8xWmzbjNWk14uhNpntnipHCftBibl0lxtQ9YqMFQLwuFOO0U2gYVocszqqDWX+XNKq9hw==",
- "dependencies": {
- "@chakra-ui/utils": "2.0.15"
- },
- "peerDependencies": {
- "react": ">=18"
- }
- },
- "node_modules/@chakra-ui/select": {
- "version": "2.1.2",
- "resolved": "https://registry.npmjs.org/@chakra-ui/select/-/select-2.1.2.tgz",
- "integrity": "sha512-ZwCb7LqKCVLJhru3DXvKXpZ7Pbu1TDZ7N0PdQ0Zj1oyVLJyrpef1u9HR5u0amOpqcH++Ugt0f5JSmirjNlctjA==",
- "dependencies": {
- "@chakra-ui/form-control": "2.2.0",
- "@chakra-ui/shared-utils": "2.0.5"
- },
- "peerDependencies": {
- "@chakra-ui/system": ">=2.0.0",
- "react": ">=18"
- }
- },
- "node_modules/@chakra-ui/shared-utils": {
- "version": "2.0.5",
- "resolved": "https://registry.npmjs.org/@chakra-ui/shared-utils/-/shared-utils-2.0.5.tgz",
- "integrity": "sha512-4/Wur0FqDov7Y0nCXl7HbHzCg4aq86h+SXdoUeuCMD3dSj7dpsVnStLYhng1vxvlbUnLpdF4oz5Myt3i/a7N3Q=="
- },
- "node_modules/@chakra-ui/skeleton": {
- "version": "2.1.0",
- "resolved": "https://registry.npmjs.org/@chakra-ui/skeleton/-/skeleton-2.1.0.tgz",
- "integrity": "sha512-JNRuMPpdZGd6zFVKjVQ0iusu3tXAdI29n4ZENYwAJEMf/fN0l12sVeirOxkJ7oEL0yOx2AgEYFSKdbcAgfUsAQ==",
- "dependencies": {
- "@chakra-ui/media-query": "3.3.0",
- "@chakra-ui/react-use-previous": "2.1.0",
- "@chakra-ui/shared-utils": "2.0.5"
- },
- "peerDependencies": {
- "@chakra-ui/system": ">=2.0.0",
- "react": ">=18"
- }
- },
- "node_modules/@chakra-ui/skip-nav": {
- "version": "2.1.0",
- "resolved": "https://registry.npmjs.org/@chakra-ui/skip-nav/-/skip-nav-2.1.0.tgz",
- "integrity": "sha512-Hk+FG+vadBSH0/7hwp9LJnLjkO0RPGnx7gBJWI4/SpoJf3e4tZlWYtwGj0toYY4aGKl93jVghuwGbDBEMoHDug==",
- "peerDependencies": {
- "@chakra-ui/system": ">=2.0.0",
- "react": ">=18"
- }
- },
- "node_modules/@chakra-ui/slider": {
- "version": "2.1.0",
- "resolved": "https://registry.npmjs.org/@chakra-ui/slider/-/slider-2.1.0.tgz",
- "integrity": "sha512-lUOBcLMCnFZiA/s2NONXhELJh6sY5WtbRykPtclGfynqqOo47lwWJx+VP7xaeuhDOPcWSSecWc9Y1BfPOCz9cQ==",
- "dependencies": {
- "@chakra-ui/number-utils": "2.0.7",
- "@chakra-ui/react-context": "2.1.0",
- "@chakra-ui/react-types": "2.0.7",
- "@chakra-ui/react-use-callback-ref": "2.1.0",
- "@chakra-ui/react-use-controllable-state": "2.1.0",
- "@chakra-ui/react-use-latest-ref": "2.1.0",
- "@chakra-ui/react-use-merge-refs": "2.1.0",
- "@chakra-ui/react-use-pan-event": "2.1.0",
- "@chakra-ui/react-use-size": "2.1.0",
- "@chakra-ui/react-use-update-effect": "2.1.0"
- },
- "peerDependencies": {
- "@chakra-ui/system": ">=2.0.0",
- "react": ">=18"
- }
- },
- "node_modules/@chakra-ui/spinner": {
- "version": "2.1.0",
- "resolved": "https://registry.npmjs.org/@chakra-ui/spinner/-/spinner-2.1.0.tgz",
- "integrity": "sha512-hczbnoXt+MMv/d3gE+hjQhmkzLiKuoTo42YhUG7Bs9OSv2lg1fZHW1fGNRFP3wTi6OIbD044U1P9HK+AOgFH3g==",
- "dependencies": {
- "@chakra-ui/shared-utils": "2.0.5"
- },
- "peerDependencies": {
- "@chakra-ui/system": ">=2.0.0",
- "react": ">=18"
- }
- },
- "node_modules/@chakra-ui/stat": {
- "version": "2.1.1",
- "resolved": "https://registry.npmjs.org/@chakra-ui/stat/-/stat-2.1.1.tgz",
- "integrity": "sha512-LDn0d/LXQNbAn2KaR3F1zivsZCewY4Jsy1qShmfBMKwn6rI8yVlbvu6SiA3OpHS0FhxbsZxQI6HefEoIgtqY6Q==",
- "dependencies": {
- "@chakra-ui/icon": "3.2.0",
- "@chakra-ui/react-context": "2.1.0",
- "@chakra-ui/shared-utils": "2.0.5"
- },
- "peerDependencies": {
- "@chakra-ui/system": ">=2.0.0",
- "react": ">=18"
- }
- },
- "node_modules/@chakra-ui/stepper": {
- "version": "2.3.1",
- "resolved": "https://registry.npmjs.org/@chakra-ui/stepper/-/stepper-2.3.1.tgz",
- "integrity": "sha512-ky77lZbW60zYkSXhYz7kbItUpAQfEdycT0Q4bkHLxfqbuiGMf8OmgZOQkOB9uM4v0zPwy2HXhe0vq4Dd0xa55Q==",
- "dependencies": {
- "@chakra-ui/icon": "3.2.0",
- "@chakra-ui/react-context": "2.1.0",
- "@chakra-ui/shared-utils": "2.0.5"
- },
- "peerDependencies": {
- "@chakra-ui/system": ">=2.0.0",
- "react": ">=18"
- }
- },
- "node_modules/@chakra-ui/styled-system": {
- "version": "2.9.2",
- "resolved": "https://registry.npmjs.org/@chakra-ui/styled-system/-/styled-system-2.9.2.tgz",
- "integrity": "sha512-To/Z92oHpIE+4nk11uVMWqo2GGRS86coeMmjxtpnErmWRdLcp1WVCVRAvn+ZwpLiNR+reWFr2FFqJRsREuZdAg==",
- "dependencies": {
- "@chakra-ui/shared-utils": "2.0.5",
- "csstype": "^3.1.2",
- "lodash.mergewith": "4.6.2"
- }
- },
- "node_modules/@chakra-ui/switch": {
- "version": "2.1.2",
- "resolved": "https://registry.npmjs.org/@chakra-ui/switch/-/switch-2.1.2.tgz",
- "integrity": "sha512-pgmi/CC+E1v31FcnQhsSGjJnOE2OcND4cKPyTE+0F+bmGm48Q/b5UmKD9Y+CmZsrt/7V3h8KNczowupfuBfIHA==",
- "dependencies": {
- "@chakra-ui/checkbox": "2.3.2",
- "@chakra-ui/shared-utils": "2.0.5"
- },
- "peerDependencies": {
- "@chakra-ui/system": ">=2.0.0",
- "framer-motion": ">=4.0.0",
- "react": ">=18"
- }
- },
- "node_modules/@chakra-ui/system": {
- "version": "2.6.2",
- "resolved": "https://registry.npmjs.org/@chakra-ui/system/-/system-2.6.2.tgz",
- "integrity": "sha512-EGtpoEjLrUu4W1fHD+a62XR+hzC5YfsWm+6lO0Kybcga3yYEij9beegO0jZgug27V+Rf7vns95VPVP6mFd/DEQ==",
- "dependencies": {
- "@chakra-ui/color-mode": "2.2.0",
- "@chakra-ui/object-utils": "2.1.0",
- "@chakra-ui/react-utils": "2.0.12",
- "@chakra-ui/styled-system": "2.9.2",
- "@chakra-ui/theme-utils": "2.0.21",
- "@chakra-ui/utils": "2.0.15",
- "react-fast-compare": "3.2.2"
- },
- "peerDependencies": {
- "@emotion/react": "^11.0.0",
- "@emotion/styled": "^11.0.0",
- "react": ">=18"
- }
- },
- "node_modules/@chakra-ui/table": {
- "version": "2.1.0",
- "resolved": "https://registry.npmjs.org/@chakra-ui/table/-/table-2.1.0.tgz",
- "integrity": "sha512-o5OrjoHCh5uCLdiUb0Oc0vq9rIAeHSIRScc2ExTC9Qg/uVZl2ygLrjToCaKfaaKl1oQexIeAcZDKvPG8tVkHyQ==",
- "dependencies": {
- "@chakra-ui/react-context": "2.1.0",
- "@chakra-ui/shared-utils": "2.0.5"
- },
- "peerDependencies": {
- "@chakra-ui/system": ">=2.0.0",
- "react": ">=18"
- }
- },
- "node_modules/@chakra-ui/tabs": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/@chakra-ui/tabs/-/tabs-3.0.0.tgz",
- "integrity": "sha512-6Mlclp8L9lqXmsGWF5q5gmemZXOiOYuh0SGT/7PgJVNPz3LXREXlXg2an4MBUD8W5oTkduCX+3KTMCwRrVrDYw==",
- "dependencies": {
- "@chakra-ui/clickable": "2.1.0",
- "@chakra-ui/descendant": "3.1.0",
- "@chakra-ui/lazy-utils": "2.0.5",
- "@chakra-ui/react-children-utils": "2.0.6",
- "@chakra-ui/react-context": "2.1.0",
- "@chakra-ui/react-use-controllable-state": "2.1.0",
- "@chakra-ui/react-use-merge-refs": "2.1.0",
- "@chakra-ui/react-use-safe-layout-effect": "2.1.0",
- "@chakra-ui/shared-utils": "2.0.5"
- },
- "peerDependencies": {
- "@chakra-ui/system": ">=2.0.0",
- "react": ">=18"
- }
- },
- "node_modules/@chakra-ui/tag": {
- "version": "3.1.1",
- "resolved": "https://registry.npmjs.org/@chakra-ui/tag/-/tag-3.1.1.tgz",
- "integrity": "sha512-Bdel79Dv86Hnge2PKOU+t8H28nm/7Y3cKd4Kfk9k3lOpUh4+nkSGe58dhRzht59lEqa4N9waCgQiBdkydjvBXQ==",
- "dependencies": {
- "@chakra-ui/icon": "3.2.0",
- "@chakra-ui/react-context": "2.1.0"
- },
- "peerDependencies": {
- "@chakra-ui/system": ">=2.0.0",
- "react": ">=18"
- }
- },
- "node_modules/@chakra-ui/textarea": {
- "version": "2.1.2",
- "resolved": "https://registry.npmjs.org/@chakra-ui/textarea/-/textarea-2.1.2.tgz",
- "integrity": "sha512-ip7tvklVCZUb2fOHDb23qPy/Fr2mzDOGdkrpbNi50hDCiV4hFX02jdQJdi3ydHZUyVgZVBKPOJ+lT9i7sKA2wA==",
- "dependencies": {
- "@chakra-ui/form-control": "2.2.0",
- "@chakra-ui/shared-utils": "2.0.5"
- },
- "peerDependencies": {
- "@chakra-ui/system": ">=2.0.0",
- "react": ">=18"
- }
- },
- "node_modules/@chakra-ui/theme": {
- "version": "3.3.1",
- "resolved": "https://registry.npmjs.org/@chakra-ui/theme/-/theme-3.3.1.tgz",
- "integrity": "sha512-Hft/VaT8GYnItGCBbgWd75ICrIrIFrR7lVOhV/dQnqtfGqsVDlrztbSErvMkoPKt0UgAkd9/o44jmZ6X4U2nZQ==",
- "dependencies": {
- "@chakra-ui/anatomy": "2.2.2",
- "@chakra-ui/shared-utils": "2.0.5",
- "@chakra-ui/theme-tools": "2.1.2"
- },
- "peerDependencies": {
- "@chakra-ui/styled-system": ">=2.8.0"
- }
- },
- "node_modules/@chakra-ui/theme-tools": {
- "version": "2.1.2",
- "resolved": "https://registry.npmjs.org/@chakra-ui/theme-tools/-/theme-tools-2.1.2.tgz",
- "integrity": "sha512-Qdj8ajF9kxY4gLrq7gA+Azp8CtFHGO9tWMN2wfF9aQNgG9AuMhPrUzMq9AMQ0MXiYcgNq/FD3eegB43nHVmXVA==",
- "dependencies": {
- "@chakra-ui/anatomy": "2.2.2",
- "@chakra-ui/shared-utils": "2.0.5",
- "color2k": "^2.0.2"
- },
- "peerDependencies": {
- "@chakra-ui/styled-system": ">=2.0.0"
- }
- },
- "node_modules/@chakra-ui/theme-utils": {
- "version": "2.0.21",
- "resolved": "https://registry.npmjs.org/@chakra-ui/theme-utils/-/theme-utils-2.0.21.tgz",
- "integrity": "sha512-FjH5LJbT794r0+VSCXB3lT4aubI24bLLRWB+CuRKHijRvsOg717bRdUN/N1fEmEpFnRVrbewttWh/OQs0EWpWw==",
- "dependencies": {
- "@chakra-ui/shared-utils": "2.0.5",
- "@chakra-ui/styled-system": "2.9.2",
- "@chakra-ui/theme": "3.3.1",
- "lodash.mergewith": "4.6.2"
- }
- },
- "node_modules/@chakra-ui/toast": {
- "version": "7.0.2",
- "resolved": "https://registry.npmjs.org/@chakra-ui/toast/-/toast-7.0.2.tgz",
- "integrity": "sha512-yvRP8jFKRs/YnkuE41BVTq9nB2v/KDRmje9u6dgDmE5+1bFt3bwjdf9gVbif4u5Ve7F7BGk5E093ARRVtvLvXA==",
- "dependencies": {
- "@chakra-ui/alert": "2.2.2",
- "@chakra-ui/close-button": "2.1.1",
- "@chakra-ui/portal": "2.1.0",
- "@chakra-ui/react-context": "2.1.0",
- "@chakra-ui/react-use-timeout": "2.1.0",
- "@chakra-ui/react-use-update-effect": "2.1.0",
- "@chakra-ui/shared-utils": "2.0.5",
- "@chakra-ui/styled-system": "2.9.2",
- "@chakra-ui/theme": "3.3.1"
- },
- "peerDependencies": {
- "@chakra-ui/system": "2.6.2",
- "framer-motion": ">=4.0.0",
- "react": ">=18",
- "react-dom": ">=18"
- }
- },
- "node_modules/@chakra-ui/tooltip": {
- "version": "2.3.1",
- "resolved": "https://registry.npmjs.org/@chakra-ui/tooltip/-/tooltip-2.3.1.tgz",
- "integrity": "sha512-Rh39GBn/bL4kZpuEMPPRwYNnccRCL+w9OqamWHIB3Qboxs6h8cOyXfIdGxjo72lvhu1QI/a4KFqkM3St+WfC0A==",
- "dependencies": {
- "@chakra-ui/dom-utils": "2.1.0",
- "@chakra-ui/popper": "3.1.0",
- "@chakra-ui/portal": "2.1.0",
- "@chakra-ui/react-types": "2.0.7",
- "@chakra-ui/react-use-disclosure": "2.1.0",
- "@chakra-ui/react-use-event-listener": "2.1.0",
- "@chakra-ui/react-use-merge-refs": "2.1.0",
- "@chakra-ui/shared-utils": "2.0.5"
- },
- "peerDependencies": {
- "@chakra-ui/system": ">=2.0.0",
- "framer-motion": ">=4.0.0",
- "react": ">=18",
- "react-dom": ">=18"
- }
- },
- "node_modules/@chakra-ui/transition": {
- "version": "2.1.0",
- "resolved": "https://registry.npmjs.org/@chakra-ui/transition/-/transition-2.1.0.tgz",
- "integrity": "sha512-orkT6T/Dt+/+kVwJNy7zwJ+U2xAZ3EU7M3XCs45RBvUnZDr/u9vdmaM/3D/rOpmQJWgQBwKPJleUXrYWUagEDQ==",
- "dependencies": {
- "@chakra-ui/shared-utils": "2.0.5"
- },
- "peerDependencies": {
- "framer-motion": ">=4.0.0",
- "react": ">=18"
- }
- },
- "node_modules/@chakra-ui/utils": {
- "version": "2.0.15",
- "resolved": "https://registry.npmjs.org/@chakra-ui/utils/-/utils-2.0.15.tgz",
- "integrity": "sha512-El4+jL0WSaYYs+rJbuYFDbjmfCcfGDmRY95GO4xwzit6YAPZBLcR65rOEwLps+XWluZTy1xdMrusg/hW0c1aAA==",
- "dependencies": {
- "@types/lodash.mergewith": "4.6.7",
- "css-box-model": "1.2.1",
- "framesync": "6.1.2",
- "lodash.mergewith": "4.6.2"
- }
- },
- "node_modules/@chakra-ui/visually-hidden": {
- "version": "2.2.0",
- "resolved": "https://registry.npmjs.org/@chakra-ui/visually-hidden/-/visually-hidden-2.2.0.tgz",
- "integrity": "sha512-KmKDg01SrQ7VbTD3+cPWf/UfpF5MSwm3v7MWi0n5t8HnnadT13MF0MJCDSXbBWnzLv1ZKJ6zlyAOeARWX+DpjQ==",
- "peerDependencies": {
- "@chakra-ui/system": ">=2.0.0",
- "react": ">=18"
- }
- },
- "node_modules/@emotion/babel-plugin": {
- "version": "11.11.0",
- "resolved": "https://registry.npmjs.org/@emotion/babel-plugin/-/babel-plugin-11.11.0.tgz",
- "integrity": "sha512-m4HEDZleaaCH+XgDDsPF15Ht6wTLsgDTeR3WYj9Q/k76JtWhrJjcP4+/XlG8LGT/Rol9qUfOIztXeA84ATpqPQ==",
- "dependencies": {
- "@babel/helper-module-imports": "^7.16.7",
- "@babel/runtime": "^7.18.3",
- "@emotion/hash": "^0.9.1",
- "@emotion/memoize": "^0.8.1",
- "@emotion/serialize": "^1.1.2",
- "babel-plugin-macros": "^3.1.0",
- "convert-source-map": "^1.5.0",
- "escape-string-regexp": "^4.0.0",
- "find-root": "^1.1.0",
- "source-map": "^0.5.7",
- "stylis": "4.2.0"
- }
- },
- "node_modules/@emotion/babel-plugin/node_modules/source-map": {
- "version": "0.5.7",
- "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz",
- "integrity": "sha512-LbrmJOMUSdEVxIKvdcJzQC+nQhe8FUZQTXQy6+I75skNgn3OoQ0DZA8YnFa7gp8tqtL3KPf1kmo0R5DoApeSGQ==",
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/@emotion/cache": {
- "version": "11.11.0",
- "resolved": "https://registry.npmjs.org/@emotion/cache/-/cache-11.11.0.tgz",
- "integrity": "sha512-P34z9ssTCBi3e9EI1ZsWpNHcfY1r09ZO0rZbRO2ob3ZQMnFI35jB536qoXbkdesr5EUhYi22anuEJuyxifaqAQ==",
- "dependencies": {
- "@emotion/memoize": "^0.8.1",
- "@emotion/sheet": "^1.2.2",
- "@emotion/utils": "^1.2.1",
- "@emotion/weak-memoize": "^0.3.1",
- "stylis": "4.2.0"
- }
- },
- "node_modules/@emotion/hash": {
- "version": "0.9.1",
- "resolved": "https://registry.npmjs.org/@emotion/hash/-/hash-0.9.1.tgz",
- "integrity": "sha512-gJB6HLm5rYwSLI6PQa+X1t5CFGrv1J1TWG+sOyMCeKz2ojaj6Fnl/rZEspogG+cvqbt4AE/2eIyD2QfLKTBNlQ=="
- },
- "node_modules/@emotion/is-prop-valid": {
- "version": "1.2.1",
- "resolved": "https://registry.npmjs.org/@emotion/is-prop-valid/-/is-prop-valid-1.2.1.tgz",
- "integrity": "sha512-61Mf7Ufx4aDxx1xlDeOm8aFFigGHE4z+0sKCa+IHCeZKiyP9RLD0Mmx7m8b9/Cf37f7NAvQOOJAbQQGVr5uERw==",
- "dependencies": {
- "@emotion/memoize": "^0.8.1"
- }
- },
- "node_modules/@emotion/memoize": {
- "version": "0.8.1",
- "resolved": "https://registry.npmjs.org/@emotion/memoize/-/memoize-0.8.1.tgz",
- "integrity": "sha512-W2P2c/VRW1/1tLox0mVUalvnWXxavmv/Oum2aPsRcoDJuob75FC3Y8FbpfLwUegRcxINtGUMPq0tFCvYNTBXNA=="
- },
- "node_modules/@emotion/react": {
- "version": "11.11.3",
- "resolved": "https://registry.npmjs.org/@emotion/react/-/react-11.11.3.tgz",
- "integrity": "sha512-Cnn0kuq4DoONOMcnoVsTOR8E+AdnKFf//6kUWc4LCdnxj31pZWn7rIULd6Y7/Js1PiPHzn7SKCM9vB/jBni8eA==",
- "dependencies": {
- "@babel/runtime": "^7.18.3",
- "@emotion/babel-plugin": "^11.11.0",
- "@emotion/cache": "^11.11.0",
- "@emotion/serialize": "^1.1.3",
- "@emotion/use-insertion-effect-with-fallbacks": "^1.0.1",
- "@emotion/utils": "^1.2.1",
- "@emotion/weak-memoize": "^0.3.1",
- "hoist-non-react-statics": "^3.3.1"
- },
- "peerDependencies": {
- "react": ">=16.8.0"
- },
- "peerDependenciesMeta": {
- "@types/react": {
- "optional": true
- }
- }
- },
- "node_modules/@emotion/serialize": {
- "version": "1.1.3",
- "resolved": "https://registry.npmjs.org/@emotion/serialize/-/serialize-1.1.3.tgz",
- "integrity": "sha512-iD4D6QVZFDhcbH0RAG1uVu1CwVLMWUkCvAqqlewO/rxf8+87yIBAlt4+AxMiiKPLs5hFc0owNk/sLLAOROw3cA==",
- "dependencies": {
- "@emotion/hash": "^0.9.1",
- "@emotion/memoize": "^0.8.1",
- "@emotion/unitless": "^0.8.1",
- "@emotion/utils": "^1.2.1",
- "csstype": "^3.0.2"
- }
- },
- "node_modules/@emotion/sheet": {
- "version": "1.2.2",
- "resolved": "https://registry.npmjs.org/@emotion/sheet/-/sheet-1.2.2.tgz",
- "integrity": "sha512-0QBtGvaqtWi+nx6doRwDdBIzhNdZrXUppvTM4dtZZWEGTXL/XE/yJxLMGlDT1Gt+UHH5IX1n+jkXyytE/av7OA=="
- },
- "node_modules/@emotion/styled": {
- "version": "11.11.0",
- "resolved": "https://registry.npmjs.org/@emotion/styled/-/styled-11.11.0.tgz",
- "integrity": "sha512-hM5Nnvu9P3midq5aaXj4I+lnSfNi7Pmd4EWk1fOZ3pxookaQTNew6bp4JaCBYM4HVFZF9g7UjJmsUmC2JlxOng==",
- "dependencies": {
- "@babel/runtime": "^7.18.3",
- "@emotion/babel-plugin": "^11.11.0",
- "@emotion/is-prop-valid": "^1.2.1",
- "@emotion/serialize": "^1.1.2",
- "@emotion/use-insertion-effect-with-fallbacks": "^1.0.1",
- "@emotion/utils": "^1.2.1"
- },
- "peerDependencies": {
- "@emotion/react": "^11.0.0-rc.0",
- "react": ">=16.8.0"
- },
- "peerDependenciesMeta": {
- "@types/react": {
- "optional": true
- }
- }
- },
- "node_modules/@emotion/unitless": {
- "version": "0.8.1",
- "resolved": "https://registry.npmjs.org/@emotion/unitless/-/unitless-0.8.1.tgz",
- "integrity": "sha512-KOEGMu6dmJZtpadb476IsZBclKvILjopjUii3V+7MnXIQCYh8W3NgNcgwo21n9LXZX6EDIKvqfjYxXebDwxKmQ=="
- },
- "node_modules/@emotion/use-insertion-effect-with-fallbacks": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/@emotion/use-insertion-effect-with-fallbacks/-/use-insertion-effect-with-fallbacks-1.0.1.tgz",
- "integrity": "sha512-jT/qyKZ9rzLErtrjGgdkMBn2OP8wl0G3sQlBb3YPryvKHsjvINUhVaPFfP+fpBcOkmrVOVEEHQFJ7nbj2TH2gw==",
- "peerDependencies": {
- "react": ">=16.8.0"
- }
- },
- "node_modules/@emotion/utils": {
- "version": "1.2.1",
- "resolved": "https://registry.npmjs.org/@emotion/utils/-/utils-1.2.1.tgz",
- "integrity": "sha512-Y2tGf3I+XVnajdItskUCn6LX+VUDmP6lTL4fcqsXAv43dnlbZiuW4MWQW38rW/BVWSE7Q/7+XQocmpnRYILUmg=="
- },
- "node_modules/@emotion/weak-memoize": {
- "version": "0.3.1",
- "resolved": "https://registry.npmjs.org/@emotion/weak-memoize/-/weak-memoize-0.3.1.tgz",
- "integrity": "sha512-EsBwpc7hBUJWAsNPBmJy4hxWx12v6bshQsldrVmjxJoc3isbxhOrF2IcCpaXxfvq03NwkI7sbsOLXbYuqF/8Ww=="
- },
- "node_modules/@esbuild/android-arm": {
- "version": "0.19.8",
- "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.19.8.tgz",
- "integrity": "sha512-31E2lxlGM1KEfivQl8Yf5aYU/mflz9g06H6S15ITUFQueMFtFjESRMoDSkvMo8thYvLBax+VKTPlpnx+sPicOA==",
- "cpu": [
- "arm"
- ],
- "dev": true,
- "optional": true,
- "os": [
- "android"
- ],
- "engines": {
- "node": ">=12"
- }
- },
- "node_modules/@esbuild/android-arm64": {
- "version": "0.19.8",
- "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.19.8.tgz",
- "integrity": "sha512-B8JbS61bEunhfx8kasogFENgQfr/dIp+ggYXwTqdbMAgGDhRa3AaPpQMuQU0rNxDLECj6FhDzk1cF9WHMVwrtA==",
- "cpu": [
- "arm64"
- ],
- "dev": true,
- "optional": true,
- "os": [
- "android"
- ],
- "engines": {
- "node": ">=12"
- }
- },
- "node_modules/@esbuild/android-x64": {
- "version": "0.19.8",
- "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.19.8.tgz",
- "integrity": "sha512-rdqqYfRIn4jWOp+lzQttYMa2Xar3OK9Yt2fhOhzFXqg0rVWEfSclJvZq5fZslnz6ypHvVf3CT7qyf0A5pM682A==",
- "cpu": [
- "x64"
- ],
- "dev": true,
- "optional": true,
- "os": [
- "android"
- ],
- "engines": {
- "node": ">=12"
- }
- },
- "node_modules/@esbuild/darwin-arm64": {
- "version": "0.19.8",
- "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.19.8.tgz",
- "integrity": "sha512-RQw9DemMbIq35Bprbboyf8SmOr4UXsRVxJ97LgB55VKKeJOOdvsIPy0nFyF2l8U+h4PtBx/1kRf0BelOYCiQcw==",
- "cpu": [
- "arm64"
- ],
- "dev": true,
- "optional": true,
- "os": [
- "darwin"
- ],
- "engines": {
- "node": ">=12"
- }
- },
- "node_modules/@esbuild/darwin-x64": {
- "version": "0.19.8",
- "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.19.8.tgz",
- "integrity": "sha512-3sur80OT9YdeZwIVgERAysAbwncom7b4bCI2XKLjMfPymTud7e/oY4y+ci1XVp5TfQp/bppn7xLw1n/oSQY3/Q==",
- "cpu": [
- "x64"
- ],
- "dev": true,
- "optional": true,
- "os": [
- "darwin"
- ],
- "engines": {
- "node": ">=12"
- }
- },
- "node_modules/@esbuild/freebsd-arm64": {
- "version": "0.19.8",
- "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.19.8.tgz",
- "integrity": "sha512-WAnPJSDattvS/XtPCTj1tPoTxERjcTpH6HsMr6ujTT+X6rylVe8ggxk8pVxzf5U1wh5sPODpawNicF5ta/9Tmw==",
- "cpu": [
- "arm64"
- ],
- "dev": true,
- "optional": true,
- "os": [
- "freebsd"
- ],
- "engines": {
- "node": ">=12"
- }
- },
- "node_modules/@esbuild/freebsd-x64": {
- "version": "0.19.8",
- "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.19.8.tgz",
- "integrity": "sha512-ICvZyOplIjmmhjd6mxi+zxSdpPTKFfyPPQMQTK/w+8eNK6WV01AjIztJALDtwNNfFhfZLux0tZLC+U9nSyA5Zg==",
- "cpu": [
- "x64"
- ],
- "dev": true,
- "optional": true,
- "os": [
- "freebsd"
- ],
- "engines": {
- "node": ">=12"
- }
- },
- "node_modules/@esbuild/linux-arm": {
- "version": "0.19.8",
- "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.19.8.tgz",
- "integrity": "sha512-H4vmI5PYqSvosPaTJuEppU9oz1dq2A7Mr2vyg5TF9Ga+3+MGgBdGzcyBP7qK9MrwFQZlvNyJrvz6GuCaj3OukQ==",
- "cpu": [
- "arm"
- ],
- "dev": true,
- "optional": true,
- "os": [
- "linux"
- ],
- "engines": {
- "node": ">=12"
- }
- },
- "node_modules/@esbuild/linux-arm64": {
- "version": "0.19.8",
- "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.19.8.tgz",
- "integrity": "sha512-z1zMZivxDLHWnyGOctT9JP70h0beY54xDDDJt4VpTX+iwA77IFsE1vCXWmprajJGa+ZYSqkSbRQ4eyLCpCmiCQ==",
- "cpu": [
- "arm64"
- ],
- "dev": true,
- "optional": true,
- "os": [
- "linux"
- ],
- "engines": {
- "node": ">=12"
- }
- },
- "node_modules/@esbuild/linux-ia32": {
- "version": "0.19.8",
- "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.19.8.tgz",
- "integrity": "sha512-1a8suQiFJmZz1khm/rDglOc8lavtzEMRo0v6WhPgxkrjcU0LkHj+TwBrALwoz/OtMExvsqbbMI0ChyelKabSvQ==",
- "cpu": [
- "ia32"
- ],
- "dev": true,
- "optional": true,
- "os": [
- "linux"
- ],
- "engines": {
- "node": ">=12"
- }
- },
- "node_modules/@esbuild/linux-loong64": {
- "version": "0.19.8",
- "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.19.8.tgz",
- "integrity": "sha512-fHZWS2JJxnXt1uYJsDv9+b60WCc2RlvVAy1F76qOLtXRO+H4mjt3Tr6MJ5l7Q78X8KgCFudnTuiQRBhULUyBKQ==",
- "cpu": [
- "loong64"
- ],
- "dev": true,
- "optional": true,
- "os": [
- "linux"
- ],
- "engines": {
- "node": ">=12"
- }
- },
- "node_modules/@esbuild/linux-mips64el": {
- "version": "0.19.8",
- "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.19.8.tgz",
- "integrity": "sha512-Wy/z0EL5qZYLX66dVnEg9riiwls5IYnziwuju2oUiuxVc+/edvqXa04qNtbrs0Ukatg5HEzqT94Zs7J207dN5Q==",
- "cpu": [
- "mips64el"
- ],
- "dev": true,
- "optional": true,
- "os": [
- "linux"
- ],
- "engines": {
- "node": ">=12"
- }
- },
- "node_modules/@esbuild/linux-ppc64": {
- "version": "0.19.8",
- "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.19.8.tgz",
- "integrity": "sha512-ETaW6245wK23YIEufhMQ3HSeHO7NgsLx8gygBVldRHKhOlD1oNeNy/P67mIh1zPn2Hr2HLieQrt6tWrVwuqrxg==",
- "cpu": [
- "ppc64"
- ],
- "dev": true,
- "optional": true,
- "os": [
- "linux"
- ],
- "engines": {
- "node": ">=12"
- }
- },
- "node_modules/@esbuild/linux-riscv64": {
- "version": "0.19.8",
- "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.19.8.tgz",
- "integrity": "sha512-T2DRQk55SgoleTP+DtPlMrxi/5r9AeFgkhkZ/B0ap99zmxtxdOixOMI570VjdRCs9pE4Wdkz7JYrsPvsl7eESg==",
- "cpu": [
- "riscv64"
- ],
- "dev": true,
- "optional": true,
- "os": [
- "linux"
- ],
- "engines": {
- "node": ">=12"
- }
- },
- "node_modules/@esbuild/linux-s390x": {
- "version": "0.19.8",
- "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.19.8.tgz",
- "integrity": "sha512-NPxbdmmo3Bk7mbNeHmcCd7R7fptJaczPYBaELk6NcXxy7HLNyWwCyDJ/Xx+/YcNH7Im5dHdx9gZ5xIwyliQCbg==",
- "cpu": [
- "s390x"
- ],
- "dev": true,
- "optional": true,
- "os": [
- "linux"
- ],
- "engines": {
- "node": ">=12"
- }
- },
- "node_modules/@esbuild/linux-x64": {
- "version": "0.19.8",
- "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.19.8.tgz",
- "integrity": "sha512-lytMAVOM3b1gPypL2TRmZ5rnXl7+6IIk8uB3eLsV1JwcizuolblXRrc5ShPrO9ls/b+RTp+E6gbsuLWHWi2zGg==",
- "cpu": [
- "x64"
- ],
- "dev": true,
- "optional": true,
- "os": [
- "linux"
- ],
- "engines": {
- "node": ">=12"
- }
- },
- "node_modules/@esbuild/netbsd-x64": {
- "version": "0.19.8",
- "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.19.8.tgz",
- "integrity": "sha512-hvWVo2VsXz/8NVt1UhLzxwAfo5sioj92uo0bCfLibB0xlOmimU/DeAEsQILlBQvkhrGjamP0/el5HU76HAitGw==",
- "cpu": [
- "x64"
- ],
- "dev": true,
- "optional": true,
- "os": [
- "netbsd"
- ],
- "engines": {
- "node": ">=12"
- }
- },
- "node_modules/@esbuild/openbsd-x64": {
- "version": "0.19.8",
- "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.19.8.tgz",
- "integrity": "sha512-/7Y7u77rdvmGTxR83PgaSvSBJCC2L3Kb1M/+dmSIvRvQPXXCuC97QAwMugBNG0yGcbEGfFBH7ojPzAOxfGNkwQ==",
- "cpu": [
- "x64"
- ],
- "dev": true,
- "optional": true,
- "os": [
- "openbsd"
- ],
- "engines": {
- "node": ">=12"
- }
- },
- "node_modules/@esbuild/sunos-x64": {
- "version": "0.19.8",
- "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.19.8.tgz",
- "integrity": "sha512-9Lc4s7Oi98GqFA4HzA/W2JHIYfnXbUYgekUP/Sm4BG9sfLjyv6GKKHKKVs83SMicBF2JwAX6A1PuOLMqpD001w==",
- "cpu": [
- "x64"
- ],
- "dev": true,
- "optional": true,
- "os": [
- "sunos"
- ],
- "engines": {
- "node": ">=12"
- }
- },
- "node_modules/@esbuild/win32-arm64": {
- "version": "0.19.8",
- "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.19.8.tgz",
- "integrity": "sha512-rq6WzBGjSzihI9deW3fC2Gqiak68+b7qo5/3kmB6Gvbh/NYPA0sJhrnp7wgV4bNwjqM+R2AApXGxMO7ZoGhIJg==",
- "cpu": [
- "arm64"
- ],
- "dev": true,
- "optional": true,
- "os": [
- "win32"
- ],
- "engines": {
- "node": ">=12"
- }
- },
- "node_modules/@esbuild/win32-ia32": {
- "version": "0.19.8",
- "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.19.8.tgz",
- "integrity": "sha512-AIAbverbg5jMvJznYiGhrd3sumfwWs8572mIJL5NQjJa06P8KfCPWZQ0NwZbPQnbQi9OWSZhFVSUWjjIrn4hSw==",
- "cpu": [
- "ia32"
- ],
- "dev": true,
- "optional": true,
- "os": [
- "win32"
- ],
- "engines": {
- "node": ">=12"
- }
- },
- "node_modules/@esbuild/win32-x64": {
- "version": "0.19.8",
- "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.19.8.tgz",
- "integrity": "sha512-bfZ0cQ1uZs2PqpulNL5j/3w+GDhP36k1K5c38QdQg+Swy51jFZWWeIkteNsufkQxp986wnqRRsb/bHbY1WQ7TA==",
- "cpu": [
- "x64"
- ],
- "dev": true,
- "optional": true,
- "os": [
- "win32"
- ],
- "engines": {
- "node": ">=12"
- }
- },
- "node_modules/@hey-api/openapi-ts": {
- "version": "0.34.1",
- "resolved": "https://registry.npmjs.org/@hey-api/openapi-ts/-/openapi-ts-0.34.1.tgz",
- "integrity": "sha512-7Ak+0nvf4Nhzk04tXGg6h4eM7lnWRgfjCPmMl2MyXrhS5urxd3Bg/PhtpB84u18wnwcM4rIeCUlTwDDQ/OB3NQ==",
- "dev": true,
- "dependencies": {
- "@apidevtools/json-schema-ref-parser": "11.5.4",
- "camelcase": "8.0.0",
- "commander": "12.0.0",
- "handlebars": "4.7.8"
- },
- "bin": {
- "openapi-ts": "bin/index.js"
- },
- "engines": {
- "node": "^18.0.0 || >=20.0.0"
- }
- },
- "node_modules/@hey-api/openapi-ts/node_modules/@apidevtools/json-schema-ref-parser": {
- "version": "11.5.4",
- "resolved": "https://registry.npmjs.org/@apidevtools/json-schema-ref-parser/-/json-schema-ref-parser-11.5.4.tgz",
- "integrity": "sha512-o2fsypTGU0WxRxbax8zQoHiIB4dyrkwYfcm8TxZ+bx9pCzcWZbQtiMqpgBvWA/nJ2TrGjK5adCLfTH8wUeU/Wg==",
- "dev": true,
- "dependencies": {
- "@jsdevtools/ono": "^7.1.3",
- "@types/json-schema": "^7.0.15",
- "js-yaml": "^4.1.0"
- },
- "engines": {
- "node": ">= 16"
- },
- "funding": {
- "url": "https://github.com/sponsors/philsturgeon"
- }
- },
- "node_modules/@hey-api/openapi-ts/node_modules/camelcase": {
- "version": "8.0.0",
- "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-8.0.0.tgz",
- "integrity": "sha512-8WB3Jcas3swSvjIeA2yvCJ+Miyz5l1ZmB6HFb9R1317dt9LCQoswg/BGrmAmkWVEszSrrg4RwmO46qIm2OEnSA==",
- "dev": true,
- "engines": {
- "node": ">=16"
- },
- "funding": {
- "url": "https://github.com/sponsors/sindresorhus"
- }
- },
- "node_modules/@hey-api/openapi-ts/node_modules/commander": {
- "version": "12.0.0",
- "resolved": "https://registry.npmjs.org/commander/-/commander-12.0.0.tgz",
- "integrity": "sha512-MwVNWlYjDTtOjX5PiD7o5pK0UrFU/OYgcJfjjK4RaHZETNtjJqrZa9Y9ds88+A+f+d5lv+561eZ+yCKoS3gbAA==",
- "dev": true,
- "engines": {
- "node": ">=18"
- }
- },
- "node_modules/@jsdevtools/ono": {
- "version": "7.1.3",
- "resolved": "https://registry.npmjs.org/@jsdevtools/ono/-/ono-7.1.3.tgz",
- "integrity": "sha512-4JQNk+3mVzK3xh2rqd6RB4J46qUR19azEHBneZyTZM+c456qOrbbM/5xcR8huNCCcbVt7+UmizG6GuUvPvKUYg==",
- "dev": true
- },
- "node_modules/@playwright/test": {
- "version": "1.45.2",
- "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.45.2.tgz",
- "integrity": "sha512-JxG9eq92ET75EbVi3s+4sYbcG7q72ECeZNbdBlaMkGcNbiDQ4cAi8U2QP5oKkOx+1gpaiL1LDStmzCaEM1Z6fQ==",
- "dev": true,
- "dependencies": {
- "playwright": "1.45.2"
- },
- "bin": {
- "playwright": "cli.js"
- },
- "engines": {
- "node": ">=18"
- }
- },
- "node_modules/@popperjs/core": {
- "version": "2.11.8",
- "resolved": "https://registry.npmjs.org/@popperjs/core/-/core-2.11.8.tgz",
- "integrity": "sha512-P1st0aksCrn9sGZhp8GMYwBnQsbvAWsZAX44oXNNvLHGqAOcoVxmjZiohstwQ7SqKnbR47akdNi+uleWD8+g6A==",
- "funding": {
- "type": "opencollective",
- "url": "https://opencollective.com/popperjs"
- }
- },
- "node_modules/@rollup/rollup-android-arm-eabi": {
- "version": "4.6.1",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.6.1.tgz",
- "integrity": "sha512-0WQ0ouLejaUCRsL93GD4uft3rOmB8qoQMU05Kb8CmMtMBe7XUDLAltxVZI1q6byNqEtU7N1ZX1Vw5lIpgulLQA==",
- "cpu": [
- "arm"
- ],
- "dev": true,
- "optional": true,
- "os": [
- "android"
- ]
- },
- "node_modules/@rollup/rollup-android-arm64": {
- "version": "4.6.1",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.6.1.tgz",
- "integrity": "sha512-1TKm25Rn20vr5aTGGZqo6E4mzPicCUD79k17EgTLAsXc1zysyi4xXKACfUbwyANEPAEIxkzwue6JZ+stYzWUTA==",
- "cpu": [
- "arm64"
- ],
- "dev": true,
- "optional": true,
- "os": [
- "android"
- ]
- },
- "node_modules/@rollup/rollup-darwin-arm64": {
- "version": "4.6.1",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.6.1.tgz",
- "integrity": "sha512-cEXJQY/ZqMACb+nxzDeX9IPLAg7S94xouJJCNVE5BJM8JUEP4HeTF+ti3cmxWeSJo+5D+o8Tc0UAWUkfENdeyw==",
- "cpu": [
- "arm64"
- ],
- "dev": true,
- "optional": true,
- "os": [
- "darwin"
- ]
- },
- "node_modules/@rollup/rollup-darwin-x64": {
- "version": "4.6.1",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.6.1.tgz",
- "integrity": "sha512-LoSU9Xu56isrkV2jLldcKspJ7sSXmZWkAxg7sW/RfF7GS4F5/v4EiqKSMCFbZtDu2Nc1gxxFdQdKwkKS4rwxNg==",
- "cpu": [
- "x64"
- ],
- "dev": true,
- "optional": true,
- "os": [
- "darwin"
- ]
- },
- "node_modules/@rollup/rollup-linux-arm-gnueabihf": {
- "version": "4.6.1",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.6.1.tgz",
- "integrity": "sha512-EfI3hzYAy5vFNDqpXsNxXcgRDcFHUWSx5nnRSCKwXuQlI5J9dD84g2Usw81n3FLBNsGCegKGwwTVsSKK9cooSQ==",
- "cpu": [
- "arm"
- ],
- "dev": true,
- "optional": true,
- "os": [
- "linux"
- ]
- },
- "node_modules/@rollup/rollup-linux-arm64-gnu": {
- "version": "4.6.1",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.6.1.tgz",
- "integrity": "sha512-9lhc4UZstsegbNLhH0Zu6TqvDfmhGzuCWtcTFXY10VjLLUe4Mr0Ye2L3rrtHaDd/J5+tFMEuo5LTCSCMXWfUKw==",
- "cpu": [
- "arm64"
- ],
- "dev": true,
- "optional": true,
- "os": [
- "linux"
- ]
- },
- "node_modules/@rollup/rollup-linux-arm64-musl": {
- "version": "4.6.1",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.6.1.tgz",
- "integrity": "sha512-FfoOK1yP5ksX3wwZ4Zk1NgyGHZyuRhf99j64I5oEmirV8EFT7+OhUZEnP+x17lcP/QHJNWGsoJwrz4PJ9fBEXw==",
- "cpu": [
- "arm64"
- ],
- "dev": true,
- "optional": true,
- "os": [
- "linux"
- ]
- },
- "node_modules/@rollup/rollup-linux-x64-gnu": {
- "version": "4.6.1",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.6.1.tgz",
- "integrity": "sha512-DNGZvZDO5YF7jN5fX8ZqmGLjZEXIJRdJEdTFMhiyXqyXubBa0WVLDWSNlQ5JR2PNgDbEV1VQowhVRUh+74D+RA==",
- "cpu": [
- "x64"
- ],
- "dev": true,
- "optional": true,
- "os": [
- "linux"
- ]
- },
- "node_modules/@rollup/rollup-linux-x64-musl": {
- "version": "4.6.1",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.6.1.tgz",
- "integrity": "sha512-RkJVNVRM+piYy87HrKmhbexCHg3A6Z6MU0W9GHnJwBQNBeyhCJG9KDce4SAMdicQnpURggSvtbGo9xAWOfSvIQ==",
- "cpu": [
- "x64"
- ],
- "dev": true,
- "optional": true,
- "os": [
- "linux"
- ]
- },
- "node_modules/@rollup/rollup-win32-arm64-msvc": {
- "version": "4.6.1",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.6.1.tgz",
- "integrity": "sha512-v2FVT6xfnnmTe3W9bJXl6r5KwJglMK/iRlkKiIFfO6ysKs0rDgz7Cwwf3tjldxQUrHL9INT/1r4VA0n9L/F1vQ==",
- "cpu": [
- "arm64"
- ],
- "dev": true,
- "optional": true,
- "os": [
- "win32"
- ]
- },
- "node_modules/@rollup/rollup-win32-ia32-msvc": {
- "version": "4.6.1",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.6.1.tgz",
- "integrity": "sha512-YEeOjxRyEjqcWphH9dyLbzgkF8wZSKAKUkldRY6dgNR5oKs2LZazqGB41cWJ4Iqqcy9/zqYgmzBkRoVz3Q9MLw==",
- "cpu": [
- "ia32"
- ],
- "dev": true,
- "optional": true,
- "os": [
- "win32"
- ]
- },
- "node_modules/@rollup/rollup-win32-x64-msvc": {
- "version": "4.6.1",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.6.1.tgz",
- "integrity": "sha512-0zfTlFAIhgz8V2G8STq8toAjsYYA6eci1hnXuyOTUFnymrtJwnS6uGKiv3v5UrPZkBlamLvrLV2iiaeqCKzb0A==",
- "cpu": [
- "x64"
- ],
- "dev": true,
- "optional": true,
- "os": [
- "win32"
- ]
- },
- "node_modules/@swc/core": {
- "version": "1.3.100",
- "resolved": "https://registry.npmjs.org/@swc/core/-/core-1.3.100.tgz",
- "integrity": "sha512-7dKgTyxJjlrMwFZYb1auj3Xq0D8ZBe+5oeIgfMlRU05doXZypYJe0LAk0yjj3WdbwYzpF+T1PLxwTWizI0pckw==",
- "dev": true,
- "hasInstallScript": true,
- "dependencies": {
- "@swc/counter": "^0.1.1",
- "@swc/types": "^0.1.5"
- },
- "engines": {
- "node": ">=10"
- },
- "funding": {
- "type": "opencollective",
- "url": "https://opencollective.com/swc"
- },
- "optionalDependencies": {
- "@swc/core-darwin-arm64": "1.3.100",
- "@swc/core-darwin-x64": "1.3.100",
- "@swc/core-linux-arm64-gnu": "1.3.100",
- "@swc/core-linux-arm64-musl": "1.3.100",
- "@swc/core-linux-x64-gnu": "1.3.100",
- "@swc/core-linux-x64-musl": "1.3.100",
- "@swc/core-win32-arm64-msvc": "1.3.100",
- "@swc/core-win32-ia32-msvc": "1.3.100",
- "@swc/core-win32-x64-msvc": "1.3.100"
- },
- "peerDependencies": {
- "@swc/helpers": "^0.5.0"
- },
- "peerDependenciesMeta": {
- "@swc/helpers": {
- "optional": true
- }
- }
- },
- "node_modules/@swc/core-darwin-arm64": {
- "version": "1.3.100",
- "resolved": "https://registry.npmjs.org/@swc/core-darwin-arm64/-/core-darwin-arm64-1.3.100.tgz",
- "integrity": "sha512-XVWFsKe6ei+SsDbwmsuRkYck1SXRpO60Hioa4hoLwR8fxbA9eVp6enZtMxzVVMBi8ej5seZ4HZQeAWepbukiBw==",
- "cpu": [
- "arm64"
- ],
- "dev": true,
- "optional": true,
- "os": [
- "darwin"
- ],
- "engines": {
- "node": ">=10"
- }
- },
- "node_modules/@swc/core-darwin-x64": {
- "version": "1.3.100",
- "resolved": "https://registry.npmjs.org/@swc/core-darwin-x64/-/core-darwin-x64-1.3.100.tgz",
- "integrity": "sha512-KF/MXrnH1nakm1wbt4XV8FS7kvqD9TGmVxeJ0U4bbvxXMvzeYUurzg3AJUTXYmXDhH/VXOYJE5N5RkwZZPs5iA==",
- "cpu": [
- "x64"
- ],
- "dev": true,
- "optional": true,
- "os": [
- "darwin"
- ],
- "engines": {
- "node": ">=10"
- }
- },
- "node_modules/@swc/core-linux-arm64-gnu": {
- "version": "1.3.100",
- "resolved": "https://registry.npmjs.org/@swc/core-linux-arm64-gnu/-/core-linux-arm64-gnu-1.3.100.tgz",
- "integrity": "sha512-p8hikNnAEJrw5vHCtKiFT4hdlQxk1V7vqPmvUDgL/qe2menQDK/i12tbz7/3BEQ4UqUPnvwpmVn2d19RdEMNxw==",
- "cpu": [
- "arm64"
- ],
- "dev": true,
- "optional": true,
- "os": [
- "linux"
- ],
- "engines": {
- "node": ">=10"
- }
- },
- "node_modules/@swc/core-linux-arm64-musl": {
- "version": "1.3.100",
- "resolved": "https://registry.npmjs.org/@swc/core-linux-arm64-musl/-/core-linux-arm64-musl-1.3.100.tgz",
- "integrity": "sha512-BWx/0EeY89WC4q3AaIaBSGfQxkYxIlS3mX19dwy2FWJs/O+fMvF9oLk/CyJPOZzbp+1DjGeeoGFuDYpiNO91JA==",
- "cpu": [
- "arm64"
- ],
- "dev": true,
- "optional": true,
- "os": [
- "linux"
- ],
- "engines": {
- "node": ">=10"
- }
- },
- "node_modules/@swc/core-linux-x64-gnu": {
- "version": "1.3.100",
- "resolved": "https://registry.npmjs.org/@swc/core-linux-x64-gnu/-/core-linux-x64-gnu-1.3.100.tgz",
- "integrity": "sha512-XUdGu3dxAkjsahLYnm8WijPfKebo+jHgHphDxaW0ovI6sTdmEGFDew7QzKZRlbYL2jRkUuuKuDGvD6lO5frmhA==",
- "cpu": [
- "x64"
- ],
- "dev": true,
- "optional": true,
- "os": [
- "linux"
- ],
- "engines": {
- "node": ">=10"
- }
- },
- "node_modules/@swc/core-linux-x64-musl": {
- "version": "1.3.100",
- "resolved": "https://registry.npmjs.org/@swc/core-linux-x64-musl/-/core-linux-x64-musl-1.3.100.tgz",
- "integrity": "sha512-PhoXKf+f0OaNW/GCuXjJ0/KfK9EJX7z2gko+7nVnEA0p3aaPtbP6cq1Ubbl6CMoPL+Ci3gZ7nYumDqXNc3CtLQ==",
- "cpu": [
- "x64"
- ],
- "dev": true,
- "optional": true,
- "os": [
- "linux"
- ],
- "engines": {
- "node": ">=10"
- }
- },
- "node_modules/@swc/core-win32-arm64-msvc": {
- "version": "1.3.100",
- "resolved": "https://registry.npmjs.org/@swc/core-win32-arm64-msvc/-/core-win32-arm64-msvc-1.3.100.tgz",
- "integrity": "sha512-PwLADZN6F9cXn4Jw52FeP/MCLVHm8vwouZZSOoOScDtihjY495SSjdPnlosMaRSR4wJQssGwiD/4MbpgQPqbAw==",
- "cpu": [
- "arm64"
- ],
- "dev": true,
- "optional": true,
- "os": [
- "win32"
- ],
- "engines": {
- "node": ">=10"
- }
- },
- "node_modules/@swc/core-win32-ia32-msvc": {
- "version": "1.3.100",
- "resolved": "https://registry.npmjs.org/@swc/core-win32-ia32-msvc/-/core-win32-ia32-msvc-1.3.100.tgz",
- "integrity": "sha512-0f6nicKSLlDKlyPRl2JEmkpBV4aeDfRQg6n8mPqgL7bliZIcDahG0ej+HxgNjZfS3e0yjDxsNRa6sAqWU2Z60A==",
- "cpu": [
- "ia32"
- ],
- "dev": true,
- "optional": true,
- "os": [
- "win32"
- ],
- "engines": {
- "node": ">=10"
- }
- },
- "node_modules/@swc/core-win32-x64-msvc": {
- "version": "1.3.100",
- "resolved": "https://registry.npmjs.org/@swc/core-win32-x64-msvc/-/core-win32-x64-msvc-1.3.100.tgz",
- "integrity": "sha512-b7J0rPoMkRTa3XyUGt8PwCaIBuYWsL2DqbirrQKRESzgCvif5iNpqaM6kjIjI/5y5q1Ycv564CB51YDpiS8EtQ==",
- "cpu": [
- "x64"
- ],
- "dev": true,
- "optional": true,
- "os": [
- "win32"
- ],
- "engines": {
- "node": ">=10"
- }
- },
- "node_modules/@swc/counter": {
- "version": "0.1.2",
- "resolved": "https://registry.npmjs.org/@swc/counter/-/counter-0.1.2.tgz",
- "integrity": "sha512-9F4ys4C74eSTEUNndnER3VJ15oru2NumfQxS8geE+f3eB5xvfxpWyqE5XlVnxb/R14uoXi6SLbBwwiDSkv+XEw==",
- "dev": true
- },
- "node_modules/@swc/types": {
- "version": "0.1.5",
- "resolved": "https://registry.npmjs.org/@swc/types/-/types-0.1.5.tgz",
- "integrity": "sha512-myfUej5naTBWnqOCc/MdVOLVjXUXtIA+NpDrDBKJtLLg2shUjBu3cZmB/85RyitKc55+lUUyl7oRfLOvkr2hsw==",
- "dev": true
- },
- "node_modules/@tanstack/history": {
- "version": "1.15.13",
- "resolved": "https://registry.npmjs.org/@tanstack/history/-/history-1.15.13.tgz",
- "integrity": "sha512-ToaeMtK5S4YaxCywAlYexc7KPFN0esjyTZ4vXzJhXEWAkro9iHgh7m/4ozPJb7oTo65WkHWX0W9GjcZbInSD8w==",
- "engines": {
- "node": ">=12"
- },
- "funding": {
- "type": "github",
- "url": "https://github.com/sponsors/tannerlinsley"
- }
- },
- "node_modules/@tanstack/query-core": {
- "version": "5.28.13",
- "resolved": "https://registry.npmjs.org/@tanstack/query-core/-/query-core-5.28.13.tgz",
- "integrity": "sha512-C3+CCOcza+mrZ7LglQbjeYEOTEC3LV0VN0eYaIN6GvqAZ8Foegdgch7n6QYPtT4FuLae5ALy+m+ZMEKpD6tMCQ==",
- "funding": {
- "type": "github",
- "url": "https://github.com/sponsors/tannerlinsley"
- }
- },
- "node_modules/@tanstack/query-devtools": {
- "version": "5.28.10",
- "resolved": "https://registry.npmjs.org/@tanstack/query-devtools/-/query-devtools-5.28.10.tgz",
- "integrity": "sha512-5UN629fKa5/1K/2Pd26gaU7epxRrYiT1gy+V+pW5K6hnf1DeUKK3pANSb2eHKlecjIKIhTwyF7k9XdyE2gREvQ==",
- "funding": {
- "type": "github",
- "url": "https://github.com/sponsors/tannerlinsley"
- }
- },
- "node_modules/@tanstack/react-query": {
- "version": "5.28.14",
- "resolved": "https://registry.npmjs.org/@tanstack/react-query/-/react-query-5.28.14.tgz",
- "integrity": "sha512-cZqt03Igb3I9tM72qNX5TAAmeYl75Z+k4Mv92VkXIXc2hCrv0fIywd7GN3JV1BBJl4mr7Cc+OOKKOPy8sNVOkA==",
- "dependencies": {
- "@tanstack/query-core": "5.28.13"
- },
- "funding": {
- "type": "github",
- "url": "https://github.com/sponsors/tannerlinsley"
- },
- "peerDependencies": {
- "react": "^18.0.0"
- }
- },
- "node_modules/@tanstack/react-query-devtools": {
- "version": "5.28.14",
- "resolved": "https://registry.npmjs.org/@tanstack/react-query-devtools/-/react-query-devtools-5.28.14.tgz",
- "integrity": "sha512-4CrFBI1O5wibV1ZdGAnBMmTuc7SiShhxWubxRMyIloeEioxs3DQkFbouGBea5nexuwIxAkvhUB8khpPnNjhxMw==",
- "dependencies": {
- "@tanstack/query-devtools": "5.28.10"
- },
- "funding": {
- "type": "github",
- "url": "https://github.com/sponsors/tannerlinsley"
- },
- "peerDependencies": {
- "@tanstack/react-query": "^5.28.14",
- "react": "^18.0.0"
- }
- },
- "node_modules/@tanstack/react-router": {
- "version": "1.19.1",
- "resolved": "https://registry.npmjs.org/@tanstack/react-router/-/react-router-1.19.1.tgz",
- "integrity": "sha512-a4Xf074qo2fQLmSi8PTncEFn8XakaH3+DT7Dted4OPClzQFS+c6yU3HONVNAsuYWZ7lDK1HMKoHPDFbnHPEWvA==",
- "dependencies": {
- "@tanstack/history": "1.15.13",
- "@tanstack/react-store": "^0.2.1",
- "tiny-invariant": "^1.3.1",
- "tiny-warning": "^1.0.3"
- },
- "engines": {
- "node": ">=12"
- },
- "funding": {
- "type": "github",
- "url": "https://github.com/sponsors/tannerlinsley"
- },
- "peerDependencies": {
- "react": ">=16",
- "react-dom": ">=16"
- }
- },
- "node_modules/@tanstack/react-store": {
- "version": "0.2.1",
- "resolved": "https://registry.npmjs.org/@tanstack/react-store/-/react-store-0.2.1.tgz",
- "integrity": "sha512-tEbMCQjbeVw9KOP/202LfqZMSNAVi6zYkkp1kBom8nFuMx/965Hzes3+6G6b/comCwVxoJU8Gg9IrcF8yRPthw==",
- "dependencies": {
- "@tanstack/store": "0.1.3",
- "use-sync-external-store": "^1.2.0"
- },
- "funding": {
- "type": "github",
- "url": "https://github.com/sponsors/tannerlinsley"
- },
- "peerDependencies": {
- "react": ">=16",
- "react-dom": ">=16"
- }
- },
- "node_modules/@tanstack/router-devtools": {
- "version": "1.19.1",
- "resolved": "https://registry.npmjs.org/@tanstack/router-devtools/-/router-devtools-1.19.1.tgz",
- "integrity": "sha512-l560JHnffcDccSTo/sOtB+gKvtgaWYpOKOu9MyvswN9XB2pt752UFFIN1Yt/Gsp2Iooq/FcYlYnEPHb4GFzalg==",
- "dev": true,
- "dependencies": {
- "@tanstack/react-router": "1.19.1",
- "clsx": "^2.1.0",
- "date-fns": "^2.29.1",
- "goober": "^2.1.14"
- },
- "engines": {
- "node": ">=12"
- },
- "funding": {
- "type": "github",
- "url": "https://github.com/sponsors/tannerlinsley"
- },
- "peerDependencies": {
- "react": ">=16",
- "react-dom": ">=16"
- }
- },
- "node_modules/@tanstack/router-generator": {
- "version": "1.19.0",
- "resolved": "https://registry.npmjs.org/@tanstack/router-generator/-/router-generator-1.19.0.tgz",
- "integrity": "sha512-vFF8Q7SdyygiYC7lfJ83GRif0vcxjak9SAcgtX/w7TLR0O+qdxRXFPvhKTQQXH6vVezy5Au9bSaSI2EgDD1ubA==",
- "dev": true,
- "dependencies": {
- "prettier": "^3.1.1",
- "zod": "^3.22.4"
- },
- "engines": {
- "node": ">=12"
- },
- "funding": {
- "type": "github",
- "url": "https://github.com/sponsors/tannerlinsley"
- }
- },
- "node_modules/@tanstack/router-vite-plugin": {
- "version": "1.19.0",
- "resolved": "https://registry.npmjs.org/@tanstack/router-vite-plugin/-/router-vite-plugin-1.19.0.tgz",
- "integrity": "sha512-yvvQnJ7JvqsnxAFqwiHhNTV2n1jKkidjc+XbgS2aNnEHC0aHnYH2ygPlmmfiVD7PMO7x64PdI5e12TzY/aKoFA==",
- "dev": true,
- "dependencies": {
- "@tanstack/router-generator": "1.19.0"
- },
- "engines": {
- "node": ">=12"
- },
- "funding": {
- "type": "github",
- "url": "https://github.com/sponsors/tannerlinsley"
- }
- },
- "node_modules/@tanstack/store": {
- "version": "0.1.3",
- "resolved": "https://registry.npmjs.org/@tanstack/store/-/store-0.1.3.tgz",
- "integrity": "sha512-GnolmC8Fr4mvsHE1fGQmR3Nm0eBO3KnZjDU0a+P3TeQNM/dDscFGxtA7p31NplQNW3KwBw4t1RVFmz0VeKLxcw==",
- "funding": {
- "type": "github",
- "url": "https://github.com/sponsors/tannerlinsley"
- }
- },
- "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/lodash": {
- "version": "4.14.202",
- "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.202.tgz",
- "integrity": "sha512-OvlIYQK9tNneDlS0VN54LLd5uiPCBOp7gS5Z0f1mjoJYBrtStzgmJBxONW3U6OZqdtNzZPmn9BS/7WI7BFFcFQ=="
- },
- "node_modules/@types/lodash.mergewith": {
- "version": "4.6.7",
- "resolved": "https://registry.npmjs.org/@types/lodash.mergewith/-/lodash.mergewith-4.6.7.tgz",
- "integrity": "sha512-3m+lkO5CLRRYU0fhGRp7zbsGi6+BZj0uTVSwvcKU+nSlhjA9/QRNfuSGnD2mX6hQA7ZbmcCkzk5h4ZYGOtk14A==",
- "dependencies": {
- "@types/lodash": "*"
- }
- },
- "node_modules/@types/node": {
- "version": "20.10.5",
- "resolved": "https://registry.npmjs.org/@types/node/-/node-20.10.5.tgz",
- "integrity": "sha512-nNPsNE65wjMxEKI93yOP+NPGGBJz/PoN3kZsVLee0XMiJolxSekEVD8wRwBUBqkwc7UWop0edW50yrCQW4CyRw==",
- "dev": true,
- "dependencies": {
- "undici-types": "~5.26.4"
- }
- },
- "node_modules/@types/parse-json": {
- "version": "4.0.2",
- "resolved": "https://registry.npmjs.org/@types/parse-json/-/parse-json-4.0.2.tgz",
- "integrity": "sha512-dISoDXWWQwUquiKsyZ4Ng+HX2KsPL7LyHKHQwgGFEA3IaKac4Obd+h2a/a6waisAoepJlBcx9paWqjA8/HVjCw=="
- },
- "node_modules/@types/prop-types": {
- "version": "15.7.11",
- "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.11.tgz",
- "integrity": "sha512-ga8y9v9uyeiLdpKddhxYQkxNDrfvuPrlFb0N1qnZZByvcElJaXthF1UhvCh9TLWJBEHeNtdnbysW7Y6Uq8CVng==",
- "devOptional": true
- },
- "node_modules/@types/react": {
- "version": "18.2.39",
- "resolved": "https://registry.npmjs.org/@types/react/-/react-18.2.39.tgz",
- "integrity": "sha512-Oiw+ppED6IremMInLV4HXGbfbG6GyziY3kqAwJYOR0PNbkYDmLWQA3a95EhdSmamsvbkJN96ZNN+YD+fGjzSBA==",
- "devOptional": true,
- "dependencies": {
- "@types/prop-types": "*",
- "@types/scheduler": "*",
- "csstype": "^3.0.2"
- }
- },
- "node_modules/@types/react-dom": {
- "version": "18.2.17",
- "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-18.2.17.tgz",
- "integrity": "sha512-rvrT/M7Df5eykWFxn6MYt5Pem/Dbyc1N8Y0S9Mrkw2WFCRiqUgw9P7ul2NpwsXCSM1DVdENzdG9J5SreqfAIWg==",
- "dev": true,
- "dependencies": {
- "@types/react": "*"
- }
- },
- "node_modules/@types/scheduler": {
- "version": "0.16.8",
- "resolved": "https://registry.npmjs.org/@types/scheduler/-/scheduler-0.16.8.tgz",
- "integrity": "sha512-WZLiwShhwLRmeV6zH+GkbOFT6Z6VklCItrDioxUnv+u4Ll+8vKeFySoFyK/0ctcRpOmwAicELfmys1sDc/Rw+A==",
- "devOptional": true
- },
- "node_modules/@vitejs/plugin-react-swc": {
- "version": "3.5.0",
- "resolved": "https://registry.npmjs.org/@vitejs/plugin-react-swc/-/plugin-react-swc-3.5.0.tgz",
- "integrity": "sha512-1PrOvAaDpqlCV+Up8RkAh9qaiUjoDUcjtttyhXDKw53XA6Ve16SOp6cCOpRs8Dj8DqUQs6eTW5YkLcLJjrXAig==",
- "dev": true,
- "dependencies": {
- "@swc/core": "^1.3.96"
- },
- "peerDependencies": {
- "vite": "^4 || ^5"
- }
- },
- "node_modules/@zag-js/dom-query": {
- "version": "0.16.0",
- "resolved": "https://registry.npmjs.org/@zag-js/dom-query/-/dom-query-0.16.0.tgz",
- "integrity": "sha512-Oqhd6+biWyKnhKwFFuZrrf6lxBz2tX2pRQe6grUnYwO6HJ8BcbqZomy2lpOdr+3itlaUqx+Ywj5E5ZZDr/LBfQ=="
- },
- "node_modules/@zag-js/element-size": {
- "version": "0.10.5",
- "resolved": "https://registry.npmjs.org/@zag-js/element-size/-/element-size-0.10.5.tgz",
- "integrity": "sha512-uQre5IidULANvVkNOBQ1tfgwTQcGl4hliPSe69Fct1VfYb2Fd0jdAcGzqQgPhfrXFpR62MxLPB7erxJ/ngtL8w=="
- },
- "node_modules/@zag-js/focus-visible": {
- "version": "0.16.0",
- "resolved": "https://registry.npmjs.org/@zag-js/focus-visible/-/focus-visible-0.16.0.tgz",
- "integrity": "sha512-a7U/HSopvQbrDU4GLerpqiMcHKEkQkNPeDZJWz38cw/6Upunh41GjHetq5TB84hxyCaDzJ6q2nEdNoBQfC0FKA==",
- "dependencies": {
- "@zag-js/dom-query": "0.16.0"
- }
- },
- "node_modules/argparse": {
- "version": "2.0.1",
- "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz",
- "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==",
- "dev": true
- },
- "node_modules/aria-hidden": {
- "version": "1.2.3",
- "resolved": "https://registry.npmjs.org/aria-hidden/-/aria-hidden-1.2.3.tgz",
- "integrity": "sha512-xcLxITLe2HYa1cnYnwCjkOO1PqUHQpozB8x9AR0OgWN2woOBi5kSDVxKfd0b7sb1hw5qFeJhXm9H1nu3xSfLeQ==",
- "dependencies": {
- "tslib": "^2.0.0"
- },
- "engines": {
- "node": ">=10"
- }
- },
- "node_modules/asynckit": {
- "version": "0.4.0",
- "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz",
- "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q=="
- },
- "node_modules/axios": {
- "version": "1.7.4",
- "resolved": "https://registry.npmjs.org/axios/-/axios-1.7.4.tgz",
- "integrity": "sha512-DukmaFRnY6AzAALSH4J2M3k6PkaC+MfaAGdEERRWcC9q3/TWQwLpHR8ZRLKTdQ3aBDL64EdluRDjJqKw+BPZEw==",
- "dependencies": {
- "follow-redirects": "^1.15.6",
- "form-data": "^4.0.0",
- "proxy-from-env": "^1.1.0"
- }
- },
- "node_modules/babel-plugin-macros": {
- "version": "3.1.0",
- "resolved": "https://registry.npmjs.org/babel-plugin-macros/-/babel-plugin-macros-3.1.0.tgz",
- "integrity": "sha512-Cg7TFGpIr01vOQNODXOOaGz2NpCU5gl8x1qJFbb6hbZxR7XrcE2vtbAsTAbJ7/xwJtUuJEw8K8Zr/AE0LHlesg==",
- "dependencies": {
- "@babel/runtime": "^7.12.5",
- "cosmiconfig": "^7.0.0",
- "resolve": "^1.19.0"
- },
- "engines": {
- "node": ">=10",
- "npm": ">=6"
- }
- },
- "node_modules/callsites": {
- "version": "3.1.0",
- "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz",
- "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==",
- "engines": {
- "node": ">=6"
- }
- },
- "node_modules/clsx": {
- "version": "2.1.0",
- "resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.0.tgz",
- "integrity": "sha512-m3iNNWpd9rl3jvvcBnu70ylMdrXt8Vlq4HYadnU5fwcOtvkSQWPmj7amUcDT2qYI7risszBjI5AUIUox9D16pg==",
- "dev": true,
- "engines": {
- "node": ">=6"
- }
- },
- "node_modules/color2k": {
- "version": "2.0.3",
- "resolved": "https://registry.npmjs.org/color2k/-/color2k-2.0.3.tgz",
- "integrity": "sha512-zW190nQTIoXcGCaU08DvVNFTmQhUpnJfVuAKfWqUQkflXKpaDdpaYoM0iluLS9lgJNHyBF58KKA2FBEwkD7wog=="
- },
- "node_modules/combined-stream": {
- "version": "1.0.8",
- "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz",
- "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==",
- "dependencies": {
- "delayed-stream": "~1.0.0"
- },
- "engines": {
- "node": ">= 0.8"
- }
- },
- "node_modules/compute-scroll-into-view": {
- "version": "3.0.3",
- "resolved": "https://registry.npmjs.org/compute-scroll-into-view/-/compute-scroll-into-view-3.0.3.tgz",
- "integrity": "sha512-nadqwNxghAGTamwIqQSG433W6OADZx2vCo3UXHNrzTRHK/htu+7+L0zhjEoaeaQVNAi3YgqWDv8+tzf0hRfR+A=="
- },
- "node_modules/convert-source-map": {
- "version": "1.9.0",
- "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.9.0.tgz",
- "integrity": "sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A=="
- },
- "node_modules/copy-to-clipboard": {
- "version": "3.3.3",
- "resolved": "https://registry.npmjs.org/copy-to-clipboard/-/copy-to-clipboard-3.3.3.tgz",
- "integrity": "sha512-2KV8NhB5JqC3ky0r9PMCAZKbUHSwtEo4CwCs0KXgruG43gX5PMqDEBbVU4OUzw2MuAWUfsuFmWvEKG5QRfSnJA==",
- "dependencies": {
- "toggle-selection": "^1.0.6"
- }
- },
- "node_modules/cosmiconfig": {
- "version": "7.1.0",
- "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-7.1.0.tgz",
- "integrity": "sha512-AdmX6xUzdNASswsFtmwSt7Vj8po9IuqXm0UXz7QKPuEUmPB4XyjGfaAr2PSuELMwkRMVH1EpIkX5bTZGRB3eCA==",
- "dependencies": {
- "@types/parse-json": "^4.0.0",
- "import-fresh": "^3.2.1",
- "parse-json": "^5.0.0",
- "path-type": "^4.0.0",
- "yaml": "^1.10.0"
- },
- "engines": {
- "node": ">=10"
- }
- },
- "node_modules/css-box-model": {
- "version": "1.2.1",
- "resolved": "https://registry.npmjs.org/css-box-model/-/css-box-model-1.2.1.tgz",
- "integrity": "sha512-a7Vr4Q/kd/aw96bnJG332W9V9LkJO69JRcaCYDUqjp6/z0w6VcZjgAcTbgFxEPfBgdnAwlh3iwu+hLopa+flJw==",
- "dependencies": {
- "tiny-invariant": "^1.0.6"
- }
- },
- "node_modules/csstype": {
- "version": "3.1.2",
- "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.2.tgz",
- "integrity": "sha512-I7K1Uu0MBPzaFKg4nI5Q7Vs2t+3gWWW648spaF+Rg7pI9ds18Ugn+lvg4SHczUdKlHI5LWBXyqfS8+DufyBsgQ=="
- },
- "node_modules/date-fns": {
- "version": "2.30.0",
- "resolved": "https://registry.npmjs.org/date-fns/-/date-fns-2.30.0.tgz",
- "integrity": "sha512-fnULvOpxnC5/Vg3NCiWelDsLiUc9bRwAPs/+LfTLNvetFCtCTN+yQz15C/fs4AwX1R9K5GLtLfn8QW+dWisaAw==",
- "dev": true,
- "dependencies": {
- "@babel/runtime": "^7.21.0"
- },
- "engines": {
- "node": ">=0.11"
- },
- "funding": {
- "type": "opencollective",
- "url": "https://opencollective.com/date-fns"
- }
- },
- "node_modules/delayed-stream": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz",
- "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==",
- "engines": {
- "node": ">=0.4.0"
- }
- },
- "node_modules/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=="
- },
- "node_modules/dotenv": {
- "version": "16.4.5",
- "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.4.5.tgz",
- "integrity": "sha512-ZmdL2rui+eB2YwhsWzjInR8LldtZHGDoQ1ugH85ppHKwpUHL7j7rN0Ti9NCnGiQbhaZ11FpR+7ao1dNsmduNUg==",
- "dev": true,
- "engines": {
- "node": ">=12"
- },
- "funding": {
- "url": "https://dotenvx.com"
- }
- },
- "node_modules/error-ex": {
- "version": "1.3.2",
- "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz",
- "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==",
- "dependencies": {
- "is-arrayish": "^0.2.1"
- }
- },
- "node_modules/esbuild": {
- "version": "0.19.8",
- "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.19.8.tgz",
- "integrity": "sha512-l7iffQpT2OrZfH2rXIp7/FkmaeZM0vxbxN9KfiCwGYuZqzMg/JdvX26R31Zxn/Pxvsrg3Y9N6XTcnknqDyyv4w==",
- "dev": true,
- "hasInstallScript": true,
- "bin": {
- "esbuild": "bin/esbuild"
- },
- "engines": {
- "node": ">=12"
- },
- "optionalDependencies": {
- "@esbuild/android-arm": "0.19.8",
- "@esbuild/android-arm64": "0.19.8",
- "@esbuild/android-x64": "0.19.8",
- "@esbuild/darwin-arm64": "0.19.8",
- "@esbuild/darwin-x64": "0.19.8",
- "@esbuild/freebsd-arm64": "0.19.8",
- "@esbuild/freebsd-x64": "0.19.8",
- "@esbuild/linux-arm": "0.19.8",
- "@esbuild/linux-arm64": "0.19.8",
- "@esbuild/linux-ia32": "0.19.8",
- "@esbuild/linux-loong64": "0.19.8",
- "@esbuild/linux-mips64el": "0.19.8",
- "@esbuild/linux-ppc64": "0.19.8",
- "@esbuild/linux-riscv64": "0.19.8",
- "@esbuild/linux-s390x": "0.19.8",
- "@esbuild/linux-x64": "0.19.8",
- "@esbuild/netbsd-x64": "0.19.8",
- "@esbuild/openbsd-x64": "0.19.8",
- "@esbuild/sunos-x64": "0.19.8",
- "@esbuild/win32-arm64": "0.19.8",
- "@esbuild/win32-ia32": "0.19.8",
- "@esbuild/win32-x64": "0.19.8"
- }
- },
- "node_modules/escape-string-regexp": {
- "version": "4.0.0",
- "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz",
- "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==",
- "engines": {
- "node": ">=10"
- },
- "funding": {
- "url": "https://github.com/sponsors/sindresorhus"
- }
- },
- "node_modules/find-root": {
- "version": "1.1.0",
- "resolved": "https://registry.npmjs.org/find-root/-/find-root-1.1.0.tgz",
- "integrity": "sha512-NKfW6bec6GfKc0SGx1e07QZY9PE99u0Bft/0rzSD5k3sO/vwkVUpDUKVm5Gpp5Ue3YfShPFTX2070tDs5kB9Ng=="
- },
- "node_modules/focus-lock": {
- "version": "1.3.3",
- "resolved": "https://registry.npmjs.org/focus-lock/-/focus-lock-1.3.3.tgz",
- "integrity": "sha512-hfXkZha7Xt4RQtrL1HBfspAuIj89Y0fb6GX0dfJilb8S2G/lvL4akPAcHq6xoD2NuZnDMCnZL/zQesMyeu6Psg==",
- "dependencies": {
- "tslib": "^2.0.3"
- },
- "engines": {
- "node": ">=10"
- }
- },
- "node_modules/follow-redirects": {
- "version": "1.15.6",
- "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.6.tgz",
- "integrity": "sha512-wWN62YITEaOpSK584EZXJafH1AGpO8RVgElfkuXbTOrPX4fIfOyEpW/CsiNd8JdYrAoOvafRTOEnvsO++qCqFA==",
- "funding": [
- {
- "type": "individual",
- "url": "https://github.com/sponsors/RubenVerborgh"
- }
- ],
- "engines": {
- "node": ">=4.0"
- },
- "peerDependenciesMeta": {
- "debug": {
- "optional": true
- }
- }
- },
- "node_modules/form-data": {
- "version": "4.0.0",
- "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz",
- "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==",
- "dependencies": {
- "asynckit": "^0.4.0",
- "combined-stream": "^1.0.8",
- "mime-types": "^2.1.12"
- },
- "engines": {
- "node": ">= 6"
- }
- },
- "node_modules/framer-motion": {
- "version": "10.16.16",
- "resolved": "https://registry.npmjs.org/framer-motion/-/framer-motion-10.16.16.tgz",
- "integrity": "sha512-je6j91rd7NmUX7L1XHouwJ4v3R+SO4umso2LUcgOct3rHZ0PajZ80ETYZTajzEXEl9DlKyzjyt4AvGQ+lrebOw==",
- "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/framer-motion/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==",
- "optional": true,
- "dependencies": {
- "@emotion/memoize": "0.7.4"
- }
- },
- "node_modules/framer-motion/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==",
- "optional": true
- },
- "node_modules/framesync": {
- "version": "6.1.2",
- "resolved": "https://registry.npmjs.org/framesync/-/framesync-6.1.2.tgz",
- "integrity": "sha512-jBTqhX6KaQVDyus8muwZbBeGGP0XgujBRbQ7gM7BRdS3CadCZIHiawyzYLnafYcvZIh5j8WE7cxZKFn7dXhu9g==",
- "dependencies": {
- "tslib": "2.4.0"
- }
- },
- "node_modules/framesync/node_modules/tslib": {
- "version": "2.4.0",
- "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.0.tgz",
- "integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ=="
- },
- "node_modules/fsevents": {
- "version": "2.3.3",
- "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz",
- "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==",
- "dev": true,
- "hasInstallScript": true,
- "optional": true,
- "os": [
- "darwin"
- ],
- "engines": {
- "node": "^8.16.0 || ^10.6.0 || >=11.0.0"
- }
- },
- "node_modules/function-bind": {
- "version": "1.1.2",
- "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz",
- "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==",
- "funding": {
- "url": "https://github.com/sponsors/ljharb"
- }
- },
- "node_modules/get-nonce": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/get-nonce/-/get-nonce-1.0.1.tgz",
- "integrity": "sha512-FJhYRoDaiatfEkUK8HKlicmu/3SGFD51q3itKDGoSTysQJBnfOcxU5GxnhE1E6soB76MbT0MBtnKJuXyAx+96Q==",
- "engines": {
- "node": ">=6"
- }
- },
- "node_modules/goober": {
- "version": "2.1.14",
- "resolved": "https://registry.npmjs.org/goober/-/goober-2.1.14.tgz",
- "integrity": "sha512-4UpC0NdGyAFqLNPnhCT2iHpza2q+RAY3GV85a/mRPdzyPQMsj0KmMMuetdIkzWRbJ+Hgau1EZztq8ImmiMGhsg==",
- "dev": true,
- "peerDependencies": {
- "csstype": "^3.0.10"
- }
- },
- "node_modules/handlebars": {
- "version": "4.7.8",
- "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.7.8.tgz",
- "integrity": "sha512-vafaFqs8MZkRrSX7sFVUdo3ap/eNiLnb4IakshzvP56X5Nr1iGKAIqdX6tMlm6HcNRIkr6AxO5jFEoJzzpT8aQ==",
- "dev": true,
- "dependencies": {
- "minimist": "^1.2.5",
- "neo-async": "^2.6.2",
- "source-map": "^0.6.1",
- "wordwrap": "^1.0.0"
- },
- "bin": {
- "handlebars": "bin/handlebars"
- },
- "engines": {
- "node": ">=0.4.7"
- },
- "optionalDependencies": {
- "uglify-js": "^3.1.4"
- }
- },
- "node_modules/hasown": {
- "version": "2.0.1",
- "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.1.tgz",
- "integrity": "sha512-1/th4MHjnwncwXsIW6QMzlvYL9kG5e/CpVvLRZe4XPa8TOUNbCELqmvhDmnkNsAjwaG4+I8gJJL0JBvTTLO9qA==",
- "dependencies": {
- "function-bind": "^1.1.2"
- },
- "engines": {
- "node": ">= 0.4"
- }
- },
- "node_modules/hoist-non-react-statics": {
- "version": "3.3.2",
- "resolved": "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz",
- "integrity": "sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw==",
- "dependencies": {
- "react-is": "^16.7.0"
- }
- },
- "node_modules/import-fresh": {
- "version": "3.3.0",
- "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz",
- "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==",
- "dependencies": {
- "parent-module": "^1.0.0",
- "resolve-from": "^4.0.0"
- },
- "engines": {
- "node": ">=6"
- },
- "funding": {
- "url": "https://github.com/sponsors/sindresorhus"
- }
- },
- "node_modules/invariant": {
- "version": "2.2.4",
- "resolved": "https://registry.npmjs.org/invariant/-/invariant-2.2.4.tgz",
- "integrity": "sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA==",
- "dependencies": {
- "loose-envify": "^1.0.0"
- }
- },
- "node_modules/is-arrayish": {
- "version": "0.2.1",
- "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz",
- "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg=="
- },
- "node_modules/is-core-module": {
- "version": "2.13.1",
- "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.13.1.tgz",
- "integrity": "sha512-hHrIjvZsftOsvKSn2TRYl63zvxsgE0K+0mYMoH6gD4omR5IWB2KynivBQczo3+wF1cCkjzvptnI9Q0sPU66ilw==",
- "dependencies": {
- "hasown": "^2.0.0"
- },
- "funding": {
- "url": "https://github.com/sponsors/ljharb"
- }
- },
- "node_modules/js-tokens": {
- "version": "4.0.0",
- "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz",
- "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ=="
- },
- "node_modules/js-yaml": {
- "version": "4.1.0",
- "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz",
- "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==",
- "dev": true,
- "dependencies": {
- "argparse": "^2.0.1"
- },
- "bin": {
- "js-yaml": "bin/js-yaml.js"
- }
- },
- "node_modules/json-parse-even-better-errors": {
- "version": "2.3.1",
- "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz",
- "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w=="
- },
- "node_modules/lines-and-columns": {
- "version": "1.2.4",
- "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz",
- "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg=="
- },
- "node_modules/lodash.mergewith": {
- "version": "4.6.2",
- "resolved": "https://registry.npmjs.org/lodash.mergewith/-/lodash.mergewith-4.6.2.tgz",
- "integrity": "sha512-GK3g5RPZWTRSeLSpgP8Xhra+pnjBC56q9FZYe1d5RN3TJ35dbkGy3YqBSMbyCrlbi+CM9Z3Jk5yTL7RCsqboyQ=="
- },
- "node_modules/loose-envify": {
- "version": "1.4.0",
- "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz",
- "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==",
- "dependencies": {
- "js-tokens": "^3.0.0 || ^4.0.0"
- },
- "bin": {
- "loose-envify": "cli.js"
- }
- },
- "node_modules/mime-db": {
- "version": "1.52.0",
- "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz",
- "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==",
- "engines": {
- "node": ">= 0.6"
- }
- },
- "node_modules/mime-types": {
- "version": "2.1.35",
- "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz",
- "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==",
- "dependencies": {
- "mime-db": "1.52.0"
- },
- "engines": {
- "node": ">= 0.6"
- }
- },
- "node_modules/minimist": {
- "version": "1.2.8",
- "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz",
- "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==",
- "dev": true,
- "funding": {
- "url": "https://github.com/sponsors/ljharb"
- }
- },
- "node_modules/nanoid": {
- "version": "3.3.7",
- "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.7.tgz",
- "integrity": "sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==",
- "dev": true,
- "funding": [
- {
- "type": "github",
- "url": "https://github.com/sponsors/ai"
- }
- ],
- "bin": {
- "nanoid": "bin/nanoid.cjs"
- },
- "engines": {
- "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1"
- }
- },
- "node_modules/neo-async": {
- "version": "2.6.2",
- "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz",
- "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==",
- "dev": true
- },
- "node_modules/object-assign": {
- "version": "4.1.1",
- "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
- "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==",
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/parent-module": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz",
- "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==",
- "dependencies": {
- "callsites": "^3.0.0"
- },
- "engines": {
- "node": ">=6"
- }
- },
- "node_modules/parse-json": {
- "version": "5.2.0",
- "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz",
- "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==",
- "dependencies": {
- "@babel/code-frame": "^7.0.0",
- "error-ex": "^1.3.1",
- "json-parse-even-better-errors": "^2.3.0",
- "lines-and-columns": "^1.1.6"
- },
- "engines": {
- "node": ">=8"
- },
- "funding": {
- "url": "https://github.com/sponsors/sindresorhus"
- }
- },
- "node_modules/path-parse": {
- "version": "1.0.7",
- "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz",
- "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw=="
- },
- "node_modules/path-type": {
- "version": "4.0.0",
- "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz",
- "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==",
- "engines": {
- "node": ">=8"
- }
- },
- "node_modules/picocolors": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz",
- "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==",
- "dev": true
- },
- "node_modules/playwright": {
- "version": "1.45.2",
- "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.45.2.tgz",
- "integrity": "sha512-ReywF2t/0teRvNBpfIgh5e4wnrI/8Su8ssdo5XsQKpjxJj+jspm00jSoz9BTg91TT0c9HRjXO7LBNVrgYj9X0g==",
- "dev": true,
- "dependencies": {
- "playwright-core": "1.45.2"
- },
- "bin": {
- "playwright": "cli.js"
- },
- "engines": {
- "node": ">=18"
- },
- "optionalDependencies": {
- "fsevents": "2.3.2"
- }
- },
- "node_modules/playwright-core": {
- "version": "1.45.2",
- "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.45.2.tgz",
- "integrity": "sha512-ha175tAWb0dTK0X4orvBIqi3jGEt701SMxMhyujxNrgd8K0Uy5wMSwwcQHtyB4om7INUkfndx02XnQ2p6dvLDw==",
- "dev": true,
- "bin": {
- "playwright-core": "cli.js"
- },
- "engines": {
- "node": ">=18"
- }
- },
- "node_modules/playwright/node_modules/fsevents": {
- "version": "2.3.2",
- "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz",
- "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==",
- "dev": true,
- "hasInstallScript": true,
- "optional": true,
- "os": [
- "darwin"
- ],
- "engines": {
- "node": "^8.16.0 || ^10.6.0 || >=11.0.0"
- }
- },
- "node_modules/postcss": {
- "version": "8.4.35",
- "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.35.tgz",
- "integrity": "sha512-u5U8qYpBCpN13BsiEB0CbR1Hhh4Gc0zLFuedrHJKMctHCHAGrMdG0PRM/KErzAL3CU6/eckEtmHNB3x6e3c0vA==",
- "dev": true,
- "funding": [
- {
- "type": "opencollective",
- "url": "https://opencollective.com/postcss/"
- },
- {
- "type": "tidelift",
- "url": "https://tidelift.com/funding/github/npm/postcss"
- },
- {
- "type": "github",
- "url": "https://github.com/sponsors/ai"
- }
- ],
- "dependencies": {
- "nanoid": "^3.3.7",
- "picocolors": "^1.0.0",
- "source-map-js": "^1.0.2"
- },
- "engines": {
- "node": "^10 || ^12 || >=14"
- }
- },
- "node_modules/prettier": {
- "version": "3.2.5",
- "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.2.5.tgz",
- "integrity": "sha512-3/GWa9aOC0YeD7LUfvOG2NiDyhOWRvt1k+rcKhOuYnMY24iiCphgneUfJDyFXd6rZCAnuLBv6UeAULtrhT/F4A==",
- "dev": true,
- "bin": {
- "prettier": "bin/prettier.cjs"
- },
- "engines": {
- "node": ">=14"
- },
- "funding": {
- "url": "https://github.com/prettier/prettier?sponsor=1"
- }
- },
- "node_modules/prop-types": {
- "version": "15.8.1",
- "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz",
- "integrity": "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==",
- "dependencies": {
- "loose-envify": "^1.4.0",
- "object-assign": "^4.1.1",
- "react-is": "^16.13.1"
- }
- },
- "node_modules/proxy-from-env": {
- "version": "1.1.0",
- "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz",
- "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg=="
- },
- "node_modules/react": {
- "version": "18.2.0",
- "resolved": "https://registry.npmjs.org/react/-/react-18.2.0.tgz",
- "integrity": "sha512-/3IjMdb2L9QbBdWiW5e3P2/npwMBaU9mHCSCUzNln0ZCYbcfTsGbTJrU/kGemdH2IWmB2ioZ+zkxtmq6g09fGQ==",
- "dependencies": {
- "loose-envify": "^1.1.0"
- },
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/react-clientside-effect": {
- "version": "1.2.6",
- "resolved": "https://registry.npmjs.org/react-clientside-effect/-/react-clientside-effect-1.2.6.tgz",
- "integrity": "sha512-XGGGRQAKY+q25Lz9a/4EPqom7WRjz3z9R2k4jhVKA/puQFH/5Nt27vFZYql4m4NVNdUvX8PS3O7r/Zzm7cjUlg==",
- "dependencies": {
- "@babel/runtime": "^7.12.13"
- },
- "peerDependencies": {
- "react": "^15.3.0 || ^16.0.0 || ^17.0.0 || ^18.0.0"
- }
- },
- "node_modules/react-dom": {
- "version": "18.2.0",
- "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.2.0.tgz",
- "integrity": "sha512-6IMTriUmvsjHUjNtEDudZfuDQUoWXVxKHhlEGSk81n4YFS+r/Kl99wXiwlVXtPBtJenozv2P+hxDsw9eA7Xo6g==",
- "dependencies": {
- "loose-envify": "^1.1.0",
- "scheduler": "^0.23.0"
- },
- "peerDependencies": {
- "react": "^18.2.0"
- }
- },
- "node_modules/react-error-boundary": {
- "version": "4.0.13",
- "resolved": "https://registry.npmjs.org/react-error-boundary/-/react-error-boundary-4.0.13.tgz",
- "integrity": "sha512-b6PwbdSv8XeOSYvjt8LpgpKrZ0yGdtZokYwkwV2wlcZbxgopHX/hgPl5VgpnoVOWd868n1hktM8Qm4b+02MiLQ==",
- "dependencies": {
- "@babel/runtime": "^7.12.5"
- },
- "peerDependencies": {
- "react": ">=16.13.1"
- }
- },
- "node_modules/react-fast-compare": {
- "version": "3.2.2",
- "resolved": "https://registry.npmjs.org/react-fast-compare/-/react-fast-compare-3.2.2.tgz",
- "integrity": "sha512-nsO+KSNgo1SbJqJEYRE9ERzo7YtYbou/OqjSQKxV7jcKox7+usiUVZOAC+XnDOABXggQTno0Y1CpVnuWEc1boQ=="
- },
- "node_modules/react-focus-lock": {
- "version": "2.11.1",
- "resolved": "https://registry.npmjs.org/react-focus-lock/-/react-focus-lock-2.11.1.tgz",
- "integrity": "sha512-IXLwnTBrLTlKTpASZXqqXJ8oymWrgAlOfuuDYN4XCuN1YJ72dwX198UCaF1QqGUk5C3QOnlMik//n3ufcfe8Ig==",
- "dependencies": {
- "@babel/runtime": "^7.0.0",
- "focus-lock": "^1.3.2",
- "prop-types": "^15.6.2",
- "react-clientside-effect": "^1.2.6",
- "use-callback-ref": "^1.3.0",
- "use-sidecar": "^1.1.2"
- },
- "peerDependencies": {
- "@types/react": "^16.8.0 || ^17.0.0 || ^18.0.0",
- "react": "^16.8.0 || ^17.0.0 || ^18.0.0"
- },
- "peerDependenciesMeta": {
- "@types/react": {
- "optional": true
- }
- }
- },
- "node_modules/react-hook-form": {
- "version": "7.49.3",
- "resolved": "https://registry.npmjs.org/react-hook-form/-/react-hook-form-7.49.3.tgz",
- "integrity": "sha512-foD6r3juidAT1cOZzpmD/gOKt7fRsDhXXZ0y28+Al1CHgX+AY1qIN9VSIIItXRq1dN68QrRwl1ORFlwjBaAqeQ==",
- "engines": {
- "node": ">=18",
- "pnpm": "8"
- },
- "funding": {
- "type": "opencollective",
- "url": "https://opencollective.com/react-hook-form"
- },
- "peerDependencies": {
- "react": "^16.8.0 || ^17 || ^18"
- }
- },
- "node_modules/react-icons": {
- "version": "5.0.1",
- "resolved": "https://registry.npmjs.org/react-icons/-/react-icons-5.0.1.tgz",
- "integrity": "sha512-WqLZJ4bLzlhmsvme6iFdgO8gfZP17rfjYEJ2m9RsZjZ+cc4k1hTzknEz63YS1MeT50kVzoa1Nz36f4BEx+Wigw==",
- "peerDependencies": {
- "react": "*"
- }
- },
- "node_modules/react-is": {
- "version": "16.13.1",
- "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz",
- "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ=="
- },
- "node_modules/react-remove-scroll": {
- "version": "2.5.7",
- "resolved": "https://registry.npmjs.org/react-remove-scroll/-/react-remove-scroll-2.5.7.tgz",
- "integrity": "sha512-FnrTWO4L7/Bhhf3CYBNArEG/yROV0tKmTv7/3h9QCFvH6sndeFf1wPqOcbFVu5VAulS5dV1wGT3GZZ/1GawqiA==",
- "dependencies": {
- "react-remove-scroll-bar": "^2.3.4",
- "react-style-singleton": "^2.2.1",
- "tslib": "^2.1.0",
- "use-callback-ref": "^1.3.0",
- "use-sidecar": "^1.1.2"
- },
- "engines": {
- "node": ">=10"
- },
- "peerDependencies": {
- "@types/react": "^16.8.0 || ^17.0.0 || ^18.0.0",
- "react": "^16.8.0 || ^17.0.0 || ^18.0.0"
- },
- "peerDependenciesMeta": {
- "@types/react": {
- "optional": true
- }
- }
- },
- "node_modules/react-remove-scroll-bar": {
- "version": "2.3.5",
- "resolved": "https://registry.npmjs.org/react-remove-scroll-bar/-/react-remove-scroll-bar-2.3.5.tgz",
- "integrity": "sha512-3cqjOqg6s0XbOjWvmasmqHch+RLxIEk2r/70rzGXuz3iIGQsQheEQyqYCBb5EECoD01Vo2SIbDqW4paLeLTASw==",
- "dependencies": {
- "react-style-singleton": "^2.2.1",
- "tslib": "^2.0.0"
- },
- "engines": {
- "node": ">=10"
- },
- "peerDependencies": {
- "@types/react": "^16.8.0 || ^17.0.0 || ^18.0.0",
- "react": "^16.8.0 || ^17.0.0 || ^18.0.0"
- },
- "peerDependenciesMeta": {
- "@types/react": {
- "optional": true
- }
- }
- },
- "node_modules/react-style-singleton": {
- "version": "2.2.1",
- "resolved": "https://registry.npmjs.org/react-style-singleton/-/react-style-singleton-2.2.1.tgz",
- "integrity": "sha512-ZWj0fHEMyWkHzKYUr2Bs/4zU6XLmq9HsgBURm7g5pAVfyn49DgUiNgY2d4lXRlYSiCif9YBGpQleewkcqddc7g==",
- "dependencies": {
- "get-nonce": "^1.0.0",
- "invariant": "^2.2.4",
- "tslib": "^2.0.0"
- },
- "engines": {
- "node": ">=10"
- },
- "peerDependencies": {
- "@types/react": "^16.8.0 || ^17.0.0 || ^18.0.0",
- "react": "^16.8.0 || ^17.0.0 || ^18.0.0"
- },
- "peerDependenciesMeta": {
- "@types/react": {
- "optional": true
- }
- }
- },
- "node_modules/regenerator-runtime": {
- "version": "0.14.1",
- "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.14.1.tgz",
- "integrity": "sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw=="
- },
- "node_modules/resolve": {
- "version": "1.22.8",
- "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.8.tgz",
- "integrity": "sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==",
- "dependencies": {
- "is-core-module": "^2.13.0",
- "path-parse": "^1.0.7",
- "supports-preserve-symlinks-flag": "^1.0.0"
- },
- "bin": {
- "resolve": "bin/resolve"
- },
- "funding": {
- "url": "https://github.com/sponsors/ljharb"
- }
- },
- "node_modules/resolve-from": {
- "version": "4.0.0",
- "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz",
- "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==",
- "engines": {
- "node": ">=4"
- }
- },
- "node_modules/rollup": {
- "version": "4.6.1",
- "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.6.1.tgz",
- "integrity": "sha512-jZHaZotEHQaHLgKr8JnQiDT1rmatjgKlMekyksz+yk9jt/8z9quNjnKNRoaM0wd9DC2QKXjmWWuDYtM3jfF8pQ==",
- "dev": true,
- "bin": {
- "rollup": "dist/bin/rollup"
- },
- "engines": {
- "node": ">=18.0.0",
- "npm": ">=8.0.0"
- },
- "optionalDependencies": {
- "@rollup/rollup-android-arm-eabi": "4.6.1",
- "@rollup/rollup-android-arm64": "4.6.1",
- "@rollup/rollup-darwin-arm64": "4.6.1",
- "@rollup/rollup-darwin-x64": "4.6.1",
- "@rollup/rollup-linux-arm-gnueabihf": "4.6.1",
- "@rollup/rollup-linux-arm64-gnu": "4.6.1",
- "@rollup/rollup-linux-arm64-musl": "4.6.1",
- "@rollup/rollup-linux-x64-gnu": "4.6.1",
- "@rollup/rollup-linux-x64-musl": "4.6.1",
- "@rollup/rollup-win32-arm64-msvc": "4.6.1",
- "@rollup/rollup-win32-ia32-msvc": "4.6.1",
- "@rollup/rollup-win32-x64-msvc": "4.6.1",
- "fsevents": "~2.3.2"
- }
- },
- "node_modules/scheduler": {
- "version": "0.23.0",
- "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.23.0.tgz",
- "integrity": "sha512-CtuThmgHNg7zIZWAXi3AsyIzA3n4xx7aNyjwC2VJldO2LMVDhFK+63xGqq6CsJH4rTAt6/M+N4GhZiDYPx9eUw==",
- "dependencies": {
- "loose-envify": "^1.1.0"
- }
- },
- "node_modules/source-map": {
- "version": "0.6.1",
- "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
- "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
- "dev": true,
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/source-map-js": {
- "version": "1.0.2",
- "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.0.2.tgz",
- "integrity": "sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==",
- "dev": true,
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/stylis": {
- "version": "4.2.0",
- "resolved": "https://registry.npmjs.org/stylis/-/stylis-4.2.0.tgz",
- "integrity": "sha512-Orov6g6BB1sDfYgzWfTHDOxamtX1bE/zo104Dh9e6fqJ3PooipYyfJ0pUmrZO2wAvO8YbEyeFrkV91XTsGMSrw=="
- },
- "node_modules/supports-preserve-symlinks-flag": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz",
- "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==",
- "engines": {
- "node": ">= 0.4"
- },
- "funding": {
- "url": "https://github.com/sponsors/ljharb"
- }
- },
- "node_modules/tiny-invariant": {
- "version": "1.3.3",
- "resolved": "https://registry.npmjs.org/tiny-invariant/-/tiny-invariant-1.3.3.tgz",
- "integrity": "sha512-+FbBPE1o9QAYvviau/qC5SE3caw21q3xkvWKBtja5vgqOWIHHJ3ioaq1VPfn/Szqctz2bU/oYeKd9/z5BL+PVg=="
- },
- "node_modules/tiny-warning": {
- "version": "1.0.3",
- "resolved": "https://registry.npmjs.org/tiny-warning/-/tiny-warning-1.0.3.tgz",
- "integrity": "sha512-lBN9zLN/oAf68o3zNXYrdCt1kP8WsiGW8Oo2ka41b2IM5JL/S1CTyX1rW0mb/zSuJun0ZUrDxx4sqvYS2FWzPA=="
- },
- "node_modules/to-fast-properties": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz",
- "integrity": "sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==",
- "engines": {
- "node": ">=4"
- }
- },
- "node_modules/toggle-selection": {
- "version": "1.0.6",
- "resolved": "https://registry.npmjs.org/toggle-selection/-/toggle-selection-1.0.6.tgz",
- "integrity": "sha512-BiZS+C1OS8g/q2RRbJmy59xpyghNBqrr6k5L/uKBGRsTfxmu3ffiRnd8mlGPUVayg8pvfi5urfnu8TU7DVOkLQ=="
- },
- "node_modules/tslib": {
- "version": "2.6.2",
- "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz",
- "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q=="
- },
- "node_modules/typescript": {
- "version": "5.3.2",
- "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.3.2.tgz",
- "integrity": "sha512-6l+RyNy7oAHDfxC4FzSJcz9vnjTKxrLpDG5M2Vu4SHRVNg6xzqZp6LYSR9zjqQTu8DU/f5xwxUdADOkbrIX2gQ==",
- "dev": true,
- "bin": {
- "tsc": "bin/tsc",
- "tsserver": "bin/tsserver"
- },
- "engines": {
- "node": ">=14.17"
- }
- },
- "node_modules/uglify-js": {
- "version": "3.17.4",
- "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.17.4.tgz",
- "integrity": "sha512-T9q82TJI9e/C1TAxYvfb16xO120tMVFZrGA3f9/P4424DNu6ypK103y0GPFVa17yotwSyZW5iYXgjYHkGrJW/g==",
- "dev": true,
- "optional": true,
- "bin": {
- "uglifyjs": "bin/uglifyjs"
- },
- "engines": {
- "node": ">=0.8.0"
- }
- },
- "node_modules/undici-types": {
- "version": "5.26.5",
- "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz",
- "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==",
- "dev": true
- },
- "node_modules/use-callback-ref": {
- "version": "1.3.1",
- "resolved": "https://registry.npmjs.org/use-callback-ref/-/use-callback-ref-1.3.1.tgz",
- "integrity": "sha512-Lg4Vx1XZQauB42Hw3kK7JM6yjVjgFmFC5/Ab797s79aARomD2nEErc4mCgM8EZrARLmmbWpi5DGCadmK50DcAQ==",
- "dependencies": {
- "tslib": "^2.0.0"
- },
- "engines": {
- "node": ">=10"
- },
- "peerDependencies": {
- "@types/react": "^16.8.0 || ^17.0.0 || ^18.0.0",
- "react": "^16.8.0 || ^17.0.0 || ^18.0.0"
- },
- "peerDependenciesMeta": {
- "@types/react": {
- "optional": true
- }
- }
- },
- "node_modules/use-sidecar": {
- "version": "1.1.2",
- "resolved": "https://registry.npmjs.org/use-sidecar/-/use-sidecar-1.1.2.tgz",
- "integrity": "sha512-epTbsLuzZ7lPClpz2TyryBfztm7m+28DlEv2ZCQ3MDr5ssiwyOwGH/e5F9CkfWjJ1t4clvI58yF822/GUkjjhw==",
- "dependencies": {
- "detect-node-es": "^1.1.0",
- "tslib": "^2.0.0"
- },
- "engines": {
- "node": ">=10"
- },
- "peerDependencies": {
- "@types/react": "^16.9.0 || ^17.0.0 || ^18.0.0",
- "react": "^16.8.0 || ^17.0.0 || ^18.0.0"
- },
- "peerDependenciesMeta": {
- "@types/react": {
- "optional": true
- }
- }
- },
- "node_modules/use-sync-external-store": {
- "version": "1.2.0",
- "resolved": "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.2.0.tgz",
- "integrity": "sha512-eEgnFxGQ1Ife9bzYs6VLi8/4X6CObHMw9Qr9tPY43iKwsPw8xE8+EFsf/2cFZ5S3esXgpWgtSCtLNS41F+sKPA==",
- "peerDependencies": {
- "react": "^16.8.0 || ^17.0.0 || ^18.0.0"
- }
- },
- "node_modules/vite": {
- "version": "5.0.13",
- "resolved": "https://registry.npmjs.org/vite/-/vite-5.0.13.tgz",
- "integrity": "sha512-/9ovhv2M2dGTuA+dY93B9trfyWMDRQw2jdVBhHNP6wr0oF34wG2i/N55801iZIpgUpnHDm4F/FabGQLyc+eOgg==",
- "dev": true,
- "dependencies": {
- "esbuild": "^0.19.3",
- "postcss": "^8.4.32",
- "rollup": "^4.2.0"
- },
- "bin": {
- "vite": "bin/vite.js"
- },
- "engines": {
- "node": "^18.0.0 || >=20.0.0"
- },
- "funding": {
- "url": "https://github.com/vitejs/vite?sponsor=1"
- },
- "optionalDependencies": {
- "fsevents": "~2.3.3"
- },
- "peerDependencies": {
- "@types/node": "^18.0.0 || >=20.0.0",
- "less": "*",
- "lightningcss": "^1.21.0",
- "sass": "*",
- "stylus": "*",
- "sugarss": "*",
- "terser": "^5.4.0"
- },
- "peerDependenciesMeta": {
- "@types/node": {
- "optional": true
- },
- "less": {
- "optional": true
- },
- "lightningcss": {
- "optional": true
- },
- "sass": {
- "optional": true
- },
- "stylus": {
- "optional": true
- },
- "sugarss": {
- "optional": true
- },
- "terser": {
- "optional": true
- }
- }
- },
- "node_modules/wordwrap": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz",
- "integrity": "sha512-gvVzJFlPycKc5dZN4yPkP8w7Dc37BtP1yczEneOb4uq34pXZcvrtRTmWV8W+Ume+XCxKgbjM+nevkyFPMybd4Q==",
- "dev": true
- },
- "node_modules/yaml": {
- "version": "1.10.2",
- "resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.2.tgz",
- "integrity": "sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==",
- "engines": {
- "node": ">= 6"
- }
- },
- "node_modules/zod": {
- "version": "3.22.4",
- "resolved": "https://registry.npmjs.org/zod/-/zod-3.22.4.tgz",
- "integrity": "sha512-iC+8Io04lddc+mVqQ9AZ7OQ2MrUKGN+oIQyq1vemgt46jwCwLfhq7/pwnBnNXXXZb8VTVLKwp9EDkx+ryxIWmg==",
- "dev": true,
- "funding": {
- "url": "https://github.com/sponsors/colinhacks"
- }
- }
- },
- "dependencies": {
- "@babel/code-frame": {
- "version": "7.23.5",
- "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.23.5.tgz",
- "integrity": "sha512-CgH3s1a96LipHCmSUmYFPwY7MNx8C3avkq7i4Wl3cfa662ldtUe4VM1TPXX70pfmrlWTb6jLqTYrZyT2ZTJBgA==",
- "requires": {
- "@babel/highlight": "^7.23.4",
- "chalk": "^2.4.2"
- },
- "dependencies": {
- "ansi-styles": {
- "version": "3.2.1",
- "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz",
- "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==",
- "requires": {
- "color-convert": "^1.9.0"
- }
- },
- "chalk": {
- "version": "2.4.2",
- "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz",
- "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==",
- "requires": {
- "ansi-styles": "^3.2.1",
- "escape-string-regexp": "^1.0.5",
- "supports-color": "^5.3.0"
- }
- },
- "color-convert": {
- "version": "1.9.3",
- "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz",
- "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==",
- "requires": {
- "color-name": "1.1.3"
- }
- },
- "color-name": {
- "version": "1.1.3",
- "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz",
- "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw=="
- },
- "escape-string-regexp": {
- "version": "1.0.5",
- "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz",
- "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg=="
- },
- "has-flag": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz",
- "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw=="
- },
- "supports-color": {
- "version": "5.5.0",
- "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz",
- "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==",
- "requires": {
- "has-flag": "^3.0.0"
- }
- }
- }
- },
- "@babel/helper-module-imports": {
- "version": "7.22.15",
- "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.22.15.tgz",
- "integrity": "sha512-0pYVBnDKZO2fnSPCrgM/6WMc7eS20Fbok+0r88fp+YtWVLZrp4CkafFGIp+W0VKw4a22sgebPT99y+FDNMdP4w==",
- "requires": {
- "@babel/types": "^7.22.15"
- }
- },
- "@babel/helper-string-parser": {
- "version": "7.23.4",
- "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.23.4.tgz",
- "integrity": "sha512-803gmbQdqwdf4olxrX4AJyFBV/RTr3rSmOj0rKwesmzlfhYNDEs+/iOcznzpNWlJlIlTJC2QfPFcHB6DlzdVLQ=="
- },
- "@babel/helper-validator-identifier": {
- "version": "7.22.20",
- "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.20.tgz",
- "integrity": "sha512-Y4OZ+ytlatR8AI+8KZfKuL5urKp7qey08ha31L8b3BwewJAoJamTzyvxPR/5D+KkdJCGPq/+8TukHBlY10FX9A=="
- },
- "@babel/highlight": {
- "version": "7.23.4",
- "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.23.4.tgz",
- "integrity": "sha512-acGdbYSfp2WheJoJm/EBBBLh/ID8KDc64ISZ9DYtBmC8/Q204PZJLHyzeB5qMzJ5trcOkybd78M4x2KWsUq++A==",
- "requires": {
- "@babel/helper-validator-identifier": "^7.22.20",
- "chalk": "^2.4.2",
- "js-tokens": "^4.0.0"
- },
- "dependencies": {
- "ansi-styles": {
- "version": "3.2.1",
- "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz",
- "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==",
- "requires": {
- "color-convert": "^1.9.0"
- }
- },
- "chalk": {
- "version": "2.4.2",
- "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz",
- "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==",
- "requires": {
- "ansi-styles": "^3.2.1",
- "escape-string-regexp": "^1.0.5",
- "supports-color": "^5.3.0"
- }
- },
- "color-convert": {
- "version": "1.9.3",
- "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz",
- "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==",
- "requires": {
- "color-name": "1.1.3"
- }
- },
- "color-name": {
- "version": "1.1.3",
- "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz",
- "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw=="
- },
- "escape-string-regexp": {
- "version": "1.0.5",
- "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz",
- "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg=="
- },
- "has-flag": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz",
- "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw=="
- },
- "supports-color": {
- "version": "5.5.0",
- "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz",
- "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==",
- "requires": {
- "has-flag": "^3.0.0"
- }
- }
- }
- },
- "@babel/runtime": {
- "version": "7.23.9",
- "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.23.9.tgz",
- "integrity": "sha512-0CX6F+BI2s9dkUqr08KFrAIZgNFj75rdBU/DjCyYLIaV/quFjkk6T+EJ2LkZHyZTbEV4L5p97mNkUsHl2wLFAw==",
- "requires": {
- "regenerator-runtime": "^0.14.0"
- }
- },
- "@babel/types": {
- "version": "7.23.9",
- "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.23.9.tgz",
- "integrity": "sha512-dQjSq/7HaSjRM43FFGnv5keM2HsxpmyV1PfaSVm0nzzjwwTmjOe6J4bC8e3+pTEIgHaHj+1ZlLThRJ2auc/w1Q==",
- "requires": {
- "@babel/helper-string-parser": "^7.23.4",
- "@babel/helper-validator-identifier": "^7.22.20",
- "to-fast-properties": "^2.0.0"
- }
- },
- "@biomejs/biome": {
- "version": "1.6.1",
- "resolved": "https://registry.npmjs.org/@biomejs/biome/-/biome-1.6.1.tgz",
- "integrity": "sha512-SILQvA2S0XeaOuu1bivv6fQmMo7zMfr2xqDEN+Sz78pGbAKZnGmg0emsXjQWoBY/RVm9kPCgX+aGEpZZTYaM7w==",
- "dev": true,
- "requires": {
- "@biomejs/cli-darwin-arm64": "1.6.1",
- "@biomejs/cli-darwin-x64": "1.6.1",
- "@biomejs/cli-linux-arm64": "1.6.1",
- "@biomejs/cli-linux-arm64-musl": "1.6.1",
- "@biomejs/cli-linux-x64": "1.6.1",
- "@biomejs/cli-linux-x64-musl": "1.6.1",
- "@biomejs/cli-win32-arm64": "1.6.1",
- "@biomejs/cli-win32-x64": "1.6.1"
- }
- },
- "@biomejs/cli-darwin-arm64": {
- "version": "1.6.1",
- "resolved": "https://registry.npmjs.org/@biomejs/cli-darwin-arm64/-/cli-darwin-arm64-1.6.1.tgz",
- "integrity": "sha512-KlvY00iB9T/vFi4m/GXxEyYkYnYy6aw06uapzUIIdiMMj7I/pmZu7CsZlzWdekVD0j+SsQbxdZMsb0wPhnRSsg==",
- "dev": true,
- "optional": true
- },
- "@biomejs/cli-darwin-x64": {
- "version": "1.6.1",
- "resolved": "https://registry.npmjs.org/@biomejs/cli-darwin-x64/-/cli-darwin-x64-1.6.1.tgz",
- "integrity": "sha512-jP4E8TXaQX5e3nvRJSzB+qicZrdIDCrjR0sSb1DaDTx4JPZH5WXq/BlTqAyWi3IijM+IYMjWqAAK4kOHsSCzxw==",
- "dev": true,
- "optional": true
- },
- "@biomejs/cli-linux-arm64": {
- "version": "1.6.1",
- "resolved": "https://registry.npmjs.org/@biomejs/cli-linux-arm64/-/cli-linux-arm64-1.6.1.tgz",
- "integrity": "sha512-nxD1UyX3bWSl/RSKlib/JsOmt+652/9yieogdSC/UTLgVCZYOF7u8L/LK7kAa0Y4nA8zSPavAQTgko7mHC2ObA==",
- "dev": true,
- "optional": true
- },
- "@biomejs/cli-linux-arm64-musl": {
- "version": "1.6.1",
- "resolved": "https://registry.npmjs.org/@biomejs/cli-linux-arm64-musl/-/cli-linux-arm64-musl-1.6.1.tgz",
- "integrity": "sha512-YdkDgFecdHJg7PJxAMaZIixVWGB6St4yH08BHagO0fEhNNiY8cAKEVo2mcXlsnEiTMpeSEAY9VxLUrVT3IVxpw==",
- "dev": true,
- "optional": true
- },
- "@biomejs/cli-linux-x64": {
- "version": "1.6.1",
- "resolved": "https://registry.npmjs.org/@biomejs/cli-linux-x64/-/cli-linux-x64-1.6.1.tgz",
- "integrity": "sha512-BYAzenlMF3QdngjNFw9QVBXKGNzeecqwF3pwDgUGEvU7OJpn1/lyVkJVxYPtVGRNdjQ9e6l/s8NjKuBpW/ZR4Q==",
- "dev": true,
- "optional": true
- },
- "@biomejs/cli-linux-x64-musl": {
- "version": "1.6.1",
- "resolved": "https://registry.npmjs.org/@biomejs/cli-linux-x64-musl/-/cli-linux-x64-musl-1.6.1.tgz",
- "integrity": "sha512-aSISIDmxq04NNy7tm4x9rBk2vH0ub2VDIE4outEmdC2LBtEJoINiphlZagx/FvjbsqUfygent9QUSn0oREnAXg==",
- "dev": true,
- "optional": true
- },
- "@biomejs/cli-win32-arm64": {
- "version": "1.6.1",
- "resolved": "https://registry.npmjs.org/@biomejs/cli-win32-arm64/-/cli-win32-arm64-1.6.1.tgz",
- "integrity": "sha512-/eCHQKZ1kEawUpkSuXq4urtxMsD1P1678OPG3zNKt3ru16AqqspLdO3jzBe3k74xCPYnQ36e9Yqc97Mo0qgPtg==",
- "dev": true,
- "optional": true
- },
- "@biomejs/cli-win32-x64": {
- "version": "1.6.1",
- "resolved": "https://registry.npmjs.org/@biomejs/cli-win32-x64/-/cli-win32-x64-1.6.1.tgz",
- "integrity": "sha512-5TUZbzBwnDLFxLVGEPsorNi6eC2Gt+z4Oei9Qvq0M/4c4/mjZ96ABgwao/tMxf4ZBr/qyy2YdvF+gX9Rc+xC0A==",
- "dev": true,
- "optional": true
- },
- "@chakra-ui/accordion": {
- "version": "2.3.1",
- "resolved": "https://registry.npmjs.org/@chakra-ui/accordion/-/accordion-2.3.1.tgz",
- "integrity": "sha512-FSXRm8iClFyU+gVaXisOSEw0/4Q+qZbFRiuhIAkVU6Boj0FxAMrlo9a8AV5TuF77rgaHytCdHk0Ng+cyUijrag==",
- "requires": {
- "@chakra-ui/descendant": "3.1.0",
- "@chakra-ui/icon": "3.2.0",
- "@chakra-ui/react-context": "2.1.0",
- "@chakra-ui/react-use-controllable-state": "2.1.0",
- "@chakra-ui/react-use-merge-refs": "2.1.0",
- "@chakra-ui/shared-utils": "2.0.5",
- "@chakra-ui/transition": "2.1.0"
- }
- },
- "@chakra-ui/alert": {
- "version": "2.2.2",
- "resolved": "https://registry.npmjs.org/@chakra-ui/alert/-/alert-2.2.2.tgz",
- "integrity": "sha512-jHg4LYMRNOJH830ViLuicjb3F+v6iriE/2G5T+Sd0Hna04nukNJ1MxUmBPE+vI22me2dIflfelu2v9wdB6Pojw==",
- "requires": {
- "@chakra-ui/icon": "3.2.0",
- "@chakra-ui/react-context": "2.1.0",
- "@chakra-ui/shared-utils": "2.0.5",
- "@chakra-ui/spinner": "2.1.0"
- }
- },
- "@chakra-ui/anatomy": {
- "version": "2.2.2",
- "resolved": "https://registry.npmjs.org/@chakra-ui/anatomy/-/anatomy-2.2.2.tgz",
- "integrity": "sha512-MV6D4VLRIHr4PkW4zMyqfrNS1mPlCTiCXwvYGtDFQYr+xHFfonhAuf9WjsSc0nyp2m0OdkSLnzmVKkZFLo25Tg=="
- },
- "@chakra-ui/avatar": {
- "version": "2.3.0",
- "resolved": "https://registry.npmjs.org/@chakra-ui/avatar/-/avatar-2.3.0.tgz",
- "integrity": "sha512-8gKSyLfygnaotbJbDMHDiJoF38OHXUYVme4gGxZ1fLnQEdPVEaIWfH+NndIjOM0z8S+YEFnT9KyGMUtvPrBk3g==",
- "requires": {
- "@chakra-ui/image": "2.1.0",
- "@chakra-ui/react-children-utils": "2.0.6",
- "@chakra-ui/react-context": "2.1.0",
- "@chakra-ui/shared-utils": "2.0.5"
- }
- },
- "@chakra-ui/breadcrumb": {
- "version": "2.2.0",
- "resolved": "https://registry.npmjs.org/@chakra-ui/breadcrumb/-/breadcrumb-2.2.0.tgz",
- "integrity": "sha512-4cWCG24flYBxjruRi4RJREWTGF74L/KzI2CognAW/d/zWR0CjiScuJhf37Am3LFbCySP6WSoyBOtTIoTA4yLEA==",
- "requires": {
- "@chakra-ui/react-children-utils": "2.0.6",
- "@chakra-ui/react-context": "2.1.0",
- "@chakra-ui/shared-utils": "2.0.5"
- }
- },
- "@chakra-ui/breakpoint-utils": {
- "version": "2.0.8",
- "resolved": "https://registry.npmjs.org/@chakra-ui/breakpoint-utils/-/breakpoint-utils-2.0.8.tgz",
- "integrity": "sha512-Pq32MlEX9fwb5j5xx8s18zJMARNHlQZH2VH1RZgfgRDpp7DcEgtRW5AInfN5CfqdHLO1dGxA7I3MqEuL5JnIsA==",
- "requires": {
- "@chakra-ui/shared-utils": "2.0.5"
- }
- },
- "@chakra-ui/button": {
- "version": "2.1.0",
- "resolved": "https://registry.npmjs.org/@chakra-ui/button/-/button-2.1.0.tgz",
- "integrity": "sha512-95CplwlRKmmUXkdEp/21VkEWgnwcx2TOBG6NfYlsuLBDHSLlo5FKIiE2oSi4zXc4TLcopGcWPNcm/NDaSC5pvA==",
- "requires": {
- "@chakra-ui/react-context": "2.1.0",
- "@chakra-ui/react-use-merge-refs": "2.1.0",
- "@chakra-ui/shared-utils": "2.0.5",
- "@chakra-ui/spinner": "2.1.0"
- }
- },
- "@chakra-ui/card": {
- "version": "2.2.0",
- "resolved": "https://registry.npmjs.org/@chakra-ui/card/-/card-2.2.0.tgz",
- "integrity": "sha512-xUB/k5MURj4CtPAhdSoXZidUbm8j3hci9vnc+eZJVDqhDOShNlD6QeniQNRPRys4lWAQLCbFcrwL29C8naDi6g==",
- "requires": {
- "@chakra-ui/shared-utils": "2.0.5"
- }
- },
- "@chakra-ui/checkbox": {
- "version": "2.3.2",
- "resolved": "https://registry.npmjs.org/@chakra-ui/checkbox/-/checkbox-2.3.2.tgz",
- "integrity": "sha512-85g38JIXMEv6M+AcyIGLh7igNtfpAN6KGQFYxY9tBj0eWvWk4NKQxvqqyVta0bSAyIl1rixNIIezNpNWk2iO4g==",
- "requires": {
- "@chakra-ui/form-control": "2.2.0",
- "@chakra-ui/react-context": "2.1.0",
- "@chakra-ui/react-types": "2.0.7",
- "@chakra-ui/react-use-callback-ref": "2.1.0",
- "@chakra-ui/react-use-controllable-state": "2.1.0",
- "@chakra-ui/react-use-merge-refs": "2.1.0",
- "@chakra-ui/react-use-safe-layout-effect": "2.1.0",
- "@chakra-ui/react-use-update-effect": "2.1.0",
- "@chakra-ui/shared-utils": "2.0.5",
- "@chakra-ui/visually-hidden": "2.2.0",
- "@zag-js/focus-visible": "0.16.0"
- }
- },
- "@chakra-ui/clickable": {
- "version": "2.1.0",
- "resolved": "https://registry.npmjs.org/@chakra-ui/clickable/-/clickable-2.1.0.tgz",
- "integrity": "sha512-flRA/ClPUGPYabu+/GLREZVZr9j2uyyazCAUHAdrTUEdDYCr31SVGhgh7dgKdtq23bOvAQJpIJjw/0Bs0WvbXw==",
- "requires": {
- "@chakra-ui/react-use-merge-refs": "2.1.0",
- "@chakra-ui/shared-utils": "2.0.5"
- }
- },
- "@chakra-ui/close-button": {
- "version": "2.1.1",
- "resolved": "https://registry.npmjs.org/@chakra-ui/close-button/-/close-button-2.1.1.tgz",
- "integrity": "sha512-gnpENKOanKexswSVpVz7ojZEALl2x5qjLYNqSQGbxz+aP9sOXPfUS56ebyBrre7T7exuWGiFeRwnM0oVeGPaiw==",
- "requires": {
- "@chakra-ui/icon": "3.2.0"
- }
- },
- "@chakra-ui/color-mode": {
- "version": "2.2.0",
- "resolved": "https://registry.npmjs.org/@chakra-ui/color-mode/-/color-mode-2.2.0.tgz",
- "integrity": "sha512-niTEA8PALtMWRI9wJ4LL0CSBDo8NBfLNp4GD6/0hstcm3IlbBHTVKxN6HwSaoNYfphDQLxCjT4yG+0BJA5tFpg==",
- "requires": {
- "@chakra-ui/react-use-safe-layout-effect": "2.1.0"
- }
- },
- "@chakra-ui/control-box": {
- "version": "2.1.0",
- "resolved": "https://registry.npmjs.org/@chakra-ui/control-box/-/control-box-2.1.0.tgz",
- "integrity": "sha512-gVrRDyXFdMd8E7rulL0SKeoljkLQiPITFnsyMO8EFHNZ+AHt5wK4LIguYVEq88APqAGZGfHFWXr79RYrNiE3Mg==",
- "requires": {}
- },
- "@chakra-ui/counter": {
- "version": "2.1.0",
- "resolved": "https://registry.npmjs.org/@chakra-ui/counter/-/counter-2.1.0.tgz",
- "integrity": "sha512-s6hZAEcWT5zzjNz2JIWUBzRubo9la/oof1W7EKZVVfPYHERnl5e16FmBC79Yfq8p09LQ+aqFKm/etYoJMMgghw==",
- "requires": {
- "@chakra-ui/number-utils": "2.0.7",
- "@chakra-ui/react-use-callback-ref": "2.1.0",
- "@chakra-ui/shared-utils": "2.0.5"
- }
- },
- "@chakra-ui/css-reset": {
- "version": "2.3.0",
- "resolved": "https://registry.npmjs.org/@chakra-ui/css-reset/-/css-reset-2.3.0.tgz",
- "integrity": "sha512-cQwwBy5O0jzvl0K7PLTLgp8ijqLPKyuEMiDXwYzl95seD3AoeuoCLyzZcJtVqaUZ573PiBdAbY/IlZcwDOItWg==",
- "requires": {}
- },
- "@chakra-ui/descendant": {
- "version": "3.1.0",
- "resolved": "https://registry.npmjs.org/@chakra-ui/descendant/-/descendant-3.1.0.tgz",
- "integrity": "sha512-VxCIAir08g5w27klLyi7PVo8BxhW4tgU/lxQyujkmi4zx7hT9ZdrcQLAted/dAa+aSIZ14S1oV0Q9lGjsAdxUQ==",
- "requires": {
- "@chakra-ui/react-context": "2.1.0",
- "@chakra-ui/react-use-merge-refs": "2.1.0"
- }
- },
- "@chakra-ui/dom-utils": {
- "version": "2.1.0",
- "resolved": "https://registry.npmjs.org/@chakra-ui/dom-utils/-/dom-utils-2.1.0.tgz",
- "integrity": "sha512-ZmF2qRa1QZ0CMLU8M1zCfmw29DmPNtfjR9iTo74U5FPr3i1aoAh7fbJ4qAlZ197Xw9eAW28tvzQuoVWeL5C7fQ=="
- },
- "@chakra-ui/editable": {
- "version": "3.1.0",
- "resolved": "https://registry.npmjs.org/@chakra-ui/editable/-/editable-3.1.0.tgz",
- "integrity": "sha512-j2JLrUL9wgg4YA6jLlbU88370eCRyor7DZQD9lzpY95tSOXpTljeg3uF9eOmDnCs6fxp3zDWIfkgMm/ExhcGTg==",
- "requires": {
- "@chakra-ui/react-context": "2.1.0",
- "@chakra-ui/react-types": "2.0.7",
- "@chakra-ui/react-use-callback-ref": "2.1.0",
- "@chakra-ui/react-use-controllable-state": "2.1.0",
- "@chakra-ui/react-use-focus-on-pointer-down": "2.1.0",
- "@chakra-ui/react-use-merge-refs": "2.1.0",
- "@chakra-ui/react-use-safe-layout-effect": "2.1.0",
- "@chakra-ui/react-use-update-effect": "2.1.0",
- "@chakra-ui/shared-utils": "2.0.5"
- }
- },
- "@chakra-ui/event-utils": {
- "version": "2.0.8",
- "resolved": "https://registry.npmjs.org/@chakra-ui/event-utils/-/event-utils-2.0.8.tgz",
- "integrity": "sha512-IGM/yGUHS+8TOQrZGpAKOJl/xGBrmRYJrmbHfUE7zrG3PpQyXvbLDP1M+RggkCFVgHlJi2wpYIf0QtQlU0XZfw=="
- },
- "@chakra-ui/focus-lock": {
- "version": "2.1.0",
- "resolved": "https://registry.npmjs.org/@chakra-ui/focus-lock/-/focus-lock-2.1.0.tgz",
- "integrity": "sha512-EmGx4PhWGjm4dpjRqM4Aa+rCWBxP+Rq8Uc/nAVnD4YVqkEhBkrPTpui2lnjsuxqNaZ24fIAZ10cF1hlpemte/w==",
- "requires": {
- "@chakra-ui/dom-utils": "2.1.0",
- "react-focus-lock": "^2.9.4"
- }
- },
- "@chakra-ui/form-control": {
- "version": "2.2.0",
- "resolved": "https://registry.npmjs.org/@chakra-ui/form-control/-/form-control-2.2.0.tgz",
- "integrity": "sha512-wehLC1t4fafCVJ2RvJQT2jyqsAwX7KymmiGqBu7nQoQz8ApTkGABWpo/QwDh3F/dBLrouHDoOvGmYTqft3Mirw==",
- "requires": {
- "@chakra-ui/icon": "3.2.0",
- "@chakra-ui/react-context": "2.1.0",
- "@chakra-ui/react-types": "2.0.7",
- "@chakra-ui/react-use-merge-refs": "2.1.0",
- "@chakra-ui/shared-utils": "2.0.5"
- }
- },
- "@chakra-ui/hooks": {
- "version": "2.2.1",
- "resolved": "https://registry.npmjs.org/@chakra-ui/hooks/-/hooks-2.2.1.tgz",
- "integrity": "sha512-RQbTnzl6b1tBjbDPf9zGRo9rf/pQMholsOudTxjy4i9GfTfz6kgp5ValGjQm2z7ng6Z31N1cnjZ1AlSzQ//ZfQ==",
- "requires": {
- "@chakra-ui/react-utils": "2.0.12",
- "@chakra-ui/utils": "2.0.15",
- "compute-scroll-into-view": "3.0.3",
- "copy-to-clipboard": "3.3.3"
- }
- },
- "@chakra-ui/icon": {
- "version": "3.2.0",
- "resolved": "https://registry.npmjs.org/@chakra-ui/icon/-/icon-3.2.0.tgz",
- "integrity": "sha512-xxjGLvlX2Ys4H0iHrI16t74rG9EBcpFvJ3Y3B7KMQTrnW34Kf7Da/UC8J67Gtx85mTHW020ml85SVPKORWNNKQ==",
- "requires": {
- "@chakra-ui/shared-utils": "2.0.5"
- }
- },
- "@chakra-ui/icons": {
- "version": "2.1.1",
- "resolved": "https://registry.npmjs.org/@chakra-ui/icons/-/icons-2.1.1.tgz",
- "integrity": "sha512-3p30hdo4LlRZTT5CwoAJq3G9fHI0wDc0pBaMHj4SUn0yomO+RcDRlzhdXqdr5cVnzax44sqXJVnf3oQG0eI+4g==",
- "requires": {
- "@chakra-ui/icon": "3.2.0"
- }
- },
- "@chakra-ui/image": {
- "version": "2.1.0",
- "resolved": "https://registry.npmjs.org/@chakra-ui/image/-/image-2.1.0.tgz",
- "integrity": "sha512-bskumBYKLiLMySIWDGcz0+D9Th0jPvmX6xnRMs4o92tT3Od/bW26lahmV2a2Op2ItXeCmRMY+XxJH5Gy1i46VA==",
- "requires": {
- "@chakra-ui/react-use-safe-layout-effect": "2.1.0",
- "@chakra-ui/shared-utils": "2.0.5"
- }
- },
- "@chakra-ui/input": {
- "version": "2.1.2",
- "resolved": "https://registry.npmjs.org/@chakra-ui/input/-/input-2.1.2.tgz",
- "integrity": "sha512-GiBbb3EqAA8Ph43yGa6Mc+kUPjh4Spmxp1Pkelr8qtudpc3p2PJOOebLpd90mcqw8UePPa+l6YhhPtp6o0irhw==",
- "requires": {
- "@chakra-ui/form-control": "2.2.0",
- "@chakra-ui/object-utils": "2.1.0",
- "@chakra-ui/react-children-utils": "2.0.6",
- "@chakra-ui/react-context": "2.1.0",
- "@chakra-ui/shared-utils": "2.0.5"
- }
- },
- "@chakra-ui/layout": {
- "version": "2.3.1",
- "resolved": "https://registry.npmjs.org/@chakra-ui/layout/-/layout-2.3.1.tgz",
- "integrity": "sha512-nXuZ6WRbq0WdgnRgLw+QuxWAHuhDtVX8ElWqcTK+cSMFg/52eVP47czYBE5F35YhnoW2XBwfNoNgZ7+e8Z01Rg==",
- "requires": {
- "@chakra-ui/breakpoint-utils": "2.0.8",
- "@chakra-ui/icon": "3.2.0",
- "@chakra-ui/object-utils": "2.1.0",
- "@chakra-ui/react-children-utils": "2.0.6",
- "@chakra-ui/react-context": "2.1.0",
- "@chakra-ui/shared-utils": "2.0.5"
- }
- },
- "@chakra-ui/lazy-utils": {
- "version": "2.0.5",
- "resolved": "https://registry.npmjs.org/@chakra-ui/lazy-utils/-/lazy-utils-2.0.5.tgz",
- "integrity": "sha512-UULqw7FBvcckQk2n3iPO56TMJvDsNv0FKZI6PlUNJVaGsPbsYxK/8IQ60vZgaTVPtVcjY6BE+y6zg8u9HOqpyg=="
- },
- "@chakra-ui/live-region": {
- "version": "2.1.0",
- "resolved": "https://registry.npmjs.org/@chakra-ui/live-region/-/live-region-2.1.0.tgz",
- "integrity": "sha512-ZOxFXwtaLIsXjqnszYYrVuswBhnIHHP+XIgK1vC6DePKtyK590Wg+0J0slDwThUAd4MSSIUa/nNX84x1GMphWw==",
- "requires": {}
- },
- "@chakra-ui/media-query": {
- "version": "3.3.0",
- "resolved": "https://registry.npmjs.org/@chakra-ui/media-query/-/media-query-3.3.0.tgz",
- "integrity": "sha512-IsTGgFLoICVoPRp9ykOgqmdMotJG0CnPsKvGQeSFOB/dZfIujdVb14TYxDU4+MURXry1MhJ7LzZhv+Ml7cr8/g==",
- "requires": {
- "@chakra-ui/breakpoint-utils": "2.0.8",
- "@chakra-ui/react-env": "3.1.0",
- "@chakra-ui/shared-utils": "2.0.5"
- }
- },
- "@chakra-ui/menu": {
- "version": "2.2.1",
- "resolved": "https://registry.npmjs.org/@chakra-ui/menu/-/menu-2.2.1.tgz",
- "integrity": "sha512-lJS7XEObzJxsOwWQh7yfG4H8FzFPRP5hVPN/CL+JzytEINCSBvsCDHrYPQGp7jzpCi8vnTqQQGQe0f8dwnXd2g==",
- "requires": {
- "@chakra-ui/clickable": "2.1.0",
- "@chakra-ui/descendant": "3.1.0",
- "@chakra-ui/lazy-utils": "2.0.5",
- "@chakra-ui/popper": "3.1.0",
- "@chakra-ui/react-children-utils": "2.0.6",
- "@chakra-ui/react-context": "2.1.0",
- "@chakra-ui/react-use-animation-state": "2.1.0",
- "@chakra-ui/react-use-controllable-state": "2.1.0",
- "@chakra-ui/react-use-disclosure": "2.1.0",
- "@chakra-ui/react-use-focus-effect": "2.1.0",
- "@chakra-ui/react-use-merge-refs": "2.1.0",
- "@chakra-ui/react-use-outside-click": "2.2.0",
- "@chakra-ui/react-use-update-effect": "2.1.0",
- "@chakra-ui/shared-utils": "2.0.5",
- "@chakra-ui/transition": "2.1.0"
- }
- },
- "@chakra-ui/modal": {
- "version": "2.3.1",
- "resolved": "https://registry.npmjs.org/@chakra-ui/modal/-/modal-2.3.1.tgz",
- "integrity": "sha512-TQv1ZaiJMZN+rR9DK0snx/OPwmtaGH1HbZtlYt4W4s6CzyK541fxLRTjIXfEzIGpvNW+b6VFuFjbcR78p4DEoQ==",
- "requires": {
- "@chakra-ui/close-button": "2.1.1",
- "@chakra-ui/focus-lock": "2.1.0",
- "@chakra-ui/portal": "2.1.0",
- "@chakra-ui/react-context": "2.1.0",
- "@chakra-ui/react-types": "2.0.7",
- "@chakra-ui/react-use-merge-refs": "2.1.0",
- "@chakra-ui/shared-utils": "2.0.5",
- "@chakra-ui/transition": "2.1.0",
- "aria-hidden": "^1.2.3",
- "react-remove-scroll": "^2.5.6"
- }
- },
- "@chakra-ui/number-input": {
- "version": "2.1.2",
- "resolved": "https://registry.npmjs.org/@chakra-ui/number-input/-/number-input-2.1.2.tgz",
- "integrity": "sha512-pfOdX02sqUN0qC2ysuvgVDiws7xZ20XDIlcNhva55Jgm095xjm8eVdIBfNm3SFbSUNxyXvLTW/YQanX74tKmuA==",
- "requires": {
- "@chakra-ui/counter": "2.1.0",
- "@chakra-ui/form-control": "2.2.0",
- "@chakra-ui/icon": "3.2.0",
- "@chakra-ui/react-context": "2.1.0",
- "@chakra-ui/react-types": "2.0.7",
- "@chakra-ui/react-use-callback-ref": "2.1.0",
- "@chakra-ui/react-use-event-listener": "2.1.0",
- "@chakra-ui/react-use-interval": "2.1.0",
- "@chakra-ui/react-use-merge-refs": "2.1.0",
- "@chakra-ui/react-use-safe-layout-effect": "2.1.0",
- "@chakra-ui/react-use-update-effect": "2.1.0",
- "@chakra-ui/shared-utils": "2.0.5"
- }
- },
- "@chakra-ui/number-utils": {
- "version": "2.0.7",
- "resolved": "https://registry.npmjs.org/@chakra-ui/number-utils/-/number-utils-2.0.7.tgz",
- "integrity": "sha512-yOGxBjXNvLTBvQyhMDqGU0Oj26s91mbAlqKHiuw737AXHt0aPllOthVUqQMeaYLwLCjGMg0jtI7JReRzyi94Dg=="
- },
- "@chakra-ui/object-utils": {
- "version": "2.1.0",
- "resolved": "https://registry.npmjs.org/@chakra-ui/object-utils/-/object-utils-2.1.0.tgz",
- "integrity": "sha512-tgIZOgLHaoti5PYGPTwK3t/cqtcycW0owaiOXoZOcpwwX/vlVb+H1jFsQyWiiwQVPt9RkoSLtxzXamx+aHH+bQ=="
- },
- "@chakra-ui/pin-input": {
- "version": "2.1.0",
- "resolved": "https://registry.npmjs.org/@chakra-ui/pin-input/-/pin-input-2.1.0.tgz",
- "integrity": "sha512-x4vBqLStDxJFMt+jdAHHS8jbh294O53CPQJoL4g228P513rHylV/uPscYUHrVJXRxsHfRztQO9k45jjTYaPRMw==",
- "requires": {
- "@chakra-ui/descendant": "3.1.0",
- "@chakra-ui/react-children-utils": "2.0.6",
- "@chakra-ui/react-context": "2.1.0",
- "@chakra-ui/react-use-controllable-state": "2.1.0",
- "@chakra-ui/react-use-merge-refs": "2.1.0",
- "@chakra-ui/shared-utils": "2.0.5"
- }
- },
- "@chakra-ui/popover": {
- "version": "2.2.1",
- "resolved": "https://registry.npmjs.org/@chakra-ui/popover/-/popover-2.2.1.tgz",
- "integrity": "sha512-K+2ai2dD0ljvJnlrzesCDT9mNzLifE3noGKZ3QwLqd/K34Ym1W/0aL1ERSynrcG78NKoXS54SdEzkhCZ4Gn/Zg==",
- "requires": {
- "@chakra-ui/close-button": "2.1.1",
- "@chakra-ui/lazy-utils": "2.0.5",
- "@chakra-ui/popper": "3.1.0",
- "@chakra-ui/react-context": "2.1.0",
- "@chakra-ui/react-types": "2.0.7",
- "@chakra-ui/react-use-animation-state": "2.1.0",
- "@chakra-ui/react-use-disclosure": "2.1.0",
- "@chakra-ui/react-use-focus-effect": "2.1.0",
- "@chakra-ui/react-use-focus-on-pointer-down": "2.1.0",
- "@chakra-ui/react-use-merge-refs": "2.1.0",
- "@chakra-ui/shared-utils": "2.0.5"
- }
- },
- "@chakra-ui/popper": {
- "version": "3.1.0",
- "resolved": "https://registry.npmjs.org/@chakra-ui/popper/-/popper-3.1.0.tgz",
- "integrity": "sha512-ciDdpdYbeFG7og6/6J8lkTFxsSvwTdMLFkpVylAF6VNC22jssiWfquj2eyD4rJnzkRFPvIWJq8hvbfhsm+AjSg==",
- "requires": {
- "@chakra-ui/react-types": "2.0.7",
- "@chakra-ui/react-use-merge-refs": "2.1.0",
- "@popperjs/core": "^2.9.3"
- }
- },
- "@chakra-ui/portal": {
- "version": "2.1.0",
- "resolved": "https://registry.npmjs.org/@chakra-ui/portal/-/portal-2.1.0.tgz",
- "integrity": "sha512-9q9KWf6SArEcIq1gGofNcFPSWEyl+MfJjEUg/un1SMlQjaROOh3zYr+6JAwvcORiX7tyHosnmWC3d3wI2aPSQg==",
- "requires": {
- "@chakra-ui/react-context": "2.1.0",
- "@chakra-ui/react-use-safe-layout-effect": "2.1.0"
- }
- },
- "@chakra-ui/progress": {
- "version": "2.2.0",
- "resolved": "https://registry.npmjs.org/@chakra-ui/progress/-/progress-2.2.0.tgz",
- "integrity": "sha512-qUXuKbuhN60EzDD9mHR7B67D7p/ZqNS2Aze4Pbl1qGGZfulPW0PY8Rof32qDtttDQBkzQIzFGE8d9QpAemToIQ==",
- "requires": {
- "@chakra-ui/react-context": "2.1.0"
- }
- },
- "@chakra-ui/provider": {
- "version": "2.4.2",
- "resolved": "https://registry.npmjs.org/@chakra-ui/provider/-/provider-2.4.2.tgz",
- "integrity": "sha512-w0Tef5ZCJK1mlJorcSjItCSbyvVuqpvyWdxZiVQmE6fvSJR83wZof42ux0+sfWD+I7rHSfj+f9nzhNaEWClysw==",
- "requires": {
- "@chakra-ui/css-reset": "2.3.0",
- "@chakra-ui/portal": "2.1.0",
- "@chakra-ui/react-env": "3.1.0",
- "@chakra-ui/system": "2.6.2",
- "@chakra-ui/utils": "2.0.15"
- }
- },
- "@chakra-ui/radio": {
- "version": "2.1.2",
- "resolved": "https://registry.npmjs.org/@chakra-ui/radio/-/radio-2.1.2.tgz",
- "integrity": "sha512-n10M46wJrMGbonaghvSRnZ9ToTv/q76Szz284gv4QUWvyljQACcGrXIONUnQ3BIwbOfkRqSk7Xl/JgZtVfll+w==",
- "requires": {
- "@chakra-ui/form-control": "2.2.0",
- "@chakra-ui/react-context": "2.1.0",
- "@chakra-ui/react-types": "2.0.7",
- "@chakra-ui/react-use-merge-refs": "2.1.0",
- "@chakra-ui/shared-utils": "2.0.5",
- "@zag-js/focus-visible": "0.16.0"
- }
- },
- "@chakra-ui/react": {
- "version": "2.8.2",
- "resolved": "https://registry.npmjs.org/@chakra-ui/react/-/react-2.8.2.tgz",
- "integrity": "sha512-Hn0moyxxyCDKuR9ywYpqgX8dvjqwu9ArwpIb9wHNYjnODETjLwazgNIliCVBRcJvysGRiV51U2/JtJVrpeCjUQ==",
- "requires": {
- "@chakra-ui/accordion": "2.3.1",
- "@chakra-ui/alert": "2.2.2",
- "@chakra-ui/avatar": "2.3.0",
- "@chakra-ui/breadcrumb": "2.2.0",
- "@chakra-ui/button": "2.1.0",
- "@chakra-ui/card": "2.2.0",
- "@chakra-ui/checkbox": "2.3.2",
- "@chakra-ui/close-button": "2.1.1",
- "@chakra-ui/control-box": "2.1.0",
- "@chakra-ui/counter": "2.1.0",
- "@chakra-ui/css-reset": "2.3.0",
- "@chakra-ui/editable": "3.1.0",
- "@chakra-ui/focus-lock": "2.1.0",
- "@chakra-ui/form-control": "2.2.0",
- "@chakra-ui/hooks": "2.2.1",
- "@chakra-ui/icon": "3.2.0",
- "@chakra-ui/image": "2.1.0",
- "@chakra-ui/input": "2.1.2",
- "@chakra-ui/layout": "2.3.1",
- "@chakra-ui/live-region": "2.1.0",
- "@chakra-ui/media-query": "3.3.0",
- "@chakra-ui/menu": "2.2.1",
- "@chakra-ui/modal": "2.3.1",
- "@chakra-ui/number-input": "2.1.2",
- "@chakra-ui/pin-input": "2.1.0",
- "@chakra-ui/popover": "2.2.1",
- "@chakra-ui/popper": "3.1.0",
- "@chakra-ui/portal": "2.1.0",
- "@chakra-ui/progress": "2.2.0",
- "@chakra-ui/provider": "2.4.2",
- "@chakra-ui/radio": "2.1.2",
- "@chakra-ui/react-env": "3.1.0",
- "@chakra-ui/select": "2.1.2",
- "@chakra-ui/skeleton": "2.1.0",
- "@chakra-ui/skip-nav": "2.1.0",
- "@chakra-ui/slider": "2.1.0",
- "@chakra-ui/spinner": "2.1.0",
- "@chakra-ui/stat": "2.1.1",
- "@chakra-ui/stepper": "2.3.1",
- "@chakra-ui/styled-system": "2.9.2",
- "@chakra-ui/switch": "2.1.2",
- "@chakra-ui/system": "2.6.2",
- "@chakra-ui/table": "2.1.0",
- "@chakra-ui/tabs": "3.0.0",
- "@chakra-ui/tag": "3.1.1",
- "@chakra-ui/textarea": "2.1.2",
- "@chakra-ui/theme": "3.3.1",
- "@chakra-ui/theme-utils": "2.0.21",
- "@chakra-ui/toast": "7.0.2",
- "@chakra-ui/tooltip": "2.3.1",
- "@chakra-ui/transition": "2.1.0",
- "@chakra-ui/utils": "2.0.15",
- "@chakra-ui/visually-hidden": "2.2.0"
- }
- },
- "@chakra-ui/react-children-utils": {
- "version": "2.0.6",
- "resolved": "https://registry.npmjs.org/@chakra-ui/react-children-utils/-/react-children-utils-2.0.6.tgz",
- "integrity": "sha512-QVR2RC7QsOsbWwEnq9YduhpqSFnZGvjjGREV8ygKi8ADhXh93C8azLECCUVgRJF2Wc+So1fgxmjLcbZfY2VmBA==",
- "requires": {}
- },
- "@chakra-ui/react-context": {
- "version": "2.1.0",
- "resolved": "https://registry.npmjs.org/@chakra-ui/react-context/-/react-context-2.1.0.tgz",
- "integrity": "sha512-iahyStvzQ4AOwKwdPReLGfDesGG+vWJfEsn0X/NoGph/SkN+HXtv2sCfYFFR9k7bb+Kvc6YfpLlSuLvKMHi2+w==",
- "requires": {}
- },
- "@chakra-ui/react-env": {
- "version": "3.1.0",
- "resolved": "https://registry.npmjs.org/@chakra-ui/react-env/-/react-env-3.1.0.tgz",
- "integrity": "sha512-Vr96GV2LNBth3+IKzr/rq1IcnkXv+MLmwjQH6C8BRtn3sNskgDFD5vLkVXcEhagzZMCh8FR3V/bzZPojBOyNhw==",
- "requires": {
- "@chakra-ui/react-use-safe-layout-effect": "2.1.0"
- }
- },
- "@chakra-ui/react-types": {
- "version": "2.0.7",
- "resolved": "https://registry.npmjs.org/@chakra-ui/react-types/-/react-types-2.0.7.tgz",
- "integrity": "sha512-12zv2qIZ8EHwiytggtGvo4iLT0APris7T0qaAWqzpUGS0cdUtR8W+V1BJ5Ocq+7tA6dzQ/7+w5hmXih61TuhWQ==",
- "requires": {}
- },
- "@chakra-ui/react-use-animation-state": {
- "version": "2.1.0",
- "resolved": "https://registry.npmjs.org/@chakra-ui/react-use-animation-state/-/react-use-animation-state-2.1.0.tgz",
- "integrity": "sha512-CFZkQU3gmDBwhqy0vC1ryf90BVHxVN8cTLpSyCpdmExUEtSEInSCGMydj2fvn7QXsz/za8JNdO2xxgJwxpLMtg==",
- "requires": {
- "@chakra-ui/dom-utils": "2.1.0",
- "@chakra-ui/react-use-event-listener": "2.1.0"
- }
- },
- "@chakra-ui/react-use-callback-ref": {
- "version": "2.1.0",
- "resolved": "https://registry.npmjs.org/@chakra-ui/react-use-callback-ref/-/react-use-callback-ref-2.1.0.tgz",
- "integrity": "sha512-efnJrBtGDa4YaxDzDE90EnKD3Vkh5a1t3w7PhnRQmsphLy3g2UieasoKTlT2Hn118TwDjIv5ZjHJW6HbzXA9wQ==",
- "requires": {}
- },
- "@chakra-ui/react-use-controllable-state": {
- "version": "2.1.0",
- "resolved": "https://registry.npmjs.org/@chakra-ui/react-use-controllable-state/-/react-use-controllable-state-2.1.0.tgz",
- "integrity": "sha512-QR/8fKNokxZUs4PfxjXuwl0fj/d71WPrmLJvEpCTkHjnzu7LnYvzoe2wB867IdooQJL0G1zBxl0Dq+6W1P3jpg==",
- "requires": {
- "@chakra-ui/react-use-callback-ref": "2.1.0"
- }
- },
- "@chakra-ui/react-use-disclosure": {
- "version": "2.1.0",
- "resolved": "https://registry.npmjs.org/@chakra-ui/react-use-disclosure/-/react-use-disclosure-2.1.0.tgz",
- "integrity": "sha512-Ax4pmxA9LBGMyEZJhhUZobg9C0t3qFE4jVF1tGBsrLDcdBeLR9fwOogIPY9Hf0/wqSlAryAimICbr5hkpa5GSw==",
- "requires": {
- "@chakra-ui/react-use-callback-ref": "2.1.0"
- }
- },
- "@chakra-ui/react-use-event-listener": {
- "version": "2.1.0",
- "resolved": "https://registry.npmjs.org/@chakra-ui/react-use-event-listener/-/react-use-event-listener-2.1.0.tgz",
- "integrity": "sha512-U5greryDLS8ISP69DKDsYcsXRtAdnTQT+jjIlRYZ49K/XhUR/AqVZCK5BkR1spTDmO9H8SPhgeNKI70ODuDU/Q==",
- "requires": {
- "@chakra-ui/react-use-callback-ref": "2.1.0"
- }
- },
- "@chakra-ui/react-use-focus-effect": {
- "version": "2.1.0",
- "resolved": "https://registry.npmjs.org/@chakra-ui/react-use-focus-effect/-/react-use-focus-effect-2.1.0.tgz",
- "integrity": "sha512-xzVboNy7J64xveLcxTIJ3jv+lUJKDwRM7Szwn9tNzUIPD94O3qwjV7DDCUzN2490nSYDF4OBMt/wuDBtaR3kUQ==",
- "requires": {
- "@chakra-ui/dom-utils": "2.1.0",
- "@chakra-ui/react-use-event-listener": "2.1.0",
- "@chakra-ui/react-use-safe-layout-effect": "2.1.0",
- "@chakra-ui/react-use-update-effect": "2.1.0"
- }
- },
- "@chakra-ui/react-use-focus-on-pointer-down": {
- "version": "2.1.0",
- "resolved": "https://registry.npmjs.org/@chakra-ui/react-use-focus-on-pointer-down/-/react-use-focus-on-pointer-down-2.1.0.tgz",
- "integrity": "sha512-2jzrUZ+aiCG/cfanrolsnSMDykCAbv9EK/4iUyZno6BYb3vziucmvgKuoXbMPAzWNtwUwtuMhkby8rc61Ue+Lg==",
- "requires": {
- "@chakra-ui/react-use-event-listener": "2.1.0"
- }
- },
- "@chakra-ui/react-use-interval": {
- "version": "2.1.0",
- "resolved": "https://registry.npmjs.org/@chakra-ui/react-use-interval/-/react-use-interval-2.1.0.tgz",
- "integrity": "sha512-8iWj+I/+A0J08pgEXP1J1flcvhLBHkk0ln7ZvGIyXiEyM6XagOTJpwNhiu+Bmk59t3HoV/VyvyJTa+44sEApuw==",
- "requires": {
- "@chakra-ui/react-use-callback-ref": "2.1.0"
- }
- },
- "@chakra-ui/react-use-latest-ref": {
- "version": "2.1.0",
- "resolved": "https://registry.npmjs.org/@chakra-ui/react-use-latest-ref/-/react-use-latest-ref-2.1.0.tgz",
- "integrity": "sha512-m0kxuIYqoYB0va9Z2aW4xP/5b7BzlDeWwyXCH6QpT2PpW3/281L3hLCm1G0eOUcdVlayqrQqOeD6Mglq+5/xoQ==",
- "requires": {}
- },
- "@chakra-ui/react-use-merge-refs": {
- "version": "2.1.0",
- "resolved": "https://registry.npmjs.org/@chakra-ui/react-use-merge-refs/-/react-use-merge-refs-2.1.0.tgz",
- "integrity": "sha512-lERa6AWF1cjEtWSGjxWTaSMvneccnAVH4V4ozh8SYiN9fSPZLlSG3kNxfNzdFvMEhM7dnP60vynF7WjGdTgQbQ==",
- "requires": {}
- },
- "@chakra-ui/react-use-outside-click": {
- "version": "2.2.0",
- "resolved": "https://registry.npmjs.org/@chakra-ui/react-use-outside-click/-/react-use-outside-click-2.2.0.tgz",
- "integrity": "sha512-PNX+s/JEaMneijbgAM4iFL+f3m1ga9+6QK0E5Yh4s8KZJQ/bLwZzdhMz8J/+mL+XEXQ5J0N8ivZN28B82N1kNw==",
- "requires": {
- "@chakra-ui/react-use-callback-ref": "2.1.0"
- }
- },
- "@chakra-ui/react-use-pan-event": {
- "version": "2.1.0",
- "resolved": "https://registry.npmjs.org/@chakra-ui/react-use-pan-event/-/react-use-pan-event-2.1.0.tgz",
- "integrity": "sha512-xmL2qOHiXqfcj0q7ZK5s9UjTh4Gz0/gL9jcWPA6GVf+A0Od5imEDa/Vz+533yQKWiNSm1QGrIj0eJAokc7O4fg==",
- "requires": {
- "@chakra-ui/event-utils": "2.0.8",
- "@chakra-ui/react-use-latest-ref": "2.1.0",
- "framesync": "6.1.2"
- }
- },
- "@chakra-ui/react-use-previous": {
- "version": "2.1.0",
- "resolved": "https://registry.npmjs.org/@chakra-ui/react-use-previous/-/react-use-previous-2.1.0.tgz",
- "integrity": "sha512-pjxGwue1hX8AFcmjZ2XfrQtIJgqbTF3Qs1Dy3d1krC77dEsiCUbQ9GzOBfDc8pfd60DrB5N2tg5JyHbypqh0Sg==",
- "requires": {}
- },
- "@chakra-ui/react-use-safe-layout-effect": {
- "version": "2.1.0",
- "resolved": "https://registry.npmjs.org/@chakra-ui/react-use-safe-layout-effect/-/react-use-safe-layout-effect-2.1.0.tgz",
- "integrity": "sha512-Knbrrx/bcPwVS1TorFdzrK/zWA8yuU/eaXDkNj24IrKoRlQrSBFarcgAEzlCHtzuhufP3OULPkELTzz91b0tCw==",
- "requires": {}
- },
- "@chakra-ui/react-use-size": {
- "version": "2.1.0",
- "resolved": "https://registry.npmjs.org/@chakra-ui/react-use-size/-/react-use-size-2.1.0.tgz",
- "integrity": "sha512-tbLqrQhbnqOjzTaMlYytp7wY8BW1JpL78iG7Ru1DlV4EWGiAmXFGvtnEt9HftU0NJ0aJyjgymkxfVGI55/1Z4A==",
- "requires": {
- "@zag-js/element-size": "0.10.5"
- }
- },
- "@chakra-ui/react-use-timeout": {
- "version": "2.1.0",
- "resolved": "https://registry.npmjs.org/@chakra-ui/react-use-timeout/-/react-use-timeout-2.1.0.tgz",
- "integrity": "sha512-cFN0sobKMM9hXUhyCofx3/Mjlzah6ADaEl/AXl5Y+GawB5rgedgAcu2ErAgarEkwvsKdP6c68CKjQ9dmTQlJxQ==",
- "requires": {
- "@chakra-ui/react-use-callback-ref": "2.1.0"
- }
- },
- "@chakra-ui/react-use-update-effect": {
- "version": "2.1.0",
- "resolved": "https://registry.npmjs.org/@chakra-ui/react-use-update-effect/-/react-use-update-effect-2.1.0.tgz",
- "integrity": "sha512-ND4Q23tETaR2Qd3zwCKYOOS1dfssojPLJMLvUtUbW5M9uW1ejYWgGUobeAiOVfSplownG8QYMmHTP86p/v0lbA==",
- "requires": {}
- },
- "@chakra-ui/react-utils": {
- "version": "2.0.12",
- "resolved": "https://registry.npmjs.org/@chakra-ui/react-utils/-/react-utils-2.0.12.tgz",
- "integrity": "sha512-GbSfVb283+YA3kA8w8xWmzbjNWk14uhNpntnipHCftBibl0lxtQ9YqMFQLwuFOO0U2gYVocszqqDWX+XNKq9hw==",
- "requires": {
- "@chakra-ui/utils": "2.0.15"
- }
- },
- "@chakra-ui/select": {
- "version": "2.1.2",
- "resolved": "https://registry.npmjs.org/@chakra-ui/select/-/select-2.1.2.tgz",
- "integrity": "sha512-ZwCb7LqKCVLJhru3DXvKXpZ7Pbu1TDZ7N0PdQ0Zj1oyVLJyrpef1u9HR5u0amOpqcH++Ugt0f5JSmirjNlctjA==",
- "requires": {
- "@chakra-ui/form-control": "2.2.0",
- "@chakra-ui/shared-utils": "2.0.5"
- }
- },
- "@chakra-ui/shared-utils": {
- "version": "2.0.5",
- "resolved": "https://registry.npmjs.org/@chakra-ui/shared-utils/-/shared-utils-2.0.5.tgz",
- "integrity": "sha512-4/Wur0FqDov7Y0nCXl7HbHzCg4aq86h+SXdoUeuCMD3dSj7dpsVnStLYhng1vxvlbUnLpdF4oz5Myt3i/a7N3Q=="
- },
- "@chakra-ui/skeleton": {
- "version": "2.1.0",
- "resolved": "https://registry.npmjs.org/@chakra-ui/skeleton/-/skeleton-2.1.0.tgz",
- "integrity": "sha512-JNRuMPpdZGd6zFVKjVQ0iusu3tXAdI29n4ZENYwAJEMf/fN0l12sVeirOxkJ7oEL0yOx2AgEYFSKdbcAgfUsAQ==",
- "requires": {
- "@chakra-ui/media-query": "3.3.0",
- "@chakra-ui/react-use-previous": "2.1.0",
- "@chakra-ui/shared-utils": "2.0.5"
- }
- },
- "@chakra-ui/skip-nav": {
- "version": "2.1.0",
- "resolved": "https://registry.npmjs.org/@chakra-ui/skip-nav/-/skip-nav-2.1.0.tgz",
- "integrity": "sha512-Hk+FG+vadBSH0/7hwp9LJnLjkO0RPGnx7gBJWI4/SpoJf3e4tZlWYtwGj0toYY4aGKl93jVghuwGbDBEMoHDug==",
- "requires": {}
- },
- "@chakra-ui/slider": {
- "version": "2.1.0",
- "resolved": "https://registry.npmjs.org/@chakra-ui/slider/-/slider-2.1.0.tgz",
- "integrity": "sha512-lUOBcLMCnFZiA/s2NONXhELJh6sY5WtbRykPtclGfynqqOo47lwWJx+VP7xaeuhDOPcWSSecWc9Y1BfPOCz9cQ==",
- "requires": {
- "@chakra-ui/number-utils": "2.0.7",
- "@chakra-ui/react-context": "2.1.0",
- "@chakra-ui/react-types": "2.0.7",
- "@chakra-ui/react-use-callback-ref": "2.1.0",
- "@chakra-ui/react-use-controllable-state": "2.1.0",
- "@chakra-ui/react-use-latest-ref": "2.1.0",
- "@chakra-ui/react-use-merge-refs": "2.1.0",
- "@chakra-ui/react-use-pan-event": "2.1.0",
- "@chakra-ui/react-use-size": "2.1.0",
- "@chakra-ui/react-use-update-effect": "2.1.0"
- }
- },
- "@chakra-ui/spinner": {
- "version": "2.1.0",
- "resolved": "https://registry.npmjs.org/@chakra-ui/spinner/-/spinner-2.1.0.tgz",
- "integrity": "sha512-hczbnoXt+MMv/d3gE+hjQhmkzLiKuoTo42YhUG7Bs9OSv2lg1fZHW1fGNRFP3wTi6OIbD044U1P9HK+AOgFH3g==",
- "requires": {
- "@chakra-ui/shared-utils": "2.0.5"
- }
- },
- "@chakra-ui/stat": {
- "version": "2.1.1",
- "resolved": "https://registry.npmjs.org/@chakra-ui/stat/-/stat-2.1.1.tgz",
- "integrity": "sha512-LDn0d/LXQNbAn2KaR3F1zivsZCewY4Jsy1qShmfBMKwn6rI8yVlbvu6SiA3OpHS0FhxbsZxQI6HefEoIgtqY6Q==",
- "requires": {
- "@chakra-ui/icon": "3.2.0",
- "@chakra-ui/react-context": "2.1.0",
- "@chakra-ui/shared-utils": "2.0.5"
- }
- },
- "@chakra-ui/stepper": {
- "version": "2.3.1",
- "resolved": "https://registry.npmjs.org/@chakra-ui/stepper/-/stepper-2.3.1.tgz",
- "integrity": "sha512-ky77lZbW60zYkSXhYz7kbItUpAQfEdycT0Q4bkHLxfqbuiGMf8OmgZOQkOB9uM4v0zPwy2HXhe0vq4Dd0xa55Q==",
- "requires": {
- "@chakra-ui/icon": "3.2.0",
- "@chakra-ui/react-context": "2.1.0",
- "@chakra-ui/shared-utils": "2.0.5"
- }
- },
- "@chakra-ui/styled-system": {
- "version": "2.9.2",
- "resolved": "https://registry.npmjs.org/@chakra-ui/styled-system/-/styled-system-2.9.2.tgz",
- "integrity": "sha512-To/Z92oHpIE+4nk11uVMWqo2GGRS86coeMmjxtpnErmWRdLcp1WVCVRAvn+ZwpLiNR+reWFr2FFqJRsREuZdAg==",
- "requires": {
- "@chakra-ui/shared-utils": "2.0.5",
- "csstype": "^3.1.2",
- "lodash.mergewith": "4.6.2"
- }
- },
- "@chakra-ui/switch": {
- "version": "2.1.2",
- "resolved": "https://registry.npmjs.org/@chakra-ui/switch/-/switch-2.1.2.tgz",
- "integrity": "sha512-pgmi/CC+E1v31FcnQhsSGjJnOE2OcND4cKPyTE+0F+bmGm48Q/b5UmKD9Y+CmZsrt/7V3h8KNczowupfuBfIHA==",
- "requires": {
- "@chakra-ui/checkbox": "2.3.2",
- "@chakra-ui/shared-utils": "2.0.5"
- }
- },
- "@chakra-ui/system": {
- "version": "2.6.2",
- "resolved": "https://registry.npmjs.org/@chakra-ui/system/-/system-2.6.2.tgz",
- "integrity": "sha512-EGtpoEjLrUu4W1fHD+a62XR+hzC5YfsWm+6lO0Kybcga3yYEij9beegO0jZgug27V+Rf7vns95VPVP6mFd/DEQ==",
- "requires": {
- "@chakra-ui/color-mode": "2.2.0",
- "@chakra-ui/object-utils": "2.1.0",
- "@chakra-ui/react-utils": "2.0.12",
- "@chakra-ui/styled-system": "2.9.2",
- "@chakra-ui/theme-utils": "2.0.21",
- "@chakra-ui/utils": "2.0.15",
- "react-fast-compare": "3.2.2"
- }
- },
- "@chakra-ui/table": {
- "version": "2.1.0",
- "resolved": "https://registry.npmjs.org/@chakra-ui/table/-/table-2.1.0.tgz",
- "integrity": "sha512-o5OrjoHCh5uCLdiUb0Oc0vq9rIAeHSIRScc2ExTC9Qg/uVZl2ygLrjToCaKfaaKl1oQexIeAcZDKvPG8tVkHyQ==",
- "requires": {
- "@chakra-ui/react-context": "2.1.0",
- "@chakra-ui/shared-utils": "2.0.5"
- }
- },
- "@chakra-ui/tabs": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/@chakra-ui/tabs/-/tabs-3.0.0.tgz",
- "integrity": "sha512-6Mlclp8L9lqXmsGWF5q5gmemZXOiOYuh0SGT/7PgJVNPz3LXREXlXg2an4MBUD8W5oTkduCX+3KTMCwRrVrDYw==",
- "requires": {
- "@chakra-ui/clickable": "2.1.0",
- "@chakra-ui/descendant": "3.1.0",
- "@chakra-ui/lazy-utils": "2.0.5",
- "@chakra-ui/react-children-utils": "2.0.6",
- "@chakra-ui/react-context": "2.1.0",
- "@chakra-ui/react-use-controllable-state": "2.1.0",
- "@chakra-ui/react-use-merge-refs": "2.1.0",
- "@chakra-ui/react-use-safe-layout-effect": "2.1.0",
- "@chakra-ui/shared-utils": "2.0.5"
- }
- },
- "@chakra-ui/tag": {
- "version": "3.1.1",
- "resolved": "https://registry.npmjs.org/@chakra-ui/tag/-/tag-3.1.1.tgz",
- "integrity": "sha512-Bdel79Dv86Hnge2PKOU+t8H28nm/7Y3cKd4Kfk9k3lOpUh4+nkSGe58dhRzht59lEqa4N9waCgQiBdkydjvBXQ==",
- "requires": {
- "@chakra-ui/icon": "3.2.0",
- "@chakra-ui/react-context": "2.1.0"
- }
- },
- "@chakra-ui/textarea": {
- "version": "2.1.2",
- "resolved": "https://registry.npmjs.org/@chakra-ui/textarea/-/textarea-2.1.2.tgz",
- "integrity": "sha512-ip7tvklVCZUb2fOHDb23qPy/Fr2mzDOGdkrpbNi50hDCiV4hFX02jdQJdi3ydHZUyVgZVBKPOJ+lT9i7sKA2wA==",
- "requires": {
- "@chakra-ui/form-control": "2.2.0",
- "@chakra-ui/shared-utils": "2.0.5"
- }
- },
- "@chakra-ui/theme": {
- "version": "3.3.1",
- "resolved": "https://registry.npmjs.org/@chakra-ui/theme/-/theme-3.3.1.tgz",
- "integrity": "sha512-Hft/VaT8GYnItGCBbgWd75ICrIrIFrR7lVOhV/dQnqtfGqsVDlrztbSErvMkoPKt0UgAkd9/o44jmZ6X4U2nZQ==",
- "requires": {
- "@chakra-ui/anatomy": "2.2.2",
- "@chakra-ui/shared-utils": "2.0.5",
- "@chakra-ui/theme-tools": "2.1.2"
- }
- },
- "@chakra-ui/theme-tools": {
- "version": "2.1.2",
- "resolved": "https://registry.npmjs.org/@chakra-ui/theme-tools/-/theme-tools-2.1.2.tgz",
- "integrity": "sha512-Qdj8ajF9kxY4gLrq7gA+Azp8CtFHGO9tWMN2wfF9aQNgG9AuMhPrUzMq9AMQ0MXiYcgNq/FD3eegB43nHVmXVA==",
- "requires": {
- "@chakra-ui/anatomy": "2.2.2",
- "@chakra-ui/shared-utils": "2.0.5",
- "color2k": "^2.0.2"
- }
- },
- "@chakra-ui/theme-utils": {
- "version": "2.0.21",
- "resolved": "https://registry.npmjs.org/@chakra-ui/theme-utils/-/theme-utils-2.0.21.tgz",
- "integrity": "sha512-FjH5LJbT794r0+VSCXB3lT4aubI24bLLRWB+CuRKHijRvsOg717bRdUN/N1fEmEpFnRVrbewttWh/OQs0EWpWw==",
- "requires": {
- "@chakra-ui/shared-utils": "2.0.5",
- "@chakra-ui/styled-system": "2.9.2",
- "@chakra-ui/theme": "3.3.1",
- "lodash.mergewith": "4.6.2"
- }
- },
- "@chakra-ui/toast": {
- "version": "7.0.2",
- "resolved": "https://registry.npmjs.org/@chakra-ui/toast/-/toast-7.0.2.tgz",
- "integrity": "sha512-yvRP8jFKRs/YnkuE41BVTq9nB2v/KDRmje9u6dgDmE5+1bFt3bwjdf9gVbif4u5Ve7F7BGk5E093ARRVtvLvXA==",
- "requires": {
- "@chakra-ui/alert": "2.2.2",
- "@chakra-ui/close-button": "2.1.1",
- "@chakra-ui/portal": "2.1.0",
- "@chakra-ui/react-context": "2.1.0",
- "@chakra-ui/react-use-timeout": "2.1.0",
- "@chakra-ui/react-use-update-effect": "2.1.0",
- "@chakra-ui/shared-utils": "2.0.5",
- "@chakra-ui/styled-system": "2.9.2",
- "@chakra-ui/theme": "3.3.1"
- }
- },
- "@chakra-ui/tooltip": {
- "version": "2.3.1",
- "resolved": "https://registry.npmjs.org/@chakra-ui/tooltip/-/tooltip-2.3.1.tgz",
- "integrity": "sha512-Rh39GBn/bL4kZpuEMPPRwYNnccRCL+w9OqamWHIB3Qboxs6h8cOyXfIdGxjo72lvhu1QI/a4KFqkM3St+WfC0A==",
- "requires": {
- "@chakra-ui/dom-utils": "2.1.0",
- "@chakra-ui/popper": "3.1.0",
- "@chakra-ui/portal": "2.1.0",
- "@chakra-ui/react-types": "2.0.7",
- "@chakra-ui/react-use-disclosure": "2.1.0",
- "@chakra-ui/react-use-event-listener": "2.1.0",
- "@chakra-ui/react-use-merge-refs": "2.1.0",
- "@chakra-ui/shared-utils": "2.0.5"
- }
- },
- "@chakra-ui/transition": {
- "version": "2.1.0",
- "resolved": "https://registry.npmjs.org/@chakra-ui/transition/-/transition-2.1.0.tgz",
- "integrity": "sha512-orkT6T/Dt+/+kVwJNy7zwJ+U2xAZ3EU7M3XCs45RBvUnZDr/u9vdmaM/3D/rOpmQJWgQBwKPJleUXrYWUagEDQ==",
- "requires": {
- "@chakra-ui/shared-utils": "2.0.5"
- }
- },
- "@chakra-ui/utils": {
- "version": "2.0.15",
- "resolved": "https://registry.npmjs.org/@chakra-ui/utils/-/utils-2.0.15.tgz",
- "integrity": "sha512-El4+jL0WSaYYs+rJbuYFDbjmfCcfGDmRY95GO4xwzit6YAPZBLcR65rOEwLps+XWluZTy1xdMrusg/hW0c1aAA==",
- "requires": {
- "@types/lodash.mergewith": "4.6.7",
- "css-box-model": "1.2.1",
- "framesync": "6.1.2",
- "lodash.mergewith": "4.6.2"
- }
- },
- "@chakra-ui/visually-hidden": {
- "version": "2.2.0",
- "resolved": "https://registry.npmjs.org/@chakra-ui/visually-hidden/-/visually-hidden-2.2.0.tgz",
- "integrity": "sha512-KmKDg01SrQ7VbTD3+cPWf/UfpF5MSwm3v7MWi0n5t8HnnadT13MF0MJCDSXbBWnzLv1ZKJ6zlyAOeARWX+DpjQ==",
- "requires": {}
- },
- "@emotion/babel-plugin": {
- "version": "11.11.0",
- "resolved": "https://registry.npmjs.org/@emotion/babel-plugin/-/babel-plugin-11.11.0.tgz",
- "integrity": "sha512-m4HEDZleaaCH+XgDDsPF15Ht6wTLsgDTeR3WYj9Q/k76JtWhrJjcP4+/XlG8LGT/Rol9qUfOIztXeA84ATpqPQ==",
- "requires": {
- "@babel/helper-module-imports": "^7.16.7",
- "@babel/runtime": "^7.18.3",
- "@emotion/hash": "^0.9.1",
- "@emotion/memoize": "^0.8.1",
- "@emotion/serialize": "^1.1.2",
- "babel-plugin-macros": "^3.1.0",
- "convert-source-map": "^1.5.0",
- "escape-string-regexp": "^4.0.0",
- "find-root": "^1.1.0",
- "source-map": "^0.5.7",
- "stylis": "4.2.0"
- },
- "dependencies": {
- "source-map": {
- "version": "0.5.7",
- "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz",
- "integrity": "sha512-LbrmJOMUSdEVxIKvdcJzQC+nQhe8FUZQTXQy6+I75skNgn3OoQ0DZA8YnFa7gp8tqtL3KPf1kmo0R5DoApeSGQ=="
- }
- }
- },
- "@emotion/cache": {
- "version": "11.11.0",
- "resolved": "https://registry.npmjs.org/@emotion/cache/-/cache-11.11.0.tgz",
- "integrity": "sha512-P34z9ssTCBi3e9EI1ZsWpNHcfY1r09ZO0rZbRO2ob3ZQMnFI35jB536qoXbkdesr5EUhYi22anuEJuyxifaqAQ==",
- "requires": {
- "@emotion/memoize": "^0.8.1",
- "@emotion/sheet": "^1.2.2",
- "@emotion/utils": "^1.2.1",
- "@emotion/weak-memoize": "^0.3.1",
- "stylis": "4.2.0"
- }
- },
- "@emotion/hash": {
- "version": "0.9.1",
- "resolved": "https://registry.npmjs.org/@emotion/hash/-/hash-0.9.1.tgz",
- "integrity": "sha512-gJB6HLm5rYwSLI6PQa+X1t5CFGrv1J1TWG+sOyMCeKz2ojaj6Fnl/rZEspogG+cvqbt4AE/2eIyD2QfLKTBNlQ=="
- },
- "@emotion/is-prop-valid": {
- "version": "1.2.1",
- "resolved": "https://registry.npmjs.org/@emotion/is-prop-valid/-/is-prop-valid-1.2.1.tgz",
- "integrity": "sha512-61Mf7Ufx4aDxx1xlDeOm8aFFigGHE4z+0sKCa+IHCeZKiyP9RLD0Mmx7m8b9/Cf37f7NAvQOOJAbQQGVr5uERw==",
- "requires": {
- "@emotion/memoize": "^0.8.1"
- }
- },
- "@emotion/memoize": {
- "version": "0.8.1",
- "resolved": "https://registry.npmjs.org/@emotion/memoize/-/memoize-0.8.1.tgz",
- "integrity": "sha512-W2P2c/VRW1/1tLox0mVUalvnWXxavmv/Oum2aPsRcoDJuob75FC3Y8FbpfLwUegRcxINtGUMPq0tFCvYNTBXNA=="
- },
- "@emotion/react": {
- "version": "11.11.3",
- "resolved": "https://registry.npmjs.org/@emotion/react/-/react-11.11.3.tgz",
- "integrity": "sha512-Cnn0kuq4DoONOMcnoVsTOR8E+AdnKFf//6kUWc4LCdnxj31pZWn7rIULd6Y7/Js1PiPHzn7SKCM9vB/jBni8eA==",
- "requires": {
- "@babel/runtime": "^7.18.3",
- "@emotion/babel-plugin": "^11.11.0",
- "@emotion/cache": "^11.11.0",
- "@emotion/serialize": "^1.1.3",
- "@emotion/use-insertion-effect-with-fallbacks": "^1.0.1",
- "@emotion/utils": "^1.2.1",
- "@emotion/weak-memoize": "^0.3.1",
- "hoist-non-react-statics": "^3.3.1"
- }
- },
- "@emotion/serialize": {
- "version": "1.1.3",
- "resolved": "https://registry.npmjs.org/@emotion/serialize/-/serialize-1.1.3.tgz",
- "integrity": "sha512-iD4D6QVZFDhcbH0RAG1uVu1CwVLMWUkCvAqqlewO/rxf8+87yIBAlt4+AxMiiKPLs5hFc0owNk/sLLAOROw3cA==",
- "requires": {
- "@emotion/hash": "^0.9.1",
- "@emotion/memoize": "^0.8.1",
- "@emotion/unitless": "^0.8.1",
- "@emotion/utils": "^1.2.1",
- "csstype": "^3.0.2"
- }
- },
- "@emotion/sheet": {
- "version": "1.2.2",
- "resolved": "https://registry.npmjs.org/@emotion/sheet/-/sheet-1.2.2.tgz",
- "integrity": "sha512-0QBtGvaqtWi+nx6doRwDdBIzhNdZrXUppvTM4dtZZWEGTXL/XE/yJxLMGlDT1Gt+UHH5IX1n+jkXyytE/av7OA=="
- },
- "@emotion/styled": {
- "version": "11.11.0",
- "resolved": "https://registry.npmjs.org/@emotion/styled/-/styled-11.11.0.tgz",
- "integrity": "sha512-hM5Nnvu9P3midq5aaXj4I+lnSfNi7Pmd4EWk1fOZ3pxookaQTNew6bp4JaCBYM4HVFZF9g7UjJmsUmC2JlxOng==",
- "requires": {
- "@babel/runtime": "^7.18.3",
- "@emotion/babel-plugin": "^11.11.0",
- "@emotion/is-prop-valid": "^1.2.1",
- "@emotion/serialize": "^1.1.2",
- "@emotion/use-insertion-effect-with-fallbacks": "^1.0.1",
- "@emotion/utils": "^1.2.1"
- }
- },
- "@emotion/unitless": {
- "version": "0.8.1",
- "resolved": "https://registry.npmjs.org/@emotion/unitless/-/unitless-0.8.1.tgz",
- "integrity": "sha512-KOEGMu6dmJZtpadb476IsZBclKvILjopjUii3V+7MnXIQCYh8W3NgNcgwo21n9LXZX6EDIKvqfjYxXebDwxKmQ=="
- },
- "@emotion/use-insertion-effect-with-fallbacks": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/@emotion/use-insertion-effect-with-fallbacks/-/use-insertion-effect-with-fallbacks-1.0.1.tgz",
- "integrity": "sha512-jT/qyKZ9rzLErtrjGgdkMBn2OP8wl0G3sQlBb3YPryvKHsjvINUhVaPFfP+fpBcOkmrVOVEEHQFJ7nbj2TH2gw==",
- "requires": {}
- },
- "@emotion/utils": {
- "version": "1.2.1",
- "resolved": "https://registry.npmjs.org/@emotion/utils/-/utils-1.2.1.tgz",
- "integrity": "sha512-Y2tGf3I+XVnajdItskUCn6LX+VUDmP6lTL4fcqsXAv43dnlbZiuW4MWQW38rW/BVWSE7Q/7+XQocmpnRYILUmg=="
- },
- "@emotion/weak-memoize": {
- "version": "0.3.1",
- "resolved": "https://registry.npmjs.org/@emotion/weak-memoize/-/weak-memoize-0.3.1.tgz",
- "integrity": "sha512-EsBwpc7hBUJWAsNPBmJy4hxWx12v6bshQsldrVmjxJoc3isbxhOrF2IcCpaXxfvq03NwkI7sbsOLXbYuqF/8Ww=="
- },
- "@esbuild/android-arm": {
- "version": "0.19.8",
- "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.19.8.tgz",
- "integrity": "sha512-31E2lxlGM1KEfivQl8Yf5aYU/mflz9g06H6S15ITUFQueMFtFjESRMoDSkvMo8thYvLBax+VKTPlpnx+sPicOA==",
- "dev": true,
- "optional": true
- },
- "@esbuild/android-arm64": {
- "version": "0.19.8",
- "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.19.8.tgz",
- "integrity": "sha512-B8JbS61bEunhfx8kasogFENgQfr/dIp+ggYXwTqdbMAgGDhRa3AaPpQMuQU0rNxDLECj6FhDzk1cF9WHMVwrtA==",
- "dev": true,
- "optional": true
- },
- "@esbuild/android-x64": {
- "version": "0.19.8",
- "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.19.8.tgz",
- "integrity": "sha512-rdqqYfRIn4jWOp+lzQttYMa2Xar3OK9Yt2fhOhzFXqg0rVWEfSclJvZq5fZslnz6ypHvVf3CT7qyf0A5pM682A==",
- "dev": true,
- "optional": true
- },
- "@esbuild/darwin-arm64": {
- "version": "0.19.8",
- "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.19.8.tgz",
- "integrity": "sha512-RQw9DemMbIq35Bprbboyf8SmOr4UXsRVxJ97LgB55VKKeJOOdvsIPy0nFyF2l8U+h4PtBx/1kRf0BelOYCiQcw==",
- "dev": true,
- "optional": true
- },
- "@esbuild/darwin-x64": {
- "version": "0.19.8",
- "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.19.8.tgz",
- "integrity": "sha512-3sur80OT9YdeZwIVgERAysAbwncom7b4bCI2XKLjMfPymTud7e/oY4y+ci1XVp5TfQp/bppn7xLw1n/oSQY3/Q==",
- "dev": true,
- "optional": true
- },
- "@esbuild/freebsd-arm64": {
- "version": "0.19.8",
- "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.19.8.tgz",
- "integrity": "sha512-WAnPJSDattvS/XtPCTj1tPoTxERjcTpH6HsMr6ujTT+X6rylVe8ggxk8pVxzf5U1wh5sPODpawNicF5ta/9Tmw==",
- "dev": true,
- "optional": true
- },
- "@esbuild/freebsd-x64": {
- "version": "0.19.8",
- "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.19.8.tgz",
- "integrity": "sha512-ICvZyOplIjmmhjd6mxi+zxSdpPTKFfyPPQMQTK/w+8eNK6WV01AjIztJALDtwNNfFhfZLux0tZLC+U9nSyA5Zg==",
- "dev": true,
- "optional": true
- },
- "@esbuild/linux-arm": {
- "version": "0.19.8",
- "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.19.8.tgz",
- "integrity": "sha512-H4vmI5PYqSvosPaTJuEppU9oz1dq2A7Mr2vyg5TF9Ga+3+MGgBdGzcyBP7qK9MrwFQZlvNyJrvz6GuCaj3OukQ==",
- "dev": true,
- "optional": true
- },
- "@esbuild/linux-arm64": {
- "version": "0.19.8",
- "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.19.8.tgz",
- "integrity": "sha512-z1zMZivxDLHWnyGOctT9JP70h0beY54xDDDJt4VpTX+iwA77IFsE1vCXWmprajJGa+ZYSqkSbRQ4eyLCpCmiCQ==",
- "dev": true,
- "optional": true
- },
- "@esbuild/linux-ia32": {
- "version": "0.19.8",
- "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.19.8.tgz",
- "integrity": "sha512-1a8suQiFJmZz1khm/rDglOc8lavtzEMRo0v6WhPgxkrjcU0LkHj+TwBrALwoz/OtMExvsqbbMI0ChyelKabSvQ==",
- "dev": true,
- "optional": true
- },
- "@esbuild/linux-loong64": {
- "version": "0.19.8",
- "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.19.8.tgz",
- "integrity": "sha512-fHZWS2JJxnXt1uYJsDv9+b60WCc2RlvVAy1F76qOLtXRO+H4mjt3Tr6MJ5l7Q78X8KgCFudnTuiQRBhULUyBKQ==",
- "dev": true,
- "optional": true
- },
- "@esbuild/linux-mips64el": {
- "version": "0.19.8",
- "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.19.8.tgz",
- "integrity": "sha512-Wy/z0EL5qZYLX66dVnEg9riiwls5IYnziwuju2oUiuxVc+/edvqXa04qNtbrs0Ukatg5HEzqT94Zs7J207dN5Q==",
- "dev": true,
- "optional": true
- },
- "@esbuild/linux-ppc64": {
- "version": "0.19.8",
- "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.19.8.tgz",
- "integrity": "sha512-ETaW6245wK23YIEufhMQ3HSeHO7NgsLx8gygBVldRHKhOlD1oNeNy/P67mIh1zPn2Hr2HLieQrt6tWrVwuqrxg==",
- "dev": true,
- "optional": true
- },
- "@esbuild/linux-riscv64": {
- "version": "0.19.8",
- "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.19.8.tgz",
- "integrity": "sha512-T2DRQk55SgoleTP+DtPlMrxi/5r9AeFgkhkZ/B0ap99zmxtxdOixOMI570VjdRCs9pE4Wdkz7JYrsPvsl7eESg==",
- "dev": true,
- "optional": true
- },
- "@esbuild/linux-s390x": {
- "version": "0.19.8",
- "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.19.8.tgz",
- "integrity": "sha512-NPxbdmmo3Bk7mbNeHmcCd7R7fptJaczPYBaELk6NcXxy7HLNyWwCyDJ/Xx+/YcNH7Im5dHdx9gZ5xIwyliQCbg==",
- "dev": true,
- "optional": true
- },
- "@esbuild/linux-x64": {
- "version": "0.19.8",
- "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.19.8.tgz",
- "integrity": "sha512-lytMAVOM3b1gPypL2TRmZ5rnXl7+6IIk8uB3eLsV1JwcizuolblXRrc5ShPrO9ls/b+RTp+E6gbsuLWHWi2zGg==",
- "dev": true,
- "optional": true
- },
- "@esbuild/netbsd-x64": {
- "version": "0.19.8",
- "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.19.8.tgz",
- "integrity": "sha512-hvWVo2VsXz/8NVt1UhLzxwAfo5sioj92uo0bCfLibB0xlOmimU/DeAEsQILlBQvkhrGjamP0/el5HU76HAitGw==",
- "dev": true,
- "optional": true
- },
- "@esbuild/openbsd-x64": {
- "version": "0.19.8",
- "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.19.8.tgz",
- "integrity": "sha512-/7Y7u77rdvmGTxR83PgaSvSBJCC2L3Kb1M/+dmSIvRvQPXXCuC97QAwMugBNG0yGcbEGfFBH7ojPzAOxfGNkwQ==",
- "dev": true,
- "optional": true
- },
- "@esbuild/sunos-x64": {
- "version": "0.19.8",
- "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.19.8.tgz",
- "integrity": "sha512-9Lc4s7Oi98GqFA4HzA/W2JHIYfnXbUYgekUP/Sm4BG9sfLjyv6GKKHKKVs83SMicBF2JwAX6A1PuOLMqpD001w==",
- "dev": true,
- "optional": true
- },
- "@esbuild/win32-arm64": {
- "version": "0.19.8",
- "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.19.8.tgz",
- "integrity": "sha512-rq6WzBGjSzihI9deW3fC2Gqiak68+b7qo5/3kmB6Gvbh/NYPA0sJhrnp7wgV4bNwjqM+R2AApXGxMO7ZoGhIJg==",
- "dev": true,
- "optional": true
- },
- "@esbuild/win32-ia32": {
- "version": "0.19.8",
- "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.19.8.tgz",
- "integrity": "sha512-AIAbverbg5jMvJznYiGhrd3sumfwWs8572mIJL5NQjJa06P8KfCPWZQ0NwZbPQnbQi9OWSZhFVSUWjjIrn4hSw==",
- "dev": true,
- "optional": true
- },
- "@esbuild/win32-x64": {
- "version": "0.19.8",
- "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.19.8.tgz",
- "integrity": "sha512-bfZ0cQ1uZs2PqpulNL5j/3w+GDhP36k1K5c38QdQg+Swy51jFZWWeIkteNsufkQxp986wnqRRsb/bHbY1WQ7TA==",
- "dev": true,
- "optional": true
- },
- "@hey-api/openapi-ts": {
- "version": "0.34.1",
- "resolved": "https://registry.npmjs.org/@hey-api/openapi-ts/-/openapi-ts-0.34.1.tgz",
- "integrity": "sha512-7Ak+0nvf4Nhzk04tXGg6h4eM7lnWRgfjCPmMl2MyXrhS5urxd3Bg/PhtpB84u18wnwcM4rIeCUlTwDDQ/OB3NQ==",
- "dev": true,
- "requires": {
- "@apidevtools/json-schema-ref-parser": "11.5.4",
- "camelcase": "8.0.0",
- "commander": "12.0.0",
- "handlebars": "4.7.8"
- },
- "dependencies": {
- "@apidevtools/json-schema-ref-parser": {
- "version": "11.5.4",
- "resolved": "https://registry.npmjs.org/@apidevtools/json-schema-ref-parser/-/json-schema-ref-parser-11.5.4.tgz",
- "integrity": "sha512-o2fsypTGU0WxRxbax8zQoHiIB4dyrkwYfcm8TxZ+bx9pCzcWZbQtiMqpgBvWA/nJ2TrGjK5adCLfTH8wUeU/Wg==",
- "dev": true,
- "requires": {
- "@jsdevtools/ono": "^7.1.3",
- "@types/json-schema": "^7.0.15",
- "js-yaml": "^4.1.0"
- }
- },
- "camelcase": {
- "version": "8.0.0",
- "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-8.0.0.tgz",
- "integrity": "sha512-8WB3Jcas3swSvjIeA2yvCJ+Miyz5l1ZmB6HFb9R1317dt9LCQoswg/BGrmAmkWVEszSrrg4RwmO46qIm2OEnSA==",
- "dev": true
- },
- "commander": {
- "version": "12.0.0",
- "resolved": "https://registry.npmjs.org/commander/-/commander-12.0.0.tgz",
- "integrity": "sha512-MwVNWlYjDTtOjX5PiD7o5pK0UrFU/OYgcJfjjK4RaHZETNtjJqrZa9Y9ds88+A+f+d5lv+561eZ+yCKoS3gbAA==",
- "dev": true
- }
- }
- },
- "@jsdevtools/ono": {
- "version": "7.1.3",
- "resolved": "https://registry.npmjs.org/@jsdevtools/ono/-/ono-7.1.3.tgz",
- "integrity": "sha512-4JQNk+3mVzK3xh2rqd6RB4J46qUR19azEHBneZyTZM+c456qOrbbM/5xcR8huNCCcbVt7+UmizG6GuUvPvKUYg==",
- "dev": true
- },
- "@playwright/test": {
- "version": "1.45.2",
- "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.45.2.tgz",
- "integrity": "sha512-JxG9eq92ET75EbVi3s+4sYbcG7q72ECeZNbdBlaMkGcNbiDQ4cAi8U2QP5oKkOx+1gpaiL1LDStmzCaEM1Z6fQ==",
- "dev": true,
- "requires": {
- "playwright": "1.45.2"
- }
- },
- "@popperjs/core": {
- "version": "2.11.8",
- "resolved": "https://registry.npmjs.org/@popperjs/core/-/core-2.11.8.tgz",
- "integrity": "sha512-P1st0aksCrn9sGZhp8GMYwBnQsbvAWsZAX44oXNNvLHGqAOcoVxmjZiohstwQ7SqKnbR47akdNi+uleWD8+g6A=="
- },
- "@rollup/rollup-android-arm-eabi": {
- "version": "4.6.1",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.6.1.tgz",
- "integrity": "sha512-0WQ0ouLejaUCRsL93GD4uft3rOmB8qoQMU05Kb8CmMtMBe7XUDLAltxVZI1q6byNqEtU7N1ZX1Vw5lIpgulLQA==",
- "dev": true,
- "optional": true
- },
- "@rollup/rollup-android-arm64": {
- "version": "4.6.1",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.6.1.tgz",
- "integrity": "sha512-1TKm25Rn20vr5aTGGZqo6E4mzPicCUD79k17EgTLAsXc1zysyi4xXKACfUbwyANEPAEIxkzwue6JZ+stYzWUTA==",
- "dev": true,
- "optional": true
- },
- "@rollup/rollup-darwin-arm64": {
- "version": "4.6.1",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.6.1.tgz",
- "integrity": "sha512-cEXJQY/ZqMACb+nxzDeX9IPLAg7S94xouJJCNVE5BJM8JUEP4HeTF+ti3cmxWeSJo+5D+o8Tc0UAWUkfENdeyw==",
- "dev": true,
- "optional": true
- },
- "@rollup/rollup-darwin-x64": {
- "version": "4.6.1",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.6.1.tgz",
- "integrity": "sha512-LoSU9Xu56isrkV2jLldcKspJ7sSXmZWkAxg7sW/RfF7GS4F5/v4EiqKSMCFbZtDu2Nc1gxxFdQdKwkKS4rwxNg==",
- "dev": true,
- "optional": true
- },
- "@rollup/rollup-linux-arm-gnueabihf": {
- "version": "4.6.1",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.6.1.tgz",
- "integrity": "sha512-EfI3hzYAy5vFNDqpXsNxXcgRDcFHUWSx5nnRSCKwXuQlI5J9dD84g2Usw81n3FLBNsGCegKGwwTVsSKK9cooSQ==",
- "dev": true,
- "optional": true
- },
- "@rollup/rollup-linux-arm64-gnu": {
- "version": "4.6.1",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.6.1.tgz",
- "integrity": "sha512-9lhc4UZstsegbNLhH0Zu6TqvDfmhGzuCWtcTFXY10VjLLUe4Mr0Ye2L3rrtHaDd/J5+tFMEuo5LTCSCMXWfUKw==",
- "dev": true,
- "optional": true
- },
- "@rollup/rollup-linux-arm64-musl": {
- "version": "4.6.1",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.6.1.tgz",
- "integrity": "sha512-FfoOK1yP5ksX3wwZ4Zk1NgyGHZyuRhf99j64I5oEmirV8EFT7+OhUZEnP+x17lcP/QHJNWGsoJwrz4PJ9fBEXw==",
- "dev": true,
- "optional": true
- },
- "@rollup/rollup-linux-x64-gnu": {
- "version": "4.6.1",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.6.1.tgz",
- "integrity": "sha512-DNGZvZDO5YF7jN5fX8ZqmGLjZEXIJRdJEdTFMhiyXqyXubBa0WVLDWSNlQ5JR2PNgDbEV1VQowhVRUh+74D+RA==",
- "dev": true,
- "optional": true
- },
- "@rollup/rollup-linux-x64-musl": {
- "version": "4.6.1",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.6.1.tgz",
- "integrity": "sha512-RkJVNVRM+piYy87HrKmhbexCHg3A6Z6MU0W9GHnJwBQNBeyhCJG9KDce4SAMdicQnpURggSvtbGo9xAWOfSvIQ==",
- "dev": true,
- "optional": true
- },
- "@rollup/rollup-win32-arm64-msvc": {
- "version": "4.6.1",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.6.1.tgz",
- "integrity": "sha512-v2FVT6xfnnmTe3W9bJXl6r5KwJglMK/iRlkKiIFfO6ysKs0rDgz7Cwwf3tjldxQUrHL9INT/1r4VA0n9L/F1vQ==",
- "dev": true,
- "optional": true
- },
- "@rollup/rollup-win32-ia32-msvc": {
- "version": "4.6.1",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.6.1.tgz",
- "integrity": "sha512-YEeOjxRyEjqcWphH9dyLbzgkF8wZSKAKUkldRY6dgNR5oKs2LZazqGB41cWJ4Iqqcy9/zqYgmzBkRoVz3Q9MLw==",
- "dev": true,
- "optional": true
- },
- "@rollup/rollup-win32-x64-msvc": {
- "version": "4.6.1",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.6.1.tgz",
- "integrity": "sha512-0zfTlFAIhgz8V2G8STq8toAjsYYA6eci1hnXuyOTUFnymrtJwnS6uGKiv3v5UrPZkBlamLvrLV2iiaeqCKzb0A==",
- "dev": true,
- "optional": true
- },
- "@swc/core": {
- "version": "1.3.100",
- "resolved": "https://registry.npmjs.org/@swc/core/-/core-1.3.100.tgz",
- "integrity": "sha512-7dKgTyxJjlrMwFZYb1auj3Xq0D8ZBe+5oeIgfMlRU05doXZypYJe0LAk0yjj3WdbwYzpF+T1PLxwTWizI0pckw==",
- "dev": true,
- "requires": {
- "@swc/core-darwin-arm64": "1.3.100",
- "@swc/core-darwin-x64": "1.3.100",
- "@swc/core-linux-arm64-gnu": "1.3.100",
- "@swc/core-linux-arm64-musl": "1.3.100",
- "@swc/core-linux-x64-gnu": "1.3.100",
- "@swc/core-linux-x64-musl": "1.3.100",
- "@swc/core-win32-arm64-msvc": "1.3.100",
- "@swc/core-win32-ia32-msvc": "1.3.100",
- "@swc/core-win32-x64-msvc": "1.3.100",
- "@swc/counter": "^0.1.1",
- "@swc/types": "^0.1.5"
- }
- },
- "@swc/core-darwin-arm64": {
- "version": "1.3.100",
- "resolved": "https://registry.npmjs.org/@swc/core-darwin-arm64/-/core-darwin-arm64-1.3.100.tgz",
- "integrity": "sha512-XVWFsKe6ei+SsDbwmsuRkYck1SXRpO60Hioa4hoLwR8fxbA9eVp6enZtMxzVVMBi8ej5seZ4HZQeAWepbukiBw==",
- "dev": true,
- "optional": true
- },
- "@swc/core-darwin-x64": {
- "version": "1.3.100",
- "resolved": "https://registry.npmjs.org/@swc/core-darwin-x64/-/core-darwin-x64-1.3.100.tgz",
- "integrity": "sha512-KF/MXrnH1nakm1wbt4XV8FS7kvqD9TGmVxeJ0U4bbvxXMvzeYUurzg3AJUTXYmXDhH/VXOYJE5N5RkwZZPs5iA==",
- "dev": true,
- "optional": true
- },
- "@swc/core-linux-arm64-gnu": {
- "version": "1.3.100",
- "resolved": "https://registry.npmjs.org/@swc/core-linux-arm64-gnu/-/core-linux-arm64-gnu-1.3.100.tgz",
- "integrity": "sha512-p8hikNnAEJrw5vHCtKiFT4hdlQxk1V7vqPmvUDgL/qe2menQDK/i12tbz7/3BEQ4UqUPnvwpmVn2d19RdEMNxw==",
- "dev": true,
- "optional": true
- },
- "@swc/core-linux-arm64-musl": {
- "version": "1.3.100",
- "resolved": "https://registry.npmjs.org/@swc/core-linux-arm64-musl/-/core-linux-arm64-musl-1.3.100.tgz",
- "integrity": "sha512-BWx/0EeY89WC4q3AaIaBSGfQxkYxIlS3mX19dwy2FWJs/O+fMvF9oLk/CyJPOZzbp+1DjGeeoGFuDYpiNO91JA==",
- "dev": true,
- "optional": true
- },
- "@swc/core-linux-x64-gnu": {
- "version": "1.3.100",
- "resolved": "https://registry.npmjs.org/@swc/core-linux-x64-gnu/-/core-linux-x64-gnu-1.3.100.tgz",
- "integrity": "sha512-XUdGu3dxAkjsahLYnm8WijPfKebo+jHgHphDxaW0ovI6sTdmEGFDew7QzKZRlbYL2jRkUuuKuDGvD6lO5frmhA==",
- "dev": true,
- "optional": true
- },
- "@swc/core-linux-x64-musl": {
- "version": "1.3.100",
- "resolved": "https://registry.npmjs.org/@swc/core-linux-x64-musl/-/core-linux-x64-musl-1.3.100.tgz",
- "integrity": "sha512-PhoXKf+f0OaNW/GCuXjJ0/KfK9EJX7z2gko+7nVnEA0p3aaPtbP6cq1Ubbl6CMoPL+Ci3gZ7nYumDqXNc3CtLQ==",
- "dev": true,
- "optional": true
- },
- "@swc/core-win32-arm64-msvc": {
- "version": "1.3.100",
- "resolved": "https://registry.npmjs.org/@swc/core-win32-arm64-msvc/-/core-win32-arm64-msvc-1.3.100.tgz",
- "integrity": "sha512-PwLADZN6F9cXn4Jw52FeP/MCLVHm8vwouZZSOoOScDtihjY495SSjdPnlosMaRSR4wJQssGwiD/4MbpgQPqbAw==",
- "dev": true,
- "optional": true
- },
- "@swc/core-win32-ia32-msvc": {
- "version": "1.3.100",
- "resolved": "https://registry.npmjs.org/@swc/core-win32-ia32-msvc/-/core-win32-ia32-msvc-1.3.100.tgz",
- "integrity": "sha512-0f6nicKSLlDKlyPRl2JEmkpBV4aeDfRQg6n8mPqgL7bliZIcDahG0ej+HxgNjZfS3e0yjDxsNRa6sAqWU2Z60A==",
- "dev": true,
- "optional": true
- },
- "@swc/core-win32-x64-msvc": {
- "version": "1.3.100",
- "resolved": "https://registry.npmjs.org/@swc/core-win32-x64-msvc/-/core-win32-x64-msvc-1.3.100.tgz",
- "integrity": "sha512-b7J0rPoMkRTa3XyUGt8PwCaIBuYWsL2DqbirrQKRESzgCvif5iNpqaM6kjIjI/5y5q1Ycv564CB51YDpiS8EtQ==",
- "dev": true,
- "optional": true
- },
- "@swc/counter": {
- "version": "0.1.2",
- "resolved": "https://registry.npmjs.org/@swc/counter/-/counter-0.1.2.tgz",
- "integrity": "sha512-9F4ys4C74eSTEUNndnER3VJ15oru2NumfQxS8geE+f3eB5xvfxpWyqE5XlVnxb/R14uoXi6SLbBwwiDSkv+XEw==",
- "dev": true
- },
- "@swc/types": {
- "version": "0.1.5",
- "resolved": "https://registry.npmjs.org/@swc/types/-/types-0.1.5.tgz",
- "integrity": "sha512-myfUej5naTBWnqOCc/MdVOLVjXUXtIA+NpDrDBKJtLLg2shUjBu3cZmB/85RyitKc55+lUUyl7oRfLOvkr2hsw==",
- "dev": true
- },
- "@tanstack/history": {
- "version": "1.15.13",
- "resolved": "https://registry.npmjs.org/@tanstack/history/-/history-1.15.13.tgz",
- "integrity": "sha512-ToaeMtK5S4YaxCywAlYexc7KPFN0esjyTZ4vXzJhXEWAkro9iHgh7m/4ozPJb7oTo65WkHWX0W9GjcZbInSD8w=="
- },
- "@tanstack/query-core": {
- "version": "5.28.13",
- "resolved": "https://registry.npmjs.org/@tanstack/query-core/-/query-core-5.28.13.tgz",
- "integrity": "sha512-C3+CCOcza+mrZ7LglQbjeYEOTEC3LV0VN0eYaIN6GvqAZ8Foegdgch7n6QYPtT4FuLae5ALy+m+ZMEKpD6tMCQ=="
- },
- "@tanstack/query-devtools": {
- "version": "5.28.10",
- "resolved": "https://registry.npmjs.org/@tanstack/query-devtools/-/query-devtools-5.28.10.tgz",
- "integrity": "sha512-5UN629fKa5/1K/2Pd26gaU7epxRrYiT1gy+V+pW5K6hnf1DeUKK3pANSb2eHKlecjIKIhTwyF7k9XdyE2gREvQ=="
- },
- "@tanstack/react-query": {
- "version": "5.28.14",
- "resolved": "https://registry.npmjs.org/@tanstack/react-query/-/react-query-5.28.14.tgz",
- "integrity": "sha512-cZqt03Igb3I9tM72qNX5TAAmeYl75Z+k4Mv92VkXIXc2hCrv0fIywd7GN3JV1BBJl4mr7Cc+OOKKOPy8sNVOkA==",
- "requires": {
- "@tanstack/query-core": "5.28.13"
- }
- },
- "@tanstack/react-query-devtools": {
- "version": "5.28.14",
- "resolved": "https://registry.npmjs.org/@tanstack/react-query-devtools/-/react-query-devtools-5.28.14.tgz",
- "integrity": "sha512-4CrFBI1O5wibV1ZdGAnBMmTuc7SiShhxWubxRMyIloeEioxs3DQkFbouGBea5nexuwIxAkvhUB8khpPnNjhxMw==",
- "requires": {
- "@tanstack/query-devtools": "5.28.10"
- }
- },
- "@tanstack/react-router": {
- "version": "1.19.1",
- "resolved": "https://registry.npmjs.org/@tanstack/react-router/-/react-router-1.19.1.tgz",
- "integrity": "sha512-a4Xf074qo2fQLmSi8PTncEFn8XakaH3+DT7Dted4OPClzQFS+c6yU3HONVNAsuYWZ7lDK1HMKoHPDFbnHPEWvA==",
- "requires": {
- "@tanstack/history": "1.15.13",
- "@tanstack/react-store": "^0.2.1",
- "tiny-invariant": "^1.3.1",
- "tiny-warning": "^1.0.3"
- }
- },
- "@tanstack/react-store": {
- "version": "0.2.1",
- "resolved": "https://registry.npmjs.org/@tanstack/react-store/-/react-store-0.2.1.tgz",
- "integrity": "sha512-tEbMCQjbeVw9KOP/202LfqZMSNAVi6zYkkp1kBom8nFuMx/965Hzes3+6G6b/comCwVxoJU8Gg9IrcF8yRPthw==",
- "requires": {
- "@tanstack/store": "0.1.3",
- "use-sync-external-store": "^1.2.0"
- }
- },
- "@tanstack/router-devtools": {
- "version": "1.19.1",
- "resolved": "https://registry.npmjs.org/@tanstack/router-devtools/-/router-devtools-1.19.1.tgz",
- "integrity": "sha512-l560JHnffcDccSTo/sOtB+gKvtgaWYpOKOu9MyvswN9XB2pt752UFFIN1Yt/Gsp2Iooq/FcYlYnEPHb4GFzalg==",
- "dev": true,
- "requires": {
- "@tanstack/react-router": "1.19.1",
- "clsx": "^2.1.0",
- "date-fns": "^2.29.1",
- "goober": "^2.1.14"
- }
- },
- "@tanstack/router-generator": {
- "version": "1.19.0",
- "resolved": "https://registry.npmjs.org/@tanstack/router-generator/-/router-generator-1.19.0.tgz",
- "integrity": "sha512-vFF8Q7SdyygiYC7lfJ83GRif0vcxjak9SAcgtX/w7TLR0O+qdxRXFPvhKTQQXH6vVezy5Au9bSaSI2EgDD1ubA==",
- "dev": true,
- "requires": {
- "prettier": "^3.1.1",
- "zod": "^3.22.4"
- }
- },
- "@tanstack/router-vite-plugin": {
- "version": "1.19.0",
- "resolved": "https://registry.npmjs.org/@tanstack/router-vite-plugin/-/router-vite-plugin-1.19.0.tgz",
- "integrity": "sha512-yvvQnJ7JvqsnxAFqwiHhNTV2n1jKkidjc+XbgS2aNnEHC0aHnYH2ygPlmmfiVD7PMO7x64PdI5e12TzY/aKoFA==",
- "dev": true,
- "requires": {
- "@tanstack/router-generator": "1.19.0"
- }
- },
- "@tanstack/store": {
- "version": "0.1.3",
- "resolved": "https://registry.npmjs.org/@tanstack/store/-/store-0.1.3.tgz",
- "integrity": "sha512-GnolmC8Fr4mvsHE1fGQmR3Nm0eBO3KnZjDU0a+P3TeQNM/dDscFGxtA7p31NplQNW3KwBw4t1RVFmz0VeKLxcw=="
- },
- "@types/json-schema": {
- "version": "7.0.15",
- "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz",
- "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==",
- "dev": true
- },
- "@types/lodash": {
- "version": "4.14.202",
- "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.202.tgz",
- "integrity": "sha512-OvlIYQK9tNneDlS0VN54LLd5uiPCBOp7gS5Z0f1mjoJYBrtStzgmJBxONW3U6OZqdtNzZPmn9BS/7WI7BFFcFQ=="
- },
- "@types/lodash.mergewith": {
- "version": "4.6.7",
- "resolved": "https://registry.npmjs.org/@types/lodash.mergewith/-/lodash.mergewith-4.6.7.tgz",
- "integrity": "sha512-3m+lkO5CLRRYU0fhGRp7zbsGi6+BZj0uTVSwvcKU+nSlhjA9/QRNfuSGnD2mX6hQA7ZbmcCkzk5h4ZYGOtk14A==",
- "requires": {
- "@types/lodash": "*"
- }
- },
- "@types/node": {
- "version": "20.10.5",
- "resolved": "https://registry.npmjs.org/@types/node/-/node-20.10.5.tgz",
- "integrity": "sha512-nNPsNE65wjMxEKI93yOP+NPGGBJz/PoN3kZsVLee0XMiJolxSekEVD8wRwBUBqkwc7UWop0edW50yrCQW4CyRw==",
- "dev": true,
- "requires": {
- "undici-types": "~5.26.4"
- }
- },
- "@types/parse-json": {
- "version": "4.0.2",
- "resolved": "https://registry.npmjs.org/@types/parse-json/-/parse-json-4.0.2.tgz",
- "integrity": "sha512-dISoDXWWQwUquiKsyZ4Ng+HX2KsPL7LyHKHQwgGFEA3IaKac4Obd+h2a/a6waisAoepJlBcx9paWqjA8/HVjCw=="
- },
- "@types/prop-types": {
- "version": "15.7.11",
- "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.11.tgz",
- "integrity": "sha512-ga8y9v9uyeiLdpKddhxYQkxNDrfvuPrlFb0N1qnZZByvcElJaXthF1UhvCh9TLWJBEHeNtdnbysW7Y6Uq8CVng==",
- "devOptional": true
- },
- "@types/react": {
- "version": "18.2.39",
- "resolved": "https://registry.npmjs.org/@types/react/-/react-18.2.39.tgz",
- "integrity": "sha512-Oiw+ppED6IremMInLV4HXGbfbG6GyziY3kqAwJYOR0PNbkYDmLWQA3a95EhdSmamsvbkJN96ZNN+YD+fGjzSBA==",
- "devOptional": true,
- "requires": {
- "@types/prop-types": "*",
- "@types/scheduler": "*",
- "csstype": "^3.0.2"
- }
- },
- "@types/react-dom": {
- "version": "18.2.17",
- "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-18.2.17.tgz",
- "integrity": "sha512-rvrT/M7Df5eykWFxn6MYt5Pem/Dbyc1N8Y0S9Mrkw2WFCRiqUgw9P7ul2NpwsXCSM1DVdENzdG9J5SreqfAIWg==",
- "dev": true,
- "requires": {
- "@types/react": "*"
- }
- },
- "@types/scheduler": {
- "version": "0.16.8",
- "resolved": "https://registry.npmjs.org/@types/scheduler/-/scheduler-0.16.8.tgz",
- "integrity": "sha512-WZLiwShhwLRmeV6zH+GkbOFT6Z6VklCItrDioxUnv+u4Ll+8vKeFySoFyK/0ctcRpOmwAicELfmys1sDc/Rw+A==",
- "devOptional": true
- },
- "@vitejs/plugin-react-swc": {
- "version": "3.5.0",
- "resolved": "https://registry.npmjs.org/@vitejs/plugin-react-swc/-/plugin-react-swc-3.5.0.tgz",
- "integrity": "sha512-1PrOvAaDpqlCV+Up8RkAh9qaiUjoDUcjtttyhXDKw53XA6Ve16SOp6cCOpRs8Dj8DqUQs6eTW5YkLcLJjrXAig==",
- "dev": true,
- "requires": {
- "@swc/core": "^1.3.96"
- }
- },
- "@zag-js/dom-query": {
- "version": "0.16.0",
- "resolved": "https://registry.npmjs.org/@zag-js/dom-query/-/dom-query-0.16.0.tgz",
- "integrity": "sha512-Oqhd6+biWyKnhKwFFuZrrf6lxBz2tX2pRQe6grUnYwO6HJ8BcbqZomy2lpOdr+3itlaUqx+Ywj5E5ZZDr/LBfQ=="
- },
- "@zag-js/element-size": {
- "version": "0.10.5",
- "resolved": "https://registry.npmjs.org/@zag-js/element-size/-/element-size-0.10.5.tgz",
- "integrity": "sha512-uQre5IidULANvVkNOBQ1tfgwTQcGl4hliPSe69Fct1VfYb2Fd0jdAcGzqQgPhfrXFpR62MxLPB7erxJ/ngtL8w=="
- },
- "@zag-js/focus-visible": {
- "version": "0.16.0",
- "resolved": "https://registry.npmjs.org/@zag-js/focus-visible/-/focus-visible-0.16.0.tgz",
- "integrity": "sha512-a7U/HSopvQbrDU4GLerpqiMcHKEkQkNPeDZJWz38cw/6Upunh41GjHetq5TB84hxyCaDzJ6q2nEdNoBQfC0FKA==",
- "requires": {
- "@zag-js/dom-query": "0.16.0"
- }
- },
- "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
- },
- "aria-hidden": {
- "version": "1.2.3",
- "resolved": "https://registry.npmjs.org/aria-hidden/-/aria-hidden-1.2.3.tgz",
- "integrity": "sha512-xcLxITLe2HYa1cnYnwCjkOO1PqUHQpozB8x9AR0OgWN2woOBi5kSDVxKfd0b7sb1hw5qFeJhXm9H1nu3xSfLeQ==",
- "requires": {
- "tslib": "^2.0.0"
- }
- },
- "asynckit": {
- "version": "0.4.0",
- "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz",
- "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q=="
- },
- "axios": {
- "version": "1.7.4",
- "resolved": "https://registry.npmjs.org/axios/-/axios-1.7.4.tgz",
- "integrity": "sha512-DukmaFRnY6AzAALSH4J2M3k6PkaC+MfaAGdEERRWcC9q3/TWQwLpHR8ZRLKTdQ3aBDL64EdluRDjJqKw+BPZEw==",
- "requires": {
- "follow-redirects": "^1.15.6",
- "form-data": "^4.0.0",
- "proxy-from-env": "^1.1.0"
- }
- },
- "babel-plugin-macros": {
- "version": "3.1.0",
- "resolved": "https://registry.npmjs.org/babel-plugin-macros/-/babel-plugin-macros-3.1.0.tgz",
- "integrity": "sha512-Cg7TFGpIr01vOQNODXOOaGz2NpCU5gl8x1qJFbb6hbZxR7XrcE2vtbAsTAbJ7/xwJtUuJEw8K8Zr/AE0LHlesg==",
- "requires": {
- "@babel/runtime": "^7.12.5",
- "cosmiconfig": "^7.0.0",
- "resolve": "^1.19.0"
- }
- },
- "callsites": {
- "version": "3.1.0",
- "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz",
- "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ=="
- },
- "clsx": {
- "version": "2.1.0",
- "resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.0.tgz",
- "integrity": "sha512-m3iNNWpd9rl3jvvcBnu70ylMdrXt8Vlq4HYadnU5fwcOtvkSQWPmj7amUcDT2qYI7risszBjI5AUIUox9D16pg==",
- "dev": true
- },
- "color2k": {
- "version": "2.0.3",
- "resolved": "https://registry.npmjs.org/color2k/-/color2k-2.0.3.tgz",
- "integrity": "sha512-zW190nQTIoXcGCaU08DvVNFTmQhUpnJfVuAKfWqUQkflXKpaDdpaYoM0iluLS9lgJNHyBF58KKA2FBEwkD7wog=="
- },
- "combined-stream": {
- "version": "1.0.8",
- "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz",
- "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==",
- "requires": {
- "delayed-stream": "~1.0.0"
- }
- },
- "compute-scroll-into-view": {
- "version": "3.0.3",
- "resolved": "https://registry.npmjs.org/compute-scroll-into-view/-/compute-scroll-into-view-3.0.3.tgz",
- "integrity": "sha512-nadqwNxghAGTamwIqQSG433W6OADZx2vCo3UXHNrzTRHK/htu+7+L0zhjEoaeaQVNAi3YgqWDv8+tzf0hRfR+A=="
- },
- "convert-source-map": {
- "version": "1.9.0",
- "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.9.0.tgz",
- "integrity": "sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A=="
- },
- "copy-to-clipboard": {
- "version": "3.3.3",
- "resolved": "https://registry.npmjs.org/copy-to-clipboard/-/copy-to-clipboard-3.3.3.tgz",
- "integrity": "sha512-2KV8NhB5JqC3ky0r9PMCAZKbUHSwtEo4CwCs0KXgruG43gX5PMqDEBbVU4OUzw2MuAWUfsuFmWvEKG5QRfSnJA==",
- "requires": {
- "toggle-selection": "^1.0.6"
- }
- },
- "cosmiconfig": {
- "version": "7.1.0",
- "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-7.1.0.tgz",
- "integrity": "sha512-AdmX6xUzdNASswsFtmwSt7Vj8po9IuqXm0UXz7QKPuEUmPB4XyjGfaAr2PSuELMwkRMVH1EpIkX5bTZGRB3eCA==",
- "requires": {
- "@types/parse-json": "^4.0.0",
- "import-fresh": "^3.2.1",
- "parse-json": "^5.0.0",
- "path-type": "^4.0.0",
- "yaml": "^1.10.0"
- }
- },
- "css-box-model": {
- "version": "1.2.1",
- "resolved": "https://registry.npmjs.org/css-box-model/-/css-box-model-1.2.1.tgz",
- "integrity": "sha512-a7Vr4Q/kd/aw96bnJG332W9V9LkJO69JRcaCYDUqjp6/z0w6VcZjgAcTbgFxEPfBgdnAwlh3iwu+hLopa+flJw==",
- "requires": {
- "tiny-invariant": "^1.0.6"
- }
- },
- "csstype": {
- "version": "3.1.2",
- "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.2.tgz",
- "integrity": "sha512-I7K1Uu0MBPzaFKg4nI5Q7Vs2t+3gWWW648spaF+Rg7pI9ds18Ugn+lvg4SHczUdKlHI5LWBXyqfS8+DufyBsgQ=="
- },
- "date-fns": {
- "version": "2.30.0",
- "resolved": "https://registry.npmjs.org/date-fns/-/date-fns-2.30.0.tgz",
- "integrity": "sha512-fnULvOpxnC5/Vg3NCiWelDsLiUc9bRwAPs/+LfTLNvetFCtCTN+yQz15C/fs4AwX1R9K5GLtLfn8QW+dWisaAw==",
- "dev": true,
- "requires": {
- "@babel/runtime": "^7.21.0"
- }
- },
- "delayed-stream": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz",
- "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ=="
- },
- "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=="
- },
- "dotenv": {
- "version": "16.4.5",
- "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.4.5.tgz",
- "integrity": "sha512-ZmdL2rui+eB2YwhsWzjInR8LldtZHGDoQ1ugH85ppHKwpUHL7j7rN0Ti9NCnGiQbhaZ11FpR+7ao1dNsmduNUg==",
- "dev": true
- },
- "error-ex": {
- "version": "1.3.2",
- "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz",
- "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==",
- "requires": {
- "is-arrayish": "^0.2.1"
- }
- },
- "esbuild": {
- "version": "0.19.8",
- "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.19.8.tgz",
- "integrity": "sha512-l7iffQpT2OrZfH2rXIp7/FkmaeZM0vxbxN9KfiCwGYuZqzMg/JdvX26R31Zxn/Pxvsrg3Y9N6XTcnknqDyyv4w==",
- "dev": true,
- "requires": {
- "@esbuild/android-arm": "0.19.8",
- "@esbuild/android-arm64": "0.19.8",
- "@esbuild/android-x64": "0.19.8",
- "@esbuild/darwin-arm64": "0.19.8",
- "@esbuild/darwin-x64": "0.19.8",
- "@esbuild/freebsd-arm64": "0.19.8",
- "@esbuild/freebsd-x64": "0.19.8",
- "@esbuild/linux-arm": "0.19.8",
- "@esbuild/linux-arm64": "0.19.8",
- "@esbuild/linux-ia32": "0.19.8",
- "@esbuild/linux-loong64": "0.19.8",
- "@esbuild/linux-mips64el": "0.19.8",
- "@esbuild/linux-ppc64": "0.19.8",
- "@esbuild/linux-riscv64": "0.19.8",
- "@esbuild/linux-s390x": "0.19.8",
- "@esbuild/linux-x64": "0.19.8",
- "@esbuild/netbsd-x64": "0.19.8",
- "@esbuild/openbsd-x64": "0.19.8",
- "@esbuild/sunos-x64": "0.19.8",
- "@esbuild/win32-arm64": "0.19.8",
- "@esbuild/win32-ia32": "0.19.8",
- "@esbuild/win32-x64": "0.19.8"
- }
- },
- "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=="
- },
- "find-root": {
- "version": "1.1.0",
- "resolved": "https://registry.npmjs.org/find-root/-/find-root-1.1.0.tgz",
- "integrity": "sha512-NKfW6bec6GfKc0SGx1e07QZY9PE99u0Bft/0rzSD5k3sO/vwkVUpDUKVm5Gpp5Ue3YfShPFTX2070tDs5kB9Ng=="
- },
- "focus-lock": {
- "version": "1.3.3",
- "resolved": "https://registry.npmjs.org/focus-lock/-/focus-lock-1.3.3.tgz",
- "integrity": "sha512-hfXkZha7Xt4RQtrL1HBfspAuIj89Y0fb6GX0dfJilb8S2G/lvL4akPAcHq6xoD2NuZnDMCnZL/zQesMyeu6Psg==",
- "requires": {
- "tslib": "^2.0.3"
- }
- },
- "follow-redirects": {
- "version": "1.15.6",
- "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.6.tgz",
- "integrity": "sha512-wWN62YITEaOpSK584EZXJafH1AGpO8RVgElfkuXbTOrPX4fIfOyEpW/CsiNd8JdYrAoOvafRTOEnvsO++qCqFA=="
- },
- "form-data": {
- "version": "4.0.0",
- "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz",
- "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==",
- "requires": {
- "asynckit": "^0.4.0",
- "combined-stream": "^1.0.8",
- "mime-types": "^2.1.12"
- }
- },
- "framer-motion": {
- "version": "10.16.16",
- "resolved": "https://registry.npmjs.org/framer-motion/-/framer-motion-10.16.16.tgz",
- "integrity": "sha512-je6j91rd7NmUX7L1XHouwJ4v3R+SO4umso2LUcgOct3rHZ0PajZ80ETYZTajzEXEl9DlKyzjyt4AvGQ+lrebOw==",
- "requires": {
- "@emotion/is-prop-valid": "^0.8.2",
- "tslib": "^2.4.0"
- },
- "dependencies": {
- "@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==",
- "optional": true,
- "requires": {
- "@emotion/memoize": "0.7.4"
- }
- },
- "@emotion/memoize": {
- "version": "0.7.4",
- "resolved": "https://registry.npmjs.org/@emotion/memoize/-/memoize-0.7.4.tgz",
- "integrity": "sha512-Ja/Vfqe3HpuzRsG1oBtWTHk2PGZ7GR+2Vz5iYGelAw8dx32K0y7PjVuxK6z1nMpZOqAFsRUPCkK1YjJ56qJlgw==",
- "optional": true
- }
- }
- },
- "framesync": {
- "version": "6.1.2",
- "resolved": "https://registry.npmjs.org/framesync/-/framesync-6.1.2.tgz",
- "integrity": "sha512-jBTqhX6KaQVDyus8muwZbBeGGP0XgujBRbQ7gM7BRdS3CadCZIHiawyzYLnafYcvZIh5j8WE7cxZKFn7dXhu9g==",
- "requires": {
- "tslib": "2.4.0"
- },
- "dependencies": {
- "tslib": {
- "version": "2.4.0",
- "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.0.tgz",
- "integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ=="
- }
- }
- },
- "fsevents": {
- "version": "2.3.3",
- "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz",
- "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==",
- "dev": true,
- "optional": true
- },
- "function-bind": {
- "version": "1.1.2",
- "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz",
- "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA=="
- },
- "get-nonce": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/get-nonce/-/get-nonce-1.0.1.tgz",
- "integrity": "sha512-FJhYRoDaiatfEkUK8HKlicmu/3SGFD51q3itKDGoSTysQJBnfOcxU5GxnhE1E6soB76MbT0MBtnKJuXyAx+96Q=="
- },
- "goober": {
- "version": "2.1.14",
- "resolved": "https://registry.npmjs.org/goober/-/goober-2.1.14.tgz",
- "integrity": "sha512-4UpC0NdGyAFqLNPnhCT2iHpza2q+RAY3GV85a/mRPdzyPQMsj0KmMMuetdIkzWRbJ+Hgau1EZztq8ImmiMGhsg==",
- "dev": true,
- "requires": {}
- },
- "handlebars": {
- "version": "4.7.8",
- "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.7.8.tgz",
- "integrity": "sha512-vafaFqs8MZkRrSX7sFVUdo3ap/eNiLnb4IakshzvP56X5Nr1iGKAIqdX6tMlm6HcNRIkr6AxO5jFEoJzzpT8aQ==",
- "dev": true,
- "requires": {
- "minimist": "^1.2.5",
- "neo-async": "^2.6.2",
- "source-map": "^0.6.1",
- "uglify-js": "^3.1.4",
- "wordwrap": "^1.0.0"
- }
- },
- "hasown": {
- "version": "2.0.1",
- "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.1.tgz",
- "integrity": "sha512-1/th4MHjnwncwXsIW6QMzlvYL9kG5e/CpVvLRZe4XPa8TOUNbCELqmvhDmnkNsAjwaG4+I8gJJL0JBvTTLO9qA==",
- "requires": {
- "function-bind": "^1.1.2"
- }
- },
- "hoist-non-react-statics": {
- "version": "3.3.2",
- "resolved": "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz",
- "integrity": "sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw==",
- "requires": {
- "react-is": "^16.7.0"
- }
- },
- "import-fresh": {
- "version": "3.3.0",
- "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz",
- "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==",
- "requires": {
- "parent-module": "^1.0.0",
- "resolve-from": "^4.0.0"
- }
- },
- "invariant": {
- "version": "2.2.4",
- "resolved": "https://registry.npmjs.org/invariant/-/invariant-2.2.4.tgz",
- "integrity": "sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA==",
- "requires": {
- "loose-envify": "^1.0.0"
- }
- },
- "is-arrayish": {
- "version": "0.2.1",
- "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz",
- "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg=="
- },
- "is-core-module": {
- "version": "2.13.1",
- "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.13.1.tgz",
- "integrity": "sha512-hHrIjvZsftOsvKSn2TRYl63zvxsgE0K+0mYMoH6gD4omR5IWB2KynivBQczo3+wF1cCkjzvptnI9Q0sPU66ilw==",
- "requires": {
- "hasown": "^2.0.0"
- }
- },
- "js-tokens": {
- "version": "4.0.0",
- "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz",
- "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ=="
- },
- "js-yaml": {
- "version": "4.1.0",
- "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz",
- "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==",
- "dev": true,
- "requires": {
- "argparse": "^2.0.1"
- }
- },
- "json-parse-even-better-errors": {
- "version": "2.3.1",
- "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz",
- "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w=="
- },
- "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=="
- },
- "lodash.mergewith": {
- "version": "4.6.2",
- "resolved": "https://registry.npmjs.org/lodash.mergewith/-/lodash.mergewith-4.6.2.tgz",
- "integrity": "sha512-GK3g5RPZWTRSeLSpgP8Xhra+pnjBC56q9FZYe1d5RN3TJ35dbkGy3YqBSMbyCrlbi+CM9Z3Jk5yTL7RCsqboyQ=="
- },
- "loose-envify": {
- "version": "1.4.0",
- "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz",
- "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==",
- "requires": {
- "js-tokens": "^3.0.0 || ^4.0.0"
- }
- },
- "mime-db": {
- "version": "1.52.0",
- "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz",
- "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg=="
- },
- "mime-types": {
- "version": "2.1.35",
- "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz",
- "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==",
- "requires": {
- "mime-db": "1.52.0"
- }
- },
- "minimist": {
- "version": "1.2.8",
- "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz",
- "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==",
- "dev": true
- },
- "nanoid": {
- "version": "3.3.7",
- "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.7.tgz",
- "integrity": "sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==",
- "dev": true
- },
- "neo-async": {
- "version": "2.6.2",
- "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz",
- "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==",
- "dev": true
- },
- "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=="
- },
- "parent-module": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz",
- "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==",
- "requires": {
- "callsites": "^3.0.0"
- }
- },
- "parse-json": {
- "version": "5.2.0",
- "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz",
- "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==",
- "requires": {
- "@babel/code-frame": "^7.0.0",
- "error-ex": "^1.3.1",
- "json-parse-even-better-errors": "^2.3.0",
- "lines-and-columns": "^1.1.6"
- }
- },
- "path-parse": {
- "version": "1.0.7",
- "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz",
- "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw=="
- },
- "path-type": {
- "version": "4.0.0",
- "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz",
- "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw=="
- },
- "picocolors": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz",
- "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==",
- "dev": true
- },
- "playwright": {
- "version": "1.45.2",
- "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.45.2.tgz",
- "integrity": "sha512-ReywF2t/0teRvNBpfIgh5e4wnrI/8Su8ssdo5XsQKpjxJj+jspm00jSoz9BTg91TT0c9HRjXO7LBNVrgYj9X0g==",
- "dev": true,
- "requires": {
- "fsevents": "2.3.2",
- "playwright-core": "1.45.2"
- },
- "dependencies": {
- "fsevents": {
- "version": "2.3.2",
- "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz",
- "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==",
- "dev": true,
- "optional": true
- }
- }
- },
- "playwright-core": {
- "version": "1.45.2",
- "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.45.2.tgz",
- "integrity": "sha512-ha175tAWb0dTK0X4orvBIqi3jGEt701SMxMhyujxNrgd8K0Uy5wMSwwcQHtyB4om7INUkfndx02XnQ2p6dvLDw==",
- "dev": true
- },
- "postcss": {
- "version": "8.4.35",
- "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.35.tgz",
- "integrity": "sha512-u5U8qYpBCpN13BsiEB0CbR1Hhh4Gc0zLFuedrHJKMctHCHAGrMdG0PRM/KErzAL3CU6/eckEtmHNB3x6e3c0vA==",
- "dev": true,
- "requires": {
- "nanoid": "^3.3.7",
- "picocolors": "^1.0.0",
- "source-map-js": "^1.0.2"
- }
- },
- "prettier": {
- "version": "3.2.5",
- "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.2.5.tgz",
- "integrity": "sha512-3/GWa9aOC0YeD7LUfvOG2NiDyhOWRvt1k+rcKhOuYnMY24iiCphgneUfJDyFXd6rZCAnuLBv6UeAULtrhT/F4A==",
- "dev": true
- },
- "prop-types": {
- "version": "15.8.1",
- "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz",
- "integrity": "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==",
- "requires": {
- "loose-envify": "^1.4.0",
- "object-assign": "^4.1.1",
- "react-is": "^16.13.1"
- }
- },
- "proxy-from-env": {
- "version": "1.1.0",
- "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz",
- "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg=="
- },
- "react": {
- "version": "18.2.0",
- "resolved": "https://registry.npmjs.org/react/-/react-18.2.0.tgz",
- "integrity": "sha512-/3IjMdb2L9QbBdWiW5e3P2/npwMBaU9mHCSCUzNln0ZCYbcfTsGbTJrU/kGemdH2IWmB2ioZ+zkxtmq6g09fGQ==",
- "requires": {
- "loose-envify": "^1.1.0"
- }
- },
- "react-clientside-effect": {
- "version": "1.2.6",
- "resolved": "https://registry.npmjs.org/react-clientside-effect/-/react-clientside-effect-1.2.6.tgz",
- "integrity": "sha512-XGGGRQAKY+q25Lz9a/4EPqom7WRjz3z9R2k4jhVKA/puQFH/5Nt27vFZYql4m4NVNdUvX8PS3O7r/Zzm7cjUlg==",
- "requires": {
- "@babel/runtime": "^7.12.13"
- }
- },
- "react-dom": {
- "version": "18.2.0",
- "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.2.0.tgz",
- "integrity": "sha512-6IMTriUmvsjHUjNtEDudZfuDQUoWXVxKHhlEGSk81n4YFS+r/Kl99wXiwlVXtPBtJenozv2P+hxDsw9eA7Xo6g==",
- "requires": {
- "loose-envify": "^1.1.0",
- "scheduler": "^0.23.0"
- }
- },
- "react-error-boundary": {
- "version": "4.0.13",
- "resolved": "https://registry.npmjs.org/react-error-boundary/-/react-error-boundary-4.0.13.tgz",
- "integrity": "sha512-b6PwbdSv8XeOSYvjt8LpgpKrZ0yGdtZokYwkwV2wlcZbxgopHX/hgPl5VgpnoVOWd868n1hktM8Qm4b+02MiLQ==",
- "requires": {
- "@babel/runtime": "^7.12.5"
- }
- },
- "react-fast-compare": {
- "version": "3.2.2",
- "resolved": "https://registry.npmjs.org/react-fast-compare/-/react-fast-compare-3.2.2.tgz",
- "integrity": "sha512-nsO+KSNgo1SbJqJEYRE9ERzo7YtYbou/OqjSQKxV7jcKox7+usiUVZOAC+XnDOABXggQTno0Y1CpVnuWEc1boQ=="
- },
- "react-focus-lock": {
- "version": "2.11.1",
- "resolved": "https://registry.npmjs.org/react-focus-lock/-/react-focus-lock-2.11.1.tgz",
- "integrity": "sha512-IXLwnTBrLTlKTpASZXqqXJ8oymWrgAlOfuuDYN4XCuN1YJ72dwX198UCaF1QqGUk5C3QOnlMik//n3ufcfe8Ig==",
- "requires": {
- "@babel/runtime": "^7.0.0",
- "focus-lock": "^1.3.2",
- "prop-types": "^15.6.2",
- "react-clientside-effect": "^1.2.6",
- "use-callback-ref": "^1.3.0",
- "use-sidecar": "^1.1.2"
- }
- },
- "react-hook-form": {
- "version": "7.49.3",
- "resolved": "https://registry.npmjs.org/react-hook-form/-/react-hook-form-7.49.3.tgz",
- "integrity": "sha512-foD6r3juidAT1cOZzpmD/gOKt7fRsDhXXZ0y28+Al1CHgX+AY1qIN9VSIIItXRq1dN68QrRwl1ORFlwjBaAqeQ==",
- "requires": {}
- },
- "react-icons": {
- "version": "5.0.1",
- "resolved": "https://registry.npmjs.org/react-icons/-/react-icons-5.0.1.tgz",
- "integrity": "sha512-WqLZJ4bLzlhmsvme6iFdgO8gfZP17rfjYEJ2m9RsZjZ+cc4k1hTzknEz63YS1MeT50kVzoa1Nz36f4BEx+Wigw==",
- "requires": {}
- },
- "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=="
- },
- "react-remove-scroll": {
- "version": "2.5.7",
- "resolved": "https://registry.npmjs.org/react-remove-scroll/-/react-remove-scroll-2.5.7.tgz",
- "integrity": "sha512-FnrTWO4L7/Bhhf3CYBNArEG/yROV0tKmTv7/3h9QCFvH6sndeFf1wPqOcbFVu5VAulS5dV1wGT3GZZ/1GawqiA==",
- "requires": {
- "react-remove-scroll-bar": "^2.3.4",
- "react-style-singleton": "^2.2.1",
- "tslib": "^2.1.0",
- "use-callback-ref": "^1.3.0",
- "use-sidecar": "^1.1.2"
- }
- },
- "react-remove-scroll-bar": {
- "version": "2.3.5",
- "resolved": "https://registry.npmjs.org/react-remove-scroll-bar/-/react-remove-scroll-bar-2.3.5.tgz",
- "integrity": "sha512-3cqjOqg6s0XbOjWvmasmqHch+RLxIEk2r/70rzGXuz3iIGQsQheEQyqYCBb5EECoD01Vo2SIbDqW4paLeLTASw==",
- "requires": {
- "react-style-singleton": "^2.2.1",
- "tslib": "^2.0.0"
- }
- },
- "react-style-singleton": {
- "version": "2.2.1",
- "resolved": "https://registry.npmjs.org/react-style-singleton/-/react-style-singleton-2.2.1.tgz",
- "integrity": "sha512-ZWj0fHEMyWkHzKYUr2Bs/4zU6XLmq9HsgBURm7g5pAVfyn49DgUiNgY2d4lXRlYSiCif9YBGpQleewkcqddc7g==",
- "requires": {
- "get-nonce": "^1.0.0",
- "invariant": "^2.2.4",
- "tslib": "^2.0.0"
- }
- },
- "regenerator-runtime": {
- "version": "0.14.1",
- "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.14.1.tgz",
- "integrity": "sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw=="
- },
- "resolve": {
- "version": "1.22.8",
- "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.8.tgz",
- "integrity": "sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==",
- "requires": {
- "is-core-module": "^2.13.0",
- "path-parse": "^1.0.7",
- "supports-preserve-symlinks-flag": "^1.0.0"
- }
- },
- "resolve-from": {
- "version": "4.0.0",
- "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz",
- "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g=="
- },
- "rollup": {
- "version": "4.6.1",
- "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.6.1.tgz",
- "integrity": "sha512-jZHaZotEHQaHLgKr8JnQiDT1rmatjgKlMekyksz+yk9jt/8z9quNjnKNRoaM0wd9DC2QKXjmWWuDYtM3jfF8pQ==",
- "dev": true,
- "requires": {
- "@rollup/rollup-android-arm-eabi": "4.6.1",
- "@rollup/rollup-android-arm64": "4.6.1",
- "@rollup/rollup-darwin-arm64": "4.6.1",
- "@rollup/rollup-darwin-x64": "4.6.1",
- "@rollup/rollup-linux-arm-gnueabihf": "4.6.1",
- "@rollup/rollup-linux-arm64-gnu": "4.6.1",
- "@rollup/rollup-linux-arm64-musl": "4.6.1",
- "@rollup/rollup-linux-x64-gnu": "4.6.1",
- "@rollup/rollup-linux-x64-musl": "4.6.1",
- "@rollup/rollup-win32-arm64-msvc": "4.6.1",
- "@rollup/rollup-win32-ia32-msvc": "4.6.1",
- "@rollup/rollup-win32-x64-msvc": "4.6.1",
- "fsevents": "~2.3.2"
- }
- },
- "scheduler": {
- "version": "0.23.0",
- "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.23.0.tgz",
- "integrity": "sha512-CtuThmgHNg7zIZWAXi3AsyIzA3n4xx7aNyjwC2VJldO2LMVDhFK+63xGqq6CsJH4rTAt6/M+N4GhZiDYPx9eUw==",
- "requires": {
- "loose-envify": "^1.1.0"
- }
- },
- "source-map": {
- "version": "0.6.1",
- "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
- "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
- "dev": true
- },
- "source-map-js": {
- "version": "1.0.2",
- "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.0.2.tgz",
- "integrity": "sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==",
- "dev": true
- },
- "stylis": {
- "version": "4.2.0",
- "resolved": "https://registry.npmjs.org/stylis/-/stylis-4.2.0.tgz",
- "integrity": "sha512-Orov6g6BB1sDfYgzWfTHDOxamtX1bE/zo104Dh9e6fqJ3PooipYyfJ0pUmrZO2wAvO8YbEyeFrkV91XTsGMSrw=="
- },
- "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=="
- },
- "tiny-invariant": {
- "version": "1.3.3",
- "resolved": "https://registry.npmjs.org/tiny-invariant/-/tiny-invariant-1.3.3.tgz",
- "integrity": "sha512-+FbBPE1o9QAYvviau/qC5SE3caw21q3xkvWKBtja5vgqOWIHHJ3ioaq1VPfn/Szqctz2bU/oYeKd9/z5BL+PVg=="
- },
- "tiny-warning": {
- "version": "1.0.3",
- "resolved": "https://registry.npmjs.org/tiny-warning/-/tiny-warning-1.0.3.tgz",
- "integrity": "sha512-lBN9zLN/oAf68o3zNXYrdCt1kP8WsiGW8Oo2ka41b2IM5JL/S1CTyX1rW0mb/zSuJun0ZUrDxx4sqvYS2FWzPA=="
- },
- "to-fast-properties": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz",
- "integrity": "sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog=="
- },
- "toggle-selection": {
- "version": "1.0.6",
- "resolved": "https://registry.npmjs.org/toggle-selection/-/toggle-selection-1.0.6.tgz",
- "integrity": "sha512-BiZS+C1OS8g/q2RRbJmy59xpyghNBqrr6k5L/uKBGRsTfxmu3ffiRnd8mlGPUVayg8pvfi5urfnu8TU7DVOkLQ=="
- },
- "tslib": {
- "version": "2.6.2",
- "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz",
- "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q=="
- },
- "typescript": {
- "version": "5.3.2",
- "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.3.2.tgz",
- "integrity": "sha512-6l+RyNy7oAHDfxC4FzSJcz9vnjTKxrLpDG5M2Vu4SHRVNg6xzqZp6LYSR9zjqQTu8DU/f5xwxUdADOkbrIX2gQ==",
- "dev": true
- },
- "uglify-js": {
- "version": "3.17.4",
- "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.17.4.tgz",
- "integrity": "sha512-T9q82TJI9e/C1TAxYvfb16xO120tMVFZrGA3f9/P4424DNu6ypK103y0GPFVa17yotwSyZW5iYXgjYHkGrJW/g==",
- "dev": true,
- "optional": true
- },
- "undici-types": {
- "version": "5.26.5",
- "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz",
- "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==",
- "dev": true
- },
- "use-callback-ref": {
- "version": "1.3.1",
- "resolved": "https://registry.npmjs.org/use-callback-ref/-/use-callback-ref-1.3.1.tgz",
- "integrity": "sha512-Lg4Vx1XZQauB42Hw3kK7JM6yjVjgFmFC5/Ab797s79aARomD2nEErc4mCgM8EZrARLmmbWpi5DGCadmK50DcAQ==",
- "requires": {
- "tslib": "^2.0.0"
- }
- },
- "use-sidecar": {
- "version": "1.1.2",
- "resolved": "https://registry.npmjs.org/use-sidecar/-/use-sidecar-1.1.2.tgz",
- "integrity": "sha512-epTbsLuzZ7lPClpz2TyryBfztm7m+28DlEv2ZCQ3MDr5ssiwyOwGH/e5F9CkfWjJ1t4clvI58yF822/GUkjjhw==",
- "requires": {
- "detect-node-es": "^1.1.0",
- "tslib": "^2.0.0"
- }
- },
- "use-sync-external-store": {
- "version": "1.2.0",
- "resolved": "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.2.0.tgz",
- "integrity": "sha512-eEgnFxGQ1Ife9bzYs6VLi8/4X6CObHMw9Qr9tPY43iKwsPw8xE8+EFsf/2cFZ5S3esXgpWgtSCtLNS41F+sKPA==",
- "requires": {}
- },
- "vite": {
- "version": "5.0.13",
- "resolved": "https://registry.npmjs.org/vite/-/vite-5.0.13.tgz",
- "integrity": "sha512-/9ovhv2M2dGTuA+dY93B9trfyWMDRQw2jdVBhHNP6wr0oF34wG2i/N55801iZIpgUpnHDm4F/FabGQLyc+eOgg==",
- "dev": true,
- "requires": {
- "esbuild": "^0.19.3",
- "fsevents": "~2.3.3",
- "postcss": "^8.4.32",
- "rollup": "^4.2.0"
- }
- },
- "wordwrap": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz",
- "integrity": "sha512-gvVzJFlPycKc5dZN4yPkP8w7Dc37BtP1yczEneOb4uq34pXZcvrtRTmWV8W+Ume+XCxKgbjM+nevkyFPMybd4Q==",
- "dev": true
- },
- "yaml": {
- "version": "1.10.2",
- "resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.2.tgz",
- "integrity": "sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg=="
- },
- "zod": {
- "version": "3.22.4",
- "resolved": "https://registry.npmjs.org/zod/-/zod-3.22.4.tgz",
- "integrity": "sha512-iC+8Io04lddc+mVqQ9AZ7OQ2MrUKGN+oIQyq1vemgt46jwCwLfhq7/pwnBnNXXXZb8VTVLKwp9EDkx+ryxIWmg==",
- "dev": true
- }
- }
-}
diff --git a/frontend/package.json b/frontend/package.json
deleted file mode 100644
index 1a7a547f68..0000000000
--- a/frontend/package.json
+++ /dev/null
@@ -1,44 +0,0 @@
-{
- "name": "frontend",
- "private": true,
- "version": "0.0.0",
- "type": "module",
- "scripts": {
- "dev": "vite",
- "build": "tsc && vite build",
- "lint": "biome check --apply-unsafe --no-errors-on-unmatched --files-ignore-unknown=true ./",
- "preview": "vite preview",
- "generate-client": "openapi-ts --input ./openapi.json --output ./src/client --client axios --exportSchemas true"
- },
- "dependencies": {
- "@chakra-ui/icons": "2.1.1",
- "@chakra-ui/react": "2.8.2",
- "@emotion/react": "11.11.3",
- "@emotion/styled": "11.11.0",
- "@tanstack/react-query": "^5.28.14",
- "@tanstack/react-query-devtools": "^5.28.14",
- "@tanstack/react-router": "1.19.1",
- "axios": "1.7.4",
- "form-data": "4.0.0",
- "framer-motion": "10.16.16",
- "react": "^18.2.0",
- "react-dom": "^18.2.0",
- "react-error-boundary": "^4.0.13",
- "react-hook-form": "7.49.3",
- "react-icons": "5.0.1"
- },
- "devDependencies": {
- "@biomejs/biome": "1.6.1",
- "@hey-api/openapi-ts": "^0.34.1",
- "@playwright/test": "^1.45.2",
- "@tanstack/router-devtools": "1.19.1",
- "@tanstack/router-vite-plugin": "1.19.0",
- "@types/node": "^20.10.5",
- "@types/react": "^18.2.37",
- "@types/react-dom": "^18.2.15",
- "@vitejs/plugin-react-swc": "^3.5.0",
- "dotenv": "^16.4.5",
- "typescript": "^5.2.2",
- "vite": "^5.0.13"
- }
-}
diff --git a/frontend/playwright.config.ts b/frontend/playwright.config.ts
deleted file mode 100644
index dcdd6fec81..0000000000
--- a/frontend/playwright.config.ts
+++ /dev/null
@@ -1,92 +0,0 @@
-import { defineConfig, devices } from '@playwright/test';
-
-
-/**
- * Read environment variables from file.
- * https://github.com/motdotla/dotenv
- */
-// require('dotenv').config();
-
-/**
- * See https://playwright.dev/docs/test-configuration.
- */
-export default defineConfig({
- testDir: './tests',
- /* Run tests in files in parallel */
- fullyParallel: true,
- /* Fail the build on CI if you accidentally left test.only in the source code. */
- forbidOnly: !!process.env.CI,
- /* Retry on CI only */
- retries: process.env.CI ? 2 : 0,
- /* Opt out of parallel tests on CI. */
- workers: process.env.CI ? 1 : undefined,
- /* Reporter to use. See https://playwright.dev/docs/test-reporters */
- reporter: 'html',
- /* Shared settings for all the projects below. See https://playwright.dev/docs/api/class-testoptions. */
- use: {
- /* Base URL to use in actions like `await page.goto('/')`. */
- baseURL: 'http://localhost:5173',
-
- /* Collect trace when retrying the failed test. See https://playwright.dev/docs/trace-viewer */
- trace: 'on-first-retry',
- },
-
- /* Configure projects for major browsers */
- projects: [
- { name: 'setup', testMatch: /.*\.setup\.ts/ },
-
- {
- name: 'chromium',
- use: {
- ...devices['Desktop Chrome'],
- storageState: 'playwright/.auth/user.json',
- },
- dependencies: ['setup'],
- },
-
- // {
- // name: 'firefox',
- // use: {
- // ...devices['Desktop Firefox'],
- // storageState: 'playwright/.auth/user.json',
- // },
- // dependencies: ['setup'],
- // },
-
- // {
- // name: 'webkit',
- // use: {
- // ...devices['Desktop Safari'],
- // storageState: 'playwright/.auth/user.json',
- // },
- // dependencies: ['setup'],
- // },
-
- /* Test against mobile viewports. */
- // {
- // name: 'Mobile Chrome',
- // use: { ...devices['Pixel 5'] },
- // },
- // {
- // name: 'Mobile Safari',
- // use: { ...devices['iPhone 12'] },
- // },
-
- /* Test against branded browsers. */
- // {
- // name: 'Microsoft Edge',
- // use: { ...devices['Desktop Edge'], channel: 'msedge' },
- // },
- // {
- // name: 'Google Chrome',
- // use: { ...devices['Desktop Chrome'], channel: 'chrome' },
- // },
- ],
-
- /* Run your local dev server before starting the tests */
- webServer: {
- command: 'npm run dev',
- url: 'http://localhost:5173',
- reuseExistingServer: !process.env.CI,
- },
-});
diff --git a/frontend/public/assets/images/fastapi-logo.svg b/frontend/public/assets/images/fastapi-logo.svg
deleted file mode 100644
index d3dad4bec8..0000000000
--- a/frontend/public/assets/images/fastapi-logo.svg
+++ /dev/null
@@ -1,51 +0,0 @@
-
-
diff --git a/frontend/public/assets/images/favicon.png b/frontend/public/assets/images/favicon.png
deleted file mode 100644
index e5b7c3ada7..0000000000
Binary files a/frontend/public/assets/images/favicon.png and /dev/null differ
diff --git a/frontend/src/client/core/ApiError.ts b/frontend/src/client/core/ApiError.ts
deleted file mode 100644
index 5499aa8f05..0000000000
--- a/frontend/src/client/core/ApiError.ts
+++ /dev/null
@@ -1,25 +0,0 @@
-import type { ApiRequestOptions } from "./ApiRequestOptions"
-import type { ApiResult } from "./ApiResult"
-
-export class ApiError extends Error {
- public readonly url: string
- public readonly status: number
- public readonly statusText: string
- public readonly body: unknown
- public readonly request: ApiRequestOptions
-
- constructor(
- request: ApiRequestOptions,
- response: ApiResult,
- message: string,
- ) {
- super(message)
-
- this.name = "ApiError"
- this.url = response.url
- this.status = response.status
- this.statusText = response.statusText
- this.body = response.body
- this.request = request
- }
-}
diff --git a/frontend/src/client/core/ApiRequestOptions.ts b/frontend/src/client/core/ApiRequestOptions.ts
deleted file mode 100644
index 4cc259284f..0000000000
--- a/frontend/src/client/core/ApiRequestOptions.ts
+++ /dev/null
@@ -1,20 +0,0 @@
-export type ApiRequestOptions = {
- readonly method:
- | "GET"
- | "PUT"
- | "POST"
- | "DELETE"
- | "OPTIONS"
- | "HEAD"
- | "PATCH"
- readonly url: string
- readonly path?: Record
- readonly cookies?: Record
- readonly headers?: Record
- readonly query?: Record
- readonly formData?: Record
- readonly body?: any
- readonly mediaType?: string
- readonly responseHeader?: string
- readonly errors?: Record
-}
diff --git a/frontend/src/client/core/ApiResult.ts b/frontend/src/client/core/ApiResult.ts
deleted file mode 100644
index f88b8c64f1..0000000000
--- a/frontend/src/client/core/ApiResult.ts
+++ /dev/null
@@ -1,7 +0,0 @@
-export type ApiResult = {
- readonly body: TData
- readonly ok: boolean
- readonly status: number
- readonly statusText: string
- readonly url: string
-}
diff --git a/frontend/src/client/core/CancelablePromise.ts b/frontend/src/client/core/CancelablePromise.ts
deleted file mode 100644
index f47db79eae..0000000000
--- a/frontend/src/client/core/CancelablePromise.ts
+++ /dev/null
@@ -1,126 +0,0 @@
-export class CancelError extends Error {
- constructor(message: string) {
- super(message)
- this.name = "CancelError"
- }
-
- public get isCancelled(): boolean {
- return true
- }
-}
-
-export interface OnCancel {
- readonly isResolved: boolean
- readonly isRejected: boolean
- readonly isCancelled: boolean
-
- (cancelHandler: () => void): void
-}
-
-export class CancelablePromise implements Promise {
- private _isResolved: boolean
- private _isRejected: boolean
- private _isCancelled: boolean
- readonly cancelHandlers: (() => void)[]
- readonly promise: Promise
- private _resolve?: (value: T | PromiseLike) => void
- private _reject?: (reason?: unknown) => void
-
- constructor(
- executor: (
- resolve: (value: T | PromiseLike) => void,
- reject: (reason?: unknown) => void,
- onCancel: OnCancel,
- ) => void,
- ) {
- this._isResolved = false
- this._isRejected = false
- this._isCancelled = false
- this.cancelHandlers = []
- this.promise = new Promise((resolve, reject) => {
- this._resolve = resolve
- this._reject = reject
-
- const onResolve = (value: T | PromiseLike): void => {
- if (this._isResolved || this._isRejected || this._isCancelled) {
- return
- }
- this._isResolved = true
- if (this._resolve) this._resolve(value)
- }
-
- const onReject = (reason?: unknown): void => {
- if (this._isResolved || this._isRejected || this._isCancelled) {
- return
- }
- this._isRejected = true
- if (this._reject) this._reject(reason)
- }
-
- const onCancel = (cancelHandler: () => void): void => {
- if (this._isResolved || this._isRejected || this._isCancelled) {
- return
- }
- this.cancelHandlers.push(cancelHandler)
- }
-
- Object.defineProperty(onCancel, "isResolved", {
- get: (): boolean => this._isResolved,
- })
-
- Object.defineProperty(onCancel, "isRejected", {
- get: (): boolean => this._isRejected,
- })
-
- Object.defineProperty(onCancel, "isCancelled", {
- get: (): boolean => this._isCancelled,
- })
-
- return executor(onResolve, onReject, onCancel as OnCancel)
- })
- }
-
- get [Symbol.toStringTag]() {
- return "Cancellable Promise"
- }
-
- public then(
- onFulfilled?: ((value: T) => TResult1 | PromiseLike) | null,
- onRejected?: ((reason: unknown) => TResult2 | PromiseLike) | null,
- ): Promise {
- return this.promise.then(onFulfilled, onRejected)
- }
-
- public catch(
- onRejected?: ((reason: unknown) => TResult | PromiseLike) | null,
- ): Promise {
- return this.promise.catch(onRejected)
- }
-
- public finally(onFinally?: (() => void) | null): Promise {
- return this.promise.finally(onFinally)
- }
-
- public cancel(): void {
- if (this._isResolved || this._isRejected || this._isCancelled) {
- return
- }
- this._isCancelled = true
- if (this.cancelHandlers.length) {
- try {
- for (const cancelHandler of this.cancelHandlers) {
- cancelHandler()
- }
- } catch (error) {
- console.warn("Cancellation threw an error", error)
- return
- }
- }
- this.cancelHandlers.length = 0
- if (this._reject) this._reject(new CancelError("Request aborted"))
- }
-
- public get isCancelled(): boolean {
- return this._isCancelled
- }
-}
diff --git a/frontend/src/client/core/OpenAPI.ts b/frontend/src/client/core/OpenAPI.ts
deleted file mode 100644
index 746df5e61d..0000000000
--- a/frontend/src/client/core/OpenAPI.ts
+++ /dev/null
@@ -1,57 +0,0 @@
-import type { AxiosRequestConfig, AxiosResponse } from "axios"
-import type { ApiRequestOptions } from "./ApiRequestOptions"
-import type { TResult } from "./types"
-
-type Headers = Record
-type Middleware = (value: T) => T | Promise
-type Resolver = (options: ApiRequestOptions) => Promise
-
-export class Interceptors {
- _fns: Middleware[]
-
- constructor() {
- this._fns = []
- }
-
- eject(fn: Middleware) {
- const index = this._fns.indexOf(fn)
- if (index !== -1) {
- this._fns = [...this._fns.slice(0, index), ...this._fns.slice(index + 1)]
- }
- }
-
- use(fn: Middleware) {
- this._fns = [...this._fns, fn]
- }
-}
-
-export type OpenAPIConfig = {
- BASE: string
- CREDENTIALS: "include" | "omit" | "same-origin"
- ENCODE_PATH?: ((path: string) => string) | undefined
- HEADERS?: Headers | Resolver | undefined
- PASSWORD?: string | Resolver | undefined
- RESULT?: TResult
- TOKEN?: string | Resolver | undefined
- USERNAME?: string | Resolver | undefined
- VERSION: string
- WITH_CREDENTIALS: boolean
- interceptors: {
- request: Interceptors
- response: Interceptors
- }
-}
-
-export const OpenAPI: OpenAPIConfig = {
- BASE: "",
- CREDENTIALS: "include",
- ENCODE_PATH: undefined,
- HEADERS: undefined,
- PASSWORD: undefined,
- RESULT: "body",
- TOKEN: undefined,
- USERNAME: undefined,
- VERSION: "0.1.0",
- WITH_CREDENTIALS: false,
- interceptors: { request: new Interceptors(), response: new Interceptors() },
-}
diff --git a/frontend/src/client/core/request.ts b/frontend/src/client/core/request.ts
deleted file mode 100644
index 99d38b46f1..0000000000
--- a/frontend/src/client/core/request.ts
+++ /dev/null
@@ -1,376 +0,0 @@
-import axios from "axios"
-import type {
- AxiosError,
- AxiosRequestConfig,
- AxiosResponse,
- AxiosInstance,
-} from "axios"
-
-import { ApiError } from "./ApiError"
-import type { ApiRequestOptions } from "./ApiRequestOptions"
-import type { ApiResult } from "./ApiResult"
-import { CancelablePromise } from "./CancelablePromise"
-import type { OnCancel } from "./CancelablePromise"
-import type { OpenAPIConfig } from "./OpenAPI"
-
-export const isString = (value: unknown): value is string => {
- return typeof value === "string"
-}
-
-export const isStringWithValue = (value: unknown): value is string => {
- return isString(value) && value !== ""
-}
-
-export const isBlob = (value: any): value is Blob => {
- return value instanceof Blob
-}
-
-export const isFormData = (value: unknown): value is FormData => {
- return value instanceof FormData
-}
-
-export const isSuccess = (status: number): boolean => {
- return status >= 200 && status < 300
-}
-
-export const base64 = (str: string): string => {
- try {
- return btoa(str)
- } catch (err) {
- // @ts-ignore
- return Buffer.from(str).toString("base64")
- }
-}
-
-export const getQueryString = (params: Record): string => {
- const qs: string[] = []
-
- const append = (key: string, value: unknown) => {
- qs.push(`${encodeURIComponent(key)}=${encodeURIComponent(String(value))}`)
- }
-
- const encodePair = (key: string, value: unknown) => {
- if (value === undefined || value === null) {
- return
- }
-
- if (Array.isArray(value)) {
- value.forEach((v) => encodePair(key, v))
- } else if (typeof value === "object") {
- Object.entries(value).forEach(([k, v]) => encodePair(`${key}[${k}]`, v))
- } else {
- append(key, value)
- }
- }
-
- Object.entries(params).forEach(([key, value]) => encodePair(key, value))
-
- return qs.length ? `?${qs.join("&")}` : ""
-}
-
-const getUrl = (config: OpenAPIConfig, options: ApiRequestOptions): string => {
- const encoder = config.ENCODE_PATH || encodeURI
-
- const path = options.url
- .replace("{api-version}", config.VERSION)
- .replace(/{(.*?)}/g, (substring: string, group: string) => {
- if (options.path?.hasOwnProperty(group)) {
- return encoder(String(options.path[group]))
- }
- return substring
- })
-
- const url = config.BASE + path
- return options.query ? url + getQueryString(options.query) : url
-}
-
-export const getFormData = (
- options: ApiRequestOptions,
-): FormData | undefined => {
- if (options.formData) {
- const formData = new FormData()
-
- const process = (key: string, value: unknown) => {
- if (isString(value) || isBlob(value)) {
- formData.append(key, value)
- } else {
- formData.append(key, JSON.stringify(value))
- }
- }
-
- Object.entries(options.formData)
- .filter(([, value]) => value !== undefined && value !== null)
- .forEach(([key, value]) => {
- if (Array.isArray(value)) {
- value.forEach((v) => process(key, v))
- } else {
- process(key, value)
- }
- })
-
- return formData
- }
- return undefined
-}
-
-type Resolver = (options: ApiRequestOptions) => Promise
-
-export const resolve = async (
- options: ApiRequestOptions,
- resolver?: T | Resolver,
-): Promise => {
- if (typeof resolver === "function") {
- return (resolver as Resolver)(options)
- }
- return resolver
-}
-
-export const getHeaders = async (
- config: OpenAPIConfig,
- options: ApiRequestOptions,
-): Promise> => {
- const [token, username, password, additionalHeaders] = await Promise.all([
- resolve(options, config.TOKEN),
- resolve(options, config.USERNAME),
- resolve(options, config.PASSWORD),
- resolve(options, config.HEADERS),
- ])
-
- const headers = Object.entries({
- Accept: "application/json",
- ...additionalHeaders,
- ...options.headers,
- })
- .filter(([, value]) => value !== undefined && value !== null)
- .reduce(
- (headers, [key, value]) => ({
- ...headers,
- [key]: String(value),
- }),
- {} as Record,
- )
-
- if (isStringWithValue(token)) {
- headers["Authorization"] = `Bearer ${token}`
- }
-
- if (isStringWithValue(username) && isStringWithValue(password)) {
- const credentials = base64(`${username}:${password}`)
- headers["Authorization"] = `Basic ${credentials}`
- }
-
- if (options.body !== undefined) {
- if (options.mediaType) {
- headers["Content-Type"] = options.mediaType
- } else if (isBlob(options.body)) {
- headers["Content-Type"] = options.body.type || "application/octet-stream"
- } else if (isString(options.body)) {
- headers["Content-Type"] = "text/plain"
- } else if (!isFormData(options.body)) {
- headers["Content-Type"] = "application/json"
- }
- } else if (options.formData !== undefined) {
- if (options.mediaType) {
- headers["Content-Type"] = options.mediaType
- }
- }
-
- return headers
-}
-
-export const getRequestBody = (options: ApiRequestOptions): unknown => {
- if (options.body) {
- return options.body
- }
- return undefined
-}
-
-export const sendRequest = async (
- config: OpenAPIConfig,
- options: ApiRequestOptions,
- url: string,
- body: unknown,
- formData: FormData | undefined,
- headers: Record,
- onCancel: OnCancel,
- axiosClient: AxiosInstance,
-): Promise> => {
- const controller = new AbortController()
-
- let requestConfig: AxiosRequestConfig = {
- data: body ?? formData,
- headers,
- method: options.method,
- signal: controller.signal,
- url,
- withCredentials: config.WITH_CREDENTIALS,
- }
-
- onCancel(() => controller.abort())
-
- for (const fn of config.interceptors.request._fns) {
- requestConfig = await fn(requestConfig)
- }
-
- try {
- return await axiosClient.request(requestConfig)
- } catch (error) {
- const axiosError = error as AxiosError
- if (axiosError.response) {
- return axiosError.response
- }
- throw error
- }
-}
-
-export const getResponseHeader = (
- response: AxiosResponse,
- responseHeader?: string,
-): string | undefined => {
- if (responseHeader) {
- const content = response.headers[responseHeader]
- if (isString(content)) {
- return content
- }
- }
- return undefined
-}
-
-export const getResponseBody = (response: AxiosResponse): unknown => {
- if (response.status !== 204) {
- return response.data
- }
- return undefined
-}
-
-export const catchErrorCodes = (
- options: ApiRequestOptions,
- result: ApiResult,
-): void => {
- const errors: Record = {
- 400: "Bad Request",
- 401: "Unauthorized",
- 402: "Payment Required",
- 403: "Forbidden",
- 404: "Not Found",
- 405: "Method Not Allowed",
- 406: "Not Acceptable",
- 407: "Proxy Authentication Required",
- 408: "Request Timeout",
- 409: "Conflict",
- 410: "Gone",
- 411: "Length Required",
- 412: "Precondition Failed",
- 413: "Payload Too Large",
- 414: "URI Too Long",
- 415: "Unsupported Media Type",
- 416: "Range Not Satisfiable",
- 417: "Expectation Failed",
- 418: "Im a teapot",
- 421: "Misdirected Request",
- 422: "Unprocessable Content",
- 423: "Locked",
- 424: "Failed Dependency",
- 425: "Too Early",
- 426: "Upgrade Required",
- 428: "Precondition Required",
- 429: "Too Many Requests",
- 431: "Request Header Fields Too Large",
- 451: "Unavailable For Legal Reasons",
- 500: "Internal Server Error",
- 501: "Not Implemented",
- 502: "Bad Gateway",
- 503: "Service Unavailable",
- 504: "Gateway Timeout",
- 505: "HTTP Version Not Supported",
- 506: "Variant Also Negotiates",
- 507: "Insufficient Storage",
- 508: "Loop Detected",
- 510: "Not Extended",
- 511: "Network Authentication Required",
- ...options.errors,
- }
-
- const error = errors[result.status]
- if (error) {
- throw new ApiError(options, result, error)
- }
-
- if (!result.ok) {
- const errorStatus = result.status ?? "unknown"
- const errorStatusText = result.statusText ?? "unknown"
- const errorBody = (() => {
- try {
- return JSON.stringify(result.body, null, 2)
- } catch (e) {
- return undefined
- }
- })()
-
- throw new ApiError(
- options,
- result,
- `Generic Error: status: ${errorStatus}; status text: ${errorStatusText}; body: ${errorBody}`,
- )
- }
-}
-
-/**
- * Request method
- * @param config The OpenAPI configuration object
- * @param options The request options from the service
- * @param axiosClient The axios client instance to use
- * @returns CancelablePromise
- * @throws ApiError
- */
-export const request = (
- config: OpenAPIConfig,
- options: ApiRequestOptions,
- axiosClient: AxiosInstance = axios,
-): CancelablePromise => {
- return new CancelablePromise(async (resolve, reject, onCancel) => {
- try {
- const url = getUrl(config, options)
- const formData = getFormData(options)
- const body = getRequestBody(options)
- const headers = await getHeaders(config, options)
-
- if (!onCancel.isCancelled) {
- let response = await sendRequest(
- config,
- options,
- url,
- body,
- formData,
- headers,
- onCancel,
- axiosClient,
- )
-
- for (const fn of config.interceptors.response._fns) {
- response = await fn(response)
- }
-
- const responseBody = getResponseBody(response)
- const responseHeader = getResponseHeader(
- response,
- options.responseHeader,
- )
-
- const result: ApiResult = {
- url,
- ok: isSuccess(response.status),
- status: response.status,
- statusText: response.statusText,
- body: responseHeader ?? responseBody,
- }
-
- catchErrorCodes(options, result)
-
- resolve(result.body)
- }
- } catch (error) {
- reject(error)
- }
- })
-}
diff --git a/frontend/src/client/core/types.ts b/frontend/src/client/core/types.ts
deleted file mode 100644
index 199c08d3df..0000000000
--- a/frontend/src/client/core/types.ts
+++ /dev/null
@@ -1,14 +0,0 @@
-import type { ApiResult } from "./ApiResult"
-
-export type TResult = "body" | "raw"
-
-export type TApiResponse = Exclude<
- T,
- "raw"
-> extends never
- ? ApiResult
- : ApiResult["body"]
-
-export type TConfig = {
- _result?: T
-}
diff --git a/frontend/src/client/index.ts b/frontend/src/client/index.ts
deleted file mode 100644
index adf1d0cabf..0000000000
--- a/frontend/src/client/index.ts
+++ /dev/null
@@ -1,8 +0,0 @@
-export { ApiError } from "./core/ApiError"
-export { CancelablePromise, CancelError } from "./core/CancelablePromise"
-export { OpenAPI } from "./core/OpenAPI"
-export type { OpenAPIConfig } from "./core/OpenAPI"
-
-export * from "./models"
-export * from "./schemas"
-export * from "./services"
diff --git a/frontend/src/client/models.ts b/frontend/src/client/models.ts
deleted file mode 100644
index 2c8074ddd6..0000000000
--- a/frontend/src/client/models.ts
+++ /dev/null
@@ -1,99 +0,0 @@
-export type Body_login_login_access_token = {
- grant_type?: string | null
- username: string
- password: string
- scope?: string
- client_id?: string | null
- client_secret?: string | null
-}
-
-export type HTTPValidationError = {
- detail?: Array
-}
-
-export type ItemCreate = {
- title: string
- description?: string | null
-}
-
-export type ItemPublic = {
- title: string
- description?: string | null
- id: string
- owner_id: string
-}
-
-export type ItemUpdate = {
- title?: string | null
- description?: string | null
-}
-
-export type ItemsPublic = {
- data: Array
- count: number
-}
-
-export type Message = {
- message: string
-}
-
-export type NewPassword = {
- token: string
- new_password: string
-}
-
-export type Token = {
- access_token: string
- token_type?: string
-}
-
-export type UpdatePassword = {
- current_password: string
- new_password: string
-}
-
-export type UserCreate = {
- email: string
- is_active?: boolean
- is_superuser?: boolean
- full_name?: string | null
- password: string
-}
-
-export type UserPublic = {
- email: string
- is_active?: boolean
- is_superuser?: boolean
- full_name?: string | null
- id: string
-}
-
-export type UserRegister = {
- email: string
- password: string
- full_name?: string | null
-}
-
-export type UserUpdate = {
- email?: string | null
- is_active?: boolean
- is_superuser?: boolean
- full_name?: string | null
- password?: string | null
-}
-
-export type UserUpdateMe = {
- full_name?: string | null
- email?: string | null
-}
-
-export type UsersPublic = {
- data: Array
- count: number
-}
-
-export type ValidationError = {
- loc: Array
- msg: string
- type: string
-}
diff --git a/frontend/src/client/schemas.ts b/frontend/src/client/schemas.ts
deleted file mode 100644
index 9e92efd106..0000000000
--- a/frontend/src/client/schemas.ts
+++ /dev/null
@@ -1,444 +0,0 @@
-export const $Body_login_login_access_token = {
- properties: {
- grant_type: {
- type: "any-of",
- contains: [
- {
- type: "string",
- pattern: "password",
- },
- {
- type: "null",
- },
- ],
- },
- username: {
- type: "string",
- isRequired: true,
- },
- password: {
- type: "string",
- isRequired: true,
- },
- scope: {
- type: "string",
- default: "",
- },
- client_id: {
- type: "any-of",
- contains: [
- {
- type: "string",
- },
- {
- type: "null",
- },
- ],
- },
- client_secret: {
- type: "any-of",
- contains: [
- {
- type: "string",
- },
- {
- type: "null",
- },
- ],
- },
- },
-} as const
-
-export const $HTTPValidationError = {
- properties: {
- detail: {
- type: "array",
- contains: {
- type: "ValidationError",
- },
- },
- },
-} as const
-
-export const $ItemCreate = {
- properties: {
- title: {
- type: "string",
- isRequired: true,
- maxLength: 255,
- minLength: 1,
- },
- description: {
- type: "any-of",
- contains: [
- {
- type: "string",
- maxLength: 255,
- },
- {
- type: "null",
- },
- ],
- },
- },
-} as const
-
-export const $ItemPublic = {
- properties: {
- title: {
- type: "string",
- isRequired: true,
- maxLength: 255,
- minLength: 1,
- },
- description: {
- type: "any-of",
- contains: [
- {
- type: "string",
- maxLength: 255,
- },
- {
- type: "null",
- },
- ],
- },
- id: {
- type: "string",
- isRequired: true,
- format: "uuid",
- },
- owner_id: {
- type: "string",
- isRequired: true,
- format: "uuid",
- },
- },
-} as const
-
-export const $ItemUpdate = {
- properties: {
- title: {
- type: "any-of",
- contains: [
- {
- type: "string",
- maxLength: 255,
- minLength: 1,
- },
- {
- type: "null",
- },
- ],
- },
- description: {
- type: "any-of",
- contains: [
- {
- type: "string",
- maxLength: 255,
- },
- {
- type: "null",
- },
- ],
- },
- },
-} as const
-
-export const $ItemsPublic = {
- properties: {
- data: {
- type: "array",
- contains: {
- type: "ItemPublic",
- },
- isRequired: true,
- },
- count: {
- type: "number",
- isRequired: true,
- },
- },
-} as const
-
-export const $Message = {
- properties: {
- message: {
- type: "string",
- isRequired: true,
- },
- },
-} as const
-
-export const $NewPassword = {
- properties: {
- token: {
- type: "string",
- isRequired: true,
- },
- new_password: {
- type: "string",
- isRequired: true,
- maxLength: 40,
- minLength: 8,
- },
- },
-} as const
-
-export const $Token = {
- properties: {
- access_token: {
- type: "string",
- isRequired: true,
- },
- token_type: {
- type: "string",
- default: "bearer",
- },
- },
-} as const
-
-export const $UpdatePassword = {
- properties: {
- current_password: {
- type: "string",
- isRequired: true,
- maxLength: 40,
- minLength: 8,
- },
- new_password: {
- type: "string",
- isRequired: true,
- maxLength: 40,
- minLength: 8,
- },
- },
-} as const
-
-export const $UserCreate = {
- properties: {
- email: {
- type: "string",
- isRequired: true,
- format: "email",
- maxLength: 255,
- },
- is_active: {
- type: "boolean",
- default: true,
- },
- is_superuser: {
- type: "boolean",
- default: false,
- },
- full_name: {
- type: "any-of",
- contains: [
- {
- type: "string",
- maxLength: 255,
- },
- {
- type: "null",
- },
- ],
- },
- password: {
- type: "string",
- isRequired: true,
- maxLength: 40,
- minLength: 8,
- },
- },
-} as const
-
-export const $UserPublic = {
- properties: {
- email: {
- type: "string",
- isRequired: true,
- format: "email",
- maxLength: 255,
- },
- is_active: {
- type: "boolean",
- default: true,
- },
- is_superuser: {
- type: "boolean",
- default: false,
- },
- full_name: {
- type: "any-of",
- contains: [
- {
- type: "string",
- maxLength: 255,
- },
- {
- type: "null",
- },
- ],
- },
- id: {
- type: "string",
- isRequired: true,
- format: "uuid",
- },
- },
-} as const
-
-export const $UserRegister = {
- properties: {
- email: {
- type: "string",
- isRequired: true,
- format: "email",
- maxLength: 255,
- },
- password: {
- type: "string",
- isRequired: true,
- maxLength: 40,
- minLength: 8,
- },
- full_name: {
- type: "any-of",
- contains: [
- {
- type: "string",
- maxLength: 255,
- },
- {
- type: "null",
- },
- ],
- },
- },
-} as const
-
-export const $UserUpdate = {
- properties: {
- email: {
- type: "any-of",
- contains: [
- {
- type: "string",
- format: "email",
- maxLength: 255,
- },
- {
- type: "null",
- },
- ],
- },
- is_active: {
- type: "boolean",
- default: true,
- },
- is_superuser: {
- type: "boolean",
- default: false,
- },
- full_name: {
- type: "any-of",
- contains: [
- {
- type: "string",
- maxLength: 255,
- },
- {
- type: "null",
- },
- ],
- },
- password: {
- type: "any-of",
- contains: [
- {
- type: "string",
- maxLength: 40,
- minLength: 8,
- },
- {
- type: "null",
- },
- ],
- },
- },
-} as const
-
-export const $UserUpdateMe = {
- properties: {
- full_name: {
- type: "any-of",
- contains: [
- {
- type: "string",
- maxLength: 255,
- },
- {
- type: "null",
- },
- ],
- },
- email: {
- type: "any-of",
- contains: [
- {
- type: "string",
- format: "email",
- maxLength: 255,
- },
- {
- type: "null",
- },
- ],
- },
- },
-} as const
-
-export const $UsersPublic = {
- properties: {
- data: {
- type: "array",
- contains: {
- type: "UserPublic",
- },
- isRequired: true,
- },
- count: {
- type: "number",
- isRequired: true,
- },
- },
-} as const
-
-export const $ValidationError = {
- properties: {
- loc: {
- type: "array",
- contains: {
- type: "any-of",
- contains: [
- {
- type: "string",
- },
- {
- type: "number",
- },
- ],
- },
- isRequired: true,
- },
- msg: {
- type: "string",
- isRequired: true,
- },
- type: {
- type: "string",
- isRequired: true,
- },
- },
-} as const
diff --git a/frontend/src/client/services.ts b/frontend/src/client/services.ts
deleted file mode 100644
index b99e4ac515..0000000000
--- a/frontend/src/client/services.ts
+++ /dev/null
@@ -1,517 +0,0 @@
-import type { CancelablePromise } from "./core/CancelablePromise"
-import { OpenAPI } from "./core/OpenAPI"
-import { request as __request } from "./core/request"
-
-import type {
- Body_login_login_access_token,
- Message,
- NewPassword,
- Token,
- UserPublic,
- UpdatePassword,
- UserCreate,
- UserRegister,
- UsersPublic,
- UserUpdate,
- UserUpdateMe,
- ItemCreate,
- ItemPublic,
- ItemsPublic,
- ItemUpdate,
-} from "./models"
-
-export type TDataLoginAccessToken = {
- formData: Body_login_login_access_token
-}
-export type TDataRecoverPassword = {
- email: string
-}
-export type TDataResetPassword = {
- requestBody: NewPassword
-}
-export type TDataRecoverPasswordHtmlContent = {
- email: string
-}
-
-export class LoginService {
- /**
- * Login Access Token
- * OAuth2 compatible token login, get an access token for future requests
- * @returns Token Successful Response
- * @throws ApiError
- */
- public static loginAccessToken(
- data: TDataLoginAccessToken,
- ): CancelablePromise {
- const { formData } = data
- return __request(OpenAPI, {
- method: "POST",
- url: "/api/v1/login/access-token",
- formData: formData,
- mediaType: "application/x-www-form-urlencoded",
- errors: {
- 422: `Validation Error`,
- },
- })
- }
-
- /**
- * Test Token
- * Test access token
- * @returns UserPublic Successful Response
- * @throws ApiError
- */
- public static testToken(): CancelablePromise {
- return __request(OpenAPI, {
- method: "POST",
- url: "/api/v1/login/test-token",
- })
- }
-
- /**
- * Recover Password
- * Password Recovery
- * @returns Message Successful Response
- * @throws ApiError
- */
- public static recoverPassword(
- data: TDataRecoverPassword,
- ): CancelablePromise {
- const { email } = data
- return __request(OpenAPI, {
- method: "POST",
- url: "/api/v1/password-recovery/{email}",
- path: {
- email,
- },
- errors: {
- 422: `Validation Error`,
- },
- })
- }
-
- /**
- * Reset Password
- * Reset password
- * @returns Message Successful Response
- * @throws ApiError
- */
- public static resetPassword(
- data: TDataResetPassword,
- ): CancelablePromise {
- const { requestBody } = data
- return __request(OpenAPI, {
- method: "POST",
- url: "/api/v1/reset-password/",
- body: requestBody,
- mediaType: "application/json",
- errors: {
- 422: `Validation Error`,
- },
- })
- }
-
- /**
- * Recover Password Html Content
- * HTML Content for Password Recovery
- * @returns string Successful Response
- * @throws ApiError
- */
- public static recoverPasswordHtmlContent(
- data: TDataRecoverPasswordHtmlContent,
- ): CancelablePromise {
- const { email } = data
- return __request(OpenAPI, {
- method: "POST",
- url: "/api/v1/password-recovery-html-content/{email}",
- path: {
- email,
- },
- errors: {
- 422: `Validation Error`,
- },
- })
- }
-}
-
-export type TDataReadUsers = {
- limit?: number
- skip?: number
-}
-export type TDataCreateUser = {
- requestBody: UserCreate
-}
-export type TDataUpdateUserMe = {
- requestBody: UserUpdateMe
-}
-export type TDataUpdatePasswordMe = {
- requestBody: UpdatePassword
-}
-export type TDataRegisterUser = {
- requestBody: UserRegister
-}
-export type TDataReadUserById = {
- userId: string
-}
-export type TDataUpdateUser = {
- requestBody: UserUpdate
- userId: string
-}
-export type TDataDeleteUser = {
- userId: string
-}
-
-export class UsersService {
- /**
- * Read Users
- * Retrieve users.
- * @returns UsersPublic Successful Response
- * @throws ApiError
- */
- public static readUsers(
- data: TDataReadUsers = {},
- ): CancelablePromise {
- const { limit = 100, skip = 0 } = data
- return __request(OpenAPI, {
- method: "GET",
- url: "/api/v1/users/",
- query: {
- skip,
- limit,
- },
- errors: {
- 422: `Validation Error`,
- },
- })
- }
-
- /**
- * Create User
- * Create new user.
- * @returns UserPublic Successful Response
- * @throws ApiError
- */
- public static createUser(
- data: TDataCreateUser,
- ): CancelablePromise {
- const { requestBody } = data
- return __request(OpenAPI, {
- method: "POST",
- url: "/api/v1/users/",
- body: requestBody,
- mediaType: "application/json",
- errors: {
- 422: `Validation Error`,
- },
- })
- }
-
- /**
- * Read User Me
- * Get current user.
- * @returns UserPublic Successful Response
- * @throws ApiError
- */
- public static readUserMe(): CancelablePromise {
- return __request(OpenAPI, {
- method: "GET",
- url: "/api/v1/users/me",
- })
- }
-
- /**
- * Delete User Me
- * Delete own user.
- * @returns Message Successful Response
- * @throws ApiError
- */
- public static deleteUserMe(): CancelablePromise {
- return __request(OpenAPI, {
- method: "DELETE",
- url: "/api/v1/users/me",
- })
- }
-
- /**
- * Update User Me
- * Update own user.
- * @returns UserPublic Successful Response
- * @throws ApiError
- */
- public static updateUserMe(
- data: TDataUpdateUserMe,
- ): CancelablePromise {
- const { requestBody } = data
- return __request(OpenAPI, {
- method: "PATCH",
- url: "/api/v1/users/me",
- body: requestBody,
- mediaType: "application/json",
- errors: {
- 422: `Validation Error`,
- },
- })
- }
-
- /**
- * Update Password Me
- * Update own password.
- * @returns Message Successful Response
- * @throws ApiError
- */
- public static updatePasswordMe(
- data: TDataUpdatePasswordMe,
- ): CancelablePromise {
- const { requestBody } = data
- return __request(OpenAPI, {
- method: "PATCH",
- url: "/api/v1/users/me/password",
- body: requestBody,
- mediaType: "application/json",
- errors: {
- 422: `Validation Error`,
- },
- })
- }
-
- /**
- * Register User
- * Create new user without the need to be logged in.
- * @returns UserPublic Successful Response
- * @throws ApiError
- */
- public static registerUser(
- data: TDataRegisterUser,
- ): CancelablePromise {
- const { requestBody } = data
- return __request(OpenAPI, {
- method: "POST",
- url: "/api/v1/users/signup",
- body: requestBody,
- mediaType: "application/json",
- errors: {
- 422: `Validation Error`,
- },
- })
- }
-
- /**
- * Read User By Id
- * Get a specific user by id.
- * @returns UserPublic Successful Response
- * @throws ApiError
- */
- public static readUserById(
- data: TDataReadUserById,
- ): CancelablePromise {
- const { userId } = data
- return __request(OpenAPI, {
- method: "GET",
- url: "/api/v1/users/{user_id}",
- path: {
- user_id: userId,
- },
- errors: {
- 422: `Validation Error`,
- },
- })
- }
-
- /**
- * Update User
- * Update a user.
- * @returns UserPublic Successful Response
- * @throws ApiError
- */
- public static updateUser(
- data: TDataUpdateUser,
- ): CancelablePromise {
- const { requestBody, userId } = data
- return __request(OpenAPI, {
- method: "PATCH",
- url: "/api/v1/users/{user_id}",
- path: {
- user_id: userId,
- },
- body: requestBody,
- mediaType: "application/json",
- errors: {
- 422: `Validation Error`,
- },
- })
- }
-
- /**
- * Delete User
- * Delete a user.
- * @returns Message Successful Response
- * @throws ApiError
- */
- public static deleteUser(data: TDataDeleteUser): CancelablePromise {
- const { userId } = data
- return __request(OpenAPI, {
- method: "DELETE",
- url: "/api/v1/users/{user_id}",
- path: {
- user_id: userId,
- },
- errors: {
- 422: `Validation Error`,
- },
- })
- }
-}
-
-export type TDataTestEmail = {
- emailTo: string
-}
-
-export class UtilsService {
- /**
- * Test Email
- * Test emails.
- * @returns Message Successful Response
- * @throws ApiError
- */
- public static testEmail(data: TDataTestEmail): CancelablePromise {
- const { emailTo } = data
- return __request(OpenAPI, {
- method: "POST",
- url: "/api/v1/utils/test-email/",
- query: {
- email_to: emailTo,
- },
- errors: {
- 422: `Validation Error`,
- },
- })
- }
-}
-
-export type TDataReadItems = {
- limit?: number
- skip?: number
-}
-export type TDataCreateItem = {
- requestBody: ItemCreate
-}
-export type TDataReadItem = {
- id: string
-}
-export type TDataUpdateItem = {
- id: string
- requestBody: ItemUpdate
-}
-export type TDataDeleteItem = {
- id: string
-}
-
-export class ItemsService {
- /**
- * Read Items
- * Retrieve items.
- * @returns ItemsPublic Successful Response
- * @throws ApiError
- */
- public static readItems(
- data: TDataReadItems = {},
- ): CancelablePromise {
- const { limit = 100, skip = 0 } = data
- return __request(OpenAPI, {
- method: "GET",
- url: "/api/v1/items/",
- query: {
- skip,
- limit,
- },
- errors: {
- 422: `Validation Error`,
- },
- })
- }
-
- /**
- * Create Item
- * Create new item.
- * @returns ItemPublic Successful Response
- * @throws ApiError
- */
- public static createItem(
- data: TDataCreateItem,
- ): CancelablePromise {
- const { requestBody } = data
- return __request(OpenAPI, {
- method: "POST",
- url: "/api/v1/items/",
- body: requestBody,
- mediaType: "application/json",
- errors: {
- 422: `Validation Error`,
- },
- })
- }
-
- /**
- * Read Item
- * Get item by ID.
- * @returns ItemPublic Successful Response
- * @throws ApiError
- */
- public static readItem(data: TDataReadItem): CancelablePromise {
- const { id } = data
- return __request(OpenAPI, {
- method: "GET",
- url: "/api/v1/items/{id}",
- path: {
- id,
- },
- errors: {
- 422: `Validation Error`,
- },
- })
- }
-
- /**
- * Update Item
- * Update an item.
- * @returns ItemPublic Successful Response
- * @throws ApiError
- */
- public static updateItem(
- data: TDataUpdateItem,
- ): CancelablePromise {
- const { id, requestBody } = data
- return __request(OpenAPI, {
- method: "PUT",
- url: "/api/v1/items/{id}",
- path: {
- id,
- },
- body: requestBody,
- mediaType: "application/json",
- errors: {
- 422: `Validation Error`,
- },
- })
- }
-
- /**
- * Delete Item
- * Delete an item.
- * @returns Message Successful Response
- * @throws ApiError
- */
- public static deleteItem(data: TDataDeleteItem): CancelablePromise {
- const { id } = data
- return __request(OpenAPI, {
- method: "DELETE",
- url: "/api/v1/items/{id}",
- path: {
- id,
- },
- errors: {
- 422: `Validation Error`,
- },
- })
- }
-}
diff --git a/frontend/src/components/Admin/AddUser.tsx b/frontend/src/components/Admin/AddUser.tsx
deleted file mode 100644
index a24a18a78e..0000000000
--- a/frontend/src/components/Admin/AddUser.tsx
+++ /dev/null
@@ -1,182 +0,0 @@
-import {
- Button,
- Checkbox,
- Flex,
- FormControl,
- FormErrorMessage,
- FormLabel,
- Input,
- Modal,
- ModalBody,
- ModalCloseButton,
- ModalContent,
- ModalFooter,
- ModalHeader,
- ModalOverlay,
-} from "@chakra-ui/react"
-import { useMutation, useQueryClient } from "@tanstack/react-query"
-import { type SubmitHandler, useForm } from "react-hook-form"
-
-import { type UserCreate, UsersService } from "../../client"
-import type { ApiError } from "../../client/core/ApiError"
-import useCustomToast from "../../hooks/useCustomToast"
-import { emailPattern, handleError } from "../../utils"
-
-interface AddUserProps {
- isOpen: boolean
- onClose: () => void
-}
-
-interface UserCreateForm extends UserCreate {
- confirm_password: string
-}
-
-const AddUser = ({ isOpen, onClose }: AddUserProps) => {
- const queryClient = useQueryClient()
- const showToast = useCustomToast()
- const {
- register,
- handleSubmit,
- reset,
- getValues,
- formState: { errors, isSubmitting },
- } = useForm({
- mode: "onBlur",
- criteriaMode: "all",
- defaultValues: {
- email: "",
- full_name: "",
- password: "",
- confirm_password: "",
- is_superuser: false,
- is_active: false,
- },
- })
-
- const mutation = useMutation({
- mutationFn: (data: UserCreate) =>
- UsersService.createUser({ requestBody: data }),
- onSuccess: () => {
- showToast("Success!", "User created successfully.", "success")
- reset()
- onClose()
- },
- onError: (err: ApiError) => {
- handleError(err, showToast)
- },
- onSettled: () => {
- queryClient.invalidateQueries({ queryKey: ["users"] })
- },
- })
-
- const onSubmit: SubmitHandler = (data) => {
- mutation.mutate(data)
- }
-
- return (
- <>
-
-
-
- Add User
-
-
-
- Email
-
- {errors.email && (
- {errors.email.message}
- )}
-
-
- Full name
-
- {errors.full_name && (
- {errors.full_name.message}
- )}
-
-
- Set Password
-
- {errors.password && (
- {errors.password.message}
- )}
-
-
- Confirm Password
-
- value === getValues().password ||
- "The passwords do not match",
- })}
- placeholder="Password"
- type="password"
- />
- {errors.confirm_password && (
-
- {errors.confirm_password.message}
-
- )}
-
-
-
-
- Is superuser?
-
-
-
-
- Is active?
-
-
-
-
-
-
-
-
-
-
- >
- )
-}
-
-export default AddUser
diff --git a/frontend/src/components/Admin/EditUser.tsx b/frontend/src/components/Admin/EditUser.tsx
deleted file mode 100644
index d7885ab174..0000000000
--- a/frontend/src/components/Admin/EditUser.tsx
+++ /dev/null
@@ -1,180 +0,0 @@
-import {
- Button,
- Checkbox,
- Flex,
- FormControl,
- FormErrorMessage,
- FormLabel,
- Input,
- Modal,
- ModalBody,
- ModalCloseButton,
- ModalContent,
- ModalFooter,
- ModalHeader,
- ModalOverlay,
-} from "@chakra-ui/react"
-import { useMutation, useQueryClient } from "@tanstack/react-query"
-import { type SubmitHandler, useForm } from "react-hook-form"
-
-import {
- type ApiError,
- type UserPublic,
- type UserUpdate,
- UsersService,
-} from "../../client"
-import useCustomToast from "../../hooks/useCustomToast"
-import { emailPattern, handleError } from "../../utils"
-
-interface EditUserProps {
- user: UserPublic
- isOpen: boolean
- onClose: () => void
-}
-
-interface UserUpdateForm extends UserUpdate {
- confirm_password: string
-}
-
-const EditUser = ({ user, isOpen, onClose }: EditUserProps) => {
- const queryClient = useQueryClient()
- const showToast = useCustomToast()
-
- const {
- register,
- handleSubmit,
- reset,
- getValues,
- formState: { errors, isSubmitting, isDirty },
- } = useForm({
- mode: "onBlur",
- criteriaMode: "all",
- defaultValues: user,
- })
-
- const mutation = useMutation({
- mutationFn: (data: UserUpdateForm) =>
- UsersService.updateUser({ userId: user.id, requestBody: data }),
- onSuccess: () => {
- showToast("Success!", "User updated successfully.", "success")
- onClose()
- },
- onError: (err: ApiError) => {
- handleError(err, showToast)
- },
- onSettled: () => {
- queryClient.invalidateQueries({ queryKey: ["users"] })
- },
- })
-
- const onSubmit: SubmitHandler = async (data) => {
- if (data.password === "") {
- data.password = undefined
- }
- mutation.mutate(data)
- }
-
- const onCancel = () => {
- reset()
- onClose()
- }
-
- return (
- <>
-
-
-
- Edit User
-
-
-
- Email
-
- {errors.email && (
- {errors.email.message}
- )}
-
-
- Full name
-
-
-
- Set Password
-
- {errors.password && (
- {errors.password.message}
- )}
-
-
- Confirm Password
-
- value === getValues().password ||
- "The passwords do not match",
- })}
- placeholder="Password"
- type="password"
- />
- {errors.confirm_password && (
-
- {errors.confirm_password.message}
-
- )}
-
-
-
-
- Is superuser?
-
-
-
-
- Is active?
-
-
-
-
-
-
-
-
-
-
-
- >
- )
-}
-
-export default EditUser
diff --git a/frontend/src/components/Common/ActionsMenu.tsx b/frontend/src/components/Common/ActionsMenu.tsx
deleted file mode 100644
index 4ff94ee3ea..0000000000
--- a/frontend/src/components/Common/ActionsMenu.tsx
+++ /dev/null
@@ -1,75 +0,0 @@
-import {
- Button,
- Menu,
- MenuButton,
- MenuItem,
- MenuList,
- useDisclosure,
-} from "@chakra-ui/react"
-import { BsThreeDotsVertical } from "react-icons/bs"
-import { FiEdit, FiTrash } from "react-icons/fi"
-
-import type { ItemPublic, UserPublic } from "../../client"
-import EditUser from "../Admin/EditUser"
-import EditItem from "../Items/EditItem"
-import Delete from "./DeleteAlert"
-
-interface ActionsMenuProps {
- type: string
- value: ItemPublic | UserPublic
- disabled?: boolean
-}
-
-const ActionsMenu = ({ type, value, disabled }: ActionsMenuProps) => {
- const editUserModal = useDisclosure()
- const deleteModal = useDisclosure()
-
- return (
- <>
-
- >
- )
-}
-
-export default ActionsMenu
diff --git a/frontend/src/components/Common/DeleteAlert.tsx b/frontend/src/components/Common/DeleteAlert.tsx
deleted file mode 100644
index 1528fc5fe1..0000000000
--- a/frontend/src/components/Common/DeleteAlert.tsx
+++ /dev/null
@@ -1,113 +0,0 @@
-import {
- AlertDialog,
- AlertDialogBody,
- AlertDialogContent,
- AlertDialogFooter,
- AlertDialogHeader,
- AlertDialogOverlay,
- Button,
-} from "@chakra-ui/react"
-import { useMutation, useQueryClient } from "@tanstack/react-query"
-import React from "react"
-import { useForm } from "react-hook-form"
-
-import { ItemsService, UsersService } from "../../client"
-import useCustomToast from "../../hooks/useCustomToast"
-
-interface DeleteProps {
- type: string
- id: string
- isOpen: boolean
- onClose: () => void
-}
-
-const Delete = ({ type, id, isOpen, onClose }: DeleteProps) => {
- const queryClient = useQueryClient()
- const showToast = useCustomToast()
- const cancelRef = React.useRef(null)
- const {
- handleSubmit,
- formState: { isSubmitting },
- } = useForm()
-
- const deleteEntity = async (id: string) => {
- if (type === "Item") {
- await ItemsService.deleteItem({ id: id })
- } else if (type === "User") {
- await UsersService.deleteUser({ userId: id })
- } else {
- throw new Error(`Unexpected type: ${type}`)
- }
- }
-
- const mutation = useMutation({
- mutationFn: deleteEntity,
- onSuccess: () => {
- showToast(
- "Success",
- `The ${type.toLowerCase()} was deleted successfully.`,
- "success",
- )
- onClose()
- },
- onError: () => {
- showToast(
- "An error occurred.",
- `An error occurred while deleting the ${type.toLowerCase()}.`,
- "error",
- )
- },
- onSettled: () => {
- queryClient.invalidateQueries({
- queryKey: [type === "Item" ? "items" : "users"],
- })
- },
- })
-
- const onSubmit = async () => {
- mutation.mutate(id)
- }
-
- return (
- <>
-
-
-
- Delete {type}
-
-
- {type === "User" && (
-
- All items associated with this user will also be{" "}
- permantly deleted.
-
- )}
- Are you sure? You will not be able to undo this action.
-
-
-
-
-
-
-
-
-
- >
- )
-}
-
-export default Delete
diff --git a/frontend/src/components/Common/Navbar.tsx b/frontend/src/components/Common/Navbar.tsx
deleted file mode 100644
index 2aba31c362..0000000000
--- a/frontend/src/components/Common/Navbar.tsx
+++ /dev/null
@@ -1,39 +0,0 @@
-import type { ComponentType, ElementType } from "react"
-
-import { Button, Flex, Icon, useDisclosure } from "@chakra-ui/react"
-import { FaPlus } from "react-icons/fa"
-
-interface NavbarProps {
- type: string
- addModalAs: ComponentType | ElementType
-}
-
-const Navbar = ({ type, addModalAs }: NavbarProps) => {
- const addModal = useDisclosure()
-
- const AddModal = addModalAs
- return (
- <>
-
- {/* TODO: Complete search functionality */}
- {/*
-
-
-
-
- */}
-
-
-
- >
- )
-}
-
-export default Navbar
diff --git a/frontend/src/components/Common/NotFound.tsx b/frontend/src/components/Common/NotFound.tsx
deleted file mode 100644
index 66ea559c01..0000000000
--- a/frontend/src/components/Common/NotFound.tsx
+++ /dev/null
@@ -1,41 +0,0 @@
-import { Button, Container, Text } from "@chakra-ui/react"
-import { Link } from "@tanstack/react-router"
-
-const NotFound = () => {
- return (
- <>
-
-
- 404
-
- Oops!
- Page not found.
-
-
- >
- )
-}
-
-export default NotFound
diff --git a/frontend/src/components/Common/Sidebar.tsx b/frontend/src/components/Common/Sidebar.tsx
deleted file mode 100644
index 3cc522cc57..0000000000
--- a/frontend/src/components/Common/Sidebar.tsx
+++ /dev/null
@@ -1,116 +0,0 @@
-import {
- Box,
- Drawer,
- DrawerBody,
- DrawerCloseButton,
- DrawerContent,
- DrawerOverlay,
- Flex,
- IconButton,
- Image,
- Text,
- useColorModeValue,
- useDisclosure,
-} from "@chakra-ui/react"
-import { useQueryClient } from "@tanstack/react-query"
-import { FiLogOut, FiMenu } from "react-icons/fi"
-
-import Logo from "/assets/images/fastapi-logo.svg"
-import type { UserPublic } from "../../client"
-import useAuth from "../../hooks/useAuth"
-import SidebarItems from "./SidebarItems"
-
-const Sidebar = () => {
- const queryClient = useQueryClient()
- const bgColor = useColorModeValue("ui.light", "ui.dark")
- const textColor = useColorModeValue("ui.dark", "ui.light")
- const secBgColor = useColorModeValue("ui.secondary", "ui.darkSlate")
- const currentUser = queryClient.getQueryData(["currentUser"])
- const { isOpen, onOpen, onClose } = useDisclosure()
- const { logout } = useAuth()
-
- const handleLogout = async () => {
- logout()
- }
-
- return (
- <>
- {/* Mobile */}
- }
- />
-
-
-
-
-
-
-
-
-
-
-
- Log out
-
-
- {currentUser?.email && (
-
- Logged in as: {currentUser.email}
-
- )}
-
-
-
-
-
- {/* Desktop */}
-
-
-
-
-
-
- {currentUser?.email && (
-
- Logged in as: {currentUser.email}
-
- )}
-
-
- >
- )
-}
-
-export default Sidebar
diff --git a/frontend/src/components/Common/SidebarItems.tsx b/frontend/src/components/Common/SidebarItems.tsx
deleted file mode 100644
index 929e8f785e..0000000000
--- a/frontend/src/components/Common/SidebarItems.tsx
+++ /dev/null
@@ -1,56 +0,0 @@
-import { Box, Flex, Icon, Text, useColorModeValue } from "@chakra-ui/react"
-import { useQueryClient } from "@tanstack/react-query"
-import { Link } from "@tanstack/react-router"
-import { FiBriefcase, FiHome, FiSettings, FiUsers } from "react-icons/fi"
-
-import type { UserPublic } from "../../client"
-
-const items = [
- { icon: FiHome, title: "Dashboard", path: "/" },
- { icon: FiBriefcase, title: "Items", path: "/items" },
- { icon: FiSettings, title: "User Settings", path: "/settings" },
-]
-
-interface SidebarItemsProps {
- onClose?: () => void
-}
-
-const SidebarItems = ({ onClose }: SidebarItemsProps) => {
- const queryClient = useQueryClient()
- const textColor = useColorModeValue("ui.main", "ui.light")
- const bgActive = useColorModeValue("#E2E8F0", "#4A5568")
- const currentUser = queryClient.getQueryData(["currentUser"])
-
- const finalItems = currentUser?.is_superuser
- ? [...items, { icon: FiUsers, title: "Admin", path: "/admin" }]
- : items
-
- const listItems = finalItems.map(({ icon, title, path }) => (
-
-
- {title}
-
- ))
-
- return (
- <>
- {listItems}
- >
- )
-}
-
-export default SidebarItems
diff --git a/frontend/src/components/Common/UserMenu.tsx b/frontend/src/components/Common/UserMenu.tsx
deleted file mode 100644
index e3d54ac26b..0000000000
--- a/frontend/src/components/Common/UserMenu.tsx
+++ /dev/null
@@ -1,59 +0,0 @@
-import {
- Box,
- IconButton,
- Menu,
- MenuButton,
- MenuItem,
- MenuList,
-} from "@chakra-ui/react"
-import { Link } from "@tanstack/react-router"
-import { FaUserAstronaut } from "react-icons/fa"
-import { FiLogOut, FiUser } from "react-icons/fi"
-
-import useAuth from "../../hooks/useAuth"
-
-const UserMenu = () => {
- const { logout } = useAuth()
-
- const handleLogout = async () => {
- logout()
- }
-
- return (
- <>
- {/* Desktop */}
-
-
-
- >
- )
-}
-
-export default UserMenu
diff --git a/frontend/src/components/Items/AddItem.tsx b/frontend/src/components/Items/AddItem.tsx
deleted file mode 100644
index fa5682da3f..0000000000
--- a/frontend/src/components/Items/AddItem.tsx
+++ /dev/null
@@ -1,114 +0,0 @@
-import {
- Button,
- FormControl,
- FormErrorMessage,
- FormLabel,
- Input,
- Modal,
- ModalBody,
- ModalCloseButton,
- ModalContent,
- ModalFooter,
- ModalHeader,
- ModalOverlay,
-} from "@chakra-ui/react"
-import { useMutation, useQueryClient } from "@tanstack/react-query"
-import { type SubmitHandler, useForm } from "react-hook-form"
-
-import { type ApiError, type ItemCreate, ItemsService } from "../../client"
-import useCustomToast from "../../hooks/useCustomToast"
-import { handleError } from "../../utils"
-
-interface AddItemProps {
- isOpen: boolean
- onClose: () => void
-}
-
-const AddItem = ({ isOpen, onClose }: AddItemProps) => {
- const queryClient = useQueryClient()
- const showToast = useCustomToast()
- const {
- register,
- handleSubmit,
- reset,
- formState: { errors, isSubmitting },
- } = useForm({
- mode: "onBlur",
- criteriaMode: "all",
- defaultValues: {
- title: "",
- description: "",
- },
- })
-
- const mutation = useMutation({
- mutationFn: (data: ItemCreate) =>
- ItemsService.createItem({ requestBody: data }),
- onSuccess: () => {
- showToast("Success!", "Item created successfully.", "success")
- reset()
- onClose()
- },
- onError: (err: ApiError) => {
- handleError(err, showToast)
- },
- onSettled: () => {
- queryClient.invalidateQueries({ queryKey: ["items"] })
- },
- })
-
- const onSubmit: SubmitHandler = (data) => {
- mutation.mutate(data)
- }
-
- return (
- <>
-
-
-
- Add Item
-
-
-
- Title
-
- {errors.title && (
- {errors.title.message}
- )}
-
-
- Description
-
-
-
-
-
-
-
-
-
-
- >
- )
-}
-
-export default AddItem
diff --git a/frontend/src/components/Items/EditItem.tsx b/frontend/src/components/Items/EditItem.tsx
deleted file mode 100644
index 3d40cdc03a..0000000000
--- a/frontend/src/components/Items/EditItem.tsx
+++ /dev/null
@@ -1,124 +0,0 @@
-import {
- Button,
- FormControl,
- FormErrorMessage,
- FormLabel,
- Input,
- Modal,
- ModalBody,
- ModalCloseButton,
- ModalContent,
- ModalFooter,
- ModalHeader,
- ModalOverlay,
-} from "@chakra-ui/react"
-import { useMutation, useQueryClient } from "@tanstack/react-query"
-import { type SubmitHandler, useForm } from "react-hook-form"
-
-import {
- type ApiError,
- type ItemPublic,
- type ItemUpdate,
- ItemsService,
-} from "../../client"
-import useCustomToast from "../../hooks/useCustomToast"
-import { handleError } from "../../utils"
-
-interface EditItemProps {
- item: ItemPublic
- isOpen: boolean
- onClose: () => void
-}
-
-const EditItem = ({ item, isOpen, onClose }: EditItemProps) => {
- const queryClient = useQueryClient()
- const showToast = useCustomToast()
- const {
- register,
- handleSubmit,
- reset,
- formState: { isSubmitting, errors, isDirty },
- } = useForm({
- mode: "onBlur",
- criteriaMode: "all",
- defaultValues: item,
- })
-
- const mutation = useMutation({
- mutationFn: (data: ItemUpdate) =>
- ItemsService.updateItem({ id: item.id, requestBody: data }),
- onSuccess: () => {
- showToast("Success!", "Item updated successfully.", "success")
- onClose()
- },
- onError: (err: ApiError) => {
- handleError(err, showToast)
- },
- onSettled: () => {
- queryClient.invalidateQueries({ queryKey: ["items"] })
- },
- })
-
- const onSubmit: SubmitHandler = async (data) => {
- mutation.mutate(data)
- }
-
- const onCancel = () => {
- reset()
- onClose()
- }
-
- return (
- <>
-
-
-
- Edit Item
-
-
-
- Title
-
- {errors.title && (
- {errors.title.message}
- )}
-
-
- Description
-
-
-
-
-
-
-
-
-
- >
- )
-}
-
-export default EditItem
diff --git a/frontend/src/components/UserSettings/Appearance.tsx b/frontend/src/components/UserSettings/Appearance.tsx
deleted file mode 100644
index a2ab4b0a60..0000000000
--- a/frontend/src/components/UserSettings/Appearance.tsx
+++ /dev/null
@@ -1,38 +0,0 @@
-import {
- Badge,
- Container,
- Heading,
- Radio,
- RadioGroup,
- Stack,
- useColorMode,
-} from "@chakra-ui/react"
-
-const Appearance = () => {
- const { colorMode, toggleColorMode } = useColorMode()
-
- return (
- <>
-
-
- Appearance
-
-
-
- {/* TODO: Add system default option */}
-
- Light Mode
-
- Default
-
-
-
- Dark Mode
-
-
-
-
- >
- )
-}
-export default Appearance
diff --git a/frontend/src/components/UserSettings/ChangePassword.tsx b/frontend/src/components/UserSettings/ChangePassword.tsx
deleted file mode 100644
index 73217939fc..0000000000
--- a/frontend/src/components/UserSettings/ChangePassword.tsx
+++ /dev/null
@@ -1,122 +0,0 @@
-import {
- Box,
- Button,
- Container,
- FormControl,
- FormErrorMessage,
- FormLabel,
- Heading,
- Input,
- useColorModeValue,
-} from "@chakra-ui/react"
-import { useMutation } from "@tanstack/react-query"
-import { type SubmitHandler, useForm } from "react-hook-form"
-
-import { type ApiError, type UpdatePassword, UsersService } from "../../client"
-import useCustomToast from "../../hooks/useCustomToast"
-import { confirmPasswordRules, handleError, passwordRules } from "../../utils"
-
-interface UpdatePasswordForm extends UpdatePassword {
- confirm_password: string
-}
-
-const ChangePassword = () => {
- const color = useColorModeValue("inherit", "ui.light")
- const showToast = useCustomToast()
- const {
- register,
- handleSubmit,
- reset,
- getValues,
- formState: { errors, isSubmitting },
- } = useForm({
- mode: "onBlur",
- criteriaMode: "all",
- })
-
- const mutation = useMutation({
- mutationFn: (data: UpdatePassword) =>
- UsersService.updatePasswordMe({ requestBody: data }),
- onSuccess: () => {
- showToast("Success!", "Password updated successfully.", "success")
- reset()
- },
- onError: (err: ApiError) => {
- handleError(err, showToast)
- },
- })
-
- const onSubmit: SubmitHandler = async (data) => {
- mutation.mutate(data)
- }
-
- return (
- <>
-
-
- Change Password
-
-
-
-
- Current Password
-
-
- {errors.current_password && (
-
- {errors.current_password.message}
-
- )}
-
-
- Set Password
-
- {errors.new_password && (
- {errors.new_password.message}
- )}
-
-
- Confirm Password
-
- {errors.confirm_password && (
-
- {errors.confirm_password.message}
-
- )}
-
-
-
-
- >
- )
-}
-export default ChangePassword
diff --git a/frontend/src/components/UserSettings/DeleteAccount.tsx b/frontend/src/components/UserSettings/DeleteAccount.tsx
deleted file mode 100644
index 7ca3b92c95..0000000000
--- a/frontend/src/components/UserSettings/DeleteAccount.tsx
+++ /dev/null
@@ -1,35 +0,0 @@
-import {
- Button,
- Container,
- Heading,
- Text,
- useDisclosure,
-} from "@chakra-ui/react"
-
-import DeleteConfirmation from "./DeleteConfirmation"
-
-const DeleteAccount = () => {
- const confirmationModal = useDisclosure()
-
- return (
- <>
-
-
- Delete Account
-
-
- Permanently delete your data and everything associated with your
- account.
-
-
-
-
- >
- )
-}
-export default DeleteAccount
diff --git a/frontend/src/components/UserSettings/DeleteConfirmation.tsx b/frontend/src/components/UserSettings/DeleteConfirmation.tsx
deleted file mode 100644
index 5bbdcdd6c7..0000000000
--- a/frontend/src/components/UserSettings/DeleteConfirmation.tsx
+++ /dev/null
@@ -1,96 +0,0 @@
-import {
- AlertDialog,
- AlertDialogBody,
- AlertDialogContent,
- AlertDialogFooter,
- AlertDialogHeader,
- AlertDialogOverlay,
- Button,
-} from "@chakra-ui/react"
-import { useMutation, useQueryClient } from "@tanstack/react-query"
-import React from "react"
-import { useForm } from "react-hook-form"
-
-import { type ApiError, UsersService } from "../../client"
-import useAuth from "../../hooks/useAuth"
-import useCustomToast from "../../hooks/useCustomToast"
-import { handleError } from "../../utils"
-
-interface DeleteProps {
- isOpen: boolean
- onClose: () => void
-}
-
-const DeleteConfirmation = ({ isOpen, onClose }: DeleteProps) => {
- const queryClient = useQueryClient()
- const showToast = useCustomToast()
- const cancelRef = React.useRef(null)
- const {
- handleSubmit,
- formState: { isSubmitting },
- } = useForm()
- const { logout } = useAuth()
-
- const mutation = useMutation({
- mutationFn: () => UsersService.deleteUserMe(),
- onSuccess: () => {
- showToast(
- "Success",
- "Your account has been successfully deleted.",
- "success",
- )
- logout()
- onClose()
- },
- onError: (err: ApiError) => {
- handleError(err, showToast)
- },
- onSettled: () => {
- queryClient.invalidateQueries({ queryKey: ["currentUser"] })
- },
- })
-
- const onSubmit = async () => {
- mutation.mutate()
- }
-
- return (
- <>
-
-
-
- Confirmation Required
-
-
- All your account data will be{" "}
- permanently deleted. If you are sure, please
- click "Confirm" to proceed. This action cannot be
- undone.
-
-
-
-
-
-
-
-
-
- >
- )
-}
-
-export default DeleteConfirmation
diff --git a/frontend/src/components/UserSettings/UserInformation.tsx b/frontend/src/components/UserSettings/UserInformation.tsx
deleted file mode 100644
index d066a846a6..0000000000
--- a/frontend/src/components/UserSettings/UserInformation.tsx
+++ /dev/null
@@ -1,157 +0,0 @@
-import {
- Box,
- Button,
- Container,
- Flex,
- FormControl,
- FormErrorMessage,
- FormLabel,
- Heading,
- Input,
- Text,
- useColorModeValue,
-} from "@chakra-ui/react"
-import { useMutation, useQueryClient } from "@tanstack/react-query"
-import { useState } from "react"
-import { type SubmitHandler, useForm } from "react-hook-form"
-
-import {
- type ApiError,
- type UserPublic,
- type UserUpdateMe,
- UsersService,
-} from "../../client"
-import useAuth from "../../hooks/useAuth"
-import useCustomToast from "../../hooks/useCustomToast"
-import { emailPattern, handleError } from "../../utils"
-
-const UserInformation = () => {
- const queryClient = useQueryClient()
- const color = useColorModeValue("inherit", "ui.light")
- const showToast = useCustomToast()
- const [editMode, setEditMode] = useState(false)
- const { user: currentUser } = useAuth()
- const {
- register,
- handleSubmit,
- reset,
- getValues,
- formState: { isSubmitting, errors, isDirty },
- } = useForm({
- mode: "onBlur",
- criteriaMode: "all",
- defaultValues: {
- full_name: currentUser?.full_name,
- email: currentUser?.email,
- },
- })
-
- const toggleEditMode = () => {
- setEditMode(!editMode)
- }
-
- const mutation = useMutation({
- mutationFn: (data: UserUpdateMe) =>
- UsersService.updateUserMe({ requestBody: data }),
- onSuccess: () => {
- showToast("Success!", "User updated successfully.", "success")
- },
- onError: (err: ApiError) => {
- handleError(err, showToast)
- },
- onSettled: () => {
- queryClient.invalidateQueries()
- },
- })
-
- const onSubmit: SubmitHandler = async (data) => {
- mutation.mutate(data)
- }
-
- const onCancel = () => {
- reset()
- toggleEditMode()
- }
-
- return (
- <>
-
-
- User Information
-
-
-
-
- Full name
-
- {editMode ? (
-
- ) : (
-
- {currentUser?.full_name || "N/A"}
-
- )}
-
-
-
- Email
-
- {editMode ? (
-
- ) : (
-
- {currentUser?.email}
-
- )}
- {errors.email && (
- {errors.email.message}
- )}
-
-
-
- {editMode && (
-
- )}
-
-
-
- >
- )
-}
-
-export default UserInformation
diff --git a/frontend/src/hooks/useAuth.ts b/frontend/src/hooks/useAuth.ts
deleted file mode 100644
index 76b0abdfd3..0000000000
--- a/frontend/src/hooks/useAuth.ts
+++ /dev/null
@@ -1,101 +0,0 @@
-import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query"
-import { useNavigate } from "@tanstack/react-router"
-import { useState } from "react"
-
-import { AxiosError } from "axios"
-import {
- type Body_login_login_access_token as AccessToken,
- type ApiError,
- LoginService,
- type UserPublic,
- type UserRegister,
- UsersService,
-} from "../client"
-import useCustomToast from "./useCustomToast"
-
-const isLoggedIn = () => {
- return localStorage.getItem("access_token") !== null
-}
-
-const useAuth = () => {
- const [error, setError] = useState(null)
- const navigate = useNavigate()
- const showToast = useCustomToast()
- const queryClient = useQueryClient()
- const { data: user, isLoading } = useQuery({
- queryKey: ["currentUser"],
- queryFn: UsersService.readUserMe,
- enabled: isLoggedIn(),
- })
-
- const signUpMutation = useMutation({
- mutationFn: (data: UserRegister) =>
- UsersService.registerUser({ requestBody: data }),
-
- onSuccess: () => {
- navigate({ to: "/login" })
- showToast(
- "Account created.",
- "Your account has been created successfully.",
- "success",
- )
- },
- onError: (err: ApiError) => {
- let errDetail = (err.body as any)?.detail
-
- if (err instanceof AxiosError) {
- errDetail = err.message
- }
-
- showToast("Something went wrong.", errDetail, "error")
- },
- onSettled: () => {
- queryClient.invalidateQueries({ queryKey: ["users"] })
- },
- })
-
- const login = async (data: AccessToken) => {
- const response = await LoginService.loginAccessToken({
- formData: data,
- })
- localStorage.setItem("access_token", response.access_token)
- }
-
- const loginMutation = useMutation({
- mutationFn: login,
- onSuccess: () => {
- navigate({ to: "/" })
- },
- onError: (err: ApiError) => {
- let errDetail = (err.body as any)?.detail
-
- if (err instanceof AxiosError) {
- errDetail = err.message
- }
-
- if (Array.isArray(errDetail)) {
- errDetail = "Something went wrong"
- }
-
- setError(errDetail)
- },
- })
-
- const logout = () => {
- localStorage.removeItem("access_token")
- navigate({ to: "/login" })
- }
-
- return {
- signUpMutation,
- loginMutation,
- logout,
- user,
- isLoading,
- error,
- resetError: () => setError(null),
- }
-}
-
-export { isLoggedIn }
-export default useAuth
diff --git a/frontend/src/hooks/useCustomToast.ts b/frontend/src/hooks/useCustomToast.ts
deleted file mode 100644
index 06bc8a6ab8..0000000000
--- a/frontend/src/hooks/useCustomToast.ts
+++ /dev/null
@@ -1,23 +0,0 @@
-import { useToast } from "@chakra-ui/react"
-import { useCallback } from "react"
-
-const useCustomToast = () => {
- const toast = useToast()
-
- const showToast = useCallback(
- (title: string, description: string, status: "success" | "error") => {
- toast({
- title,
- description,
- status,
- isClosable: true,
- position: "bottom-right",
- })
- },
- [toast],
- )
-
- return showToast
-}
-
-export default useCustomToast
diff --git a/frontend/src/main.tsx b/frontend/src/main.tsx
deleted file mode 100644
index afc904538b..0000000000
--- a/frontend/src/main.tsx
+++ /dev/null
@@ -1,33 +0,0 @@
-import { ChakraProvider } from "@chakra-ui/react"
-import { QueryClient, QueryClientProvider } from "@tanstack/react-query"
-import { RouterProvider, createRouter } from "@tanstack/react-router"
-import ReactDOM from "react-dom/client"
-import { routeTree } from "./routeTree.gen"
-
-import { StrictMode } from "react"
-import { OpenAPI } from "./client"
-import theme from "./theme"
-
-OpenAPI.BASE = import.meta.env.VITE_API_URL
-OpenAPI.TOKEN = async () => {
- return localStorage.getItem("access_token") || ""
-}
-
-const queryClient = new QueryClient()
-
-const router = createRouter({ routeTree })
-declare module "@tanstack/react-router" {
- interface Register {
- router: typeof router
- }
-}
-
-ReactDOM.createRoot(document.getElementById("root")!).render(
-
-
-
-
-
-
- ,
-)
diff --git a/frontend/src/routeTree.gen.ts b/frontend/src/routeTree.gen.ts
deleted file mode 100644
index 0e78c9ba20..0000000000
--- a/frontend/src/routeTree.gen.ts
+++ /dev/null
@@ -1,129 +0,0 @@
-/* prettier-ignore-start */
-
-/* eslint-disable */
-
-// @ts-nocheck
-
-// noinspection JSUnusedGlobalSymbols
-
-// This file is auto-generated by TanStack Router
-
-// Import Routes
-
-import { Route as rootRoute } from './routes/__root'
-import { Route as SignupImport } from './routes/signup'
-import { Route as ResetPasswordImport } from './routes/reset-password'
-import { Route as RecoverPasswordImport } from './routes/recover-password'
-import { Route as LoginImport } from './routes/login'
-import { Route as LayoutImport } from './routes/_layout'
-import { Route as LayoutIndexImport } from './routes/_layout/index'
-import { Route as LayoutSettingsImport } from './routes/_layout/settings'
-import { Route as LayoutItemsImport } from './routes/_layout/items'
-import { Route as LayoutAdminImport } from './routes/_layout/admin'
-
-// Create/Update Routes
-
-const SignupRoute = SignupImport.update({
- path: '/signup',
- getParentRoute: () => rootRoute,
-} as any)
-
-const ResetPasswordRoute = ResetPasswordImport.update({
- path: '/reset-password',
- getParentRoute: () => rootRoute,
-} as any)
-
-const RecoverPasswordRoute = RecoverPasswordImport.update({
- path: '/recover-password',
- getParentRoute: () => rootRoute,
-} as any)
-
-const LoginRoute = LoginImport.update({
- path: '/login',
- getParentRoute: () => rootRoute,
-} as any)
-
-const LayoutRoute = LayoutImport.update({
- id: '/_layout',
- getParentRoute: () => rootRoute,
-} as any)
-
-const LayoutIndexRoute = LayoutIndexImport.update({
- path: '/',
- getParentRoute: () => LayoutRoute,
-} as any)
-
-const LayoutSettingsRoute = LayoutSettingsImport.update({
- path: '/settings',
- getParentRoute: () => LayoutRoute,
-} as any)
-
-const LayoutItemsRoute = LayoutItemsImport.update({
- path: '/items',
- getParentRoute: () => LayoutRoute,
-} as any)
-
-const LayoutAdminRoute = LayoutAdminImport.update({
- path: '/admin',
- getParentRoute: () => LayoutRoute,
-} as any)
-
-// Populate the FileRoutesByPath interface
-
-declare module '@tanstack/react-router' {
- interface FileRoutesByPath {
- '/_layout': {
- preLoaderRoute: typeof LayoutImport
- parentRoute: typeof rootRoute
- }
- '/login': {
- preLoaderRoute: typeof LoginImport
- parentRoute: typeof rootRoute
- }
- '/recover-password': {
- preLoaderRoute: typeof RecoverPasswordImport
- parentRoute: typeof rootRoute
- }
- '/reset-password': {
- preLoaderRoute: typeof ResetPasswordImport
- parentRoute: typeof rootRoute
- }
- '/signup': {
- preLoaderRoute: typeof SignupImport
- parentRoute: typeof rootRoute
- }
- '/_layout/admin': {
- preLoaderRoute: typeof LayoutAdminImport
- parentRoute: typeof LayoutImport
- }
- '/_layout/items': {
- preLoaderRoute: typeof LayoutItemsImport
- parentRoute: typeof LayoutImport
- }
- '/_layout/settings': {
- preLoaderRoute: typeof LayoutSettingsImport
- parentRoute: typeof LayoutImport
- }
- '/_layout/': {
- preLoaderRoute: typeof LayoutIndexImport
- parentRoute: typeof LayoutImport
- }
- }
-}
-
-// Create and export the route tree
-
-export const routeTree = rootRoute.addChildren([
- LayoutRoute.addChildren([
- LayoutAdminRoute,
- LayoutItemsRoute,
- LayoutSettingsRoute,
- LayoutIndexRoute,
- ]),
- LoginRoute,
- RecoverPasswordRoute,
- ResetPasswordRoute,
- SignupRoute,
-])
-
-/* prettier-ignore-end */
diff --git a/frontend/src/routes/__root.tsx b/frontend/src/routes/__root.tsx
deleted file mode 100644
index 5da6383f2a..0000000000
--- a/frontend/src/routes/__root.tsx
+++ /dev/null
@@ -1,34 +0,0 @@
-import { Outlet, createRootRoute } from "@tanstack/react-router"
-import React, { Suspense } from "react"
-
-import NotFound from "../components/Common/NotFound"
-
-const loadDevtools = () =>
- Promise.all([
- import("@tanstack/router-devtools"),
- import("@tanstack/react-query-devtools"),
- ]).then(([routerDevtools, reactQueryDevtools]) => {
- return {
- default: () => (
- <>
-
-
- >
- ),
- }
- })
-
-const TanStackDevtools =
- process.env.NODE_ENV === "production" ? () => null : React.lazy(loadDevtools)
-
-export const Route = createRootRoute({
- component: () => (
- <>
-
-
-
-
- >
- ),
- notFoundComponent: () => ,
-})
diff --git a/frontend/src/routes/_layout.tsx b/frontend/src/routes/_layout.tsx
deleted file mode 100644
index 9a6cfa3b81..0000000000
--- a/frontend/src/routes/_layout.tsx
+++ /dev/null
@@ -1,35 +0,0 @@
-import { Flex, Spinner } from "@chakra-ui/react"
-import { Outlet, createFileRoute, redirect } from "@tanstack/react-router"
-
-import Sidebar from "../components/Common/Sidebar"
-import UserMenu from "../components/Common/UserMenu"
-import useAuth, { isLoggedIn } from "../hooks/useAuth"
-
-export const Route = createFileRoute("/_layout")({
- component: Layout,
- beforeLoad: async () => {
- if (!isLoggedIn()) {
- throw redirect({
- to: "/login",
- })
- }
- },
-})
-
-function Layout() {
- const { isLoading } = useAuth()
-
- return (
-
-
- {isLoading ? (
-
-
-
- ) : (
-
- )}
-
-
- )
-}
diff --git a/frontend/src/routes/_layout/admin.tsx b/frontend/src/routes/_layout/admin.tsx
deleted file mode 100644
index 644653ff79..0000000000
--- a/frontend/src/routes/_layout/admin.tsx
+++ /dev/null
@@ -1,170 +0,0 @@
-import {
- Badge,
- Box,
- Button,
- Container,
- Flex,
- Heading,
- SkeletonText,
- Table,
- TableContainer,
- Tbody,
- Td,
- Th,
- Thead,
- Tr,
-} from "@chakra-ui/react"
-import { useQuery, useQueryClient } from "@tanstack/react-query"
-import { createFileRoute, useNavigate } from "@tanstack/react-router"
-import { useEffect } from "react"
-import { z } from "zod"
-
-import { type UserPublic, UsersService } from "../../client"
-import AddUser from "../../components/Admin/AddUser"
-import ActionsMenu from "../../components/Common/ActionsMenu"
-import Navbar from "../../components/Common/Navbar"
-
-const usersSearchSchema = z.object({
- page: z.number().catch(1),
-})
-
-export const Route = createFileRoute("/_layout/admin")({
- component: Admin,
- validateSearch: (search) => usersSearchSchema.parse(search),
-})
-
-const PER_PAGE = 5
-
-function getUsersQueryOptions({ page }: { page: number }) {
- return {
- queryFn: () =>
- UsersService.readUsers({ skip: (page - 1) * PER_PAGE, limit: PER_PAGE }),
- queryKey: ["users", { page }],
- }
-}
-
-function UsersTable() {
- const queryClient = useQueryClient()
- const currentUser = queryClient.getQueryData(["currentUser"])
- const { page } = Route.useSearch()
- const navigate = useNavigate({ from: Route.fullPath })
- const setPage = (page: number) =>
- navigate({ search: (prev) => ({ ...prev, page }) })
-
- const {
- data: users,
- isPending,
- isPlaceholderData,
- } = useQuery({
- ...getUsersQueryOptions({ page }),
- placeholderData: (prevData) => prevData,
- })
-
- const hasNextPage = !isPlaceholderData && users?.data.length === PER_PAGE
- const hasPreviousPage = page > 1
-
- useEffect(() => {
- if (hasNextPage) {
- queryClient.prefetchQuery(getUsersQueryOptions({ page: page + 1 }))
- }
- }, [page, queryClient, hasNextPage])
-
- return (
- <>
-
-
-
-
- Full name |
- Email |
- Role |
- Status |
- Actions |
-
-
- {isPending ? (
-
-
- {new Array(4).fill(null).map((_, index) => (
-
-
- |
- ))}
-
-
- ) : (
-
- {users?.data.map((user) => (
-
-
- {user.full_name || "N/A"}
- {currentUser?.id === user.id && (
-
- You
-
- )}
- |
-
- {user.email}
- |
- {user.is_superuser ? "Superuser" : "User"} |
-
-
-
- {user.is_active ? "Active" : "Inactive"}
-
- |
-
-
- |
-
- ))}
-
- )}
-
-
-
-
- Page {page}
-
-
- >
- )
-}
-
-function Admin() {
- return (
-
-
- Users Management
-
-
-
-
-
- )
-}
diff --git a/frontend/src/routes/_layout/index.tsx b/frontend/src/routes/_layout/index.tsx
deleted file mode 100644
index 80cc934083..0000000000
--- a/frontend/src/routes/_layout/index.tsx
+++ /dev/null
@@ -1,25 +0,0 @@
-import { Box, Container, Text } from "@chakra-ui/react"
-import { createFileRoute } from "@tanstack/react-router"
-
-import useAuth from "../../hooks/useAuth"
-
-export const Route = createFileRoute("/_layout/")({
- component: Dashboard,
-})
-
-function Dashboard() {
- const { user: currentUser } = useAuth()
-
- return (
- <>
-
-
-
- Hi, {currentUser?.full_name || currentUser?.email} 👋🏼
-
- Welcome back, nice to see you again!
-
-
- >
- )
-}
diff --git a/frontend/src/routes/_layout/items.tsx b/frontend/src/routes/_layout/items.tsx
deleted file mode 100644
index 174fa83c9b..0000000000
--- a/frontend/src/routes/_layout/items.tsx
+++ /dev/null
@@ -1,145 +0,0 @@
-import {
- Button,
- Container,
- Flex,
- Heading,
- SkeletonText,
- Table,
- TableContainer,
- Tbody,
- Td,
- Th,
- Thead,
- Tr,
-} from "@chakra-ui/react"
-import { useQuery, useQueryClient } from "@tanstack/react-query"
-import { createFileRoute, useNavigate } from "@tanstack/react-router"
-import { useEffect } from "react"
-import { z } from "zod"
-
-import { ItemsService } from "../../client"
-import ActionsMenu from "../../components/Common/ActionsMenu"
-import Navbar from "../../components/Common/Navbar"
-import AddItem from "../../components/Items/AddItem"
-
-const itemsSearchSchema = z.object({
- page: z.number().catch(1),
-})
-
-export const Route = createFileRoute("/_layout/items")({
- component: Items,
- validateSearch: (search) => itemsSearchSchema.parse(search),
-})
-
-const PER_PAGE = 5
-
-function getItemsQueryOptions({ page }: { page: number }) {
- return {
- queryFn: () =>
- ItemsService.readItems({ skip: (page - 1) * PER_PAGE, limit: PER_PAGE }),
- queryKey: ["items", { page }],
- }
-}
-
-function ItemsTable() {
- const queryClient = useQueryClient()
- const { page } = Route.useSearch()
- const navigate = useNavigate({ from: Route.fullPath })
- const setPage = (page: number) =>
- navigate({ search: (prev) => ({ ...prev, page }) })
-
- const {
- data: items,
- isPending,
- isPlaceholderData,
- } = useQuery({
- ...getItemsQueryOptions({ page }),
- placeholderData: (prevData) => prevData,
- })
-
- const hasNextPage = !isPlaceholderData && items?.data.length === PER_PAGE
- const hasPreviousPage = page > 1
-
- useEffect(() => {
- if (hasNextPage) {
- queryClient.prefetchQuery(getItemsQueryOptions({ page: page + 1 }))
- }
- }, [page, queryClient, hasNextPage])
-
- return (
- <>
-
-
-
-
- ID |
- Title |
- Description |
- Actions |
-
-
- {isPending ? (
-
-
- {new Array(4).fill(null).map((_, index) => (
-
-
- |
- ))}
-
-
- ) : (
-
- {items?.data.map((item) => (
-
- {item.id} |
-
- {item.title}
- |
-
- {item.description || "N/A"}
- |
-
-
- |
-
- ))}
-
- )}
-
-
-
-
- Page {page}
-
-
- >
- )
-}
-
-function Items() {
- return (
-
-
- Items Management
-
-
-
-
-
- )
-}
diff --git a/frontend/src/routes/_layout/settings.tsx b/frontend/src/routes/_layout/settings.tsx
deleted file mode 100644
index 68266c6b9a..0000000000
--- a/frontend/src/routes/_layout/settings.tsx
+++ /dev/null
@@ -1,58 +0,0 @@
-import {
- Container,
- Heading,
- Tab,
- TabList,
- TabPanel,
- TabPanels,
- Tabs,
-} from "@chakra-ui/react"
-import { useQueryClient } from "@tanstack/react-query"
-import { createFileRoute } from "@tanstack/react-router"
-
-import type { UserPublic } from "../../client"
-import Appearance from "../../components/UserSettings/Appearance"
-import ChangePassword from "../../components/UserSettings/ChangePassword"
-import DeleteAccount from "../../components/UserSettings/DeleteAccount"
-import UserInformation from "../../components/UserSettings/UserInformation"
-
-const tabsConfig = [
- { title: "My profile", component: UserInformation },
- { title: "Password", component: ChangePassword },
- { title: "Appearance", component: Appearance },
- { title: "Danger zone", component: DeleteAccount },
-]
-
-export const Route = createFileRoute("/_layout/settings")({
- component: UserSettings,
-})
-
-function UserSettings() {
- const queryClient = useQueryClient()
- const currentUser = queryClient.getQueryData(["currentUser"])
- const finalTabs = currentUser?.is_superuser
- ? tabsConfig.slice(0, 3)
- : tabsConfig
-
- return (
-
-
- User Settings
-
-
-
- {finalTabs.map((tab, index) => (
- {tab.title}
- ))}
-
-
- {finalTabs.map((tab, index) => (
-
-
-
- ))}
-
-
-
- )
-}
diff --git a/frontend/src/routes/login.tsx b/frontend/src/routes/login.tsx
deleted file mode 100644
index 20a9be6564..0000000000
--- a/frontend/src/routes/login.tsx
+++ /dev/null
@@ -1,144 +0,0 @@
-import { ViewIcon, ViewOffIcon } from "@chakra-ui/icons"
-import {
- Button,
- Container,
- FormControl,
- FormErrorMessage,
- Icon,
- Image,
- Input,
- InputGroup,
- InputRightElement,
- Link,
- Text,
- useBoolean,
-} from "@chakra-ui/react"
-import {
- Link as RouterLink,
- createFileRoute,
- redirect,
-} from "@tanstack/react-router"
-import { type SubmitHandler, useForm } from "react-hook-form"
-
-import Logo from "/assets/images/fastapi-logo.svg"
-import type { Body_login_login_access_token as AccessToken } from "../client"
-import useAuth, { isLoggedIn } from "../hooks/useAuth"
-import { emailPattern } from "../utils"
-
-export const Route = createFileRoute("/login")({
- component: Login,
- beforeLoad: async () => {
- if (isLoggedIn()) {
- throw redirect({
- to: "/",
- })
- }
- },
-})
-
-function Login() {
- const [show, setShow] = useBoolean()
- const { loginMutation, error, resetError } = useAuth()
- const {
- register,
- handleSubmit,
- formState: { errors, isSubmitting },
- } = useForm({
- mode: "onBlur",
- criteriaMode: "all",
- defaultValues: {
- username: "",
- password: "",
- },
- })
-
- const onSubmit: SubmitHandler = async (data) => {
- if (isSubmitting) return
-
- resetError()
-
- try {
- await loginMutation.mutateAsync(data)
- } catch {
- // error is handled by useAuth hook
- }
- }
-
- return (
- <>
-
-
-
-
- {errors.username && (
- {errors.username.message}
- )}
-
-
-
-
-
-
- {show ? : }
-
-
-
- {error && {error}}
-
-
- Forgot password?
-
-
-
- Don't have an account?{" "}
-
- Sign up
-
-
-
- >
- )
-}
diff --git a/frontend/src/routes/recover-password.tsx b/frontend/src/routes/recover-password.tsx
deleted file mode 100644
index 5716728bbb..0000000000
--- a/frontend/src/routes/recover-password.tsx
+++ /dev/null
@@ -1,104 +0,0 @@
-import {
- Button,
- Container,
- FormControl,
- FormErrorMessage,
- Heading,
- Input,
- Text,
-} from "@chakra-ui/react"
-import { useMutation } from "@tanstack/react-query"
-import { createFileRoute, redirect } from "@tanstack/react-router"
-import { type SubmitHandler, useForm } from "react-hook-form"
-
-import { type ApiError, LoginService } from "../client"
-import { isLoggedIn } from "../hooks/useAuth"
-import useCustomToast from "../hooks/useCustomToast"
-import { emailPattern, handleError } from "../utils"
-
-interface FormData {
- email: string
-}
-
-export const Route = createFileRoute("/recover-password")({
- component: RecoverPassword,
- beforeLoad: async () => {
- if (isLoggedIn()) {
- throw redirect({
- to: "/",
- })
- }
- },
-})
-
-function RecoverPassword() {
- const {
- register,
- handleSubmit,
- reset,
- formState: { errors, isSubmitting },
- } = useForm()
- const showToast = useCustomToast()
-
- const recoverPassword = async (data: FormData) => {
- await LoginService.recoverPassword({
- email: data.email,
- })
- }
-
- const mutation = useMutation({
- mutationFn: recoverPassword,
- onSuccess: () => {
- showToast(
- "Email sent.",
- "We sent an email with a link to get back into your account.",
- "success",
- )
- reset()
- },
- onError: (err: ApiError) => {
- handleError(err, showToast)
- },
- })
-
- const onSubmit: SubmitHandler = async (data) => {
- mutation.mutate(data)
- }
-
- return (
-
-
- Password Recovery
-
-
- A password recovery email will be sent to the registered account.
-
-
-
- {errors.email && (
- {errors.email.message}
- )}
-
-
-
- )
-}
diff --git a/frontend/src/routes/reset-password.tsx b/frontend/src/routes/reset-password.tsx
deleted file mode 100644
index f5ee763a3e..0000000000
--- a/frontend/src/routes/reset-password.tsx
+++ /dev/null
@@ -1,122 +0,0 @@
-import {
- Button,
- Container,
- FormControl,
- FormErrorMessage,
- FormLabel,
- Heading,
- Input,
- Text,
-} from "@chakra-ui/react"
-import { useMutation } from "@tanstack/react-query"
-import { createFileRoute, redirect, useNavigate } from "@tanstack/react-router"
-import { type SubmitHandler, useForm } from "react-hook-form"
-
-import { type ApiError, LoginService, type NewPassword } from "../client"
-import { isLoggedIn } from "../hooks/useAuth"
-import useCustomToast from "../hooks/useCustomToast"
-import { confirmPasswordRules, handleError, passwordRules } from "../utils"
-
-interface NewPasswordForm extends NewPassword {
- confirm_password: string
-}
-
-export const Route = createFileRoute("/reset-password")({
- component: ResetPassword,
- beforeLoad: async () => {
- if (isLoggedIn()) {
- throw redirect({
- to: "/",
- })
- }
- },
-})
-
-function ResetPassword() {
- const {
- register,
- handleSubmit,
- getValues,
- reset,
- formState: { errors },
- } = useForm({
- mode: "onBlur",
- criteriaMode: "all",
- defaultValues: {
- new_password: "",
- },
- })
- const showToast = useCustomToast()
- const navigate = useNavigate()
-
- const resetPassword = async (data: NewPassword) => {
- const token = new URLSearchParams(window.location.search).get("token")
- if (!token) return
- await LoginService.resetPassword({
- requestBody: { new_password: data.new_password, token: token },
- })
- }
-
- const mutation = useMutation({
- mutationFn: resetPassword,
- onSuccess: () => {
- showToast("Success!", "Password updated successfully.", "success")
- reset()
- navigate({ to: "/login" })
- },
- onError: (err: ApiError) => {
- handleError(err, showToast)
- },
- })
-
- const onSubmit: SubmitHandler = async (data) => {
- mutation.mutate(data)
- }
-
- return (
-
-
- Reset Password
-
-
- Please enter your new password and confirm it to reset your password.
-
-
- Set Password
-
- {errors.new_password && (
- {errors.new_password.message}
- )}
-
-
- Confirm Password
-
- {errors.confirm_password && (
- {errors.confirm_password.message}
- )}
-
-
-
- )
-}
diff --git a/frontend/src/routes/signup.tsx b/frontend/src/routes/signup.tsx
deleted file mode 100644
index b021e73698..0000000000
--- a/frontend/src/routes/signup.tsx
+++ /dev/null
@@ -1,164 +0,0 @@
-import {
- Button,
- Container,
- Flex,
- FormControl,
- FormErrorMessage,
- FormLabel,
- Image,
- Input,
- Link,
- Text,
-} from "@chakra-ui/react"
-import {
- Link as RouterLink,
- createFileRoute,
- redirect,
-} from "@tanstack/react-router"
-import { type SubmitHandler, useForm } from "react-hook-form"
-
-import Logo from "/assets/images/fastapi-logo.svg"
-import type { UserRegister } from "../client"
-import useAuth, { isLoggedIn } from "../hooks/useAuth"
-import { confirmPasswordRules, emailPattern, passwordRules } from "../utils"
-
-export const Route = createFileRoute("/signup")({
- component: SignUp,
- beforeLoad: async () => {
- if (isLoggedIn()) {
- throw redirect({
- to: "/",
- })
- }
- },
-})
-
-interface UserRegisterForm extends UserRegister {
- confirm_password: string
-}
-
-function SignUp() {
- const { signUpMutation } = useAuth()
- const {
- register,
- handleSubmit,
- getValues,
- formState: { errors, isSubmitting },
- } = useForm({
- mode: "onBlur",
- criteriaMode: "all",
- defaultValues: {
- email: "",
- full_name: "",
- password: "",
- confirm_password: "",
- },
- })
-
- const onSubmit: SubmitHandler = (data) => {
- signUpMutation.mutate(data)
- }
-
- return (
- <>
-
-
-
-
-
- Full Name
-
-
- {errors.full_name && (
- {errors.full_name.message}
- )}
-
-
-
- Email
-
-
- {errors.email && (
- {errors.email.message}
- )}
-
-
-
- Password
-
-
- {errors.password && (
- {errors.password.message}
- )}
-
-
-
- Confirm Password
-
-
-
- {errors.confirm_password && (
-
- {errors.confirm_password.message}
-
- )}
-
-
-
- Already have an account?{" "}
-
- Log In
-
-
-
-
- >
- )
-}
-
-export default SignUp
diff --git a/frontend/src/theme.tsx b/frontend/src/theme.tsx
deleted file mode 100644
index 71675dddca..0000000000
--- a/frontend/src/theme.tsx
+++ /dev/null
@@ -1,61 +0,0 @@
-import { extendTheme } from "@chakra-ui/react"
-
-const disabledStyles = {
- _disabled: {
- backgroundColor: "ui.main",
- },
-}
-
-const theme = extendTheme({
- colors: {
- ui: {
- main: "#009688",
- secondary: "#EDF2F7",
- success: "#48BB78",
- danger: "#E53E3E",
- light: "#FAFAFA",
- dark: "#1A202C",
- darkSlate: "#252D3D",
- dim: "#A0AEC0",
- },
- },
- components: {
- Button: {
- variants: {
- primary: {
- backgroundColor: "ui.main",
- color: "ui.light",
- _hover: {
- backgroundColor: "#00766C",
- },
- _disabled: {
- ...disabledStyles,
- _hover: {
- ...disabledStyles,
- },
- },
- },
- danger: {
- backgroundColor: "ui.danger",
- color: "ui.light",
- _hover: {
- backgroundColor: "#E32727",
- },
- },
- },
- },
- Tabs: {
- variants: {
- enclosed: {
- tab: {
- _selected: {
- color: "ui.main",
- },
- },
- },
- },
- },
- },
-})
-
-export default theme
diff --git a/frontend/src/utils.ts b/frontend/src/utils.ts
deleted file mode 100644
index 99f906303c..0000000000
--- a/frontend/src/utils.ts
+++ /dev/null
@@ -1,53 +0,0 @@
-import type { ApiError } from "./client"
-
-export const emailPattern = {
- value: /^[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,}$/i,
- message: "Invalid email address",
-}
-
-export const namePattern = {
- value: /^[A-Za-z\s\u00C0-\u017F]{1,30}$/,
- message: "Invalid name",
-}
-
-export const passwordRules = (isRequired = true) => {
- const rules: any = {
- minLength: {
- value: 8,
- message: "Password must be at least 8 characters",
- },
- }
-
- if (isRequired) {
- rules.required = "Password is required"
- }
-
- return rules
-}
-
-export const confirmPasswordRules = (
- getValues: () => any,
- isRequired = true,
-) => {
- const rules: any = {
- validate: (value: string) => {
- const password = getValues().password || getValues().new_password
- return value === password ? true : "The passwords do not match"
- },
- }
-
- if (isRequired) {
- rules.required = "Password confirmation is required"
- }
-
- return rules
-}
-
-export const handleError = (err: ApiError, showToast: any) => {
- const errDetail = (err.body as any)?.detail
- let errorMessage = errDetail || "Something went wrong."
- if (Array.isArray(errDetail) && errDetail.length > 0) {
- errorMessage = errDetail[0].msg
- }
- showToast("Error", errorMessage, "error")
-}
diff --git a/frontend/src/vite-env.d.ts b/frontend/src/vite-env.d.ts
deleted file mode 100644
index 11f02fe2a0..0000000000
--- a/frontend/src/vite-env.d.ts
+++ /dev/null
@@ -1 +0,0 @@
-///
diff --git a/frontend/tests/auth.setup.ts b/frontend/tests/auth.setup.ts
deleted file mode 100644
index 3882f4f810..0000000000
--- a/frontend/tests/auth.setup.ts
+++ /dev/null
@@ -1,13 +0,0 @@
-import { test as setup } from "@playwright/test"
-import { firstSuperuser, firstSuperuserPassword } from "./config.ts"
-
-const authFile = "playwright/.auth/user.json"
-
-setup("authenticate", async ({ page }) => {
- await page.goto("/login")
- await page.getByPlaceholder("Email").fill(firstSuperuser)
- await page.getByPlaceholder("Password").fill(firstSuperuserPassword)
- await page.getByRole("button", { name: "Log In" }).click()
- await page.waitForURL("/")
- await page.context().storageState({ path: authFile })
-})
diff --git a/frontend/tests/config.ts b/frontend/tests/config.ts
deleted file mode 100644
index 188cb367e3..0000000000
--- a/frontend/tests/config.ts
+++ /dev/null
@@ -1,21 +0,0 @@
-import path from "node:path"
-import { fileURLToPath } from "node:url"
-import dotenv from "dotenv"
-
-const __filename = fileURLToPath(import.meta.url)
-const __dirname = path.dirname(__filename)
-
-dotenv.config({ path: path.join(__dirname, "../../.env") })
-
-const { FIRST_SUPERUSER, FIRST_SUPERUSER_PASSWORD } = process.env
-
-if (typeof FIRST_SUPERUSER !== "string") {
- throw new Error("Environment variable FIRST_SUPERUSER is undefined")
-}
-
-if (typeof FIRST_SUPERUSER_PASSWORD !== "string") {
- throw new Error("Environment variable FIRST_SUPERUSER_PASSWORD is undefined")
-}
-
-export const firstSuperuser = FIRST_SUPERUSER as string
-export const firstSuperuserPassword = FIRST_SUPERUSER_PASSWORD as string
diff --git a/frontend/tests/login.spec.ts b/frontend/tests/login.spec.ts
deleted file mode 100644
index 97c2284f40..0000000000
--- a/frontend/tests/login.spec.ts
+++ /dev/null
@@ -1,117 +0,0 @@
-import { type Page, expect, test } from "@playwright/test"
-import { firstSuperuser, firstSuperuserPassword } from "./config.ts"
-import { randomPassword } from "./utils/random.ts"
-
-test.use({ storageState: { cookies: [], origins: [] } })
-
-type OptionsType = {
- exact?: boolean
-}
-
-const fillForm = async (page: Page, email: string, password: string) => {
- await page.getByPlaceholder("Email").fill(email)
- await page.getByPlaceholder("Password", { exact: true }).fill(password)
-}
-
-const verifyInput = async (
- page: Page,
- placeholder: string,
- options?: OptionsType,
-) => {
- const input = page.getByPlaceholder(placeholder, options)
- await expect(input).toBeVisible()
- await expect(input).toHaveText("")
- await expect(input).toBeEditable()
-}
-
-test("Inputs are visible, empty and editable", async ({ page }) => {
- await page.goto("/login")
-
- await verifyInput(page, "Email")
- await verifyInput(page, "Password", { exact: true })
-})
-
-test("Log In button is visible", async ({ page }) => {
- await page.goto("/login")
-
- await expect(page.getByRole("button", { name: "Log In" })).toBeVisible()
-})
-
-test("Forgot Password link is visible", async ({ page }) => {
- await page.goto("/login")
-
- await expect(
- page.getByRole("link", { name: "Forgot password?" }),
- ).toBeVisible()
-})
-
-test("Log in with valid email and password ", async ({ page }) => {
- await page.goto("/login")
-
- await fillForm(page, firstSuperuser, firstSuperuserPassword)
- await page.getByRole("button", { name: "Log In" }).click()
-
- await page.waitForURL("/")
-
- await expect(
- page.getByText("Welcome back, nice to see you again!"),
- ).toBeVisible()
-})
-
-test("Log in with invalid email", async ({ page }) => {
- await page.goto("/login")
-
- await fillForm(page, "invalidemail", firstSuperuserPassword)
- await page.getByRole("button", { name: "Log In" }).click()
-
- await expect(page.getByText("Invalid email address")).toBeVisible()
-})
-
-test("Log in with invalid password", async ({ page }) => {
- const password = randomPassword()
-
- await page.goto("/login")
- await fillForm(page, firstSuperuser, password)
- await page.getByRole("button", { name: "Log In" }).click()
-
- await expect(page.getByText("Incorrect email or password")).toBeVisible()
-})
-
-// Log out
-
-test("Successful log out", async ({ page }) => {
- await page.goto("/login")
-
- await fillForm(page, firstSuperuser, firstSuperuserPassword)
- await page.getByRole("button", { name: "Log In" }).click()
-
- await page.waitForURL("/")
-
- await expect(
- page.getByText("Welcome back, nice to see you again!"),
- ).toBeVisible()
-
- await page.getByTestId("user-menu").click()
- await page.getByRole("menuitem", { name: "Log out" }).click()
- await page.waitForURL("/login")
-})
-
-test("Logged-out user cannot access protected routes", async ({ page }) => {
- await page.goto("/login")
-
- await fillForm(page, firstSuperuser, firstSuperuserPassword)
- await page.getByRole("button", { name: "Log In" }).click()
-
- await page.waitForURL("/")
-
- await expect(
- page.getByText("Welcome back, nice to see you again!"),
- ).toBeVisible()
-
- await page.getByTestId("user-menu").click()
- await page.getByRole("menuitem", { name: "Log out" }).click()
- await page.waitForURL("/login")
-
- await page.goto("/settings")
- await page.waitForURL("/login")
-})
diff --git a/frontend/tests/reset-password.spec.ts b/frontend/tests/reset-password.spec.ts
deleted file mode 100644
index 88ec798791..0000000000
--- a/frontend/tests/reset-password.spec.ts
+++ /dev/null
@@ -1,121 +0,0 @@
-import { expect, test } from "@playwright/test"
-import { findLastEmail } from "./utils/mailcatcher"
-import { randomEmail, randomPassword } from "./utils/random"
-import { logInUser, signUpNewUser } from "./utils/user"
-
-test.use({ storageState: { cookies: [], origins: [] } })
-
-test("Password Recovery title is visible", async ({ page }) => {
- await page.goto("/recover-password")
-
- await expect(
- page.getByRole("heading", { name: "Password Recovery" }),
- ).toBeVisible()
-})
-
-test("Input is visible, empty and editable", async ({ page }) => {
- await page.goto("/recover-password")
-
- await expect(page.getByPlaceholder("Email")).toBeVisible()
- await expect(page.getByPlaceholder("Email")).toHaveText("")
- await expect(page.getByPlaceholder("Email")).toBeEditable()
-})
-
-test("Continue button is visible", async ({ page }) => {
- await page.goto("/recover-password")
-
- await expect(page.getByRole("button", { name: "Continue" })).toBeVisible()
-})
-
-test("User can reset password successfully using the link", async ({
- page,
- request,
-}) => {
- const fullName = "Test User"
- const email = randomEmail()
- const password = randomPassword()
- const newPassword = randomPassword()
-
- // Sign up a new user
- await signUpNewUser(page, fullName, email, password)
-
- await page.goto("/recover-password")
- await page.getByPlaceholder("Email").fill(email)
-
- await page.getByRole("button", { name: "Continue" }).click()
-
- const emailData = await findLastEmail({
- request,
- filter: (e) => e.recipients.includes(`<${email}>`),
- timeout: 5000,
- })
-
- await page.goto(`http://localhost:1080/messages/${emailData.id}.html`)
-
- const selector = 'a[href*="/reset-password?token="]'
-
- let url = await page.getAttribute(selector, "href")
-
- // TODO: update var instead of doing a replace
- url = url!.replace("http://localhost/", "http://localhost:5173/")
-
- // Set the new password and confirm it
- await page.goto(url)
-
- await page.getByLabel("Set Password").fill(newPassword)
- await page.getByLabel("Confirm Password").fill(newPassword)
- await page.getByRole("button", { name: "Reset Password" }).click()
- await expect(page.getByText("Password updated successfully")).toBeVisible()
-
- // Check if the user is able to login with the new password
- await logInUser(page, email, newPassword)
-})
-
-test("Expired or invalid reset link", async ({ page }) => {
- const password = randomPassword()
- const invalidUrl = "/reset-password?token=invalidtoken"
-
- await page.goto(invalidUrl)
-
- await page.getByLabel("Set Password").fill(password)
- await page.getByLabel("Confirm Password").fill(password)
- await page.getByRole("button", { name: "Reset Password" }).click()
-
- await expect(page.getByText("Invalid token")).toBeVisible()
-})
-
-test("Weak new password validation", async ({ page, request }) => {
- const fullName = "Test User"
- const email = randomEmail()
- const password = randomPassword()
- const weakPassword = "123"
-
- // Sign up a new user
- await signUpNewUser(page, fullName, email, password)
-
- await page.goto("/recover-password")
- await page.getByPlaceholder("Email").fill(email)
- await page.getByRole("button", { name: "Continue" }).click()
-
- const emailData = await findLastEmail({
- request,
- filter: (e) => e.recipients.includes(`<${email}>`),
- timeout: 5000,
- })
-
- await page.goto(`http://localhost:1080/messages/${emailData.id}.html`)
-
- const selector = 'a[href*="/reset-password?token="]'
- let url = await page.getAttribute(selector, "href")
- url = url!.replace("http://localhost/", "http://localhost:5173/")
-
- // Set a weak new password
- await page.goto(url)
- await page.getByLabel("Set Password").fill(weakPassword)
- await page.getByLabel("Confirm Password").fill(weakPassword)
- await page.getByRole("button", { name: "Reset Password" }).click()
-
- await expect(
- page.getByText("Password must be at least 8 characters"),
- ).toBeVisible()
-})
diff --git a/frontend/tests/sign-up.spec.ts b/frontend/tests/sign-up.spec.ts
deleted file mode 100644
index a666123280..0000000000
--- a/frontend/tests/sign-up.spec.ts
+++ /dev/null
@@ -1,169 +0,0 @@
-import { type Page, expect, test } from "@playwright/test"
-
-import { randomEmail, randomPassword } from "./utils/random"
-
-test.use({ storageState: { cookies: [], origins: [] } })
-
-type OptionsType = {
- exact?: boolean
-}
-
-const fillForm = async (
- page: Page,
- full_name: string,
- email: string,
- password: string,
- confirm_password: string,
-) => {
- await page.getByPlaceholder("Full Name").fill(full_name)
- await page.getByPlaceholder("Email").fill(email)
- await page.getByPlaceholder("Password", { exact: true }).fill(password)
- await page.getByPlaceholder("Repeat Password").fill(confirm_password)
-}
-
-const verifyInput = async (
- page: Page,
- placeholder: string,
- options?: OptionsType,
-) => {
- const input = page.getByPlaceholder(placeholder, options)
- await expect(input).toBeVisible()
- await expect(input).toHaveText("")
- await expect(input).toBeEditable()
-}
-
-test("Inputs are visible, empty and editable", async ({ page }) => {
- await page.goto("/signup")
-
- await verifyInput(page, "Full Name")
- await verifyInput(page, "Email")
- await verifyInput(page, "Password", { exact: true })
- await verifyInput(page, "Repeat Password")
-})
-
-test("Sign Up button is visible", async ({ page }) => {
- await page.goto("/signup")
-
- await expect(page.getByRole("button", { name: "Sign Up" })).toBeVisible()
-})
-
-test("Log In link is visible", async ({ page }) => {
- await page.goto("/signup")
-
- await expect(page.getByRole("link", { name: "Log In" })).toBeVisible()
-})
-
-test("Sign up with valid name, email, and password", async ({ page }) => {
- const full_name = "Test User"
- const email = randomEmail()
- const password = randomPassword()
-
- await page.goto("/signup")
- await fillForm(page, full_name, email, password, password)
- await page.getByRole("button", { name: "Sign Up" }).click()
-})
-
-test("Sign up with invalid email", async ({ page }) => {
- await page.goto("/signup")
-
- await fillForm(
- page,
- "Playwright Test",
- "invalid-email",
- "changethis",
- "changethis",
- )
- await page.getByRole("button", { name: "Sign Up" }).click()
-
- await expect(page.getByText("Invalid email address")).toBeVisible()
-})
-
-test("Sign up with existing email", async ({ page }) => {
- const fullName = "Test User"
- const email = randomEmail()
- const password = randomPassword()
-
- // Sign up with an email
- await page.goto("/signup")
-
- await fillForm(page, fullName, email, password, password)
- await page.getByRole("button", { name: "Sign Up" }).click()
-
- // Sign up again with the same email
- await page.goto("/signup")
-
- await fillForm(page, fullName, email, password, password)
- await page.getByRole("button", { name: "Sign Up" }).click()
-
- await page
- .getByText("The user with this email already exists in the system")
- .click()
-})
-
-test("Sign up with weak password", async ({ page }) => {
- const fullName = "Test User"
- const email = randomEmail()
- const password = "weak"
-
- await page.goto("/signup")
-
- await fillForm(page, fullName, email, password, password)
- await page.getByRole("button", { name: "Sign Up" }).click()
-
- await expect(
- page.getByText("Password must be at least 8 characters"),
- ).toBeVisible()
-})
-
-test("Sign up with mismatched passwords", async ({ page }) => {
- const fullName = "Test User"
- const email = randomEmail()
- const password = randomPassword()
- const password2 = randomPassword()
-
- await page.goto("/signup")
-
- await fillForm(page, fullName, email, password, password2)
- await page.getByRole("button", { name: "Sign Up" }).click()
-
- await expect(page.getByText("Passwords do not match")).toBeVisible()
-})
-
-test("Sign up with missing full name", async ({ page }) => {
- const fullName = ""
- const email = randomEmail()
- const password = randomPassword()
-
- await page.goto("/signup")
-
- await fillForm(page, fullName, email, password, password)
- await page.getByRole("button", { name: "Sign Up" }).click()
-
- await expect(page.getByText("Full Name is required")).toBeVisible()
-})
-
-test("Sign up with missing email", async ({ page }) => {
- const fullName = "Test User"
- const email = ""
- const password = randomPassword()
-
- await page.goto("/signup")
-
- await fillForm(page, fullName, email, password, password)
- await page.getByRole("button", { name: "Sign Up" }).click()
-
- await expect(page.getByText("Email is required")).toBeVisible()
-})
-
-test("Sign up with missing password", async ({ page }) => {
- const fullName = ""
- const email = randomEmail()
- const password = ""
-
- await page.goto("/signup")
-
- await fillForm(page, fullName, email, password, password)
- await page.getByRole("button", { name: "Sign Up" }).click()
-
- await expect(page.getByText("Password is required")).toBeVisible()
-})
diff --git a/frontend/tests/user-settings.spec.ts b/frontend/tests/user-settings.spec.ts
deleted file mode 100644
index a3a8a27490..0000000000
--- a/frontend/tests/user-settings.spec.ts
+++ /dev/null
@@ -1,288 +0,0 @@
-import { expect, test } from "@playwright/test"
-import { firstSuperuser, firstSuperuserPassword } from "./config.ts"
-import { randomEmail, randomPassword } from "./utils/random"
-import { logInUser, logOutUser, signUpNewUser } from "./utils/user"
-
-const tabs = ["My profile", "Password", "Appearance"]
-
-// User Information
-
-test("My profile tab is active by default", async ({ page }) => {
- await page.goto("/settings")
- await expect(page.getByRole("tab", { name: "My profile" })).toHaveAttribute(
- "aria-selected",
- "true",
- )
-})
-
-test("All tabs are visible", async ({ page }) => {
- await page.goto("/settings")
- for (const tab of tabs) {
- await expect(page.getByRole("tab", { name: tab })).toBeVisible()
- }
-})
-
-test.describe("Edit user full name and email successfully", () => {
- test.use({ storageState: { cookies: [], origins: [] } })
-
- test("Edit user name with a valid name", async ({ page }) => {
- const fullName = "Test User"
- const email = randomEmail()
- const updatedName = "Test User 2"
- const password = randomPassword()
-
- // Sign up a new user
- await signUpNewUser(page, fullName, email, password)
-
- // Log in the user
- await logInUser(page, email, password)
-
- await page.goto("/settings")
- await page.getByRole("tab", { name: "My profile" }).click()
- await page.getByRole("button", { name: "Edit" }).click()
- await page.getByLabel("Full name").fill(updatedName)
- await page.getByRole("button", { name: "Save" }).click()
- await expect(page.getByText("User updated successfully")).toBeVisible()
- // Check if the new name is displayed on the page
- await expect(
- page.getByLabel("My profile").getByText(updatedName, { exact: true }),
- ).toBeVisible()
- })
-
- test("Edit user email with a valid email", async ({ page }) => {
- const fullName = "Test User"
- const email = randomEmail()
- const updatedEmail = randomEmail()
- const password = randomPassword()
-
- // Sign up a new user
- await signUpNewUser(page, fullName, email, password)
-
- // Log in the user
- await logInUser(page, email, password)
-
- await page.goto("/settings")
- await page.getByRole("tab", { name: "My profile" }).click()
- await page.getByRole("button", { name: "Edit" }).click()
- await page.getByLabel("Email").fill(updatedEmail)
- await page.getByRole("button", { name: "Save" }).click()
- await expect(page.getByText("User updated successfully")).toBeVisible()
- await expect(
- page.getByLabel("My profile").getByText(updatedEmail, { exact: true }),
- ).toBeVisible()
- })
-})
-
-test.describe("Edit user with invalid data", () => {
- test.use({ storageState: { cookies: [], origins: [] } })
-
- test("Edit user email with an invalid email", async ({ page }) => {
- const fullName = "Test User"
- const email = randomEmail()
- const password = randomPassword()
- const invalidEmail = ""
-
- // Sign up a new user
- await signUpNewUser(page, fullName, email, password)
-
- // Log in the user
- await logInUser(page, email, password)
-
- await page.goto("/settings")
- await page.getByRole("tab", { name: "My profile" }).click()
- await page.getByRole("button", { name: "Edit" }).click()
- await page.getByLabel("Email").fill(invalidEmail)
- await page.locator("body").click()
- await expect(page.getByText("Email is required")).toBeVisible()
- })
-
- test("Cancel edit action restores original name", async ({ page }) => {
- const fullName = "Test User"
- const email = randomEmail()
- const password = randomPassword()
- const updatedName = "Test User"
-
- // Sign up a new user
- await signUpNewUser(page, fullName, email, password)
-
- // Log in the user
- await logInUser(page, email, password)
-
- await page.goto("/settings")
- await page.getByRole("tab", { name: "My profile" }).click()
- await page.getByRole("button", { name: "Edit" }).click()
- await page.getByLabel("Full name").fill(updatedName)
- await page.getByRole("button", { name: "Cancel" }).first().click()
- await expect(
- page.getByLabel("My profile").getByText(fullName, { exact: true }),
- ).toBeVisible()
- })
-
- test("Cancel edit action restores original email", async ({ page }) => {
- const fullName = "Test User"
- const email = randomEmail()
- const password = randomPassword()
- const updatedEmail = randomEmail()
-
- // Sign up a new user
- await signUpNewUser(page, fullName, email, password)
-
- // Log in the user
- await logInUser(page, email, password)
-
- await page.goto("/settings")
- await page.getByRole("tab", { name: "My profile" }).click()
- await page.getByRole("button", { name: "Edit" }).click()
- await page.getByLabel("Email").fill(updatedEmail)
- await page.getByRole("button", { name: "Cancel" }).first().click()
- await expect(
- page.getByLabel("My profile").getByText(email, { exact: true }),
- ).toBeVisible()
- })
-})
-
-// Change Password
-
-test.describe("Change password successfully", () => {
- test.use({ storageState: { cookies: [], origins: [] } })
-
- test("Update password successfully", async ({ page }) => {
- const fullName = "Test User"
- const email = randomEmail()
- const password = randomPassword()
- const NewPassword = randomPassword()
-
- // Sign up a new user
- await signUpNewUser(page, fullName, email, password)
-
- // Log in the user
- await logInUser(page, email, password)
-
- await page.goto("/settings")
- await page.getByRole("tab", { name: "Password" }).click()
- await page.getByLabel("Current Password*").fill(password)
- await page.getByLabel("Set Password*").fill(NewPassword)
- await page.getByLabel("Confirm Password*").fill(NewPassword)
- await page.getByRole("button", { name: "Save" }).click()
- await expect(page.getByText("Password updated successfully.")).toBeVisible()
-
- await logOutUser(page)
-
- // Check if the user can log in with the new password
- await logInUser(page, email, NewPassword)
- })
-})
-
-test.describe("Change password with invalid data", () => {
- test.use({ storageState: { cookies: [], origins: [] } })
-
- test("Update password with weak passwords", async ({ page }) => {
- const fullName = "Test User"
- const email = randomEmail()
- const password = randomPassword()
- const weakPassword = "weak"
-
- // Sign up a new user
- await signUpNewUser(page, fullName, email, password)
-
- // Log in the user
- await logInUser(page, email, password)
-
- await page.goto("/settings")
- await page.getByRole("tab", { name: "Password" }).click()
- await page.getByLabel("Current Password*").fill(password)
- await page.getByLabel("Set Password*").fill(weakPassword)
- await page.getByLabel("Confirm Password*").fill(weakPassword)
- await expect(
- page.getByText("Password must be at least 8 characters"),
- ).toBeVisible()
- })
-
- test("New password and confirmation password do not match", async ({
- page,
- }) => {
- const fullName = "Test User"
- const email = randomEmail()
- const password = randomPassword()
- const newPassword = randomPassword()
- const confirmPassword = randomPassword()
-
- // Sign up a new user
- await signUpNewUser(page, fullName, email, password)
-
- // Log in the user
- await logInUser(page, email, password)
-
- await page.goto("/settings")
- await page.getByRole("tab", { name: "Password" }).click()
- await page.getByLabel("Current Password*").fill(password)
- await page.getByLabel("Set Password*").fill(newPassword)
- await page.getByLabel("Confirm Password*").fill(confirmPassword)
- await page.getByRole("button", { name: "Save" }).click()
- await expect(page.getByText("Passwords do not match")).toBeVisible()
- })
-
- test("Current password and new password are the same", async ({ page }) => {
- const fullName = "Test User"
- const email = randomEmail()
- const password = randomPassword()
-
- // Sign up a new user
- await signUpNewUser(page, fullName, email, password)
-
- // Log in the user
- await logInUser(page, email, password)
-
- await page.goto("/settings")
- await page.getByRole("tab", { name: "Password" }).click()
- await page.getByLabel("Current Password*").fill(password)
- await page.getByLabel("Set Password*").fill(password)
- await page.getByLabel("Confirm Password*").fill(password)
- await page.getByRole("button", { name: "Save" }).click()
- await expect(
- page.getByText("New password cannot be the same as the current one"),
- ).toBeVisible()
- })
-})
-
-// Appearance
-
-test("Appearance tab is visible", async ({ page }) => {
- await page.goto("/settings")
- await page.getByRole("tab", { name: "Appearance" }).click()
- await expect(page.getByLabel("Appearance")).toBeVisible()
-})
-
-test("User can switch from light mode to dark mode", async ({ page }) => {
- await page.goto("/settings")
- await page.getByRole("tab", { name: "Appearance" }).click()
- await page.getByLabel("Appearance").locator("span").nth(3).click()
- const isDarkMode = await page.evaluate(() =>
- document.body.classList.contains("chakra-ui-dark"),
- )
- expect(isDarkMode).toBe(true)
-})
-
-test("User can switch from dark mode to light mode", async ({ page }) => {
- await page.goto("/settings")
- await page.getByRole("tab", { name: "Appearance" }).click()
- await page.getByLabel("Appearance").locator("span").first().click()
- const isLightMode = await page.evaluate(() =>
- document.body.classList.contains("chakra-ui-light"),
- )
- expect(isLightMode).toBe(true)
-})
-
-test("Selected mode is preserved across sessions", async ({ page }) => {
- await page.goto("/settings")
- await page.getByRole("tab", { name: "Appearance" }).click()
- await page.getByLabel("Appearance").locator("span").nth(3).click()
-
- await logOutUser(page)
-
- await logInUser(page, firstSuperuser, firstSuperuserPassword)
- const isDarkMode = await page.evaluate(() =>
- document.body.classList.contains("chakra-ui-dark"),
- )
- expect(isDarkMode).toBe(true)
-})
diff --git a/frontend/tests/utils/mailcatcher.ts b/frontend/tests/utils/mailcatcher.ts
deleted file mode 100644
index 601ce434fb..0000000000
--- a/frontend/tests/utils/mailcatcher.ts
+++ /dev/null
@@ -1,59 +0,0 @@
-import type { APIRequestContext } from "@playwright/test"
-
-type Email = {
- id: number
- recipients: string[]
- subject: string
-}
-
-async function findEmail({
- request,
- filter,
-}: { request: APIRequestContext; filter?: (email: Email) => boolean }) {
- const response = await request.get("http://localhost:1080/messages")
-
- let emails = await response.json()
-
- if (filter) {
- emails = emails.filter(filter)
- }
-
- const email = emails[emails.length - 1]
-
- if (email) {
- return email as Email
- }
-
- return null
-}
-
-export function findLastEmail({
- request,
- filter,
- timeout = 5000,
-}: {
- request: APIRequestContext
- filter?: (email: Email) => boolean
- timeout?: number
-}) {
- const timeoutPromise = new Promise((_, reject) =>
- setTimeout(
- () => reject(new Error("Timeout while trying to get latest email")),
- timeout,
- ),
- )
-
- const checkEmails = async () => {
- while (true) {
- const emailData = await findEmail({ request, filter })
-
- if (emailData) {
- return emailData
- }
- // Wait for 100ms before checking again
- await new Promise((resolve) => setTimeout(resolve, 100))
- }
- }
-
- return Promise.race([timeoutPromise, checkEmails()])
-}
diff --git a/frontend/tests/utils/random.ts b/frontend/tests/utils/random.ts
deleted file mode 100644
index d96f0833ce..0000000000
--- a/frontend/tests/utils/random.ts
+++ /dev/null
@@ -1,13 +0,0 @@
-export const randomEmail = () =>
- `test_${Math.random().toString(36).substring(7)}@example.com`
-
-export const randomTeamName = () =>
- `Team ${Math.random().toString(36).substring(7)}`
-
-export const randomPassword = () => `${Math.random().toString(36).substring(2)}`
-
-export const slugify = (text: string) =>
- text
- .toLowerCase()
- .replace(/\s+/g, "-")
- .replace(/[^\w-]+/g, "")
diff --git a/frontend/tests/utils/user.ts b/frontend/tests/utils/user.ts
deleted file mode 100644
index 8fcfd26cb5..0000000000
--- a/frontend/tests/utils/user.ts
+++ /dev/null
@@ -1,38 +0,0 @@
-import { type Page, expect } from "@playwright/test"
-
-export async function signUpNewUser(
- page: Page,
- name: string,
- email: string,
- password: string,
-) {
- await page.goto("/signup")
-
- await page.getByPlaceholder("Full Name").fill(name)
- await page.getByPlaceholder("Email").fill(email)
- await page.getByPlaceholder("Password", { exact: true }).fill(password)
- await page.getByPlaceholder("Repeat Password").fill(password)
- await page.getByRole("button", { name: "Sign Up" }).click()
- await expect(
- page.getByText("Your account has been created successfully"),
- ).toBeVisible()
- await page.goto("/login")
-}
-
-export async function logInUser(page: Page, email: string, password: string) {
- await page.goto("/login")
-
- await page.getByPlaceholder("Email").fill(email)
- await page.getByPlaceholder("Password", { exact: true }).fill(password)
- await page.getByRole("button", { name: "Log In" }).click()
- await page.waitForURL("/")
- await expect(
- page.getByText("Welcome back, nice to see you again!"),
- ).toBeVisible()
-}
-
-export async function logOutUser(page: Page) {
- await page.getByTestId("user-menu").click()
- await page.getByRole("menuitem", { name: "Log out" }).click()
- await page.goto("/login")
-}
diff --git a/frontend/tsconfig.json b/frontend/tsconfig.json
deleted file mode 100644
index baadbb9fb1..0000000000
--- a/frontend/tsconfig.json
+++ /dev/null
@@ -1,25 +0,0 @@
-{
- "compilerOptions": {
- "target": "ES2020",
- "useDefineForClassFields": true,
- "lib": ["ES2020", "DOM", "DOM.Iterable"],
- "module": "ESNext",
- "skipLibCheck": true,
-
- /* Bundler mode */
- "moduleResolution": "bundler",
- "allowImportingTsExtensions": true,
- "resolveJsonModule": true,
- "isolatedModules": true,
- "noEmit": true,
- "jsx": "react-jsx",
-
- /* Linting */
- "strict": true,
- "noUnusedLocals": true,
- "noUnusedParameters": true,
- "noFallthroughCasesInSwitch": true
- },
- "include": ["src", "*.ts", "**/*.ts"],
- "references": [{ "path": "./tsconfig.node.json" }]
-}
diff --git a/frontend/tsconfig.node.json b/frontend/tsconfig.node.json
deleted file mode 100644
index 42872c59f5..0000000000
--- a/frontend/tsconfig.node.json
+++ /dev/null
@@ -1,10 +0,0 @@
-{
- "compilerOptions": {
- "composite": true,
- "skipLibCheck": true,
- "module": "ESNext",
- "moduleResolution": "bundler",
- "allowSyntheticDefaultImports": true
- },
- "include": ["vite.config.ts"]
-}
diff --git a/frontend/vite.config.ts b/frontend/vite.config.ts
deleted file mode 100644
index 572745b8cf..0000000000
--- a/frontend/vite.config.ts
+++ /dev/null
@@ -1,8 +0,0 @@
-import { TanStackRouterVite } from "@tanstack/router-vite-plugin"
-import react from "@vitejs/plugin-react-swc"
-import { defineConfig } from "vite"
-
-// https://vitejs.dev/config/
-export default defineConfig({
- plugins: [react(), TanStackRouterVite()],
-})
diff --git a/img/dashboard-create.png b/img/dashboard-create.png
deleted file mode 100644
index a394141f7b..0000000000
Binary files a/img/dashboard-create.png and /dev/null differ
diff --git a/img/dashboard-dark.png b/img/dashboard-dark.png
deleted file mode 100644
index 51040a157b..0000000000
Binary files a/img/dashboard-dark.png and /dev/null differ
diff --git a/img/dashboard-items.png b/img/dashboard-items.png
deleted file mode 100644
index f50e2e834e..0000000000
Binary files a/img/dashboard-items.png and /dev/null differ
diff --git a/img/dashboard-user-settings.png b/img/dashboard-user-settings.png
deleted file mode 100644
index 8da2e21df7..0000000000
Binary files a/img/dashboard-user-settings.png and /dev/null differ
diff --git a/img/dashboard.png b/img/dashboard.png
deleted file mode 100644
index 0f034d691b..0000000000
Binary files a/img/dashboard.png and /dev/null differ
diff --git a/img/docs.png b/img/docs.png
deleted file mode 100644
index d61c2071c7..0000000000
Binary files a/img/docs.png and /dev/null differ
diff --git a/img/github-social-preview.png b/img/github-social-preview.png
deleted file mode 100644
index f1dc5959fb..0000000000
Binary files a/img/github-social-preview.png and /dev/null differ
diff --git a/img/github-social-preview.svg b/img/github-social-preview.svg
deleted file mode 100644
index 4b7a75760e..0000000000
--- a/img/github-social-preview.svg
+++ /dev/null
@@ -1,100 +0,0 @@
-
-
diff --git a/img/login.png b/img/login.png
deleted file mode 100644
index 66e3a7202f..0000000000
Binary files a/img/login.png and /dev/null differ
diff --git a/scripts/build-push.sh b/scripts/build-push.sh
deleted file mode 100644
index 3fa3aa7e6b..0000000000
--- a/scripts/build-push.sh
+++ /dev/null
@@ -1,10 +0,0 @@
-#! /usr/bin/env sh
-
-# Exit in case of error
-set -e
-
-TAG=${TAG?Variable not set} \
-FRONTEND_ENV=${FRONTEND_ENV-production} \
-sh ./scripts/build.sh
-
-docker-compose -f docker-compose.yml push
diff --git a/scripts/build.sh b/scripts/build.sh
deleted file mode 100644
index 21528c538e..0000000000
--- a/scripts/build.sh
+++ /dev/null
@@ -1,10 +0,0 @@
-#! /usr/bin/env sh
-
-# Exit in case of error
-set -e
-
-TAG=${TAG?Variable not set} \
-FRONTEND_ENV=${FRONTEND_ENV-production} \
-docker-compose \
--f docker-compose.yml \
-build
diff --git a/scripts/generate-client.sh b/scripts/generate-client.sh
deleted file mode 100644
index 1327ee6fd1..0000000000
--- a/scripts/generate-client.sh
+++ /dev/null
@@ -1,8 +0,0 @@
-#! /usr/bin/env bash
-
-PYTHONPATH=backend python -c "import app.main; import json; print(json.dumps(app.main.app.openapi()))" > openapi.json
-node frontend/modify-openapi-operationids.js
-mv openapi.json frontend/
-cd frontend
-npm run generate-client
-npx biome format --write ./src/client