diff --git a/.github/workflows/docker-build.yaml b/.github/workflows/docker-build.yaml
index 5966f3da..80dd2781 100644
--- a/.github/workflows/docker-build.yaml
+++ b/.github/workflows/docker-build.yaml
@@ -1,6 +1,8 @@
name: App Build
on:
push:
+ branches:
+ - dev
paths:
- "**"
pull_request:
@@ -14,3 +16,14 @@ jobs:
- name: Build Docker Image
run: docker build -t learnhouse .
working-directory: .
+ - name: Repository Dispatch
+ uses: peter-evans/repository-dispatch@v2
+ with:
+ token: ${{ secrets.PAT_TOKEN }}
+ repository: learnhouse/images
+ event-type: build-images
+ client-payload: |
+ {
+ "ref": "${{ github.sha }}",
+ "branch": "${{ github.head_ref || github.ref_name }}"
+ }
diff --git a/apps/api/cli.py b/apps/api/cli.py
index 603dbf0b..b4238ac4 100644
--- a/apps/api/cli.py
+++ b/apps/api/cli.py
@@ -48,6 +48,7 @@ def install(
slug="default",
email="",
logo_image="",
+ thumbnail_image="",
)
install_create_organization(org, db_session)
print("Default organization created ✅")
@@ -89,6 +90,7 @@ def install(
slug=slug.lower(),
email="",
logo_image="",
+ thumbnail_image="",
)
install_create_organization(org, db_session)
print(orgname + " Organization created ✅")
diff --git a/apps/api/config/config.py b/apps/api/config/config.py
index 4ff84e96..c9a7bd23 100644
--- a/apps/api/config/config.py
+++ b/apps/api/config/config.py
@@ -71,6 +71,18 @@ class RedisConfig(BaseModel):
redis_connection_string: Optional[str]
+class InternalStripeConfig(BaseModel):
+ stripe_secret_key: str | None
+ stripe_publishable_key: str | None
+ stripe_webhook_standard_secret: str | None
+ stripe_webhook_connect_secret: str | None
+ stripe_client_id: str | None
+
+
+class InternalPaymentsConfig(BaseModel):
+ stripe: InternalStripeConfig
+
+
class LearnHouseConfig(BaseModel):
site_name: str
site_description: str
@@ -82,6 +94,7 @@ class LearnHouseConfig(BaseModel):
security_config: SecurityConfig
ai_config: AIConfig
mailing_config: MailingConfig
+ payments_config: InternalPaymentsConfig
def get_learnhouse_config() -> LearnHouseConfig:
@@ -261,6 +274,33 @@ def get_learnhouse_config() -> LearnHouseConfig:
else:
sentry_config = None
+ # Payments config
+ env_stripe_secret_key = os.environ.get("LEARNHOUSE_STRIPE_SECRET_KEY")
+ env_stripe_publishable_key = os.environ.get("LEARNHOUSE_STRIPE_PUBLISHABLE_KEY")
+ env_stripe_webhook_standard_secret = os.environ.get("LEARNHOUSE_STRIPE_WEBHOOK_STANDARD_SECRET")
+ env_stripe_webhook_connect_secret = os.environ.get("LEARNHOUSE_STRIPE_WEBHOOK_CONNECT_SECRET")
+ env_stripe_client_id = os.environ.get("LEARNHOUSE_STRIPE_CLIENT_ID")
+
+ stripe_secret_key = env_stripe_secret_key or yaml_config.get("payments_config", {}).get(
+ "stripe", {}
+ ).get("stripe_secret_key")
+
+ stripe_publishable_key = env_stripe_publishable_key or yaml_config.get("payments_config", {}).get(
+ "stripe", {}
+ ).get("stripe_publishable_key")
+
+ stripe_webhook_standard_secret = env_stripe_webhook_standard_secret or yaml_config.get("payments_config", {}).get(
+ "stripe", {}
+ ).get("stripe_webhook_standard_secret")
+
+ stripe_webhook_connect_secret = env_stripe_webhook_connect_secret or yaml_config.get("payments_config", {}).get(
+ "stripe", {}
+ ).get("stripe_webhook_connect_secret")
+
+ stripe_client_id = env_stripe_client_id or yaml_config.get("payments_config", {}).get(
+ "stripe", {}
+ ).get("stripe_client_id")
+
# Create HostingConfig and DatabaseConfig objects
hosting_config = HostingConfig(
domain=domain,
@@ -303,6 +343,15 @@ def get_learnhouse_config() -> LearnHouseConfig:
mailing_config=MailingConfig(
resend_api_key=resend_api_key, system_email_address=system_email_address
),
+ payments_config=InternalPaymentsConfig(
+ stripe=InternalStripeConfig(
+ stripe_secret_key=stripe_secret_key,
+ stripe_publishable_key=stripe_publishable_key,
+ stripe_webhook_standard_secret=stripe_webhook_standard_secret,
+ stripe_webhook_connect_secret=stripe_webhook_connect_secret,
+ stripe_client_id=stripe_client_id
+ )
+ )
)
return config
diff --git a/apps/api/config/config.yaml b/apps/api/config/config.yaml
index b9a8c0b6..c9dcec79 100644
--- a/apps/api/config/config.yaml
+++ b/apps/api/config/config.yaml
@@ -37,6 +37,13 @@ database_config:
redis_config:
redis_connection_string: redis://localhost:6379/learnhouse
+payments_config:
+ stripe:
+ stripe_secret_key: ""
+ stripe_publishable_key: ""
+ stripe_webhook_standard_secret: ""
+ stripe_client_id: ""
+
ai_config:
chromadb_config:
isSeparateDatabaseEnabled: True
diff --git a/apps/api/migrations/versions/0314ec7791e1_payments.py b/apps/api/migrations/versions/0314ec7791e1_payments.py
new file mode 100644
index 00000000..91d71a87
--- /dev/null
+++ b/apps/api/migrations/versions/0314ec7791e1_payments.py
@@ -0,0 +1,90 @@
+"""Payments
+
+Revision ID: 0314ec7791e1
+Revises: 040ccb1d456e
+Create Date: 2024-11-23 19:41:14.064680
+
+"""
+from typing import Sequence, Union
+
+from alembic import op
+import sqlalchemy as sa # noqa: F401
+import sqlmodel # noqa: F401
+from sqlalchemy.dialects import postgresql
+
+# revision identifiers, used by Alembic.
+revision: str = '0314ec7791e1'
+down_revision: Union[str, None] = '040ccb1d456e'
+branch_labels: Union[str, Sequence[str], None] = None
+depends_on: Union[str, Sequence[str], None] = None
+
+
+def upgrade() -> None:
+ # ### commands auto generated by Alembic - please adjust! ###
+ op.create_table('paymentsconfig',
+ sa.Column('enabled', sa.Boolean(), nullable=False),
+ sa.Column('active', sa.Boolean(), nullable=False),
+ sa.Column('provider', postgresql.ENUM('STRIPE', name='paymentproviderenum', create_type=False), nullable=False),
+ sa.Column('provider_specific_id', sqlmodel.sql.sqltypes.AutoString(), nullable=True),
+ sa.Column('provider_config', sa.JSON(), nullable=True),
+ sa.Column('id', sa.Integer(), nullable=False),
+ sa.Column('org_id', sa.BigInteger(), nullable=True),
+ sa.Column('creation_date', sa.DateTime(), nullable=False),
+ sa.Column('update_date', sa.DateTime(), nullable=False),
+ sa.ForeignKeyConstraint(['org_id'], ['organization.id'], ondelete='CASCADE'),
+ sa.PrimaryKeyConstraint('id')
+ )
+ op.create_table('paymentsproduct',
+ sa.Column('name', sqlmodel.sql.sqltypes.AutoString(), nullable=False),
+ sa.Column('description', sqlmodel.sql.sqltypes.AutoString(), nullable=True),
+ sa.Column('product_type', postgresql.ENUM('SUBSCRIPTION', 'ONE_TIME', name='paymentproducttypeenum', create_type=False), nullable=False),
+ sa.Column('price_type', postgresql.ENUM('CUSTOMER_CHOICE', 'FIXED_PRICE', name='paymentpricetypeenum', create_type=False), nullable=False),
+ sa.Column('benefits', sqlmodel.sql.sqltypes.AutoString(), nullable=False),
+ sa.Column('amount', sa.Float(), nullable=False),
+ sa.Column('currency', sqlmodel.sql.sqltypes.AutoString(), nullable=False),
+ sa.Column('id', sa.Integer(), nullable=False),
+ sa.Column('org_id', sa.BigInteger(), nullable=True),
+ sa.Column('payments_config_id', sa.BigInteger(), nullable=True),
+ sa.Column('provider_product_id', sa.String(), nullable=True),
+ sa.Column('creation_date', sa.DateTime(), nullable=False),
+ sa.Column('update_date', sa.DateTime(), nullable=False),
+ sa.ForeignKeyConstraint(['org_id'], ['organization.id'], ondelete='CASCADE'),
+ sa.ForeignKeyConstraint(['payments_config_id'], ['paymentsconfig.id'], ondelete='CASCADE'),
+ sa.PrimaryKeyConstraint('id')
+ )
+ op.create_table('paymentscourse',
+ sa.Column('course_id', sa.BigInteger(), nullable=True),
+ sa.Column('id', sa.Integer(), nullable=False),
+ sa.Column('payment_product_id', sa.BigInteger(), nullable=True),
+ sa.Column('org_id', sa.BigInteger(), nullable=True),
+ sa.Column('creation_date', sa.DateTime(), nullable=False),
+ sa.Column('update_date', sa.DateTime(), nullable=False),
+ sa.ForeignKeyConstraint(['course_id'], ['course.id'], ondelete='CASCADE'),
+ sa.ForeignKeyConstraint(['org_id'], ['organization.id'], ondelete='CASCADE'),
+ sa.ForeignKeyConstraint(['payment_product_id'], ['paymentsproduct.id'], ondelete='CASCADE'),
+ sa.PrimaryKeyConstraint('id')
+ )
+ op.create_table('paymentsuser',
+ sa.Column('status', postgresql.ENUM('PENDING', 'COMPLETED', 'ACTIVE', 'CANCELLED', 'FAILED', 'REFUNDED', name='paymentstatusenum', create_type=False), nullable=False),
+ sa.Column('provider_specific_data', sa.JSON(), nullable=True),
+ sa.Column('id', sa.Integer(), nullable=False),
+ sa.Column('user_id', sa.BigInteger(), nullable=True),
+ sa.Column('org_id', sa.BigInteger(), nullable=True),
+ sa.Column('payment_product_id', sa.BigInteger(), nullable=True),
+ sa.Column('creation_date', sa.DateTime(), nullable=False),
+ sa.Column('update_date', sa.DateTime(), nullable=False),
+ sa.ForeignKeyConstraint(['org_id'], ['organization.id'], ondelete='CASCADE'),
+ sa.ForeignKeyConstraint(['payment_product_id'], ['paymentsproduct.id'], ondelete='CASCADE'),
+ sa.ForeignKeyConstraint(['user_id'], ['user.id'], ondelete='CASCADE'),
+ sa.PrimaryKeyConstraint('id')
+ )
+ # ### end Alembic commands ###
+
+
+def downgrade() -> None:
+ # ### commands auto generated by Alembic - please adjust! ###
+ op.drop_table('paymentsuser')
+ op.drop_table('paymentscourse')
+ op.drop_table('paymentsproduct')
+ op.drop_table('paymentsconfig')
+ # ### end Alembic commands ###
diff --git a/apps/api/poetry.lock b/apps/api/poetry.lock
index e5ad5e70..7a4f2827 100644
--- a/apps/api/poetry.lock
+++ b/apps/api/poetry.lock
@@ -1,4 +1,4 @@
-# This file is automatically @generated by Poetry 1.8.4 and should not be changed by hand.
+# This file is automatically @generated by Poetry 1.8.3 and should not be changed by hand.
[[package]]
name = "aiohappyeyeballs"
@@ -276,17 +276,17 @@ typecheck = ["mypy"]
[[package]]
name = "boto3"
-version = "1.35.49"
+version = "1.35.52"
description = "The AWS SDK for Python"
optional = false
python-versions = ">=3.8"
files = [
- {file = "boto3-1.35.49-py3-none-any.whl", hash = "sha256:b660c649a27a6b47a34f6f858f5bd7c3b0a798a16dec8dda7cbebeee80fd1f60"},
- {file = "boto3-1.35.49.tar.gz", hash = "sha256:ddecb27f5699ca9f97711c52b6c0652c2e63bf6c2bfbc13b819b4f523b4d30ff"},
+ {file = "boto3-1.35.52-py3-none-any.whl", hash = "sha256:ec0e797441db56af63b1150bba49f114b0f885f5d76c3b6dc18075f73030d2bb"},
+ {file = "boto3-1.35.52.tar.gz", hash = "sha256:68299da8ab2bb37cc843d61b9f4c1c9367438406cfd65a8f593afc7b3bfe226d"},
]
[package.dependencies]
-botocore = ">=1.35.49,<1.36.0"
+botocore = ">=1.35.52,<1.36.0"
jmespath = ">=0.7.1,<2.0.0"
s3transfer = ">=0.10.0,<0.11.0"
@@ -295,13 +295,13 @@ crt = ["botocore[crt] (>=1.21.0,<2.0a0)"]
[[package]]
name = "botocore"
-version = "1.35.49"
+version = "1.35.52"
description = "Low-level, data-driven core of boto 3."
optional = false
python-versions = ">=3.8"
files = [
- {file = "botocore-1.35.49-py3-none-any.whl", hash = "sha256:aed4d3643afd702920792b68fbe712a8c3847993820d1048cd238a6469354da1"},
- {file = "botocore-1.35.49.tar.gz", hash = "sha256:07d0c1325fdbfa49a4a054413dbdeab0a6030449b2aa66099241af2dac48afd8"},
+ {file = "botocore-1.35.52-py3-none-any.whl", hash = "sha256:cdbb5e43c9c3a977763e2a10d3b8b9c405d51279f9fcfd4ca4800763b22acba5"},
+ {file = "botocore-1.35.52.tar.gz", hash = "sha256:1fe7485ea13d638b089103addd818c12984ff1e4d208de15f180b1e25ad944c5"},
]
[package.dependencies]
@@ -514,13 +514,13 @@ numpy = "*"
[[package]]
name = "chromadb"
-version = "0.5.11"
+version = "0.5.16"
description = "Chroma."
optional = false
python-versions = ">=3.8"
files = [
- {file = "chromadb-0.5.11-py3-none-any.whl", hash = "sha256:f02d9326869cea926f980bd6c9a0150a0ef2e151072f325998c16a9502fb4b25"},
- {file = "chromadb-0.5.11.tar.gz", hash = "sha256:252e970b3e1a27b594cc7b3685238691bf8eaa232225d4dee9e33ec83580775f"},
+ {file = "chromadb-0.5.16-py3-none-any.whl", hash = "sha256:ae96f1c81fa691a163a2d625dc769c5c1afa3219d1ac26796fbf9d60d7924d71"},
+ {file = "chromadb-0.5.16.tar.gz", hash = "sha256:ab947065125908b228cc343e7d9f21bcea5036dcd237d993caa66e5fc262dd9e"},
]
[package.dependencies]
@@ -1053,70 +1053,70 @@ test = ["objgraph", "psutil"]
[[package]]
name = "grpcio"
-version = "1.67.0"
+version = "1.67.1"
description = "HTTP/2-based RPC framework"
optional = false
python-versions = ">=3.8"
files = [
- {file = "grpcio-1.67.0-cp310-cp310-linux_armv7l.whl", hash = "sha256:bd79929b3bb96b54df1296cd3bf4d2b770bd1df6c2bdf549b49bab286b925cdc"},
- {file = "grpcio-1.67.0-cp310-cp310-macosx_12_0_universal2.whl", hash = "sha256:16724ffc956ea42967f5758c2f043faef43cb7e48a51948ab593570570d1e68b"},
- {file = "grpcio-1.67.0-cp310-cp310-manylinux_2_17_aarch64.whl", hash = "sha256:2b7183c80b602b0ad816315d66f2fb7887614ead950416d60913a9a71c12560d"},
- {file = "grpcio-1.67.0-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:efe32b45dd6d118f5ea2e5deaed417d8a14976325c93812dd831908522b402c9"},
- {file = "grpcio-1.67.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fe89295219b9c9e47780a0f1c75ca44211e706d1c598242249fe717af3385ec8"},
- {file = "grpcio-1.67.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:aa8d025fae1595a207b4e47c2e087cb88d47008494db258ac561c00877d4c8f8"},
- {file = "grpcio-1.67.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:f95e15db43e75a534420e04822df91f645664bf4ad21dfaad7d51773c80e6bb4"},
- {file = "grpcio-1.67.0-cp310-cp310-win32.whl", hash = "sha256:a6b9a5c18863fd4b6624a42e2712103fb0f57799a3b29651c0e5b8119a519d65"},
- {file = "grpcio-1.67.0-cp310-cp310-win_amd64.whl", hash = "sha256:b6eb68493a05d38b426604e1dc93bfc0137c4157f7ab4fac5771fd9a104bbaa6"},
- {file = "grpcio-1.67.0-cp311-cp311-linux_armv7l.whl", hash = "sha256:e91d154689639932305b6ea6f45c6e46bb51ecc8ea77c10ef25aa77f75443ad4"},
- {file = "grpcio-1.67.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:cb204a742997277da678611a809a8409657b1398aaeebf73b3d9563b7d154c13"},
- {file = "grpcio-1.67.0-cp311-cp311-manylinux_2_17_aarch64.whl", hash = "sha256:ae6de510f670137e755eb2a74b04d1041e7210af2444103c8c95f193340d17ee"},
- {file = "grpcio-1.67.0-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:74b900566bdf68241118f2918d312d3bf554b2ce0b12b90178091ea7d0a17b3d"},
- {file = "grpcio-1.67.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a4e95e43447a02aa603abcc6b5e727d093d161a869c83b073f50b9390ecf0fa8"},
- {file = "grpcio-1.67.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:0bb94e66cd8f0baf29bd3184b6aa09aeb1a660f9ec3d85da615c5003154bc2bf"},
- {file = "grpcio-1.67.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:82e5bd4b67b17c8c597273663794a6a46a45e44165b960517fe6d8a2f7f16d23"},
- {file = "grpcio-1.67.0-cp311-cp311-win32.whl", hash = "sha256:7fc1d2b9fd549264ae585026b266ac2db53735510a207381be509c315b4af4e8"},
- {file = "grpcio-1.67.0-cp311-cp311-win_amd64.whl", hash = "sha256:ac11ecb34a86b831239cc38245403a8de25037b448464f95c3315819e7519772"},
- {file = "grpcio-1.67.0-cp312-cp312-linux_armv7l.whl", hash = "sha256:227316b5631260e0bef8a3ce04fa7db4cc81756fea1258b007950b6efc90c05d"},
- {file = "grpcio-1.67.0-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:d90cfdafcf4b45a7a076e3e2a58e7bc3d59c698c4f6470b0bb13a4d869cf2273"},
- {file = "grpcio-1.67.0-cp312-cp312-manylinux_2_17_aarch64.whl", hash = "sha256:77196216d5dd6f99af1c51e235af2dd339159f657280e65ce7e12c1a8feffd1d"},
- {file = "grpcio-1.67.0-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:15c05a26a0f7047f720da41dc49406b395c1470eef44ff7e2c506a47ac2c0591"},
- {file = "grpcio-1.67.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3840994689cc8cbb73d60485c594424ad8adb56c71a30d8948d6453083624b52"},
- {file = "grpcio-1.67.0-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:5a1e03c3102b6451028d5dc9f8591131d6ab3c8a0e023d94c28cb930ed4b5f81"},
- {file = "grpcio-1.67.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:682968427a63d898759474e3b3178d42546e878fdce034fd7474ef75143b64e3"},
- {file = "grpcio-1.67.0-cp312-cp312-win32.whl", hash = "sha256:d01793653248f49cf47e5695e0a79805b1d9d4eacef85b310118ba1dfcd1b955"},
- {file = "grpcio-1.67.0-cp312-cp312-win_amd64.whl", hash = "sha256:985b2686f786f3e20326c4367eebdaed3e7aa65848260ff0c6644f817042cb15"},
- {file = "grpcio-1.67.0-cp313-cp313-linux_armv7l.whl", hash = "sha256:8c9a35b8bc50db35ab8e3e02a4f2a35cfba46c8705c3911c34ce343bd777813a"},
- {file = "grpcio-1.67.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:42199e704095b62688998c2d84c89e59a26a7d5d32eed86d43dc90e7a3bd04aa"},
- {file = "grpcio-1.67.0-cp313-cp313-manylinux_2_17_aarch64.whl", hash = "sha256:c4c425f440fb81f8d0237c07b9322fc0fb6ee2b29fbef5f62a322ff8fcce240d"},
- {file = "grpcio-1.67.0-cp313-cp313-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:323741b6699cd2b04a71cb38f502db98f90532e8a40cb675393d248126a268af"},
- {file = "grpcio-1.67.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:662c8e105c5e5cee0317d500eb186ed7a93229586e431c1bf0c9236c2407352c"},
- {file = "grpcio-1.67.0-cp313-cp313-musllinux_1_1_i686.whl", hash = "sha256:f6bd2ab135c64a4d1e9e44679a616c9bc944547357c830fafea5c3caa3de5153"},
- {file = "grpcio-1.67.0-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:2f55c1e0e2ae9bdd23b3c63459ee4c06d223b68aeb1961d83c48fb63dc29bc03"},
- {file = "grpcio-1.67.0-cp313-cp313-win32.whl", hash = "sha256:fd6bc27861e460fe28e94226e3673d46e294ca4673d46b224428d197c5935e69"},
- {file = "grpcio-1.67.0-cp313-cp313-win_amd64.whl", hash = "sha256:cf51d28063338608cd8d3cd64677e922134837902b70ce00dad7f116e3998210"},
- {file = "grpcio-1.67.0-cp38-cp38-linux_armv7l.whl", hash = "sha256:7f200aca719c1c5dc72ab68be3479b9dafccdf03df530d137632c534bb6f1ee3"},
- {file = "grpcio-1.67.0-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:0892dd200ece4822d72dd0952f7112c542a487fc48fe77568deaaa399c1e717d"},
- {file = "grpcio-1.67.0-cp38-cp38-manylinux_2_17_aarch64.whl", hash = "sha256:f4d613fbf868b2e2444f490d18af472ccb47660ea3df52f068c9c8801e1f3e85"},
- {file = "grpcio-1.67.0-cp38-cp38-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0c69bf11894cad9da00047f46584d5758d6ebc9b5950c0dc96fec7e0bce5cde9"},
- {file = "grpcio-1.67.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b9bca3ca0c5e74dea44bf57d27e15a3a3996ce7e5780d61b7c72386356d231db"},
- {file = "grpcio-1.67.0-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:014dfc020e28a0d9be7e93a91f85ff9f4a87158b7df9952fe23cc42d29d31e1e"},
- {file = "grpcio-1.67.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:d4ea4509d42c6797539e9ec7496c15473177ce9abc89bc5c71e7abe50fc25737"},
- {file = "grpcio-1.67.0-cp38-cp38-win32.whl", hash = "sha256:9d75641a2fca9ae1ae86454fd25d4c298ea8cc195dbc962852234d54a07060ad"},
- {file = "grpcio-1.67.0-cp38-cp38-win_amd64.whl", hash = "sha256:cff8e54d6a463883cda2fab94d2062aad2f5edd7f06ae3ed030f2a74756db365"},
- {file = "grpcio-1.67.0-cp39-cp39-linux_armv7l.whl", hash = "sha256:62492bd534979e6d7127b8a6b29093161a742dee3875873e01964049d5250a74"},
- {file = "grpcio-1.67.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:eef1dce9d1a46119fd09f9a992cf6ab9d9178b696382439446ca5f399d7b96fe"},
- {file = "grpcio-1.67.0-cp39-cp39-manylinux_2_17_aarch64.whl", hash = "sha256:f623c57a5321461c84498a99dddf9d13dac0e40ee056d884d6ec4ebcab647a78"},
- {file = "grpcio-1.67.0-cp39-cp39-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:54d16383044e681f8beb50f905249e4e7261dd169d4aaf6e52eab67b01cbbbe2"},
- {file = "grpcio-1.67.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b2a44e572fb762c668e4812156b81835f7aba8a721b027e2d4bb29fb50ff4d33"},
- {file = "grpcio-1.67.0-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:391df8b0faac84d42f5b8dfc65f5152c48ed914e13c522fd05f2aca211f8bfad"},
- {file = "grpcio-1.67.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:cfd9306511fdfc623a1ba1dc3bc07fbd24e6cfbe3c28b4d1e05177baa2f99617"},
- {file = "grpcio-1.67.0-cp39-cp39-win32.whl", hash = "sha256:30d47dbacfd20cbd0c8be9bfa52fdb833b395d4ec32fe5cff7220afc05d08571"},
- {file = "grpcio-1.67.0-cp39-cp39-win_amd64.whl", hash = "sha256:f55f077685f61f0fbd06ea355142b71e47e4a26d2d678b3ba27248abfe67163a"},
- {file = "grpcio-1.67.0.tar.gz", hash = "sha256:e090b2553e0da1c875449c8e75073dd4415dd71c9bde6a406240fdf4c0ee467c"},
+ {file = "grpcio-1.67.1-cp310-cp310-linux_armv7l.whl", hash = "sha256:8b0341d66a57f8a3119b77ab32207072be60c9bf79760fa609c5609f2deb1f3f"},
+ {file = "grpcio-1.67.1-cp310-cp310-macosx_12_0_universal2.whl", hash = "sha256:f5a27dddefe0e2357d3e617b9079b4bfdc91341a91565111a21ed6ebbc51b22d"},
+ {file = "grpcio-1.67.1-cp310-cp310-manylinux_2_17_aarch64.whl", hash = "sha256:43112046864317498a33bdc4797ae6a268c36345a910de9b9c17159d8346602f"},
+ {file = "grpcio-1.67.1-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c9b929f13677b10f63124c1a410994a401cdd85214ad83ab67cc077fc7e480f0"},
+ {file = "grpcio-1.67.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e7d1797a8a3845437d327145959a2c0c47c05947c9eef5ff1a4c80e499dcc6fa"},
+ {file = "grpcio-1.67.1-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:0489063974d1452436139501bf6b180f63d4977223ee87488fe36858c5725292"},
+ {file = "grpcio-1.67.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:9fd042de4a82e3e7aca44008ee2fb5da01b3e5adb316348c21980f7f58adc311"},
+ {file = "grpcio-1.67.1-cp310-cp310-win32.whl", hash = "sha256:638354e698fd0c6c76b04540a850bf1db27b4d2515a19fcd5cf645c48d3eb1ed"},
+ {file = "grpcio-1.67.1-cp310-cp310-win_amd64.whl", hash = "sha256:608d87d1bdabf9e2868b12338cd38a79969eaf920c89d698ead08f48de9c0f9e"},
+ {file = "grpcio-1.67.1-cp311-cp311-linux_armv7l.whl", hash = "sha256:7818c0454027ae3384235a65210bbf5464bd715450e30a3d40385453a85a70cb"},
+ {file = "grpcio-1.67.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:ea33986b70f83844cd00814cee4451055cd8cab36f00ac64a31f5bb09b31919e"},
+ {file = "grpcio-1.67.1-cp311-cp311-manylinux_2_17_aarch64.whl", hash = "sha256:c7a01337407dd89005527623a4a72c5c8e2894d22bead0895306b23c6695698f"},
+ {file = "grpcio-1.67.1-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:80b866f73224b0634f4312a4674c1be21b2b4afa73cb20953cbbb73a6b36c3cc"},
+ {file = "grpcio-1.67.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f9fff78ba10d4250bfc07a01bd6254a6d87dc67f9627adece85c0b2ed754fa96"},
+ {file = "grpcio-1.67.1-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:8a23cbcc5bb11ea7dc6163078be36c065db68d915c24f5faa4f872c573bb400f"},
+ {file = "grpcio-1.67.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:1a65b503d008f066e994f34f456e0647e5ceb34cfcec5ad180b1b44020ad4970"},
+ {file = "grpcio-1.67.1-cp311-cp311-win32.whl", hash = "sha256:e29ca27bec8e163dca0c98084040edec3bc49afd10f18b412f483cc68c712744"},
+ {file = "grpcio-1.67.1-cp311-cp311-win_amd64.whl", hash = "sha256:786a5b18544622bfb1e25cc08402bd44ea83edfb04b93798d85dca4d1a0b5be5"},
+ {file = "grpcio-1.67.1-cp312-cp312-linux_armv7l.whl", hash = "sha256:267d1745894200e4c604958da5f856da6293f063327cb049a51fe67348e4f953"},
+ {file = "grpcio-1.67.1-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:85f69fdc1d28ce7cff8de3f9c67db2b0ca9ba4449644488c1e0303c146135ddb"},
+ {file = "grpcio-1.67.1-cp312-cp312-manylinux_2_17_aarch64.whl", hash = "sha256:f26b0b547eb8d00e195274cdfc63ce64c8fc2d3e2d00b12bf468ece41a0423a0"},
+ {file = "grpcio-1.67.1-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4422581cdc628f77302270ff839a44f4c24fdc57887dc2a45b7e53d8fc2376af"},
+ {file = "grpcio-1.67.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1d7616d2ded471231c701489190379e0c311ee0a6c756f3c03e6a62b95a7146e"},
+ {file = "grpcio-1.67.1-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:8a00efecde9d6fcc3ab00c13f816313c040a28450e5e25739c24f432fc6d3c75"},
+ {file = "grpcio-1.67.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:699e964923b70f3101393710793289e42845791ea07565654ada0969522d0a38"},
+ {file = "grpcio-1.67.1-cp312-cp312-win32.whl", hash = "sha256:4e7b904484a634a0fff132958dabdb10d63e0927398273917da3ee103e8d1f78"},
+ {file = "grpcio-1.67.1-cp312-cp312-win_amd64.whl", hash = "sha256:5721e66a594a6c4204458004852719b38f3d5522082be9061d6510b455c90afc"},
+ {file = "grpcio-1.67.1-cp313-cp313-linux_armv7l.whl", hash = "sha256:aa0162e56fd10a5547fac8774c4899fc3e18c1aa4a4759d0ce2cd00d3696ea6b"},
+ {file = "grpcio-1.67.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:beee96c8c0b1a75d556fe57b92b58b4347c77a65781ee2ac749d550f2a365dc1"},
+ {file = "grpcio-1.67.1-cp313-cp313-manylinux_2_17_aarch64.whl", hash = "sha256:a93deda571a1bf94ec1f6fcda2872dad3ae538700d94dc283c672a3b508ba3af"},
+ {file = "grpcio-1.67.1-cp313-cp313-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0e6f255980afef598a9e64a24efce87b625e3e3c80a45162d111a461a9f92955"},
+ {file = "grpcio-1.67.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9e838cad2176ebd5d4a8bb03955138d6589ce9e2ce5d51c3ada34396dbd2dba8"},
+ {file = "grpcio-1.67.1-cp313-cp313-musllinux_1_1_i686.whl", hash = "sha256:a6703916c43b1d468d0756c8077b12017a9fcb6a1ef13faf49e67d20d7ebda62"},
+ {file = "grpcio-1.67.1-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:917e8d8994eed1d86b907ba2a61b9f0aef27a2155bca6cbb322430fc7135b7bb"},
+ {file = "grpcio-1.67.1-cp313-cp313-win32.whl", hash = "sha256:e279330bef1744040db8fc432becc8a727b84f456ab62b744d3fdb83f327e121"},
+ {file = "grpcio-1.67.1-cp313-cp313-win_amd64.whl", hash = "sha256:fa0c739ad8b1996bd24823950e3cb5152ae91fca1c09cc791190bf1627ffefba"},
+ {file = "grpcio-1.67.1-cp38-cp38-linux_armv7l.whl", hash = "sha256:178f5db771c4f9a9facb2ab37a434c46cb9be1a75e820f187ee3d1e7805c4f65"},
+ {file = "grpcio-1.67.1-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:0f3e49c738396e93b7ba9016e153eb09e0778e776df6090c1b8c91877cc1c426"},
+ {file = "grpcio-1.67.1-cp38-cp38-manylinux_2_17_aarch64.whl", hash = "sha256:24e8a26dbfc5274d7474c27759b54486b8de23c709d76695237515bc8b5baeab"},
+ {file = "grpcio-1.67.1-cp38-cp38-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3b6c16489326d79ead41689c4b84bc40d522c9a7617219f4ad94bc7f448c5085"},
+ {file = "grpcio-1.67.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:60e6a4dcf5af7bbc36fd9f81c9f372e8ae580870a9e4b6eafe948cd334b81cf3"},
+ {file = "grpcio-1.67.1-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:95b5f2b857856ed78d72da93cd7d09b6db8ef30102e5e7fe0961fe4d9f7d48e8"},
+ {file = "grpcio-1.67.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:b49359977c6ec9f5d0573ea4e0071ad278ef905aa74e420acc73fd28ce39e9ce"},
+ {file = "grpcio-1.67.1-cp38-cp38-win32.whl", hash = "sha256:f5b76ff64aaac53fede0cc93abf57894ab2a7362986ba22243d06218b93efe46"},
+ {file = "grpcio-1.67.1-cp38-cp38-win_amd64.whl", hash = "sha256:804c6457c3cd3ec04fe6006c739579b8d35c86ae3298ffca8de57b493524b771"},
+ {file = "grpcio-1.67.1-cp39-cp39-linux_armv7l.whl", hash = "sha256:a25bdea92b13ff4d7790962190bf6bf5c4639876e01c0f3dda70fc2769616335"},
+ {file = "grpcio-1.67.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:cdc491ae35a13535fd9196acb5afe1af37c8237df2e54427be3eecda3653127e"},
+ {file = "grpcio-1.67.1-cp39-cp39-manylinux_2_17_aarch64.whl", hash = "sha256:85f862069b86a305497e74d0dc43c02de3d1d184fc2c180993aa8aa86fbd19b8"},
+ {file = "grpcio-1.67.1-cp39-cp39-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ec74ef02010186185de82cc594058a3ccd8d86821842bbac9873fd4a2cf8be8d"},
+ {file = "grpcio-1.67.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:01f616a964e540638af5130469451cf580ba8c7329f45ca998ab66e0c7dcdb04"},
+ {file = "grpcio-1.67.1-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:299b3d8c4f790c6bcca485f9963b4846dd92cf6f1b65d3697145d005c80f9fe8"},
+ {file = "grpcio-1.67.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:60336bff760fbb47d7e86165408126f1dded184448e9a4c892189eb7c9d3f90f"},
+ {file = "grpcio-1.67.1-cp39-cp39-win32.whl", hash = "sha256:5ed601c4c6008429e3d247ddb367fe8c7259c355757448d7c1ef7bd4a6739e8e"},
+ {file = "grpcio-1.67.1-cp39-cp39-win_amd64.whl", hash = "sha256:5db70d32d6703b89912af16d6d45d78406374a8b8ef0d28140351dd0ec610e98"},
+ {file = "grpcio-1.67.1.tar.gz", hash = "sha256:3dc2ed4cabea4dc14d5e708c2b426205956077cc5de419b4d4079315017e9732"},
]
[package.extras]
-protobuf = ["grpcio-tools (>=1.67.0)"]
+protobuf = ["grpcio-tools (>=1.67.1)"]
[[package]]
name = "h11"
@@ -1232,13 +1232,13 @@ zstd = ["zstandard (>=0.18.0)"]
[[package]]
name = "huggingface-hub"
-version = "0.26.1"
+version = "0.26.2"
description = "Client library to download and publish models, datasets and other repos on the huggingface.co hub"
optional = false
python-versions = ">=3.8.0"
files = [
- {file = "huggingface_hub-0.26.1-py3-none-any.whl", hash = "sha256:5927a8fc64ae68859cd954b7cc29d1c8390a5e15caba6d3d349c973be8fdacf3"},
- {file = "huggingface_hub-0.26.1.tar.gz", hash = "sha256:414c0d9b769eecc86c70f9d939d0f48bb28e8461dd1130021542eff0212db890"},
+ {file = "huggingface_hub-0.26.2-py3-none-any.whl", hash = "sha256:98c2a5a8e786c7b2cb6fdeb2740893cba4d53e312572ed3d8afafda65b128c46"},
+ {file = "huggingface_hub-0.26.2.tar.gz", hash = "sha256:b100d853465d965733964d123939ba287da60a547087783ddff8a323f340332b"},
]
[package.dependencies]
@@ -1599,13 +1599,13 @@ tenacity = ">=8.1.0,<9.0.0"
[[package]]
name = "langchain-core"
-version = "0.2.41"
+version = "0.2.42"
description = "Building applications with LLMs through composability"
optional = false
python-versions = "<4.0,>=3.8.1"
files = [
- {file = "langchain_core-0.2.41-py3-none-any.whl", hash = "sha256:3278fda5ba9a05defae8bb19f1226032add6aab21917db7b3bc74e750e263e84"},
- {file = "langchain_core-0.2.41.tar.gz", hash = "sha256:bc12032c5a298d85be754ccb129bc13ea21ccb1d6e22f8d7ba18b8da64315bb5"},
+ {file = "langchain_core-0.2.42-py3-none-any.whl", hash = "sha256:09503fdfb9efa163e51f2d9762894fde04797d0a41462c0e6072ef78028e48fd"},
+ {file = "langchain_core-0.2.42.tar.gz", hash = "sha256:e4ea04b22bd6398048d0ef97cd3132fbdd80e6c749863ee96e6b7c88502ff913"},
]
[package.dependencies]
@@ -1698,13 +1698,13 @@ requests = ">=2,<3"
[[package]]
name = "langsmith"
-version = "0.1.137"
+version = "0.1.138"
description = "Client library to connect to the LangSmith LLM Tracing and Evaluation Platform."
optional = false
python-versions = "<4.0,>=3.8.1"
files = [
- {file = "langsmith-0.1.137-py3-none-any.whl", hash = "sha256:4256d5c61133749890f7b5c88321dbb133ce0f440c621ea28e76513285859b81"},
- {file = "langsmith-0.1.137.tar.gz", hash = "sha256:56cdfcc6c74cb20a3f437d5bd144feb5bf93f54c5a2918d1e568cbd084a372d4"},
+ {file = "langsmith-0.1.138-py3-none-any.whl", hash = "sha256:5c2bd5c11c75f7b3d06a0f06b115186e7326ca969fd26d66ffc65a0669012aee"},
+ {file = "langsmith-0.1.138.tar.gz", hash = "sha256:1ecf613bb52f6bf17f1510e24ad8b70d4b0259bc9d3dbfd69b648c66d4644f0b"},
]
[package.dependencies]
@@ -2215,13 +2215,13 @@ sympy = "*"
[[package]]
name = "openai"
-version = "1.52.2"
+version = "1.53.0"
description = "The official Python library for the openai API"
optional = false
python-versions = ">=3.7.1"
files = [
- {file = "openai-1.52.2-py3-none-any.whl", hash = "sha256:57e9e37bc407f39bb6ec3a27d7e8fb9728b2779936daa1fcf95df17d3edfaccc"},
- {file = "openai-1.52.2.tar.gz", hash = "sha256:87b7d0f69d85f5641678d414b7ee3082363647a5c66a462ed7f3ccb59582da0d"},
+ {file = "openai-1.53.0-py3-none-any.whl", hash = "sha256:20f408c32fc5cb66e60c6882c994cdca580a5648e10045cd840734194f033418"},
+ {file = "openai-1.53.0.tar.gz", hash = "sha256:be2c4e77721b166cce8130e544178b7d579f751b4b074ffbaade3854b6f85ec5"},
]
[package.dependencies]
@@ -3332,23 +3332,23 @@ tornado = ["tornado (>=6)"]
[[package]]
name = "setuptools"
-version = "75.2.0"
+version = "75.3.0"
description = "Easily download, build, install, upgrade, and uninstall Python packages"
optional = false
python-versions = ">=3.8"
files = [
- {file = "setuptools-75.2.0-py3-none-any.whl", hash = "sha256:a7fcb66f68b4d9e8e66b42f9876150a3371558f98fa32222ffaa5bced76406f8"},
- {file = "setuptools-75.2.0.tar.gz", hash = "sha256:753bb6ebf1f465a1912e19ed1d41f403a79173a9acf66a42e7e6aec45c3c16ec"},
+ {file = "setuptools-75.3.0-py3-none-any.whl", hash = "sha256:f2504966861356aa38616760c0f66568e535562374995367b4e69c7143cf6bcd"},
+ {file = "setuptools-75.3.0.tar.gz", hash = "sha256:fba5dd4d766e97be1b1681d98712680ae8f2f26d7881245f2ce9e40714f1a686"},
]
[package.extras]
check = ["pytest-checkdocs (>=2.4)", "pytest-ruff (>=0.2.1)", "ruff (>=0.5.2)"]
-core = ["importlib-metadata (>=6)", "importlib-resources (>=5.10.2)", "jaraco.collections", "jaraco.functools", "jaraco.text (>=3.7)", "more-itertools", "more-itertools (>=8.8)", "packaging", "packaging (>=24)", "platformdirs (>=2.6.2)", "tomli (>=2.0.1)", "wheel (>=0.43.0)"]
+core = ["importlib-metadata (>=6)", "importlib-resources (>=5.10.2)", "jaraco.collections", "jaraco.functools", "jaraco.text (>=3.7)", "more-itertools", "more-itertools (>=8.8)", "packaging", "packaging (>=24)", "platformdirs (>=4.2.2)", "tomli (>=2.0.1)", "wheel (>=0.43.0)"]
cover = ["pytest-cov"]
doc = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "pygments-github-lexers (==0.0.5)", "pyproject-hooks (!=1.1)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-favicon", "sphinx-inline-tabs", "sphinx-lint", "sphinx-notfound-page (>=1,<2)", "sphinx-reredirects", "sphinxcontrib-towncrier", "towncrier (<24.7)"]
enabler = ["pytest-enabler (>=2.2)"]
-test = ["build[virtualenv] (>=1.0.3)", "filelock (>=3.4.0)", "ini2toml[lite] (>=0.14)", "jaraco.develop (>=7.21)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "jaraco.test", "packaging (>=23.2)", "pip (>=19.1)", "pyproject-hooks (!=1.1)", "pytest (>=6,!=8.1.*)", "pytest-home (>=0.5)", "pytest-perf", "pytest-subprocess", "pytest-timeout", "pytest-xdist (>=3)", "tomli-w (>=1.0.0)", "virtualenv (>=13.0.0)", "wheel (>=0.44.0)"]
-type = ["importlib-metadata (>=7.0.2)", "jaraco.develop (>=7.21)", "mypy (==1.11.*)", "pytest-mypy"]
+test = ["build[virtualenv] (>=1.0.3)", "filelock (>=3.4.0)", "ini2toml[lite] (>=0.14)", "jaraco.develop (>=7.21)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "jaraco.test (>=5.5)", "packaging (>=23.2)", "pip (>=19.1)", "pyproject-hooks (!=1.1)", "pytest (>=6,!=8.1.*)", "pytest-home (>=0.5)", "pytest-perf", "pytest-subprocess", "pytest-timeout", "pytest-xdist (>=3)", "tomli-w (>=1.0.0)", "virtualenv (>=13.0.0)", "wheel (>=0.44.0)"]
+type = ["importlib-metadata (>=7.0.2)", "jaraco.develop (>=7.21)", "mypy (==1.12.*)", "pytest-mypy"]
[[package]]
name = "shellingham"
@@ -3538,6 +3538,21 @@ anyio = ">=3.4.0,<5"
[package.extras]
full = ["httpx (>=0.22.0)", "itsdangerous", "jinja2", "python-multipart (>=0.0.7)", "pyyaml"]
+[[package]]
+name = "stripe"
+version = "11.2.0"
+description = "Python bindings for the Stripe API"
+optional = false
+python-versions = ">=3.6"
+files = [
+ {file = "stripe-11.2.0-py2.py3-none-any.whl", hash = "sha256:dec812eabc95488862be40e6c799acdaf2e1225d686490a793f949fab745fdd0"},
+ {file = "stripe-11.2.0.tar.gz", hash = "sha256:4c53d61d7b596070324bfa5d7215843145fe5466e48973d828aab41ad209b5ce"},
+]
+
+[package.dependencies]
+requests = {version = ">=2.20", markers = "python_version >= \"3.0\""}
+typing-extensions = {version = ">=4.5.0", markers = "python_version >= \"3.7\""}
+
[[package]]
name = "sympy"
version = "1.13.3"
@@ -3741,13 +3756,13 @@ testing = ["black (==22.3)", "datasets", "numpy", "pytest", "requests", "ruff"]
[[package]]
name = "tqdm"
-version = "4.66.5"
+version = "4.66.6"
description = "Fast, Extensible Progress Meter"
optional = false
python-versions = ">=3.7"
files = [
- {file = "tqdm-4.66.5-py3-none-any.whl", hash = "sha256:90279a3770753eafc9194a0364852159802111925aa30eb3f9d85b0e805ac7cd"},
- {file = "tqdm-4.66.5.tar.gz", hash = "sha256:e1020aef2e5096702d8a025ac7d16b1577279c9d63f8375b63083e9a5f0fcbad"},
+ {file = "tqdm-4.66.6-py3-none-any.whl", hash = "sha256:223e8b5359c2efc4b30555531f09e9f2f3589bcd7fdd389271191031b49b7a63"},
+ {file = "tqdm-4.66.6.tar.gz", hash = "sha256:4bdd694238bef1485ce839d67967ab50af8f9272aab687c0d7702a01da0be090"},
]
[package.dependencies]
@@ -4182,93 +4197,93 @@ files = [
[[package]]
name = "yarl"
-version = "1.16.0"
+version = "1.17.1"
description = "Yet another URL library"
optional = false
python-versions = ">=3.9"
files = [
- {file = "yarl-1.16.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:32468f41242d72b87ab793a86d92f885355bcf35b3355aa650bfa846a5c60058"},
- {file = "yarl-1.16.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:234f3a3032b505b90e65b5bc6652c2329ea7ea8855d8de61e1642b74b4ee65d2"},
- {file = "yarl-1.16.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:8a0296040e5cddf074c7f5af4a60f3fc42c0237440df7bcf5183be5f6c802ed5"},
- {file = "yarl-1.16.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:de6c14dd7c7c0badba48157474ea1f03ebee991530ba742d381b28d4f314d6f3"},
- {file = "yarl-1.16.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b140e532fe0266003c936d017c1ac301e72ee4a3fd51784574c05f53718a55d8"},
- {file = "yarl-1.16.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:019f5d58093402aa8f6661e60fd82a28746ad6d156f6c5336a70a39bd7b162b9"},
- {file = "yarl-1.16.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8c42998fd1cbeb53cd985bff0e4bc25fbe55fd6eb3a545a724c1012d69d5ec84"},
- {file = "yarl-1.16.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7c7c30fb38c300fe8140df30a046a01769105e4cf4282567a29b5cdb635b66c4"},
- {file = "yarl-1.16.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:e49e0fd86c295e743fd5be69b8b0712f70a686bc79a16e5268386c2defacaade"},
- {file = "yarl-1.16.0-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:b9ca7b9147eb1365c8bab03c003baa1300599575effad765e0b07dd3501ea9af"},
- {file = "yarl-1.16.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:27e11db3f1e6a51081a981509f75617b09810529de508a181319193d320bc5c7"},
- {file = "yarl-1.16.0-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:8994c42f4ca25df5380ddf59f315c518c81df6a68fed5bb0c159c6cb6b92f120"},
- {file = "yarl-1.16.0-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:542fa8e09a581bcdcbb30607c7224beff3fdfb598c798ccd28a8184ffc18b7eb"},
- {file = "yarl-1.16.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:2bd6a51010c7284d191b79d3b56e51a87d8e1c03b0902362945f15c3d50ed46b"},
- {file = "yarl-1.16.0-cp310-cp310-win32.whl", hash = "sha256:178ccb856e265174a79f59721031060f885aca428983e75c06f78aa24b91d929"},
- {file = "yarl-1.16.0-cp310-cp310-win_amd64.whl", hash = "sha256:fe8bba2545427418efc1929c5c42852bdb4143eb8d0a46b09de88d1fe99258e7"},
- {file = "yarl-1.16.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:d8643975a0080f361639787415a038bfc32d29208a4bf6b783ab3075a20b1ef3"},
- {file = "yarl-1.16.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:676d96bafc8c2d0039cea0cd3fd44cee7aa88b8185551a2bb93354668e8315c2"},
- {file = "yarl-1.16.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:d9525f03269e64310416dbe6c68d3b23e5d34aaa8f47193a1c45ac568cecbc49"},
- {file = "yarl-1.16.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8b37d5ec034e668b22cf0ce1074d6c21fd2a08b90d11b1b73139b750a8b0dd97"},
- {file = "yarl-1.16.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4f32c4cb7386b41936894685f6e093c8dfaf0960124d91fe0ec29fe439e201d0"},
- {file = "yarl-1.16.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5b8e265a0545637492a7e12fd7038370d66c9375a61d88c5567d0e044ded9202"},
- {file = "yarl-1.16.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:789a3423f28a5fff46fbd04e339863c169ece97c827b44de16e1a7a42bc915d2"},
- {file = "yarl-1.16.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f1d1f45e3e8d37c804dca99ab3cf4ab3ed2e7a62cd82542924b14c0a4f46d243"},
- {file = "yarl-1.16.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:621280719c4c5dad4c1391160a9b88925bb8b0ff6a7d5af3224643024871675f"},
- {file = "yarl-1.16.0-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:ed097b26f18a1f5ff05f661dc36528c5f6735ba4ce8c9645e83b064665131349"},
- {file = "yarl-1.16.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:2f1fe2b2e3ee418862f5ebc0c0083c97f6f6625781382f828f6d4e9b614eba9b"},
- {file = "yarl-1.16.0-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:87dd10bc0618991c66cee0cc65fa74a45f4ecb13bceec3c62d78ad2e42b27a16"},
- {file = "yarl-1.16.0-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:4199db024b58a8abb2cfcedac7b1292c3ad421684571aeb622a02f242280e8d6"},
- {file = "yarl-1.16.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:99a9dcd4b71dd5f5f949737ab3f356cfc058c709b4f49833aeffedc2652dac56"},
- {file = "yarl-1.16.0-cp311-cp311-win32.whl", hash = "sha256:a9394c65ae0ed95679717d391c862dece9afacd8fa311683fc8b4362ce8a410c"},
- {file = "yarl-1.16.0-cp311-cp311-win_amd64.whl", hash = "sha256:5b9101f528ae0f8f65ac9d64dda2bb0627de8a50344b2f582779f32fda747c1d"},
- {file = "yarl-1.16.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:4ffb7c129707dd76ced0a4a4128ff452cecf0b0e929f2668ea05a371d9e5c104"},
- {file = "yarl-1.16.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:1a5e9d8ce1185723419c487758d81ac2bde693711947032cce600ca7c9cda7d6"},
- {file = "yarl-1.16.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:d743e3118b2640cef7768ea955378c3536482d95550222f908f392167fe62059"},
- {file = "yarl-1.16.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:26768342f256e6e3c37533bf9433f5f15f3e59e3c14b2409098291b3efaceacb"},
- {file = "yarl-1.16.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d1b0796168b953bca6600c5f97f5ed407479889a36ad7d17183366260f29a6b9"},
- {file = "yarl-1.16.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:858728086914f3a407aa7979cab743bbda1fe2bdf39ffcd991469a370dd7414d"},
- {file = "yarl-1.16.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5570e6d47bcb03215baf4c9ad7bf7c013e56285d9d35013541f9ac2b372593e7"},
- {file = "yarl-1.16.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:66ea8311422a7ba1fc79b4c42c2baa10566469fe5a78500d4e7754d6e6db8724"},
- {file = "yarl-1.16.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:649bddcedee692ee8a9b7b6e38582cb4062dc4253de9711568e5620d8707c2a3"},
- {file = "yarl-1.16.0-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:3a91654adb7643cb21b46f04244c5a315a440dcad63213033826549fa2435f71"},
- {file = "yarl-1.16.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:b439cae82034ade094526a8f692b9a2b5ee936452de5e4c5f0f6c48df23f8604"},
- {file = "yarl-1.16.0-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:571f781ae8ac463ce30bacebfaef2c6581543776d5970b2372fbe31d7bf31a07"},
- {file = "yarl-1.16.0-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:aa7943f04f36d6cafc0cf53ea89824ac2c37acbdb4b316a654176ab8ffd0f968"},
- {file = "yarl-1.16.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:1a5cf32539373ff39d97723e39a9283a7277cbf1224f7aef0c56c9598b6486c3"},
- {file = "yarl-1.16.0-cp312-cp312-win32.whl", hash = "sha256:a5b6c09b9b4253d6a208b0f4a2f9206e511ec68dce9198e0fbec4f160137aa67"},
- {file = "yarl-1.16.0-cp312-cp312-win_amd64.whl", hash = "sha256:1208ca14eed2fda324042adf8d6c0adf4a31522fa95e0929027cd487875f0240"},
- {file = "yarl-1.16.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:a5ace0177520bd4caa99295a9b6fb831d0e9a57d8e0501a22ffaa61b4c024283"},
- {file = "yarl-1.16.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:7118bdb5e3ed81acaa2095cba7ec02a0fe74b52a16ab9f9ac8e28e53ee299732"},
- {file = "yarl-1.16.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:38fec8a2a94c58bd47c9a50a45d321ab2285ad133adefbbadf3012c054b7e656"},
- {file = "yarl-1.16.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8791d66d81ee45866a7bb15a517b01a2bcf583a18ebf5d72a84e6064c417e64b"},
- {file = "yarl-1.16.0-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1cf936ba67bc6c734f3aa1c01391da74ab7fc046a9f8bbfa230b8393b90cf472"},
- {file = "yarl-1.16.0-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d1aab176dd55b59f77a63b27cffaca67d29987d91a5b615cbead41331e6b7428"},
- {file = "yarl-1.16.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:995d0759004c08abd5d1b81300a91d18c8577c6389300bed1c7c11675105a44d"},
- {file = "yarl-1.16.0-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1bc22e00edeb068f71967ab99081e9406cd56dbed864fc3a8259442999d71552"},
- {file = "yarl-1.16.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:35b4f7842154176523e0a63c9b871168c69b98065d05a4f637fce342a6a2693a"},
- {file = "yarl-1.16.0-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:7ace71c4b7a0c41f317ae24be62bb61e9d80838d38acb20e70697c625e71f120"},
- {file = "yarl-1.16.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:8f639e3f5795a6568aa4f7d2ac6057c757dcd187593679f035adbf12b892bb00"},
- {file = "yarl-1.16.0-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:e8be3aff14f0120ad049121322b107f8a759be76a6a62138322d4c8a337a9e2c"},
- {file = "yarl-1.16.0-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:122d8e7986043d0549e9eb23c7fd23be078be4b70c9eb42a20052b3d3149c6f2"},
- {file = "yarl-1.16.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:0fd9c227990f609c165f56b46107d0bc34553fe0387818c42c02f77974402c36"},
- {file = "yarl-1.16.0-cp313-cp313-win32.whl", hash = "sha256:595ca5e943baed31d56b33b34736461a371c6ea0038d3baec399949dd628560b"},
- {file = "yarl-1.16.0-cp313-cp313-win_amd64.whl", hash = "sha256:921b81b8d78f0e60242fb3db615ea3f368827a76af095d5a69f1c3366db3f596"},
- {file = "yarl-1.16.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:ab2b2ac232110a1fdb0d3ffcd087783edd3d4a6ced432a1bf75caf7b7be70916"},
- {file = "yarl-1.16.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:7f8713717a09acbfee7c47bfc5777e685539fefdd34fa72faf504c8be2f3df4e"},
- {file = "yarl-1.16.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:cdcffe1dbcb4477d2b4202f63cd972d5baa155ff5a3d9e35801c46a415b7f71a"},
- {file = "yarl-1.16.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9a91217208306d82357c67daeef5162a41a28c8352dab7e16daa82e3718852a7"},
- {file = "yarl-1.16.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3ab3ed42c78275477ea8e917491365e9a9b69bb615cb46169020bd0aa5e2d6d3"},
- {file = "yarl-1.16.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:707ae579ccb3262dfaef093e202b4c3fb23c3810e8df544b1111bd2401fd7b09"},
- {file = "yarl-1.16.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ad7a852d1cd0b8d8b37fc9d7f8581152add917a98cfe2ea6e241878795f917ae"},
- {file = "yarl-1.16.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d3f1cc3d3d4dc574bebc9b387f6875e228ace5748a7c24f49d8f01ac1bc6c31b"},
- {file = "yarl-1.16.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:5ff96da263740779b0893d02b718293cc03400c3a208fc8d8cd79d9b0993e532"},
- {file = "yarl-1.16.0-cp39-cp39-musllinux_1_2_armv7l.whl", hash = "sha256:3d375a19ba2bfe320b6d873f3fb165313b002cef8b7cc0a368ad8b8a57453837"},
- {file = "yarl-1.16.0-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:62c7da0ad93a07da048b500514ca47b759459ec41924143e2ddb5d7e20fd3db5"},
- {file = "yarl-1.16.0-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:147b0fcd0ee33b4b5f6edfea80452d80e419e51b9a3f7a96ce98eaee145c1581"},
- {file = "yarl-1.16.0-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:504e1fe1cc4f170195320eb033d2b0ccf5c6114ce5bf2f617535c01699479bca"},
- {file = "yarl-1.16.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:bdcf667a5dec12a48f669e485d70c54189f0639c2157b538a4cffd24a853624f"},
- {file = "yarl-1.16.0-cp39-cp39-win32.whl", hash = "sha256:e9951afe6557c75a71045148890052cb942689ee4c9ec29f5436240e1fcc73b7"},
- {file = "yarl-1.16.0-cp39-cp39-win_amd64.whl", hash = "sha256:7d7aaa8ff95d0840e289423e7dc35696c2b058d635f945bf05b5cd633146b027"},
- {file = "yarl-1.16.0-py3-none-any.whl", hash = "sha256:e6980a558d8461230c457218bd6c92dfc1d10205548215c2c21d79dc8d0a96f3"},
- {file = "yarl-1.16.0.tar.gz", hash = "sha256:b6f687ced5510a9a2474bbae96a4352e5ace5fa34dc44a217b0537fec1db00b4"},
+ {file = "yarl-1.17.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:0b1794853124e2f663f0ea54efb0340b457f08d40a1cef78edfa086576179c91"},
+ {file = "yarl-1.17.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:fbea1751729afe607d84acfd01efd95e3b31db148a181a441984ce9b3d3469da"},
+ {file = "yarl-1.17.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:8ee427208c675f1b6e344a1f89376a9613fc30b52646a04ac0c1f6587c7e46ec"},
+ {file = "yarl-1.17.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3b74ff4767d3ef47ffe0cd1d89379dc4d828d4873e5528976ced3b44fe5b0a21"},
+ {file = "yarl-1.17.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:62a91aefff3d11bf60e5956d340eb507a983a7ec802b19072bb989ce120cd948"},
+ {file = "yarl-1.17.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:846dd2e1243407133d3195d2d7e4ceefcaa5f5bf7278f0a9bda00967e6326b04"},
+ {file = "yarl-1.17.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3e844be8d536afa129366d9af76ed7cb8dfefec99f5f1c9e4f8ae542279a6dc3"},
+ {file = "yarl-1.17.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:cc7c92c1baa629cb03ecb0c3d12564f172218fb1739f54bf5f3881844daadc6d"},
+ {file = "yarl-1.17.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:ae3476e934b9d714aa8000d2e4c01eb2590eee10b9d8cd03e7983ad65dfbfcba"},
+ {file = "yarl-1.17.1-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:c7e177c619342e407415d4f35dec63d2d134d951e24b5166afcdfd1362828e17"},
+ {file = "yarl-1.17.1-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:64cc6e97f14cf8a275d79c5002281f3040c12e2e4220623b5759ea7f9868d6a5"},
+ {file = "yarl-1.17.1-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:84c063af19ef5130084db70ada40ce63a84f6c1ef4d3dbc34e5e8c4febb20822"},
+ {file = "yarl-1.17.1-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:482c122b72e3c5ec98f11457aeb436ae4aecca75de19b3d1de7cf88bc40db82f"},
+ {file = "yarl-1.17.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:380e6c38ef692b8fd5a0f6d1fa8774d81ebc08cfbd624b1bca62a4d4af2f9931"},
+ {file = "yarl-1.17.1-cp310-cp310-win32.whl", hash = "sha256:16bca6678a83657dd48df84b51bd56a6c6bd401853aef6d09dc2506a78484c7b"},
+ {file = "yarl-1.17.1-cp310-cp310-win_amd64.whl", hash = "sha256:561c87fea99545ef7d692403c110b2f99dced6dff93056d6e04384ad3bc46243"},
+ {file = "yarl-1.17.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:cbad927ea8ed814622305d842c93412cb47bd39a496ed0f96bfd42b922b4a217"},
+ {file = "yarl-1.17.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:fca4b4307ebe9c3ec77a084da3a9d1999d164693d16492ca2b64594340999988"},
+ {file = "yarl-1.17.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:ff5c6771c7e3511a06555afa317879b7db8d640137ba55d6ab0d0c50425cab75"},
+ {file = "yarl-1.17.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5b29beab10211a746f9846baa39275e80034e065460d99eb51e45c9a9495bcca"},
+ {file = "yarl-1.17.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1a52a1ffdd824fb1835272e125385c32fd8b17fbdefeedcb4d543cc23b332d74"},
+ {file = "yarl-1.17.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:58c8e9620eb82a189c6c40cb6b59b4e35b2ee68b1f2afa6597732a2b467d7e8f"},
+ {file = "yarl-1.17.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d216e5d9b8749563c7f2c6f7a0831057ec844c68b4c11cb10fc62d4fd373c26d"},
+ {file = "yarl-1.17.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:881764d610e3269964fc4bb3c19bb6fce55422828e152b885609ec176b41cf11"},
+ {file = "yarl-1.17.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:8c79e9d7e3d8a32d4824250a9c6401194fb4c2ad9a0cec8f6a96e09a582c2cc0"},
+ {file = "yarl-1.17.1-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:299f11b44d8d3a588234adbe01112126010bd96d9139c3ba7b3badd9829261c3"},
+ {file = "yarl-1.17.1-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:cc7d768260f4ba4ea01741c1b5fe3d3a6c70eb91c87f4c8761bbcce5181beafe"},
+ {file = "yarl-1.17.1-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:de599af166970d6a61accde358ec9ded821234cbbc8c6413acfec06056b8e860"},
+ {file = "yarl-1.17.1-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:2b24ec55fad43e476905eceaf14f41f6478780b870eda5d08b4d6de9a60b65b4"},
+ {file = "yarl-1.17.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:9fb815155aac6bfa8d86184079652c9715c812d506b22cfa369196ef4e99d1b4"},
+ {file = "yarl-1.17.1-cp311-cp311-win32.whl", hash = "sha256:7615058aabad54416ddac99ade09a5510cf77039a3b903e94e8922f25ed203d7"},
+ {file = "yarl-1.17.1-cp311-cp311-win_amd64.whl", hash = "sha256:14bc88baa44e1f84164a392827b5defb4fa8e56b93fecac3d15315e7c8e5d8b3"},
+ {file = "yarl-1.17.1-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:327828786da2006085a4d1feb2594de6f6d26f8af48b81eb1ae950c788d97f61"},
+ {file = "yarl-1.17.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:cc353841428d56b683a123a813e6a686e07026d6b1c5757970a877195f880c2d"},
+ {file = "yarl-1.17.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:c73df5b6e8fabe2ddb74876fb82d9dd44cbace0ca12e8861ce9155ad3c886139"},
+ {file = "yarl-1.17.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0bdff5e0995522706c53078f531fb586f56de9c4c81c243865dd5c66c132c3b5"},
+ {file = "yarl-1.17.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:06157fb3c58f2736a5e47c8fcbe1afc8b5de6fb28b14d25574af9e62150fcaac"},
+ {file = "yarl-1.17.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1654ec814b18be1af2c857aa9000de7a601400bd4c9ca24629b18486c2e35463"},
+ {file = "yarl-1.17.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7f6595c852ca544aaeeb32d357e62c9c780eac69dcd34e40cae7b55bc4fb1147"},
+ {file = "yarl-1.17.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:459e81c2fb920b5f5df744262d1498ec2c8081acdcfe18181da44c50f51312f7"},
+ {file = "yarl-1.17.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:7e48cdb8226644e2fbd0bdb0a0f87906a3db07087f4de77a1b1b1ccfd9e93685"},
+ {file = "yarl-1.17.1-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:d9b6b28a57feb51605d6ae5e61a9044a31742db557a3b851a74c13bc61de5172"},
+ {file = "yarl-1.17.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:e594b22688d5747b06e957f1ef822060cb5cb35b493066e33ceac0cf882188b7"},
+ {file = "yarl-1.17.1-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:5f236cb5999ccd23a0ab1bd219cfe0ee3e1c1b65aaf6dd3320e972f7ec3a39da"},
+ {file = "yarl-1.17.1-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:a2a64e62c7a0edd07c1c917b0586655f3362d2c2d37d474db1a509efb96fea1c"},
+ {file = "yarl-1.17.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:d0eea830b591dbc68e030c86a9569826145df485b2b4554874b07fea1275a199"},
+ {file = "yarl-1.17.1-cp312-cp312-win32.whl", hash = "sha256:46ddf6e0b975cd680eb83318aa1d321cb2bf8d288d50f1754526230fcf59ba96"},
+ {file = "yarl-1.17.1-cp312-cp312-win_amd64.whl", hash = "sha256:117ed8b3732528a1e41af3aa6d4e08483c2f0f2e3d3d7dca7cf538b3516d93df"},
+ {file = "yarl-1.17.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:5d1d42556b063d579cae59e37a38c61f4402b47d70c29f0ef15cee1acaa64488"},
+ {file = "yarl-1.17.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:c0167540094838ee9093ef6cc2c69d0074bbf84a432b4995835e8e5a0d984374"},
+ {file = "yarl-1.17.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:2f0a6423295a0d282d00e8701fe763eeefba8037e984ad5de44aa349002562ac"},
+ {file = "yarl-1.17.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e5b078134f48552c4d9527db2f7da0b5359abd49393cdf9794017baec7506170"},
+ {file = "yarl-1.17.1-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d401f07261dc5aa36c2e4efc308548f6ae943bfff20fcadb0a07517a26b196d8"},
+ {file = "yarl-1.17.1-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b5f1ac7359e17efe0b6e5fec21de34145caef22b260e978336f325d5c84e6938"},
+ {file = "yarl-1.17.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7f63d176a81555984e91f2c84c2a574a61cab7111cc907e176f0f01538e9ff6e"},
+ {file = "yarl-1.17.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9e275792097c9f7e80741c36de3b61917aebecc08a67ae62899b074566ff8556"},
+ {file = "yarl-1.17.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:81713b70bea5c1386dc2f32a8f0dab4148a2928c7495c808c541ee0aae614d67"},
+ {file = "yarl-1.17.1-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:aa46dce75078fceaf7cecac5817422febb4355fbdda440db55206e3bd288cfb8"},
+ {file = "yarl-1.17.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:1ce36ded585f45b1e9bb36d0ae94765c6608b43bd2e7f5f88079f7a85c61a4d3"},
+ {file = "yarl-1.17.1-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:2d374d70fdc36f5863b84e54775452f68639bc862918602d028f89310a034ab0"},
+ {file = "yarl-1.17.1-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:2d9f0606baaec5dd54cb99667fcf85183a7477f3766fbddbe3f385e7fc253299"},
+ {file = "yarl-1.17.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:b0341e6d9a0c0e3cdc65857ef518bb05b410dbd70d749a0d33ac0f39e81a4258"},
+ {file = "yarl-1.17.1-cp313-cp313-win32.whl", hash = "sha256:2e7ba4c9377e48fb7b20dedbd473cbcbc13e72e1826917c185157a137dac9df2"},
+ {file = "yarl-1.17.1-cp313-cp313-win_amd64.whl", hash = "sha256:949681f68e0e3c25377462be4b658500e85ca24323d9619fdc41f68d46a1ffda"},
+ {file = "yarl-1.17.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:8994b29c462de9a8fce2d591028b986dbbe1b32f3ad600b2d3e1c482c93abad6"},
+ {file = "yarl-1.17.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:f9cbfbc5faca235fbdf531b93aa0f9f005ec7d267d9d738761a4d42b744ea159"},
+ {file = "yarl-1.17.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:b40d1bf6e6f74f7c0a567a9e5e778bbd4699d1d3d2c0fe46f4b717eef9e96b95"},
+ {file = "yarl-1.17.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f5efe0661b9fcd6246f27957f6ae1c0eb29bc60552820f01e970b4996e016004"},
+ {file = "yarl-1.17.1-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b5c4804e4039f487e942c13381e6c27b4b4e66066d94ef1fae3f6ba8b953f383"},
+ {file = "yarl-1.17.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b5d6a6c9602fd4598fa07e0389e19fe199ae96449008d8304bf5d47cb745462e"},
+ {file = "yarl-1.17.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6f4c9156c4d1eb490fe374fb294deeb7bc7eaccda50e23775b2354b6a6739934"},
+ {file = "yarl-1.17.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d6324274b4e0e2fa1b3eccb25997b1c9ed134ff61d296448ab8269f5ac068c4c"},
+ {file = "yarl-1.17.1-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:d8a8b74d843c2638f3864a17d97a4acda58e40d3e44b6303b8cc3d3c44ae2d29"},
+ {file = "yarl-1.17.1-cp39-cp39-musllinux_1_2_armv7l.whl", hash = "sha256:7fac95714b09da9278a0b52e492466f773cfe37651cf467a83a1b659be24bf71"},
+ {file = "yarl-1.17.1-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:c180ac742a083e109c1a18151f4dd8675f32679985a1c750d2ff806796165b55"},
+ {file = "yarl-1.17.1-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:578d00c9b7fccfa1745a44f4eddfdc99d723d157dad26764538fbdda37209857"},
+ {file = "yarl-1.17.1-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:1a3b91c44efa29e6c8ef8a9a2b583347998e2ba52c5d8280dbd5919c02dfc3b5"},
+ {file = "yarl-1.17.1-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:a7ac5b4984c468ce4f4a553df281450df0a34aefae02e58d77a0847be8d1e11f"},
+ {file = "yarl-1.17.1-cp39-cp39-win32.whl", hash = "sha256:7294e38f9aa2e9f05f765b28ffdc5d81378508ce6dadbe93f6d464a8c9594473"},
+ {file = "yarl-1.17.1-cp39-cp39-win_amd64.whl", hash = "sha256:eb6dce402734575e1a8cc0bb1509afca508a400a57ce13d306ea2c663bad1138"},
+ {file = "yarl-1.17.1-py3-none-any.whl", hash = "sha256:f1790a4b1e8e8e028c391175433b9c8122c39b46e1663228158e61e6f915bf06"},
+ {file = "yarl-1.17.1.tar.gz", hash = "sha256:067a63fcfda82da6b198fa73079b1ca40b7c9b7994995b6ee38acda728b64d47"},
]
[package.dependencies]
@@ -4298,4 +4313,4 @@ type = ["pytest-mypy"]
[metadata]
lock-version = "2.0"
python-versions = "^3.12"
-content-hash = "8d540e367903004280ea01872d13776353e3a2dc144e6568267da01f35026bf0"
+content-hash = "a654acc8816c96d1ec25bce16fe1afdd8d37e5ac38a0fa59ee7871dde5ca5229"
diff --git a/apps/api/pyproject.toml b/apps/api/pyproject.toml
index e12a1762..1216c45a 100644
--- a/apps/api/pyproject.toml
+++ b/apps/api/pyproject.toml
@@ -37,10 +37,11 @@ sqlmodel = "^0.0.19"
tiktoken = "^0.7.0"
uvicorn = "0.30.1"
typer = "^0.12.5"
-chromadb = "0.5.11"
+chromadb = "0.5.16"
alembic = "^1.13.2"
alembic-postgresql-enum = "^1.2.0"
sqlalchemy-utils = "^0.41.2"
+stripe = "^11.1.1"
[build-system]
build-backend = "poetry.core.masonry.api"
diff --git a/apps/api/src/core/events/database.py b/apps/api/src/core/events/database.py
index 01318298..e910f628 100644
--- a/apps/api/src/core/events/database.py
+++ b/apps/api/src/core/events/database.py
@@ -1,26 +1,54 @@
import logging
+import os
+import importlib
from config.config import get_learnhouse_config
from fastapi import FastAPI
from sqlmodel import SQLModel, Session, create_engine
+def import_all_models():
+ base_dir = 'src/db'
+ base_module_path = 'src.db'
+
+ # Recursively walk through the base directory
+ for root, dirs, files in os.walk(base_dir):
+ # Filter out __init__.py and non-Python files
+ module_files = [f for f in files if f.endswith('.py') and f != '__init__.py']
+
+ # Calculate the module's base path from its directory structure
+ path_diff = os.path.relpath(root, base_dir)
+ if path_diff == '.':
+ current_module_base = base_module_path
+ else:
+ current_module_base = f"{base_module_path}.{path_diff.replace(os.sep, '.')}"
+
+ # Dynamically import each module
+ for file_name in module_files:
+ module_name = file_name[:-3] # Remove the '.py' extension
+ full_module_path = f"{current_module_base}.{module_name}"
+ importlib.import_module(full_module_path)
+
+# Import all models before creating engine
+import_all_models()
+
learnhouse_config = get_learnhouse_config()
engine = create_engine(
- learnhouse_config.database_config.sql_connection_string, echo=False, pool_pre_ping=True # type: ignore
+ learnhouse_config.database_config.sql_connection_string, # type: ignore
+ echo=False,
+ pool_pre_ping=True # type: ignore
)
-SQLModel.metadata.create_all(engine)
+# Create all tables after importing all models
+SQLModel.metadata.create_all(engine)
async def connect_to_db(app: FastAPI):
app.db_engine = engine # type: ignore
logging.info("LearnHouse database has been started.")
SQLModel.metadata.create_all(engine)
-
def get_db_session():
with Session(engine) as session:
yield session
-
async def close_database(app: FastAPI):
logging.info("LearnHouse has been shut down.")
return app
diff --git a/apps/api/src/db/collections.py b/apps/api/src/db/collections.py
index fb0b1e94..7fd3c524 100644
--- a/apps/api/src/db/collections.py
+++ b/apps/api/src/db/collections.py
@@ -30,7 +30,7 @@ class CollectionUpdate(CollectionBase):
courses: Optional[list]
name: Optional[str]
public: Optional[bool]
- description: Optional[str]
+ description: Optional[str] = ""
class CollectionRead(CollectionBase):
diff --git a/apps/api/src/db/organization_config.py b/apps/api/src/db/organization_config.py
index 54f45484..a54e537e 100644
--- a/apps/api/src/db/organization_config.py
+++ b/apps/api/src/db/organization_config.py
@@ -40,7 +40,6 @@ class AssignmentOrgConfig(BaseModel):
class PaymentOrgConfig(BaseModel):
enabled: bool = True
- stripe_key: str = ""
class DiscussionOrgConfig(BaseModel):
@@ -91,7 +90,7 @@ class OrgCloudConfig(BaseModel):
# Main Config
class OrganizationConfigBase(BaseModel):
- config_version: str = "1.1"
+ config_version: str = "1.2"
general: OrgGeneralConfig
features: OrgFeatureConfig
cloud: OrgCloudConfig
diff --git a/apps/api/src/db/payments/payments.py b/apps/api/src/db/payments/payments.py
new file mode 100644
index 00000000..595fc8cf
--- /dev/null
+++ b/apps/api/src/db/payments/payments.py
@@ -0,0 +1,46 @@
+from datetime import datetime
+from enum import Enum
+from typing import Optional
+from sqlalchemy import JSON
+from sqlmodel import Field, SQLModel, Column, BigInteger, ForeignKey
+
+# PaymentsConfig
+class PaymentProviderEnum(str, Enum):
+ STRIPE = "stripe"
+
+class PaymentsConfigBase(SQLModel):
+ enabled: bool = True
+ active: bool = False
+ provider: PaymentProviderEnum = PaymentProviderEnum.STRIPE
+ provider_specific_id: str | None = None
+ provider_config: dict = Field(default={}, sa_column=Column(JSON))
+
+
+class PaymentsConfig(PaymentsConfigBase, table=True):
+ id: Optional[int] = Field(default=None, primary_key=True)
+ org_id: int = Field(
+ sa_column=Column(BigInteger, ForeignKey("organization.id", ondelete="CASCADE"))
+ )
+ creation_date: datetime = Field(default=datetime.now())
+ update_date: datetime = Field(default=datetime.now())
+
+
+class PaymentsConfigCreate(PaymentsConfigBase):
+ pass
+
+
+class PaymentsConfigUpdate(PaymentsConfigBase):
+ enabled: Optional[bool] = True
+ provider_config: Optional[dict] = None
+ provider_specific_id: Optional[str] = None
+
+
+class PaymentsConfigRead(PaymentsConfigBase):
+ id: int
+ org_id: int
+ creation_date: datetime
+ update_date: datetime
+
+
+class PaymentsConfigDelete(SQLModel):
+ id: int
\ No newline at end of file
diff --git a/apps/api/src/db/payments/payments_courses.py b/apps/api/src/db/payments/payments_courses.py
new file mode 100644
index 00000000..bdcfbead
--- /dev/null
+++ b/apps/api/src/db/payments/payments_courses.py
@@ -0,0 +1,15 @@
+from sqlmodel import SQLModel, Field, Column, BigInteger, ForeignKey
+from typing import Optional
+from datetime import datetime
+
+class PaymentsCourseBase(SQLModel):
+ course_id: int = Field(sa_column=Column(BigInteger, ForeignKey("course.id", ondelete="CASCADE")))
+
+class PaymentsCourse(PaymentsCourseBase, table=True):
+ id: Optional[int] = Field(default=None, primary_key=True)
+ payment_product_id: int = Field(sa_column=Column(BigInteger, ForeignKey("paymentsproduct.id", ondelete="CASCADE")))
+ org_id: int = Field(
+ sa_column=Column(BigInteger, ForeignKey("organization.id", ondelete="CASCADE"))
+ )
+ creation_date: datetime = Field(default=datetime.now())
+ update_date: datetime = Field(default=datetime.now())
\ No newline at end of file
diff --git a/apps/api/src/db/payments/payments_products.py b/apps/api/src/db/payments/payments_products.py
new file mode 100644
index 00000000..56cdcc73
--- /dev/null
+++ b/apps/api/src/db/payments/payments_products.py
@@ -0,0 +1,44 @@
+from enum import Enum
+from sqlmodel import SQLModel, Field, Column, BigInteger, ForeignKey, String
+from typing import Optional
+from datetime import datetime
+
+class PaymentProductTypeEnum(str, Enum):
+ SUBSCRIPTION = "subscription"
+ ONE_TIME = "one_time"
+
+class PaymentPriceTypeEnum(str, Enum):
+ CUSTOMER_CHOICE = "customer_choice"
+ FIXED_PRICE = "fixed_price"
+
+class PaymentsProductBase(SQLModel):
+ name: str = ""
+ description: Optional[str] = ""
+ product_type: PaymentProductTypeEnum = PaymentProductTypeEnum.ONE_TIME
+ price_type: PaymentPriceTypeEnum = PaymentPriceTypeEnum.FIXED_PRICE
+ benefits: str = ""
+ amount: float = 0.0
+ currency: str = "USD"
+
+class PaymentsProduct(PaymentsProductBase, table=True):
+ id: Optional[int] = Field(default=None, primary_key=True)
+ org_id: int = Field(
+ sa_column=Column(BigInteger, ForeignKey("organization.id", ondelete="CASCADE"))
+ )
+ payments_config_id: int = Field(sa_column=Column(BigInteger, ForeignKey("paymentsconfig.id", ondelete="CASCADE")))
+ provider_product_id: str = Field(sa_column=Column(String))
+ creation_date: datetime = Field(default=datetime.now())
+ update_date: datetime = Field(default=datetime.now())
+
+class PaymentsProductCreate(PaymentsProductBase):
+ pass
+
+class PaymentsProductUpdate(PaymentsProductBase):
+ pass
+
+class PaymentsProductRead(PaymentsProductBase):
+ id: int
+ org_id: int
+ payments_config_id: int
+ creation_date: datetime
+ update_date: datetime
diff --git a/apps/api/src/db/payments/payments_users.py b/apps/api/src/db/payments/payments_users.py
new file mode 100644
index 00000000..ce3b3f68
--- /dev/null
+++ b/apps/api/src/db/payments/payments_users.py
@@ -0,0 +1,37 @@
+from openai import BaseModel
+from sqlmodel import SQLModel, Field, Column, BigInteger, ForeignKey, JSON
+from typing import Optional
+from datetime import datetime
+from enum import Enum
+
+class PaymentStatusEnum(str, Enum):
+ PENDING = "pending"
+ COMPLETED = "completed"
+ ACTIVE = "active"
+ CANCELLED = "cancelled"
+ FAILED = "failed"
+ REFUNDED = "refunded"
+
+
+class ProviderSpecificData(BaseModel):
+ stripe_customer: dict | None = None
+ custom_customer: dict | None = None
+
+class PaymentsUserBase(SQLModel):
+ status: PaymentStatusEnum = PaymentStatusEnum.PENDING
+ provider_specific_data: dict = Field(default={}, sa_column=Column(JSON))
+
+class PaymentsUser(PaymentsUserBase, table=True):
+ id: Optional[int] = Field(default=None, primary_key=True)
+ user_id: int = Field(
+ sa_column=Column(BigInteger, ForeignKey("user.id", ondelete="CASCADE"))
+ )
+ org_id: int = Field(
+ sa_column=Column(BigInteger, ForeignKey("organization.id", ondelete="CASCADE"))
+ )
+ payment_product_id: int = Field(
+ sa_column=Column(BigInteger, ForeignKey("paymentsproduct.id", ondelete="CASCADE"))
+ )
+ creation_date: datetime = Field(default=datetime.now())
+ update_date: datetime = Field(default=datetime.now())
+
diff --git a/apps/api/src/db/users.py b/apps/api/src/db/users.py
index 8d98c3e7..d7412e30 100644
--- a/apps/api/src/db/users.py
+++ b/apps/api/src/db/users.py
@@ -59,6 +59,11 @@ class AnonymousUser(SQLModel):
user_uuid: str = "user_anonymous"
username: str = "anonymous"
+class InternalUser(SQLModel):
+ id: int = 0
+ user_uuid: str = "user_internal"
+ username: str = "internal"
+
class User(UserBase, table=True):
id: Optional[int] = Field(default=None, primary_key=True)
diff --git a/apps/api/src/router.py b/apps/api/src/router.py
index bc4aaaa8..2725a263 100644
--- a/apps/api/src/router.py
+++ b/apps/api/src/router.py
@@ -1,11 +1,12 @@
import os
from fastapi import APIRouter, Depends
+from src.routers import health
from src.routers import usergroups
from src.routers import dev, trail, users, auth, orgs, roles
from src.routers.ai import ai
from src.routers.courses import chapters, collections, courses, assignments
from src.routers.courses.activities import activities, blocks
-from src.routers.ee import cloud_internal
+from src.routers.ee import cloud_internal, payments
from src.routers.install import install
from src.services.dev.dev import isDevModeEnabledOrRaise
from src.services.install.install import isInstallModeEnabled
@@ -32,6 +33,7 @@ v1_router.include_router(
)
v1_router.include_router(trail.router, prefix="/trail", tags=["trail"])
v1_router.include_router(ai.router, prefix="/ai", tags=["ai"])
+v1_router.include_router(payments.router, prefix="/payments", tags=["payments"])
if os.environ.get("CLOUD_INTERNAL_KEY"):
v1_router.include_router(
@@ -41,6 +43,8 @@ if os.environ.get("CLOUD_INTERNAL_KEY"):
dependencies=[Depends(cloud_internal.check_internal_cloud_key)],
)
+v1_router.include_router(health.router, prefix="/health", tags=["health"])
+
# Dev Routes
v1_router.include_router(
dev.router,
diff --git a/apps/api/src/routers/ee/payments.py b/apps/api/src/routers/ee/payments.py
new file mode 100644
index 00000000..5b7f4ca2
--- /dev/null
+++ b/apps/api/src/routers/ee/payments.py
@@ -0,0 +1,265 @@
+from typing import Literal
+from fastapi import APIRouter, Depends, Request
+from sqlmodel import Session
+from src.core.events.database import get_db_session
+from src.db.payments.payments import PaymentsConfig, PaymentsConfigRead
+from src.db.users import PublicUser
+from src.security.auth import get_current_user
+from src.services.payments.payments_config import (
+ init_payments_config,
+ get_payments_config,
+ delete_payments_config,
+)
+from src.db.payments.payments_products import PaymentsProductCreate, PaymentsProductRead, PaymentsProductUpdate
+from src.services.payments.payments_products import create_payments_product, delete_payments_product, get_payments_product, get_products_by_course, list_payments_products, update_payments_product
+from src.services.payments.payments_courses import (
+ link_course_to_product,
+ unlink_course_from_product,
+ get_courses_by_product,
+)
+from src.services.payments.payments_users import get_owned_courses
+from src.services.payments.payments_stripe import create_checkout_session, handle_stripe_oauth_callback, update_stripe_account_id
+from src.services.payments.payments_access import check_course_paid_access
+from src.services.payments.payments_customers import get_customers
+from src.services.payments.payments_stripe import generate_stripe_connect_link
+from src.services.payments.webhooks.payments_webhooks import handle_stripe_webhook
+
+
+router = APIRouter()
+
+@router.post("/{org_id}/config")
+async def api_create_payments_config(
+ request: Request,
+ org_id: int,
+ provider: Literal["stripe"],
+ current_user: PublicUser = Depends(get_current_user),
+ db_session: Session = Depends(get_db_session),
+) -> PaymentsConfig:
+ return await init_payments_config(request, org_id, provider, current_user, db_session)
+
+
+@router.get("/{org_id}/config")
+async def api_get_payments_config(
+ request: Request,
+ org_id: int,
+ current_user: PublicUser = Depends(get_current_user),
+ db_session: Session = Depends(get_db_session),
+) -> list[PaymentsConfigRead]:
+ return await get_payments_config(request, org_id, current_user, db_session)
+
+@router.delete("/{org_id}/config")
+async def api_delete_payments_config(
+ request: Request,
+ org_id: int,
+ current_user: PublicUser = Depends(get_current_user),
+ db_session: Session = Depends(get_db_session),
+):
+ await delete_payments_config(request, org_id, current_user, db_session)
+ return {"message": "Payments config deleted successfully"}
+
+@router.post("/{org_id}/products")
+async def api_create_payments_product(
+ request: Request,
+ org_id: int,
+ payments_product: PaymentsProductCreate,
+ current_user: PublicUser = Depends(get_current_user),
+ db_session: Session = Depends(get_db_session),
+) -> PaymentsProductRead:
+ return await create_payments_product(request, org_id, payments_product, current_user, db_session)
+
+@router.get("/{org_id}/products")
+async def api_get_payments_products(
+ request: Request,
+ org_id: int,
+ current_user: PublicUser = Depends(get_current_user),
+ db_session: Session = Depends(get_db_session),
+) -> list[PaymentsProductRead]:
+ return await list_payments_products(request, org_id, current_user, db_session)
+
+@router.get("/{org_id}/products/{product_id}")
+async def api_get_payments_product(
+ request: Request,
+ org_id: int,
+ product_id: int,
+ current_user: PublicUser = Depends(get_current_user),
+ db_session: Session = Depends(get_db_session),
+) -> PaymentsProductRead:
+ return await get_payments_product(request, org_id, product_id, current_user, db_session)
+
+@router.put("/{org_id}/products/{product_id}")
+async def api_update_payments_product(
+ request: Request,
+ org_id: int,
+ product_id: int,
+ payments_product: PaymentsProductUpdate,
+ current_user: PublicUser = Depends(get_current_user),
+ db_session: Session = Depends(get_db_session),
+) -> PaymentsProductRead:
+ return await update_payments_product(request, org_id, product_id, payments_product, current_user, db_session)
+
+@router.delete("/{org_id}/products/{product_id}")
+async def api_delete_payments_product(
+ request: Request,
+ org_id: int,
+ product_id: int,
+ current_user: PublicUser = Depends(get_current_user),
+ db_session: Session = Depends(get_db_session),
+):
+ await delete_payments_product(request, org_id, product_id, current_user, db_session)
+ return {"message": "Payments product deleted successfully"}
+
+@router.post("/{org_id}/products/{product_id}/courses/{course_id}")
+async def api_link_course_to_product(
+ request: Request,
+ org_id: int,
+ product_id: int,
+ course_id: int,
+ current_user: PublicUser = Depends(get_current_user),
+ db_session: Session = Depends(get_db_session),
+):
+ return await link_course_to_product(
+ request, org_id, course_id, product_id, current_user, db_session
+ )
+
+@router.delete("/{org_id}/products/{product_id}/courses/{course_id}")
+async def api_unlink_course_from_product(
+ request: Request,
+ org_id: int,
+ product_id: int,
+ course_id: int,
+ current_user: PublicUser = Depends(get_current_user),
+ db_session: Session = Depends(get_db_session),
+):
+ return await unlink_course_from_product(
+ request, org_id, course_id, current_user, db_session
+ )
+
+@router.get("/{org_id}/products/{product_id}/courses")
+async def api_get_courses_by_product(
+ request: Request,
+ org_id: int,
+ product_id: int,
+ current_user: PublicUser = Depends(get_current_user),
+ db_session: Session = Depends(get_db_session),
+):
+ return await get_courses_by_product(
+ request, org_id, product_id, current_user, db_session
+ )
+
+@router.get("/{org_id}/courses/{course_id}/products")
+async def api_get_products_by_course(
+ request: Request,
+ org_id: int,
+ course_id: int,
+ current_user: PublicUser = Depends(get_current_user),
+ db_session: Session = Depends(get_db_session),
+):
+ return await get_products_by_course(
+ request, org_id, course_id, current_user, db_session
+ )
+
+# Payments webhooks
+
+@router.post("/stripe/webhook")
+async def api_handle_connected_accounts_stripe_webhook(
+ request: Request,
+ db_session: Session = Depends(get_db_session),
+):
+ return await handle_stripe_webhook(request, "standard", db_session)
+
+@router.post("/stripe/webhook/connect")
+async def api_handle_connected_accounts_stripe_webhook_connect(
+ request: Request,
+ db_session: Session = Depends(get_db_session),
+):
+ return await handle_stripe_webhook(request, "connect", db_session)
+
+# Payments checkout
+
+@router.post("/{org_id}/stripe/checkout/product/{product_id}")
+async def api_create_checkout_session(
+ request: Request,
+ org_id: int,
+ product_id: int,
+ redirect_uri: str,
+ current_user: PublicUser = Depends(get_current_user),
+ db_session: Session = Depends(get_db_session),
+):
+ return await create_checkout_session(request, org_id, product_id, redirect_uri, current_user, db_session)
+
+@router.get("/{org_id}/courses/{course_id}/access")
+async def api_check_course_paid_access(
+ request: Request,
+ org_id: int,
+ course_id: int,
+ current_user: PublicUser = Depends(get_current_user),
+ db_session: Session = Depends(get_db_session),
+):
+ """
+ Check if current user has paid access to a specific course
+ """
+ return {
+ "has_access": await check_course_paid_access(
+ course_id=course_id,
+ user=current_user,
+ db_session=db_session
+ )
+ }
+
+@router.get("/{org_id}/customers")
+async def api_get_customers(
+ request: Request,
+ org_id: int,
+ current_user: PublicUser = Depends(get_current_user),
+ db_session: Session = Depends(get_db_session),
+):
+ """
+ Get list of customers and their subscriptions for an organization
+ """
+ return await get_customers(request, org_id, current_user, db_session)
+
+@router.get("/{org_id}/courses/owned")
+async def api_get_owned_courses(
+ request: Request,
+ org_id: int,
+ current_user: PublicUser = Depends(get_current_user),
+ db_session: Session = Depends(get_db_session),
+):
+ return await get_owned_courses(request, current_user, db_session)
+
+@router.put("/{org_id}/stripe/account")
+async def api_update_stripe_account_id(
+ request: Request,
+ org_id: int,
+ stripe_account_id: str,
+ current_user: PublicUser = Depends(get_current_user),
+ db_session: Session = Depends(get_db_session),
+):
+ return await update_stripe_account_id(
+ request, org_id, stripe_account_id, current_user, db_session
+ )
+
+@router.post("/{org_id}/stripe/connect/link")
+async def api_generate_stripe_connect_link(
+ request: Request,
+ org_id: int,
+ redirect_uri: str,
+ current_user: PublicUser = Depends(get_current_user),
+ db_session: Session = Depends(get_db_session),
+):
+ """
+ Generate a Stripe OAuth link for connecting a Stripe account
+ """
+ return await generate_stripe_connect_link(
+ request, org_id, redirect_uri, current_user, db_session
+ )
+
+@router.get("/stripe/oauth/callback")
+async def stripe_oauth_callback(
+ request: Request,
+ code: str,
+ org_id: int,
+ current_user: PublicUser = Depends(get_current_user),
+ db_session: Session = Depends(get_db_session),
+):
+ return await handle_stripe_oauth_callback(request, org_id, code, current_user, db_session)
\ No newline at end of file
diff --git a/apps/api/src/routers/health.py b/apps/api/src/routers/health.py
new file mode 100644
index 00000000..68d78588
--- /dev/null
+++ b/apps/api/src/routers/health.py
@@ -0,0 +1,11 @@
+from fastapi import Depends, APIRouter
+from sqlmodel import Session
+from src.services.health.health import check_health
+from src.core.events.database import get_db_session
+
+
+router = APIRouter()
+
+@router.get("")
+async def health(db_session: Session = Depends(get_db_session)):
+ return await check_health(db_session)
\ No newline at end of file
diff --git a/apps/api/src/security/rbac/rbac.py b/apps/api/src/security/rbac/rbac.py
index 04b64ca1..66a2dc89 100644
--- a/apps/api/src/security/rbac/rbac.py
+++ b/apps/api/src/security/rbac/rbac.py
@@ -142,7 +142,7 @@ async def authorization_verify_based_on_org_admin_status(
# Tested and working
-async def authorization_verify_based_on_roles_and_authorship_and_usergroups(
+async def authorization_verify_based_on_roles_and_authorship(
request: Request,
user_id: int,
action: Literal["read", "update", "delete", "create"],
diff --git a/apps/api/src/services/courses/activities/activities.py b/apps/api/src/services/courses/activities/activities.py
index 8b5c7d03..985a48af 100644
--- a/apps/api/src/services/courses/activities/activities.py
+++ b/apps/api/src/services/courses/activities/activities.py
@@ -3,7 +3,7 @@ from sqlmodel import Session, select
from src.db.courses.courses import Course
from src.db.courses.chapters import Chapter
from src.security.rbac.rbac import (
- authorization_verify_based_on_roles_and_authorship_and_usergroups,
+ authorization_verify_based_on_roles_and_authorship,
authorization_verify_if_element_is_public,
authorization_verify_if_user_is_anon,
)
@@ -14,6 +14,8 @@ from fastapi import HTTPException, Request
from uuid import uuid4
from datetime import datetime
+from src.services.payments.payments_access import check_activity_paid_access
+
####################################################
# CRUD
@@ -112,7 +114,16 @@ async def get_activity(
# RBAC check
await rbac_check(request, course.course_uuid, current_user, "read", db_session)
- activity = ActivityRead.model_validate(activity)
+ # Paid access check
+ has_paid_access = await check_activity_paid_access(
+ activity_id=activity.id if activity.id else 0,
+ user=current_user,
+ db_session=db_session
+ )
+
+ activity_read = ActivityRead.model_validate(activity)
+ activity_read.content = activity_read.content if has_paid_access else { "paid_access": False }
+ activity = activity_read
return activity
@@ -258,30 +269,32 @@ async def get_activities(
async def rbac_check(
request: Request,
- course_uuid: str,
+ element_uuid: str,
current_user: PublicUser | AnonymousUser,
action: Literal["create", "read", "update", "delete"],
db_session: Session,
):
+
+
if action == "read":
if current_user.id == 0: # Anonymous user
res = await authorization_verify_if_element_is_public(
- request, course_uuid, action, db_session
+ request, element_uuid, action, db_session
)
return res
else:
- res = await authorization_verify_based_on_roles_and_authorship_and_usergroups(
- request, current_user.id, action, course_uuid, db_session
+ res = await authorization_verify_based_on_roles_and_authorship(
+ request, current_user.id, action, element_uuid, db_session
)
return res
else:
+ # For non-read actions, proceed with regular RBAC checks
await authorization_verify_if_user_is_anon(current_user.id)
-
- await authorization_verify_based_on_roles_and_authorship_and_usergroups(
+ await authorization_verify_based_on_roles_and_authorship(
request,
current_user.id,
action,
- course_uuid,
+ element_uuid,
db_session,
)
diff --git a/apps/api/src/services/courses/activities/assignments.py b/apps/api/src/services/courses/activities/assignments.py
index 99b59b26..ae563e08 100644
--- a/apps/api/src/services/courses/activities/assignments.py
+++ b/apps/api/src/services/courses/activities/assignments.py
@@ -34,7 +34,7 @@ from src.security.features_utils.usage import (
increase_feature_usage,
)
from src.security.rbac.rbac import (
- authorization_verify_based_on_roles_and_authorship_and_usergroups,
+ authorization_verify_based_on_roles_and_authorship,
authorization_verify_if_element_is_public,
authorization_verify_if_user_is_anon,
)
@@ -1666,7 +1666,7 @@ async def rbac_check(
return res
else:
res = (
- await authorization_verify_based_on_roles_and_authorship_and_usergroups(
+ await authorization_verify_based_on_roles_and_authorship(
request, current_user.id, action, course_uuid, db_session
)
)
@@ -1674,7 +1674,7 @@ async def rbac_check(
else:
await authorization_verify_if_user_is_anon(current_user.id)
- await authorization_verify_based_on_roles_and_authorship_and_usergroups(
+ await authorization_verify_based_on_roles_and_authorship(
request,
current_user.id,
action,
diff --git a/apps/api/src/services/courses/activities/pdf.py b/apps/api/src/services/courses/activities/pdf.py
index 30b4db9d..e6f2ca51 100644
--- a/apps/api/src/services/courses/activities/pdf.py
+++ b/apps/api/src/services/courses/activities/pdf.py
@@ -3,7 +3,7 @@ from src.db.courses.courses import Course
from src.db.organizations import Organization
from sqlmodel import Session, select
from src.security.rbac.rbac import (
- authorization_verify_based_on_roles_and_authorship_and_usergroups,
+ authorization_verify_based_on_roles_and_authorship,
authorization_verify_if_user_is_anon,
)
from src.db.courses.chapters import Chapter
@@ -150,7 +150,7 @@ async def rbac_check(
):
await authorization_verify_if_user_is_anon(current_user.id)
- await authorization_verify_based_on_roles_and_authorship_and_usergroups(
+ await authorization_verify_based_on_roles_and_authorship(
request,
current_user.id,
action,
diff --git a/apps/api/src/services/courses/activities/video.py b/apps/api/src/services/courses/activities/video.py
index 3396607c..da428865 100644
--- a/apps/api/src/services/courses/activities/video.py
+++ b/apps/api/src/services/courses/activities/video.py
@@ -5,7 +5,7 @@ from src.db.organizations import Organization
from pydantic import BaseModel
from sqlmodel import Session, select
from src.security.rbac.rbac import (
- authorization_verify_based_on_roles_and_authorship_and_usergroups,
+ authorization_verify_based_on_roles_and_authorship,
authorization_verify_if_user_is_anon,
)
from src.db.courses.chapters import Chapter
@@ -232,7 +232,7 @@ async def rbac_check(
):
await authorization_verify_if_user_is_anon(current_user.id)
- await authorization_verify_based_on_roles_and_authorship_and_usergroups(
+ await authorization_verify_based_on_roles_and_authorship(
request,
current_user.id,
action,
diff --git a/apps/api/src/services/courses/chapters.py b/apps/api/src/services/courses/chapters.py
index fd34d842..5bc3e9bb 100644
--- a/apps/api/src/services/courses/chapters.py
+++ b/apps/api/src/services/courses/chapters.py
@@ -4,7 +4,7 @@ from uuid import uuid4
from sqlmodel import Session, select
from src.db.users import AnonymousUser
from src.security.rbac.rbac import (
- authorization_verify_based_on_roles_and_authorship_and_usergroups,
+ authorization_verify_based_on_roles_and_authorship,
authorization_verify_if_element_is_public,
authorization_verify_if_user_is_anon,
)
@@ -561,14 +561,14 @@ async def rbac_check(
)
return res
else:
- res = await authorization_verify_based_on_roles_and_authorship_and_usergroups(
+ res = await authorization_verify_based_on_roles_and_authorship(
request, current_user.id, action, course_uuid, db_session
)
return res
else:
await authorization_verify_if_user_is_anon(current_user.id)
- await authorization_verify_based_on_roles_and_authorship_and_usergroups(
+ await authorization_verify_based_on_roles_and_authorship(
request,
current_user.id,
action,
diff --git a/apps/api/src/services/courses/collections.py b/apps/api/src/services/courses/collections.py
index 9c5f8412..d54faedf 100644
--- a/apps/api/src/services/courses/collections.py
+++ b/apps/api/src/services/courses/collections.py
@@ -4,7 +4,7 @@ from uuid import uuid4
from sqlmodel import Session, select
from src.db.users import AnonymousUser
from src.security.rbac.rbac import (
- authorization_verify_based_on_roles_and_authorship_and_usergroups,
+ authorization_verify_based_on_roles_and_authorship,
authorization_verify_if_element_is_public,
authorization_verify_if_user_is_anon,
)
@@ -300,7 +300,7 @@ async def rbac_check(
)
else:
res = (
- await authorization_verify_based_on_roles_and_authorship_and_usergroups(
+ await authorization_verify_based_on_roles_and_authorship(
request, current_user.id, action, collection_uuid, db_session
)
)
@@ -308,7 +308,7 @@ async def rbac_check(
else:
await authorization_verify_if_user_is_anon(current_user.id)
- await authorization_verify_based_on_roles_and_authorship_and_usergroups(
+ await authorization_verify_based_on_roles_and_authorship(
request,
current_user.id,
action,
diff --git a/apps/api/src/services/courses/courses.py b/apps/api/src/services/courses/courses.py
index fa140944..2233d9c9 100644
--- a/apps/api/src/services/courses/courses.py
+++ b/apps/api/src/services/courses/courses.py
@@ -1,7 +1,6 @@
-from typing import Literal
+from typing import Literal, List
from uuid import uuid4
-from sqlalchemy import union
-from sqlmodel import Session, select
+from sqlmodel import Session, select, or_, and_
from src.db.usergroup_resources import UserGroupResource
from src.db.usergroup_user import UserGroupUser
from src.db.organizations import Organization
@@ -21,7 +20,7 @@ from src.db.courses.courses import (
FullCourseReadWithTrail,
)
from src.security.rbac.rbac import (
- authorization_verify_based_on_roles_and_authorship_and_usergroups,
+ authorization_verify_based_on_roles_and_authorship,
authorization_verify_if_element_is_public,
authorization_verify_if_user_is_anon,
)
@@ -151,6 +150,69 @@ async def get_course_meta(
trail=trail if trail else None,
)
+async def get_courses_orgslug(
+ request: Request,
+ current_user: PublicUser | AnonymousUser,
+ org_slug: str,
+ db_session: Session,
+ page: int = 1,
+ limit: int = 10,
+) -> List[CourseRead]:
+ offset = (page - 1) * limit
+
+ # Base query
+ query = (
+ select(Course)
+ .join(Organization)
+ .where(Organization.slug == org_slug)
+ )
+
+ if isinstance(current_user, AnonymousUser):
+ # For anonymous users, only show public courses
+ query = query.where(Course.public == True)
+ else:
+ # For authenticated users, show:
+ # 1. Public courses
+ # 2. Courses not in any UserGroup
+ # 3. Courses in UserGroups where the user is a member
+ # 4. Courses where the user is a resource author
+ query = (
+ query
+ .outerjoin(UserGroupResource, UserGroupResource.resource_uuid == Course.course_uuid) # type: ignore
+ .outerjoin(UserGroupUser, and_(
+ UserGroupUser.usergroup_id == UserGroupResource.usergroup_id,
+ UserGroupUser.user_id == current_user.id
+ ))
+ .outerjoin(ResourceAuthor, ResourceAuthor.resource_uuid == Course.course_uuid) # type: ignore
+ .where(or_(
+ Course.public == True,
+ UserGroupResource.resource_uuid == None, # Courses not in any UserGroup # noqa: E711
+ UserGroupUser.user_id == current_user.id, # Courses in UserGroups where user is a member
+ ResourceAuthor.user_id == current_user.id # Courses where user is a resource author
+ ))
+ )
+
+ # Apply pagination
+ query = query.offset(offset).limit(limit).distinct()
+
+ courses = db_session.exec(query).all()
+
+ # Fetch authors for each course
+ course_reads = []
+ for course in courses:
+ authors_query = (
+ select(User)
+ .join(ResourceAuthor, ResourceAuthor.user_id == User.id) # type: ignore
+ .where(ResourceAuthor.resource_uuid == course.course_uuid)
+ )
+ authors = db_session.exec(authors_query).all()
+
+ course_read = CourseRead.model_validate(course)
+ course_read.authors = [UserRead.model_validate(author) for author in authors]
+ course_reads.append(course_read)
+
+ return course_reads
+
async def create_course(
request: Request,
@@ -366,72 +428,7 @@ async def delete_course(
return {"detail": "Course deleted"}
-async def get_courses_orgslug(
- request: Request,
- current_user: PublicUser | AnonymousUser,
- org_slug: str,
- db_session: Session,
- page: int = 1,
- limit: int = 10,
-):
- # TODO : This entire function is a mess. It needs to be rewritten.
-
- # Query for public courses
- statement_public = (
- select(Course)
- .join(Organization)
- .where(Organization.slug == org_slug, Course.public == True)
- )
-
- # Query for courses where the current user is an author
- statement_author = (
- select(Course)
- .join(Organization)
- .join(ResourceAuthor, ResourceAuthor.user_id == current_user.id) # type: ignore
- .where(
- Organization.slug == org_slug,
- ResourceAuthor.resource_uuid == Course.course_uuid,
- )
- )
-
- # Query for courses where the current user is in a user group that has access to the course
- statement_usergroup = (
- select(Course)
- .join(Organization)
- .join(UserGroupResource, UserGroupResource.resource_uuid == Course.course_uuid) # type: ignore
- .join(
- UserGroupUser, UserGroupUser.usergroup_id == UserGroupResource.usergroup_id # type: ignore
- )
- .where(Organization.slug == org_slug, UserGroupUser.user_id == current_user.id)
- )
-
- # Combine the results
- statement_complete = union(
- statement_public, statement_author, statement_usergroup
- ).subquery()
-
- # TODO: migrate this to exec
- courses = db_session.execute(select(statement_complete)).all() # type: ignore
-
- # TODO: I have no idea why this is necessary, but it is
- courses = [CourseRead(**course._asdict(), authors=[]) for course in courses]
-
- # for every course, get the authors
- for course in courses:
- authors_statement = (
- select(User)
- .join(ResourceAuthor)
- .where(ResourceAuthor.resource_uuid == course.course_uuid)
- )
- authors = db_session.exec(authors_statement).all()
-
- # convert from User to UserRead
- authors = [UserRead.model_validate(author) for author in authors]
-
- course.authors = authors
-
- return courses
## 🔒 RBAC Utils ##
@@ -452,7 +449,7 @@ async def rbac_check(
return res
else:
res = (
- await authorization_verify_based_on_roles_and_authorship_and_usergroups(
+ await authorization_verify_based_on_roles_and_authorship(
request, current_user.id, action, course_uuid, db_session
)
)
@@ -460,7 +457,7 @@ async def rbac_check(
else:
await authorization_verify_if_user_is_anon(current_user.id)
- await authorization_verify_based_on_roles_and_authorship_and_usergroups(
+ await authorization_verify_based_on_roles_and_authorship(
request,
current_user.id,
action,
diff --git a/apps/api/src/services/health/__init__.py b/apps/api/src/services/health/__init__.py
new file mode 100644
index 00000000..e69de29b
diff --git a/apps/api/src/services/health/health.py b/apps/api/src/services/health/health.py
new file mode 100644
index 00000000..8daccbd9
--- /dev/null
+++ b/apps/api/src/services/health/health.py
@@ -0,0 +1,21 @@
+from fastapi import HTTPException
+from sqlmodel import Session, select
+from src.db.organizations import Organization
+
+async def check_database_health(db_session: Session) -> bool:
+ statement = select(Organization)
+ result = db_session.exec(statement)
+
+ if not result:
+ return False
+
+ return True
+
+async def check_health(db_session: Session) -> bool:
+ # Check database health
+ database_healthy = await check_database_health(db_session)
+
+ if not database_healthy:
+ raise HTTPException(status_code=503, detail="Database is not healthy")
+
+ return True
diff --git a/apps/api/src/services/install/install.py b/apps/api/src/services/install/install.py
index 935caf80..84841fb4 100644
--- a/apps/api/src/services/install/install.py
+++ b/apps/api/src/services/install/install.py
@@ -330,7 +330,7 @@ def install_create_organization(org_object: OrganizationCreate, db_session: Sess
# Org Config
org_config = OrganizationConfigBase(
- config_version="1.1",
+ config_version="1.2",
general=OrgGeneralConfig(
enabled=True,
color="normal",
@@ -345,7 +345,7 @@ def install_create_organization(org_object: OrganizationCreate, db_session: Sess
storage=StorageOrgConfig(enabled=True, limit=0),
ai=AIOrgConfig(enabled=True, limit=0, model="gpt-4o-mini"),
assignments=AssignmentOrgConfig(enabled=True, limit=0),
- payments=PaymentOrgConfig(enabled=True, stripe_key=""),
+ payments=PaymentOrgConfig(enabled=False),
discussions=DiscussionOrgConfig(enabled=True, limit=0),
analytics=AnalyticsOrgConfig(enabled=True, limit=0),
collaboration=CollaborationOrgConfig(enabled=True, limit=0),
diff --git a/apps/api/src/services/orgs/orgs.py b/apps/api/src/services/orgs/orgs.py
index c7e92000..13c084ed 100644
--- a/apps/api/src/services/orgs/orgs.py
+++ b/apps/api/src/services/orgs/orgs.py
@@ -26,7 +26,7 @@ from src.security.rbac.rbac import (
authorization_verify_based_on_org_admin_status,
authorization_verify_if_user_is_anon,
)
-from src.db.users import AnonymousUser, PublicUser
+from src.db.users import AnonymousUser, InternalUser, PublicUser
from src.db.user_organizations import UserOrganization
from src.db.organizations import (
Organization,
@@ -682,13 +682,17 @@ async def get_org_join_mechanism(
async def rbac_check(
request: Request,
org_uuid: str,
- current_user: PublicUser | AnonymousUser,
+ current_user: PublicUser | AnonymousUser | InternalUser,
action: Literal["create", "read", "update", "delete"],
db_session: Session,
):
# Organizations are readable by anyone
if action == "read":
return True
+
+ # Internal users can do anything
+ if isinstance(current_user, InternalUser):
+ return True
else:
isUserAnon = await authorization_verify_if_user_is_anon(current_user.id)
diff --git a/apps/api/src/services/payments/payments_access.py b/apps/api/src/services/payments/payments_access.py
new file mode 100644
index 00000000..6f2632ac
--- /dev/null
+++ b/apps/api/src/services/payments/payments_access.py
@@ -0,0 +1,98 @@
+from sqlmodel import Session, select
+from src.db.payments.payments_users import PaymentStatusEnum, PaymentsUser
+from src.db.users import PublicUser, AnonymousUser
+from src.db.payments.payments_courses import PaymentsCourse
+from src.db.courses.activities import Activity
+from src.db.courses.courses import Course
+from fastapi import HTTPException
+
+async def check_activity_paid_access(
+ activity_id: int,
+ user: PublicUser | AnonymousUser,
+ db_session: Session,
+) -> bool:
+ """
+ Check if a user has access to a specific activity
+ Returns True if:
+ - User is an author of the course
+ - Activity is in a free course
+ - User has a valid subscription for the course
+ """
+
+
+ # Get activity and associated course
+ statement = select(Activity).where(Activity.id == activity_id)
+ activity = db_session.exec(statement).first()
+
+ if not activity:
+ raise HTTPException(status_code=404, detail="Activity not found")
+
+ # Check if course exists
+ statement = select(Course).where(Course.id == activity.course_id)
+ course = db_session.exec(statement).first()
+
+ if not course:
+ raise HTTPException(status_code=404, detail="Course not found")
+
+ # Check if course is linked to a product
+ statement = select(PaymentsCourse).where(PaymentsCourse.course_id == course.id)
+ course_payment = db_session.exec(statement).first()
+
+ # If course is not linked to any product, it's free
+ if not course_payment:
+ return True
+
+ # Anonymous users have no access to paid activities
+ if isinstance(user, AnonymousUser):
+ return False
+
+ # Check if user has a valid subscription or payment
+ statement = select(PaymentsUser).where(
+ PaymentsUser.user_id == user.id,
+ PaymentsUser.payment_product_id == course_payment.payment_product_id,
+ PaymentsUser.status.in_( # type: ignore
+ [PaymentStatusEnum.ACTIVE, PaymentStatusEnum.COMPLETED]
+ ),
+ )
+ access = db_session.exec(statement).first()
+
+ return bool(access)
+
+async def check_course_paid_access(
+ course_id: int,
+ user: PublicUser | AnonymousUser,
+ db_session: Session,
+) -> bool:
+ """
+ Check if a user has paid access to a specific course
+ Returns True if:
+ - User is an author of the course
+ - Course is free (not linked to any product)
+ - User has a valid subscription for the course
+ """
+ # Check if course exists
+ statement = select(Course).where(Course.id == course_id)
+ course = db_session.exec(statement).first()
+
+ if not course:
+ raise HTTPException(status_code=404, detail="Course not found")
+
+ # Check if course is linked to a product
+ statement = select(PaymentsCourse).where(PaymentsCourse.course_id == course.id)
+ course_payment = db_session.exec(statement).first()
+
+ # If course is not linked to any product, it's free
+ if not course_payment:
+ return True
+
+ # Check if user has a valid subscription
+ statement = select(PaymentsUser).where(
+ PaymentsUser.user_id == user.id,
+ PaymentsUser.payment_product_id == course_payment.payment_product_id,
+ PaymentsUser.status.in_( # type: ignore
+ [PaymentStatusEnum.ACTIVE, PaymentStatusEnum.COMPLETED]
+ ),
+ )
+ subscription = db_session.exec(statement).first()
+
+ return bool(subscription)
diff --git a/apps/api/src/services/payments/payments_config.py b/apps/api/src/services/payments/payments_config.py
new file mode 100644
index 00000000..35c60640
--- /dev/null
+++ b/apps/api/src/services/payments/payments_config.py
@@ -0,0 +1,139 @@
+from typing import Literal
+from fastapi import HTTPException, Request
+from sqlmodel import Session, select
+from src.db.payments.payments import (
+ PaymentProviderEnum,
+ PaymentsConfig,
+ PaymentsConfigUpdate,
+ PaymentsConfigRead,
+)
+from src.db.users import PublicUser, AnonymousUser, InternalUser
+from src.db.organizations import Organization
+from src.services.orgs.orgs import rbac_check
+
+
+async def init_payments_config(
+ request: Request,
+ org_id: int,
+ provider: Literal["stripe"],
+ current_user: PublicUser | AnonymousUser,
+ db_session: Session,
+) -> PaymentsConfig:
+ # Validate organization exists
+ org = db_session.exec(
+ select(Organization).where(Organization.id == org_id)
+ ).first()
+ if not org:
+ raise HTTPException(status_code=404, detail="Organization not found")
+
+ # Verify permissions
+ await rbac_check(request, org.org_uuid, current_user, "create", db_session)
+
+ # Check for existing config
+ existing_config = db_session.exec(
+ select(PaymentsConfig).where(PaymentsConfig.org_id == org_id)
+ ).first()
+
+ if existing_config:
+ raise HTTPException(
+ status_code=409,
+ detail="Payments config already exists for this organization"
+ )
+
+ # Initialize new config
+ new_config = PaymentsConfig(
+ org_id=org_id,
+ provider=PaymentProviderEnum.STRIPE,
+ provider_config={
+ "onboarding_completed": False,
+ },
+ provider_specific_id=None
+ )
+
+ # Save to database
+ db_session.add(new_config)
+ db_session.commit()
+ db_session.refresh(new_config)
+
+ return new_config
+
+
+async def get_payments_config(
+ request: Request,
+ org_id: int,
+ current_user: PublicUser | AnonymousUser | InternalUser,
+ db_session: Session,
+) -> list[PaymentsConfigRead]:
+ # Check if organization exists
+ statement = select(Organization).where(Organization.id == org_id)
+ org = db_session.exec(statement).first()
+ if not org:
+ raise HTTPException(status_code=404, detail="Organization not found")
+
+ # RBAC check
+ await rbac_check(request, org.org_uuid, current_user, "read", db_session)
+
+ # Get payments config
+ statement = select(PaymentsConfig).where(PaymentsConfig.org_id == org_id)
+ configs = db_session.exec(statement).all()
+
+ return [PaymentsConfigRead.model_validate(config) for config in configs]
+
+
+async def update_payments_config(
+ request: Request,
+ org_id: int,
+ payments_config: PaymentsConfigUpdate,
+ current_user: PublicUser | AnonymousUser | InternalUser,
+ db_session: Session,
+) -> PaymentsConfig:
+ # Check if organization exists
+ statement = select(Organization).where(Organization.id == org_id)
+ org = db_session.exec(statement).first()
+ if not org:
+ raise HTTPException(status_code=404, detail="Organization not found")
+
+ # RBAC check
+ await rbac_check(request, org.org_uuid, current_user, "update", db_session)
+
+ # Get existing payments config
+ statement = select(PaymentsConfig).where(PaymentsConfig.org_id == org_id)
+ config = db_session.exec(statement).first()
+ if not config:
+ raise HTTPException(status_code=404, detail="Payments config not found")
+
+ # Update config
+ for key, value in payments_config.model_dump().items():
+ setattr(config, key, value)
+
+ db_session.add(config)
+ db_session.commit()
+ db_session.refresh(config)
+
+ return config
+
+
+async def delete_payments_config(
+ request: Request,
+ org_id: int,
+ current_user: PublicUser | AnonymousUser,
+ db_session: Session,
+) -> None:
+ # Check if organization exists
+ statement = select(Organization).where(Organization.id == org_id)
+ org = db_session.exec(statement).first()
+ if not org:
+ raise HTTPException(status_code=404, detail="Organization not found")
+
+ # RBAC check
+ await rbac_check(request, org.org_uuid, current_user, "delete", db_session)
+
+ # Get existing payments config
+ statement = select(PaymentsConfig).where(PaymentsConfig.org_id == org_id)
+ config = db_session.exec(statement).first()
+ if not config:
+ raise HTTPException(status_code=404, detail="Payments config not found")
+
+ # Delete config
+ db_session.delete(config)
+ db_session.commit()
diff --git a/apps/api/src/services/payments/payments_courses.py b/apps/api/src/services/payments/payments_courses.py
new file mode 100644
index 00000000..1382e408
--- /dev/null
+++ b/apps/api/src/services/payments/payments_courses.py
@@ -0,0 +1,123 @@
+from fastapi import HTTPException, Request
+from sqlmodel import Session, select
+from src.db.payments.payments_courses import PaymentsCourse
+from src.db.payments.payments_products import PaymentsProduct
+from src.db.courses.courses import Course
+from src.db.users import PublicUser, AnonymousUser
+from src.services.courses.courses import rbac_check
+
+async def link_course_to_product(
+ request: Request,
+ org_id: int,
+ course_id: int,
+ product_id: int,
+ current_user: PublicUser | AnonymousUser,
+ db_session: Session,
+):
+ # Check if course exists and user has permission
+ statement = select(Course).where(Course.id == course_id)
+ course = db_session.exec(statement).first()
+
+ if not course:
+ raise HTTPException(status_code=404, detail="Course not found")
+
+ # RBAC check
+ await rbac_check(request, course.course_uuid, current_user, "update", db_session)
+
+ # Check if product exists
+ statement = select(PaymentsProduct).where(
+ PaymentsProduct.id == product_id,
+ PaymentsProduct.org_id == org_id
+ )
+ product = db_session.exec(statement).first()
+
+ if not product:
+ raise HTTPException(status_code=404, detail="Product not found")
+
+ # Check if course is already linked to another product
+ statement = select(PaymentsCourse).where(PaymentsCourse.course_id == course.id)
+ existing_link = db_session.exec(statement).first()
+
+ if existing_link:
+ raise HTTPException(
+ status_code=400,
+ detail="Course is already linked to a product"
+ )
+
+ # Create new payment course link
+ payment_course = PaymentsCourse(
+ course_id=course.id, # type: ignore
+ payment_product_id=product_id,
+ org_id=org_id,
+ )
+
+ db_session.add(payment_course)
+ db_session.commit()
+
+ return {"message": "Course linked to product successfully"}
+
+async def unlink_course_from_product(
+ request: Request,
+ org_id: int,
+ course_id: int,
+ current_user: PublicUser | AnonymousUser,
+ db_session: Session,
+):
+ # Check if course exists and user has permission
+ statement = select(Course).where(Course.id == course_id)
+ course = db_session.exec(statement).first()
+
+ if not course:
+ raise HTTPException(status_code=404, detail="Course not found")
+
+ # RBAC check
+ await rbac_check(request, course.course_uuid, current_user, "update", db_session)
+
+ # Find and delete the payment course link
+ statement = select(PaymentsCourse).where(
+ PaymentsCourse.course_id == course.id,
+ PaymentsCourse.org_id == org_id
+ )
+ payment_course = db_session.exec(statement).first()
+
+ if not payment_course:
+ raise HTTPException(
+ status_code=404,
+ detail="Course is not linked to any product"
+ )
+
+ db_session.delete(payment_course)
+ db_session.commit()
+
+ return {"message": "Course unlinked from product successfully"}
+
+async def get_courses_by_product(
+ request: Request,
+ org_id: int,
+ product_id: int,
+ current_user: PublicUser | AnonymousUser,
+ db_session: Session,
+):
+ # Check if product exists
+ statement = select(PaymentsProduct).where(
+ PaymentsProduct.id == product_id,
+ PaymentsProduct.org_id == org_id
+ )
+ product = db_session.exec(statement).first()
+
+ if not product:
+ raise HTTPException(status_code=404, detail="Product not found")
+
+ # Get all courses linked to this product with explicit join
+ statement = (
+ select(Course)
+ .select_from(Course)
+ .join(PaymentsCourse, Course.id == PaymentsCourse.course_id) # type: ignore
+ .where(
+ PaymentsCourse.payment_product_id == product_id,
+ PaymentsCourse.org_id == org_id
+ )
+ )
+ courses = db_session.exec(statement).all()
+
+ return courses
diff --git a/apps/api/src/services/payments/payments_customers.py b/apps/api/src/services/payments/payments_customers.py
new file mode 100644
index 00000000..3922bb0e
--- /dev/null
+++ b/apps/api/src/services/payments/payments_customers.py
@@ -0,0 +1,50 @@
+from fastapi import HTTPException, Request
+from sqlmodel import Session, select
+from src.db.organizations import Organization
+from src.db.users import PublicUser, AnonymousUser
+from src.db.payments.payments_users import PaymentsUser
+from src.services.orgs.orgs import rbac_check
+from src.services.payments.payments_products import get_payments_product
+from src.services.users.users import read_user_by_id
+
+async def get_customers(
+ request: Request,
+ org_id: int,
+ current_user: PublicUser | AnonymousUser,
+ db_session: Session,
+):
+ # Check if organization exists
+ statement = select(Organization).where(Organization.id == org_id)
+ org = db_session.exec(statement).first()
+ if not org:
+ raise HTTPException(status_code=404, detail="Organization not found")
+
+ # RBAC check
+ await rbac_check(request, org.org_uuid, current_user, "read", db_session)
+
+ # Get all payment users for the organization
+ statement = select(PaymentsUser).where(PaymentsUser.org_id == org_id)
+ payment_users = db_session.exec(statement).all()
+
+ customers_data = []
+
+ for payment_user in payment_users:
+ # Get user data
+ user = await read_user_by_id(request, db_session, current_user, payment_user.user_id)
+
+ # Get product data
+ if org.id is None:
+ raise HTTPException(status_code=400, detail="Invalid organization ID")
+ product = await get_payments_product(request, org.id, payment_user.payment_product_id, current_user, db_session)
+
+ customer_data = {
+ 'payment_user_id': payment_user.id,
+ 'user': user if user else None,
+ 'product': product if product else None,
+ 'status': payment_user.status,
+ 'creation_date': payment_user.creation_date,
+ 'update_date': payment_user.update_date
+ }
+ customers_data.append(customer_data)
+
+ return customers_data
\ No newline at end of file
diff --git a/apps/api/src/services/payments/payments_products.py b/apps/api/src/services/payments/payments_products.py
new file mode 100644
index 00000000..81874e80
--- /dev/null
+++ b/apps/api/src/services/payments/payments_products.py
@@ -0,0 +1,216 @@
+from fastapi import HTTPException, Request
+from sqlmodel import Session, select
+from src.db.courses.courses import Course
+from src.db.payments.payments import PaymentsConfig
+from src.db.payments.payments_courses import PaymentsCourse
+from src.db.payments.payments_products import (
+ PaymentsProduct,
+ PaymentsProductCreate,
+ PaymentsProductUpdate,
+ PaymentsProductRead,
+)
+from src.db.payments.payments_users import PaymentStatusEnum, PaymentsUser
+from src.db.users import PublicUser, AnonymousUser
+from src.db.organizations import Organization
+from src.services.orgs.orgs import rbac_check
+from datetime import datetime
+
+from src.services.payments.payments_stripe import archive_stripe_product, create_stripe_product, update_stripe_product
+
+async def create_payments_product(
+ request: Request,
+ org_id: int,
+ payments_product: PaymentsProductCreate,
+ current_user: PublicUser | AnonymousUser,
+ db_session: Session,
+) -> PaymentsProductRead:
+ # Check if organization exists
+ statement = select(Organization).where(Organization.id == org_id)
+ org = db_session.exec(statement).first()
+ if not org:
+ raise HTTPException(status_code=404, detail="Organization not found")
+
+ # RBAC check
+ await rbac_check(request, org.org_uuid, current_user, "create", db_session)
+
+ # Check if payments config exists, has a valid id, and is active
+ statement = select(PaymentsConfig).where(PaymentsConfig.org_id == org_id)
+ config = db_session.exec(statement).first()
+ if not config or config.id is None:
+ raise HTTPException(status_code=404, detail="Valid payments config not found")
+
+ if not config.active:
+ raise HTTPException(status_code=400, detail="Payments config is not active")
+
+ # Create new payments product
+ new_product = PaymentsProduct(**payments_product.model_dump(), org_id=org_id, payments_config_id=config.id)
+ new_product.creation_date = datetime.now()
+ new_product.update_date = datetime.now()
+
+ # Create product in Stripe
+ stripe_product = await create_stripe_product(request, org_id, new_product, current_user, db_session)
+ new_product.provider_product_id = stripe_product.id
+
+ # Save to DB
+ db_session.add(new_product)
+ db_session.commit()
+ db_session.refresh(new_product)
+
+ return PaymentsProductRead.model_validate(new_product)
+
+async def get_payments_product(
+ request: Request,
+ org_id: int,
+ product_id: int,
+ current_user: PublicUser | AnonymousUser,
+ db_session: Session,
+) -> PaymentsProductRead:
+ # Check if organization exists
+ statement = select(Organization).where(Organization.id == org_id)
+ org = db_session.exec(statement).first()
+ if not org:
+ raise HTTPException(status_code=404, detail="Organization not found")
+
+ # RBAC check
+ await rbac_check(request, org.org_uuid, current_user, "read", db_session)
+
+ # Get payments product
+ statement = select(PaymentsProduct).where(PaymentsProduct.id == product_id, PaymentsProduct.org_id == org_id)
+ product = db_session.exec(statement).first()
+ if not product:
+ raise HTTPException(status_code=404, detail="Payments product not found")
+
+ return PaymentsProductRead.model_validate(product)
+
+async def update_payments_product(
+ request: Request,
+ org_id: int,
+ product_id: int,
+ payments_product: PaymentsProductUpdate,
+ current_user: PublicUser | AnonymousUser,
+ db_session: Session,
+) -> PaymentsProductRead:
+ # Check if organization exists
+ statement = select(Organization).where(Organization.id == org_id)
+ org = db_session.exec(statement).first()
+ if not org:
+ raise HTTPException(status_code=404, detail="Organization not found")
+
+ # RBAC check
+ await rbac_check(request, org.org_uuid, current_user, "update", db_session)
+
+ # Get existing payments product
+ statement = select(PaymentsProduct).where(PaymentsProduct.id == product_id, PaymentsProduct.org_id == org_id)
+ product = db_session.exec(statement).first()
+ if not product:
+ raise HTTPException(status_code=404, detail="Payments product not found")
+
+ # Update product
+ for key, value in payments_product.model_dump().items():
+ setattr(product, key, value)
+
+ product.update_date = datetime.now()
+
+ db_session.add(product)
+ db_session.commit()
+ db_session.refresh(product)
+
+ # Update product in Stripe
+ await update_stripe_product(request, org_id, product.provider_product_id, product, current_user, db_session)
+
+ return PaymentsProductRead.model_validate(product)
+
+async def delete_payments_product(
+ request: Request,
+ org_id: int,
+ product_id: int,
+ current_user: PublicUser | AnonymousUser,
+ db_session: Session,
+) -> None:
+ # Check if organization exists
+ statement = select(Organization).where(Organization.id == org_id)
+ org = db_session.exec(statement).first()
+ if not org:
+ raise HTTPException(status_code=404, detail="Organization not found")
+
+ # RBAC check
+ await rbac_check(request, org.org_uuid, current_user, "delete", db_session)
+
+ # Get existing payments product
+ statement = select(PaymentsProduct).where(PaymentsProduct.id == product_id, PaymentsProduct.org_id == org_id)
+ product = db_session.exec(statement).first()
+ if not product:
+ raise HTTPException(status_code=404, detail="Payments product not found")
+
+ # Check if there are any payment users linked to this product
+ statement = select(PaymentsUser).where(
+ PaymentsUser.payment_product_id == product_id,
+ PaymentsUser.status.in_([PaymentStatusEnum.ACTIVE, PaymentStatusEnum.COMPLETED]) # type: ignore
+ )
+ payment_users = db_session.exec(statement).all()
+ if payment_users:
+ raise HTTPException(
+ status_code=400,
+ detail="Cannot delete product because users have paid access to it."
+ )
+
+ # Archive product in Stripe
+ await archive_stripe_product(request, org_id, product.provider_product_id, current_user, db_session)
+
+ # Delete product
+ db_session.delete(product)
+ db_session.commit()
+
+async def list_payments_products(
+ request: Request,
+ org_id: int,
+ current_user: PublicUser | AnonymousUser,
+ db_session: Session,
+) -> list[PaymentsProductRead]:
+ # Check if organization exists
+ statement = select(Organization).where(Organization.id == org_id)
+ org = db_session.exec(statement).first()
+ if not org:
+ raise HTTPException(status_code=404, detail="Organization not found")
+
+ # RBAC check
+ await rbac_check(request, org.org_uuid, current_user, "read", db_session)
+
+ # Get payments products ordered by id
+ statement = select(PaymentsProduct).where(PaymentsProduct.org_id == org_id).order_by(PaymentsProduct.id.desc()) # type: ignore
+ products = db_session.exec(statement).all()
+
+ return [PaymentsProductRead.model_validate(product) for product in products]
+
+async def get_products_by_course(
+ request: Request,
+ org_id: int,
+ course_id: int,
+ current_user: PublicUser | AnonymousUser,
+ db_session: Session,
+) -> list[PaymentsProductRead]:
+ # Check if course exists and user has permission
+ statement = select(Course).where(Course.id == course_id)
+ course = db_session.exec(statement).first()
+
+ if not course:
+ raise HTTPException(status_code=404, detail="Course not found")
+
+ # RBAC check
+ await rbac_check(request, course.course_uuid, current_user, "read", db_session)
+
+ # Get all products linked to this course with explicit join
+ statement = (
+ select(PaymentsProduct)
+ .select_from(PaymentsProduct)
+ .join(PaymentsCourse, PaymentsProduct.id == PaymentsCourse.payment_product_id) # type: ignore
+ .where(
+ PaymentsCourse.course_id == course_id,
+ PaymentsCourse.org_id == org_id
+ )
+ )
+ products = db_session.exec(statement).all()
+
+ return [PaymentsProductRead.model_validate(product) for product in products]
+
+
diff --git a/apps/api/src/services/payments/payments_stripe.py b/apps/api/src/services/payments/payments_stripe.py
new file mode 100644
index 00000000..536fac92
--- /dev/null
+++ b/apps/api/src/services/payments/payments_stripe.py
@@ -0,0 +1,473 @@
+import logging
+from typing import Literal
+from fastapi import HTTPException, Request
+from sqlmodel import Session
+import stripe
+from config.config import get_learnhouse_config
+from src.db.payments.payments import PaymentsConfigUpdate, PaymentsConfig
+from src.db.payments.payments_products import (
+ PaymentPriceTypeEnum,
+ PaymentProductTypeEnum,
+ PaymentsProduct,
+)
+from src.db.payments.payments_users import PaymentStatusEnum
+from src.db.users import AnonymousUser, InternalUser, PublicUser
+from src.services.payments.payments_config import (
+ get_payments_config,
+ update_payments_config,
+)
+from sqlmodel import select
+
+from src.services.payments.payments_users import (
+ create_payment_user,
+ delete_payment_user,
+)
+
+
+async def get_stripe_connected_account_id(
+ request: Request,
+ org_id: int,
+ current_user: PublicUser | AnonymousUser | InternalUser,
+ db_session: Session,
+):
+ # Get payments config
+ payments_config = await get_payments_config(request, org_id, current_user, db_session)
+
+ return payments_config[0].provider_specific_id
+
+
+async def get_stripe_internal_credentials(
+):
+ # Get payments config from config file
+ learnhouse_config = get_learnhouse_config()
+
+ if not learnhouse_config.payments_config.stripe.stripe_secret_key:
+ raise HTTPException(status_code=400, detail="Stripe secret key not configured")
+
+ if not learnhouse_config.payments_config.stripe.stripe_publishable_key:
+ raise HTTPException(
+ status_code=400, detail="Stripe publishable key not configured"
+ )
+
+ return {
+ "stripe_secret_key": learnhouse_config.payments_config.stripe.stripe_secret_key,
+ "stripe_publishable_key": learnhouse_config.payments_config.stripe.stripe_publishable_key,
+ "stripe_webhook_standard_secret": learnhouse_config.payments_config.stripe.stripe_webhook_standard_secret,
+ "stripe_webhook_connect_secret": learnhouse_config.payments_config.stripe.stripe_webhook_connect_secret,
+ }
+
+
+async def create_stripe_product(
+ request: Request,
+ org_id: int,
+ product_data: PaymentsProduct,
+ current_user: PublicUser | AnonymousUser,
+ db_session: Session,
+):
+ creds = await get_stripe_internal_credentials()
+
+ # Set the Stripe API key using the credentials
+ stripe.api_key = creds.get("stripe_secret_key")
+
+ # Prepare default_price_data based on price_type
+ if product_data.price_type == PaymentPriceTypeEnum.CUSTOMER_CHOICE:
+ default_price_data = {
+ "currency": product_data.currency,
+ "custom_unit_amount": {
+ "enabled": True,
+ "minimum": int(product_data.amount * 100), # Convert to cents
+ },
+ }
+ else:
+ default_price_data = {
+ "currency": product_data.currency,
+ "unit_amount": int(product_data.amount * 100), # Convert to cents
+ }
+
+ if product_data.product_type == PaymentProductTypeEnum.SUBSCRIPTION:
+ default_price_data["recurring"] = {"interval": "month"}
+
+ stripe_acc_id = await get_stripe_connected_account_id(request, org_id, current_user, db_session)
+
+ product = stripe.Product.create(
+ name=product_data.name,
+ description=product_data.description or "",
+ marketing_features=[
+ {"name": benefit.strip()}
+ for benefit in product_data.benefits.split(",")
+ if benefit.strip()
+ ],
+ default_price_data=default_price_data, # type: ignore
+ stripe_account=stripe_acc_id,
+ )
+
+ return product
+
+
+async def archive_stripe_product(
+ request: Request,
+ org_id: int,
+ product_id: str,
+ current_user: PublicUser | AnonymousUser,
+ db_session: Session,
+):
+ creds = await get_stripe_internal_credentials()
+
+ # Set the Stripe API key using the credentials
+ stripe.api_key = creds.get("stripe_secret_key")
+
+ stripe_acc_id = await get_stripe_connected_account_id(request, org_id, current_user, db_session)
+
+ try:
+ # Archive the product in Stripe
+ archived_product = stripe.Product.modify(product_id, active=False, stripe_account=stripe_acc_id)
+
+ return archived_product
+ except stripe.StripeError as e:
+ print(f"Error archiving Stripe product: {str(e)}")
+ raise HTTPException(
+ status_code=400, detail=f"Error archiving Stripe product: {str(e)}"
+ )
+
+
+async def update_stripe_product(
+ request: Request,
+ org_id: int,
+ product_id: str,
+ product_data: PaymentsProduct,
+ current_user: PublicUser | AnonymousUser,
+ db_session: Session,
+):
+ creds = await get_stripe_internal_credentials()
+
+ # Set the Stripe API key using the credentials
+ stripe.api_key = creds.get("stripe_secret_key")
+
+ stripe_acc_id = await get_stripe_connected_account_id(request, org_id, current_user, db_session)
+
+ try:
+ # Create new price based on price_type
+ if product_data.price_type == PaymentPriceTypeEnum.CUSTOMER_CHOICE:
+ new_price_data = {
+ "currency": product_data.currency,
+ "product": product_id,
+ "custom_unit_amount": {
+ "enabled": True,
+ "minimum": int(product_data.amount * 100), # Convert to cents
+ },
+ }
+ else:
+ new_price_data = {
+ "currency": product_data.currency,
+ "unit_amount": int(product_data.amount * 100), # Convert to cents
+ "product": product_id,
+ }
+
+ if product_data.product_type == PaymentProductTypeEnum.SUBSCRIPTION:
+ new_price_data["recurring"] = {"interval": "month"}
+
+ new_price = stripe.Price.create(**new_price_data)
+
+ # Prepare the update data
+ update_data = {
+ "name": product_data.name,
+ "description": product_data.description or "",
+ "metadata": {"benefits": product_data.benefits},
+ "marketing_features": [
+ {"name": benefit.strip()}
+ for benefit in product_data.benefits.split(",")
+ if benefit.strip()
+ ],
+ "default_price": new_price.id,
+ }
+
+ # Update the product in Stripe
+ updated_product = stripe.Product.modify(product_id, **update_data, stripe_account=stripe_acc_id)
+
+ # Archive all existing prices for the product
+ existing_prices = stripe.Price.list(product=product_id, active=True)
+ for price in existing_prices:
+ if price.id != new_price.id:
+ stripe.Price.modify(price.id, active=False, stripe_account=stripe_acc_id)
+
+ return updated_product
+ except stripe.StripeError as e:
+ raise HTTPException(
+ status_code=400, detail=f"Error updating Stripe product: {str(e)}"
+ )
+
+
+async def create_checkout_session(
+ request: Request,
+ org_id: int,
+ product_id: int,
+ redirect_uri: str,
+ current_user: PublicUser | AnonymousUser,
+ db_session: Session,
+):
+ # Get Stripe credentials
+ creds = await get_stripe_internal_credentials()
+ stripe.api_key = creds.get("stripe_secret_key")
+
+
+ stripe_acc_id = await get_stripe_connected_account_id(request, org_id, current_user, db_session)
+
+ # Get product details
+ statement = select(PaymentsProduct).where(
+ PaymentsProduct.id == product_id, PaymentsProduct.org_id == org_id
+ )
+ product = db_session.exec(statement).first()
+
+ if not product:
+ raise HTTPException(status_code=404, detail="Product not found")
+
+ success_url = redirect_uri
+ cancel_url = redirect_uri
+
+ # Get the default price for the product
+ stripe_product = stripe.Product.retrieve(product.provider_product_id, stripe_account=stripe_acc_id)
+ line_items = [{"price": stripe_product.default_price, "quantity": 1}]
+
+
+ # Create or retrieve Stripe customer
+ try:
+ customers = stripe.Customer.list(
+ email=current_user.email, stripe_account=stripe_acc_id
+ )
+ if customers.data:
+ customer = customers.data[0]
+ else:
+ customer = stripe.Customer.create(
+ email=current_user.email,
+ metadata={
+ "user_id": str(current_user.id),
+ "org_id": str(org_id),
+ },
+ stripe_account=stripe_acc_id,
+ )
+
+ # Create initial payment user with pending status
+ payment_user = await create_payment_user(
+ request=request,
+ org_id=org_id,
+ user_id=current_user.id,
+ product_id=product_id,
+ status=PaymentStatusEnum.PENDING,
+ provider_data=customer,
+ current_user=InternalUser(),
+ db_session=db_session,
+ )
+
+ if not payment_user:
+ raise HTTPException(status_code=400, detail="Error creating payment user")
+
+ except stripe.StripeError as e:
+ # Clean up payment user if customer creation fails
+ if payment_user and payment_user.id:
+ await delete_payment_user(
+ request, org_id, payment_user.id, InternalUser(), db_session
+ )
+ raise HTTPException(
+ status_code=400, detail=f"Error creating/retrieving customer: {str(e)}"
+ )
+
+ # Create checkout session with customer
+ try:
+ checkout_session_params = {
+ "success_url": success_url,
+ "cancel_url": cancel_url,
+ "mode": (
+ "payment"
+ if product.product_type == PaymentProductTypeEnum.ONE_TIME
+ else "subscription"
+ ),
+ "line_items": line_items,
+ "customer": customer.id,
+ "metadata": {
+ "product_id": str(product.id),
+ "payment_user_id": str(payment_user.id),
+ }
+ }
+
+ # Add payment_intent_data only for one-time payments
+ if product.product_type == PaymentProductTypeEnum.ONE_TIME:
+ checkout_session_params["payment_intent_data"] = {
+ "metadata": {
+ "product_id": str(product.id),
+ "payment_user_id": str(payment_user.id),
+ }
+ }
+ # Add subscription_data for subscription payments
+ else:
+ checkout_session_params["subscription_data"] = {
+ "metadata": {
+ "product_id": str(product.id),
+ "payment_user_id": str(payment_user.id),
+ }
+ }
+
+ checkout_session = stripe.checkout.Session.create(**checkout_session_params, stripe_account=stripe_acc_id)
+
+ return {"checkout_url": checkout_session.url, "session_id": checkout_session.id}
+
+ except stripe.StripeError as e:
+ # Clean up payment user if checkout session creation fails
+ if payment_user and payment_user.id:
+ await delete_payment_user(
+ request, org_id, payment_user.id, InternalUser(), db_session
+ )
+ logging.error(f"Error creating checkout session: {str(e)}")
+ raise HTTPException(status_code=400, detail=str(e))
+
+
+async def generate_stripe_connect_link(
+ request: Request,
+ org_id: int,
+ redirect_uri: str,
+ current_user: PublicUser | AnonymousUser | InternalUser,
+ db_session: Session,
+):
+ """
+ Generate a Stripe OAuth link for connecting a Stripe account
+ """
+ # Get credentials
+ creds = await get_stripe_internal_credentials()
+ stripe.api_key = creds.get("stripe_secret_key")
+
+ # Get learnhouse config for client_id
+ learnhouse_config = get_learnhouse_config()
+ client_id = learnhouse_config.payments_config.stripe.stripe_client_id
+
+ if not client_id:
+ raise HTTPException(status_code=400, detail="Stripe client ID not configured")
+
+ state = f"org_id={org_id}"
+
+ # Generate OAuth link for existing accounts
+ oauth_link = f"https://connect.stripe.com/oauth/authorize?response_type=code&client_id={client_id}&scope=read_write&redirect_uri={redirect_uri}&state={state}"
+
+ return {"connect_url": oauth_link}
+
+async def create_stripe_account(
+ request: Request,
+ org_id: int,
+ type: Literal["standard"], # Only standard is supported for now, we'll see if we need express later
+ current_user: PublicUser | AnonymousUser | InternalUser,
+ db_session: Session,
+):
+ # Get credentials
+ creds = await get_stripe_internal_credentials()
+ stripe.api_key = creds.get("stripe_secret_key")
+
+ # Get existing payments config
+ statement = select(PaymentsConfig).where(PaymentsConfig.org_id == org_id)
+ existing_config = db_session.exec(statement).first()
+
+ if existing_config and existing_config.provider_specific_id:
+ logging.error(f"A Stripe Account is already linked to this organization: {existing_config.provider_specific_id}")
+ return existing_config.provider_specific_id
+
+ # Create Stripe account
+ stripe_account = stripe.Account.create(
+ type="standard",
+ capabilities={
+ "card_payments": {"requested": True},
+ "transfers": {"requested": True},
+ },
+ )
+
+ config_data = existing_config.model_dump() if existing_config else {}
+ config_data.update({
+ "enabled": True,
+ "provider_specific_id": stripe_account.id, # Use the ID directly
+ "provider_config": {"onboarding_completed": False}
+ })
+
+ # Update payments config for the org
+ await update_payments_config(
+ request,
+ org_id,
+ PaymentsConfigUpdate(**config_data),
+ current_user,
+ db_session,
+ )
+
+ return stripe_account
+
+
+async def update_stripe_account_id(
+ request: Request,
+ org_id: int,
+ stripe_account_id: str,
+ current_user: PublicUser | AnonymousUser | InternalUser,
+ db_session: Session,
+):
+ """
+ Update the Stripe account ID for an organization
+ """
+ # Get existing payments config
+ statement = select(PaymentsConfig).where(PaymentsConfig.org_id == org_id)
+ existing_config = db_session.exec(statement).first()
+
+ if not existing_config:
+ raise HTTPException(
+ status_code=404,
+ detail="No payments configuration found for this organization"
+ )
+
+ # Create config update with existing values but new stripe account id
+ config_data = existing_config.model_dump()
+ config_data["provider_specific_id"] = stripe_account_id
+
+ # Update payments config
+ await update_payments_config(
+ request,
+ org_id,
+ PaymentsConfigUpdate(**config_data),
+ current_user,
+ db_session,
+ )
+
+ return {"message": "Stripe account ID updated successfully"}
+
+async def handle_stripe_oauth_callback(
+ request: Request,
+ org_id: int,
+ code: str,
+ current_user: PublicUser | AnonymousUser | InternalUser,
+ db_session: Session,
+):
+ """
+ Handle the OAuth callback from Stripe and complete the account connection
+ """
+ creds = await get_stripe_internal_credentials()
+ stripe.api_key = creds.get("stripe_secret_key")
+
+ try:
+ # Exchange the authorization code for an access token
+ response = stripe.OAuth.token(
+ grant_type='authorization_code',
+ code=code,
+ )
+
+ connected_account_id = response.stripe_user_id
+ if not connected_account_id:
+ raise HTTPException(status_code=400, detail="No account ID received from Stripe")
+
+ # Now connected_account_id is guaranteed to be a string
+ await update_stripe_account_id(
+ request,
+ org_id,
+ connected_account_id,
+ current_user,
+ db_session,
+ )
+
+ return {"success": True, "account_id": connected_account_id}
+
+ except stripe.StripeError as e:
+ logging.error(f"Error connecting Stripe account: {str(e)}")
+ raise HTTPException(
+ status_code=400,
+ detail=f"Error connecting Stripe account: {str(e)}"
+ )
diff --git a/apps/api/src/services/payments/payments_users.py b/apps/api/src/services/payments/payments_users.py
new file mode 100644
index 00000000..16af55a6
--- /dev/null
+++ b/apps/api/src/services/payments/payments_users.py
@@ -0,0 +1,250 @@
+from fastapi import HTTPException, Request
+from sqlmodel import Session, select
+from typing import Any
+from src.db.courses.courses import Course, CourseRead
+from src.db.payments.payments_courses import PaymentsCourse
+from src.db.payments.payments_users import PaymentsUser, PaymentStatusEnum, ProviderSpecificData
+from src.db.payments.payments_products import PaymentsProduct
+from src.db.resource_authors import ResourceAuthor
+from src.db.users import InternalUser, PublicUser, AnonymousUser, User, UserRead
+from src.db.organizations import Organization
+from src.services.orgs.orgs import rbac_check
+from datetime import datetime
+
+async def create_payment_user(
+ request: Request,
+ org_id: int,
+ user_id: int,
+ product_id: int,
+ status: PaymentStatusEnum,
+ provider_data: Any,
+ current_user: PublicUser | AnonymousUser | InternalUser,
+ db_session: Session,
+) -> PaymentsUser:
+ # Check if organization exists
+ statement = select(Organization).where(Organization.id == org_id)
+ org = db_session.exec(statement).first()
+ if not org:
+ raise HTTPException(status_code=404, detail="Organization not found")
+
+ # RBAC check
+ await rbac_check(request, org.org_uuid, current_user, "create", db_session)
+
+ # Check if product exists
+ statement = select(PaymentsProduct).where(
+ PaymentsProduct.id == product_id,
+ PaymentsProduct.org_id == org_id
+ )
+ product = db_session.exec(statement).first()
+ if not product:
+ raise HTTPException(status_code=404, detail="Product not found")
+
+ provider_specific_data = ProviderSpecificData(
+ stripe_customer=provider_data if provider_data else None,
+ )
+
+ # Check if user already has a payment user for this product
+ statement = select(PaymentsUser).where(
+ PaymentsUser.user_id == user_id,
+ PaymentsUser.org_id == org_id,
+ PaymentsUser.payment_product_id == product_id
+ )
+ existing_payment_user = db_session.exec(statement).first()
+
+ if existing_payment_user:
+ # If status is PENDING, CANCELLED, or FAILED, delete the existing record
+ if existing_payment_user.status in [
+ PaymentStatusEnum.PENDING,
+ PaymentStatusEnum.CANCELLED,
+ PaymentStatusEnum.FAILED
+ ]:
+ db_session.delete(existing_payment_user)
+ db_session.commit()
+ else:
+ raise HTTPException(status_code=400, detail="User already has purchase for this product")
+
+ # Create new payment user
+ payment_user = PaymentsUser(
+ user_id=user_id,
+ org_id=org_id,
+ payment_product_id=product_id,
+ provider_specific_data=provider_specific_data.model_dump(),
+ status=status
+ )
+
+ db_session.add(payment_user)
+ db_session.commit()
+ db_session.refresh(payment_user)
+
+ return payment_user
+
+async def get_payment_user(
+ request: Request,
+ org_id: int,
+ payment_user_id: int,
+ current_user: PublicUser | AnonymousUser | InternalUser,
+ db_session: Session,
+) -> PaymentsUser:
+ # Check if organization exists
+ statement = select(Organization).where(Organization.id == org_id)
+ org = db_session.exec(statement).first()
+ if not org:
+ raise HTTPException(status_code=404, detail="Organization not found")
+
+ # RBAC check
+ await rbac_check(request, org.org_uuid, current_user, "read", db_session)
+
+ # Get payment user
+ statement = select(PaymentsUser).where(
+ PaymentsUser.id == payment_user_id,
+ PaymentsUser.org_id == org_id
+ )
+ payment_user = db_session.exec(statement).first()
+ if not payment_user:
+ raise HTTPException(status_code=404, detail="Payment user not found")
+
+ return payment_user
+
+async def update_payment_user_status(
+ request: Request,
+ org_id: int,
+ payment_user_id: int,
+ status: PaymentStatusEnum,
+ current_user: PublicUser | AnonymousUser | InternalUser,
+ db_session: Session,
+) -> PaymentsUser:
+ # Check if organization exists
+ statement = select(Organization).where(Organization.id == org_id)
+ org = db_session.exec(statement).first()
+ if not org:
+ raise HTTPException(status_code=404, detail="Organization not found")
+
+ # RBAC check
+ await rbac_check(request, org.org_uuid, current_user, "update", db_session)
+
+ # Get existing payment user
+ statement = select(PaymentsUser).where(
+ PaymentsUser.id == payment_user_id,
+ PaymentsUser.org_id == org_id
+ )
+ payment_user = db_session.exec(statement).first()
+ if not payment_user:
+ raise HTTPException(status_code=404, detail="Payment user not found")
+
+ # Update status
+ payment_user.status = status
+ payment_user.update_date = datetime.now()
+
+ db_session.add(payment_user)
+ db_session.commit()
+ db_session.refresh(payment_user)
+
+ return payment_user
+
+async def list_payment_users(
+ request: Request,
+ org_id: int,
+ current_user: PublicUser | AnonymousUser | InternalUser,
+ db_session: Session,
+) -> list[PaymentsUser]:
+ # Check if organization exists
+ statement = select(Organization).where(Organization.id == org_id)
+ org = db_session.exec(statement).first()
+ if not org:
+ raise HTTPException(status_code=404, detail="Organization not found")
+
+ # RBAC check
+ await rbac_check(request, org.org_uuid, current_user, "read", db_session)
+
+ # Get all payment users for org ordered by id
+ statement = select(PaymentsUser).where(
+ PaymentsUser.org_id == org_id
+ ).order_by(PaymentsUser.id.desc()) # type: ignore
+ payment_users = list(db_session.exec(statement).all()) # Convert to list
+
+ return payment_users
+
+async def delete_payment_user(
+ request: Request,
+ org_id: int,
+ payment_user_id: int,
+ current_user: PublicUser | AnonymousUser | InternalUser,
+ db_session: Session,
+) -> None:
+ # Check if organization exists
+ statement = select(Organization).where(Organization.id == org_id)
+ org = db_session.exec(statement).first()
+ if not org:
+ raise HTTPException(status_code=404, detail="Organization not found")
+
+ # RBAC check
+ await rbac_check(request, org.org_uuid, current_user, "delete", db_session)
+
+ # Get existing payment user
+ statement = select(PaymentsUser).where(
+ PaymentsUser.id == payment_user_id,
+ PaymentsUser.org_id == org_id
+ )
+ payment_user = db_session.exec(statement).first()
+ if not payment_user:
+ raise HTTPException(status_code=404, detail="Payment user not found")
+
+ # Delete payment user
+ db_session.delete(payment_user)
+ db_session.commit()
+
+
+async def get_owned_courses(
+ request: Request,
+ current_user: PublicUser | AnonymousUser,
+ db_session: Session,
+) -> list[CourseRead]:
+ # Anonymous users don't own any courses
+ if isinstance(current_user, AnonymousUser):
+ return []
+
+ # Get all active/completed payment users for the current user
+ statement = select(PaymentsUser).where(
+ PaymentsUser.user_id == current_user.id,
+ PaymentsUser.status.in_([PaymentStatusEnum.ACTIVE, PaymentStatusEnum.COMPLETED]) # type: ignore
+ )
+ payment_users = db_session.exec(statement).all()
+
+ # Get all product IDs from payment users
+ product_ids = [pu.payment_product_id for pu in payment_users]
+
+ # Get all courses linked to these products
+ courses = []
+ for product_id in product_ids:
+ # Get courses linked to this product through PaymentsCourse
+ statement = (
+ select(Course)
+ .join(PaymentsCourse, Course.id == PaymentsCourse.course_id) # type: ignore
+ .where(PaymentsCourse.payment_product_id == product_id)
+ )
+ product_courses = db_session.exec(statement).all()
+ courses.extend(product_courses)
+
+ # Remove duplicates by converting to set and back to list
+ unique_courses = list({course.id: course for course in courses}.values())
+
+ # Get authors for each course and convert to CourseRead
+ course_reads = []
+ for course in unique_courses:
+ # Get course authors
+ authors_statement = (
+ select(User)
+ .join(ResourceAuthor)
+ .where(ResourceAuthor.resource_uuid == course.course_uuid)
+ )
+ authors = db_session.exec(authors_statement).all()
+
+ # Convert authors to UserRead
+ author_reads = [UserRead.model_validate(author) for author in authors]
+
+ # Create CourseRead object
+ course_read = CourseRead(**course.model_dump(), authors=author_reads)
+ course_reads.append(course_read)
+
+ return course_reads
+
diff --git a/apps/api/src/services/payments/utils/stripe_utils.py b/apps/api/src/services/payments/utils/stripe_utils.py
new file mode 100644
index 00000000..11a30d5e
--- /dev/null
+++ b/apps/api/src/services/payments/utils/stripe_utils.py
@@ -0,0 +1,59 @@
+from fastapi import HTTPException
+from sqlmodel import Session, select
+import stripe
+import logging
+
+from src.db.payments.payments_products import PaymentsProduct
+from src.db.users import User
+from src.db.payments.payments import PaymentsConfig
+
+logger = logging.getLogger(__name__)
+
+async def get_user_from_customer(customer_id: str, db_session: Session) -> User:
+ """Helper function to get user from Stripe customer ID"""
+ try:
+ customer = stripe.Customer.retrieve(customer_id)
+ statement = select(User).where(User.email == customer.email)
+ user = db_session.exec(statement).first()
+ if not user:
+ raise HTTPException(
+ status_code=404, detail=f"User not found for customer {customer_id}"
+ )
+ return user
+ except stripe.StripeError as e:
+ logger.error(f"Stripe error retrieving customer {customer_id}: {str(e)}")
+ raise HTTPException(
+ status_code=400, detail="Error retrieving customer information"
+ )
+
+
+async def get_product_from_stripe_id(
+ product_id: str, db_session: Session
+) -> PaymentsProduct:
+ """Helper function to get product from Stripe product ID"""
+ statement = select(PaymentsProduct).where(
+ PaymentsProduct.provider_product_id == product_id
+ )
+ product = db_session.exec(statement).first()
+ if not product:
+ raise HTTPException(status_code=404, detail=f"Product not found: {product_id}")
+ return product
+
+
+async def get_org_id_from_stripe_account(
+ stripe_account_id: str,
+ db_session: Session,
+) -> int:
+ """Get organization ID from Stripe account ID"""
+ statement = select(PaymentsConfig).where(
+ PaymentsConfig.provider_specific_id == stripe_account_id
+ )
+ config = db_session.exec(statement).first()
+
+ if not config:
+ raise HTTPException(
+ status_code=404,
+ detail=f"No organization found for Stripe account {stripe_account_id}",
+ )
+
+ return config.org_id
diff --git a/apps/api/src/services/payments/webhooks/payments_webhooks.py b/apps/api/src/services/payments/webhooks/payments_webhooks.py
new file mode 100644
index 00000000..c357f77e
--- /dev/null
+++ b/apps/api/src/services/payments/webhooks/payments_webhooks.py
@@ -0,0 +1,179 @@
+from typing import Literal
+from fastapi import HTTPException, Request
+from sqlmodel import Session, select
+import stripe
+import logging
+from src.db.payments.payments_users import PaymentStatusEnum
+from src.db.users import InternalUser
+from src.services.payments.payments_users import update_payment_user_status
+from src.services.payments.payments_stripe import get_stripe_internal_credentials
+from src.db.payments.payments import PaymentsConfig, PaymentsConfigUpdate
+from src.services.payments.payments_config import update_payments_config
+from src.services.payments.utils.stripe_utils import get_org_id_from_stripe_account
+
+logger = logging.getLogger(__name__)
+
+
+async def handle_stripe_webhook(
+ request: Request,
+ webhook_type: Literal["connect", "standard"],
+ db_session: Session,
+) -> dict:
+ # Get Stripe credentials
+ creds = await get_stripe_internal_credentials()
+ webhook_secret = creds.get(f'stripe_webhook_{webhook_type}_secret')
+ stripe.api_key = creds.get("stripe_secret_key")
+
+ if not webhook_secret:
+ logger.error("Stripe webhook secret not configured")
+ raise HTTPException(status_code=400, detail="Stripe webhook secret not configured")
+
+ # Get request data
+ payload = await request.body()
+ sig_header = request.headers.get('stripe-signature')
+
+ try:
+ # Verify webhook signature
+ event = stripe.Webhook.construct_event(payload, sig_header, webhook_secret)
+ except ValueError:
+ logger.error(ValueError)
+ raise HTTPException(status_code=400, detail="Invalid payload")
+ except stripe.SignatureVerificationError:
+ logger.error(stripe.SignatureVerificationError)
+ raise HTTPException(status_code=400, detail="Invalid signature")
+
+ try:
+ event_type = event.type
+ event_data = event.data.object
+
+ # Get organization ID based on the event type
+ stripe_account_id = event.account
+ if not stripe_account_id:
+ logger.error("Stripe account ID not found")
+ raise HTTPException(status_code=400, detail="Stripe account ID not found")
+
+ org_id = await get_org_id_from_stripe_account(stripe_account_id, db_session)
+
+ # Handle internal account events
+ if event_type == 'account.application.authorized':
+ statement = select(PaymentsConfig).where(PaymentsConfig.org_id == org_id)
+ config = db_session.exec(statement).first()
+
+ if not config:
+ logger.error("No payments configuration found for this organization")
+ raise HTTPException(
+ status_code=404,
+ detail="No payments configuration found for this organization"
+ )
+
+ config_data = config.model_dump()
+ config_data.update({
+ "enabled": True,
+ "active": True,
+ "provider_config": {
+ **config.provider_config,
+ "onboarding_completed": True
+ }
+ })
+ await update_payments_config(
+ request,
+ org_id,
+ PaymentsConfigUpdate(**config_data),
+ InternalUser(),
+ db_session,
+ )
+
+ logger.info(f"Account authorized for organization {org_id}")
+ return {"status": "success", "message": "Account authorized successfully"}
+
+ elif event_type == 'account.application.deauthorized':
+ statement = select(PaymentsConfig).where(PaymentsConfig.org_id == org_id)
+ config = db_session.exec(statement).first()
+
+ if not config:
+ raise HTTPException(
+ status_code=404,
+ detail="No payments configuration found for this organization"
+ )
+
+ config_data = config.model_dump()
+ config_data.update({
+ "enabled": True,
+ "active": False,
+ "provider_config": {
+ **config.provider_config,
+ "onboarding_completed": False
+ }
+ })
+ await update_payments_config(
+ request,
+ org_id,
+ PaymentsConfigUpdate(**config_data),
+ InternalUser(),
+ db_session,
+ )
+
+ logger.info(f"Account deauthorized for organization {org_id}")
+ return {"status": "success", "message": "Account deauthorized successfully"}
+
+ # Handle payment-related events
+ elif event_type == "checkout.session.completed":
+ session = event_data
+ payment_user_id = int(session.get("metadata", {}).get("payment_user_id"))
+
+ if session.get("mode") == "subscription":
+ if session.get("subscription"):
+ await update_payment_user_status(
+ request=request,
+ org_id=org_id,
+ payment_user_id=payment_user_id,
+ status=PaymentStatusEnum.ACTIVE,
+ current_user=InternalUser(),
+ db_session=db_session,
+ )
+ else:
+ if session.get("payment_status") == "paid":
+ await update_payment_user_status(
+ request=request,
+ org_id=org_id,
+ payment_user_id=payment_user_id,
+ status=PaymentStatusEnum.COMPLETED,
+ current_user=InternalUser(),
+ db_session=db_session,
+ )
+
+ elif event_type == "customer.subscription.deleted":
+ subscription = event_data
+ payment_user_id = int(subscription.get("metadata", {}).get("payment_user_id"))
+
+ await update_payment_user_status(
+ request=request,
+ org_id=org_id,
+ payment_user_id=payment_user_id,
+ status=PaymentStatusEnum.CANCELLED,
+ current_user=InternalUser(),
+ db_session=db_session,
+ )
+
+ elif event_type == "payment_intent.payment_failed":
+ payment_intent = event_data
+ payment_user_id = int(payment_intent.get("metadata", {}).get("payment_user_id"))
+
+ await update_payment_user_status(
+ request=request,
+ org_id=org_id,
+ payment_user_id=payment_user_id,
+ status=PaymentStatusEnum.FAILED,
+ current_user=InternalUser(),
+ db_session=db_session,
+ )
+
+ else:
+ logger.warning(f"Unhandled event type: {event_type}")
+ return {"status": "ignored", "message": f"Unhandled event type: {event_type}"}
+
+ return {"status": "success"}
+
+ except Exception as e:
+ logger.error(f"Error processing webhook: {str(e)}")
+ raise HTTPException(status_code=400, detail=f"Error processing webhook: {str(e)}")
\ No newline at end of file
diff --git a/apps/api/src/services/roles/roles.py b/apps/api/src/services/roles/roles.py
index c5939c2c..ea5d0715 100644
--- a/apps/api/src/services/roles/roles.py
+++ b/apps/api/src/services/roles/roles.py
@@ -2,7 +2,7 @@ from typing import Literal
from uuid import uuid4
from sqlmodel import Session, select
from src.security.rbac.rbac import (
- authorization_verify_based_on_roles_and_authorship_and_usergroups,
+ authorization_verify_based_on_roles_and_authorship,
authorization_verify_if_user_is_anon,
)
from src.db.users import AnonymousUser, PublicUser
@@ -133,7 +133,7 @@ async def rbac_check(
):
await authorization_verify_if_user_is_anon(current_user.id)
- await authorization_verify_based_on_roles_and_authorship_and_usergroups(
+ await authorization_verify_based_on_roles_and_authorship(
request, current_user.id, action, role_uuid, db_session
)
diff --git a/apps/api/src/services/users/usergroups.py b/apps/api/src/services/users/usergroups.py
index f443b6b2..acc68ba3 100644
--- a/apps/api/src/services/users/usergroups.py
+++ b/apps/api/src/services/users/usergroups.py
@@ -9,7 +9,7 @@ from src.security.features_utils.usage import (
increase_feature_usage,
)
from src.security.rbac.rbac import (
- authorization_verify_based_on_roles_and_authorship_and_usergroups,
+ authorization_verify_based_on_roles_and_authorship,
authorization_verify_if_user_is_anon,
)
from src.db.usergroup_resources import UserGroupResource
@@ -492,7 +492,7 @@ async def rbac_check(
):
await authorization_verify_if_user_is_anon(current_user.id)
- await authorization_verify_based_on_roles_and_authorship_and_usergroups(
+ await authorization_verify_based_on_roles_and_authorship(
request,
current_user.id,
action,
diff --git a/apps/api/src/services/users/users.py b/apps/api/src/services/users/users.py
index c7277122..401f48ab 100644
--- a/apps/api/src/services/users/users.py
+++ b/apps/api/src/services/users/users.py
@@ -15,7 +15,7 @@ from src.services.orgs.invites import get_invite_code
from src.services.users.avatars import upload_avatar
from src.db.roles import Role, RoleRead
from src.security.rbac.rbac import (
- authorization_verify_based_on_roles_and_authorship_and_usergroups,
+ authorization_verify_based_on_roles_and_authorship,
authorization_verify_if_user_is_anon,
)
from src.db.organizations import Organization, OrganizationRead
@@ -491,7 +491,7 @@ async def authorize_user_action(
# RBAC check
authorized = (
- await authorization_verify_based_on_roles_and_authorship_and_usergroups(
+ await authorization_verify_based_on_roles_and_authorship(
request, current_user.id, action, resource_uuid, db_session
)
)
@@ -564,7 +564,7 @@ async def rbac_check(
if current_user.id == 0: # if user is anonymous
return True
else:
- await authorization_verify_based_on_roles_and_authorship_and_usergroups(
+ await authorization_verify_based_on_roles_and_authorship(
request, current_user.id, "create", "user_x", db_session
)
@@ -575,7 +575,7 @@ async def rbac_check(
if current_user.user_uuid == user_uuid:
return True
- await authorization_verify_based_on_roles_and_authorship_and_usergroups(
+ await authorization_verify_based_on_roles_and_authorship(
request, current_user.id, action, user_uuid, db_session
)
diff --git a/apps/api/src/tests/utils/init_data_for_tests.py b/apps/api/src/tests/utils/init_data_for_tests.py
index 8229cfe2..7e140d2b 100644
--- a/apps/api/src/tests/utils/init_data_for_tests.py
+++ b/apps/api/src/tests/utils/init_data_for_tests.py
@@ -8,7 +8,7 @@ from src.services.install.install import (
install_default_elements,
)
-
+# TODO: Depreceated and need to be removed and remade
async def create_initial_data_for_tests(db_session: Session):
# Install default elements
await install_default_elements({}, db_session)
diff --git a/apps/web/app/api/health/route.ts b/apps/web/app/api/health/route.ts
new file mode 100644
index 00000000..182501ae
--- /dev/null
+++ b/apps/web/app/api/health/route.ts
@@ -0,0 +1,39 @@
+export const dynamic = 'force-dynamic' // defaults to auto
+export const revalidate = 0
+
+import { NextResponse } from 'next/server';
+import { checkHealth } from '@services/utils/health';
+
+export async function GET() {
+ const health = await checkHealth()
+ if (health.success === true) {
+ return NextResponse.json(
+ {
+ status: 'healthy',
+ timestamp: new Date().toISOString(),
+ health: health.data,
+ },
+ {
+ status: 200,
+ headers: {
+ 'Content-Type': 'application/json',
+ },
+ }
+ )
+ } else {
+ return NextResponse.json(
+ {
+ status: 'unhealthy',
+ timestamp: new Date().toISOString(),
+ health: null,
+ error: health.HTTPmessage,
+ },
+ {
+ status: 503,
+ headers: {
+ 'Content-Type': 'application/json',
+ },
+ }
+ );
+ }
+}
diff --git a/apps/web/app/auth/forgot/forgot.tsx b/apps/web/app/auth/forgot/forgot.tsx
index ecfce06e..c37af352 100644
--- a/apps/web/app/auth/forgot/forgot.tsx
+++ b/apps/web/app/auth/forgot/forgot.tsx
@@ -6,7 +6,7 @@ import FormLayout, {
FormField,
FormLabelAndMessage,
Input,
-} from '@components/StyledElements/Form/Form'
+} from '@components/Objects/StyledElements/Form/Form'
import * as Form from '@radix-ui/react-form'
import { getOrgLogoMediaDirectory } from '@services/media/media'
import { AlertTriangle, Info } from 'lucide-react'
diff --git a/apps/web/app/auth/layout.tsx b/apps/web/app/auth/layout.tsx
index 37fbd3a3..95dbe69b 100644
--- a/apps/web/app/auth/layout.tsx
+++ b/apps/web/app/auth/layout.tsx
@@ -1,6 +1,6 @@
'use client'
import { OrgProvider } from '@components/Contexts/OrgContext'
-import ErrorUI from '@components/StyledElements/Error/Error'
+import ErrorUI from '@components/Objects/StyledElements/Error/Error'
import { useSearchParams } from 'next/navigation'
diff --git a/apps/web/app/auth/login/login.tsx b/apps/web/app/auth/login/login.tsx
index 6420396e..24fec8ea 100644
--- a/apps/web/app/auth/login/login.tsx
+++ b/apps/web/app/auth/login/login.tsx
@@ -4,7 +4,7 @@ import FormLayout, {
FormField,
FormLabelAndMessage,
Input,
-} from '@components/StyledElements/Form/Form'
+} from '@components/Objects/StyledElements/Form/Form'
import Image from 'next/image'
import * as Form from '@radix-ui/react-form'
import { useFormik } from 'formik'
diff --git a/apps/web/app/auth/reset/reset.tsx b/apps/web/app/auth/reset/reset.tsx
index c275f98f..c9d16bb9 100644
--- a/apps/web/app/auth/reset/reset.tsx
+++ b/apps/web/app/auth/reset/reset.tsx
@@ -6,7 +6,7 @@ import FormLayout, {
FormField,
FormLabelAndMessage,
Input,
-} from '@components/StyledElements/Form/Form'
+} from '@components/Objects/StyledElements/Form/Form'
import * as Form from '@radix-ui/react-form'
import { getOrgLogoMediaDirectory } from '@services/media/media'
import { AlertTriangle, Info } from 'lucide-react'
diff --git a/apps/web/app/auth/signup/InviteOnlySignUp.tsx b/apps/web/app/auth/signup/InviteOnlySignUp.tsx
index 0ffa7952..41a21618 100644
--- a/apps/web/app/auth/signup/InviteOnlySignUp.tsx
+++ b/apps/web/app/auth/signup/InviteOnlySignUp.tsx
@@ -7,7 +7,7 @@ import FormLayout, {
FormLabelAndMessage,
Input,
Textarea,
-} from '@components/StyledElements/Form/Form'
+} from '@components/Objects/StyledElements/Form/Form'
import * as Form from '@radix-ui/react-form'
import { AlertTriangle, Check, User } from 'lucide-react'
import Link from 'next/link'
@@ -110,8 +110,10 @@ function InviteOnlySignUpComponent(props: InviteOnlySignUpProps) {
{message}
-
- Login
+
+ Login to your account
)}
diff --git a/apps/web/app/auth/signup/OpenSignup.tsx b/apps/web/app/auth/signup/OpenSignup.tsx
index 36a2e821..20fe9c91 100644
--- a/apps/web/app/auth/signup/OpenSignup.tsx
+++ b/apps/web/app/auth/signup/OpenSignup.tsx
@@ -7,7 +7,7 @@ import FormLayout, {
FormLabelAndMessage,
Input,
Textarea,
-} from '@components/StyledElements/Form/Form'
+} from '@components/Objects/StyledElements/Form/Form'
import * as Form from '@radix-ui/react-form'
import { AlertTriangle, Check, User } from 'lucide-react'
import Link from 'next/link'
diff --git a/apps/web/app/auth/signup/signup.tsx b/apps/web/app/auth/signup/signup.tsx
index 1ed093c9..71e977e1 100644
--- a/apps/web/app/auth/signup/signup.tsx
+++ b/apps/web/app/auth/signup/signup.tsx
@@ -14,7 +14,7 @@ import InviteOnlySignUpComponent from './InviteOnlySignUp'
import { useRouter, useSearchParams } from 'next/navigation'
import { validateInviteCode } from '@services/organizations/invites'
import PageLoading from '@components/Objects/Loaders/PageLoading'
-import Toast from '@components/StyledElements/Toast/Toast'
+import Toast from '@components/Objects/StyledElements/Toast/Toast'
import toast from 'react-hot-toast'
import { BarLoader } from 'react-spinners'
import { joinOrg } from '@services/organizations/orgs'
diff --git a/apps/web/app/install/install.tsx b/apps/web/app/install/install.tsx
index d5753dd9..f2d7b1aa 100644
--- a/apps/web/app/install/install.tsx
+++ b/apps/web/app/install/install.tsx
@@ -1,7 +1,7 @@
'use client'
import React, { useEffect } from 'react'
import { INSTALL_STEPS } from './steps/steps'
-import GeneralWrapperStyled from '@components/StyledElements/Wrappers/GeneralWrapper'
+import GeneralWrapperStyled from '@components/Objects/StyledElements/Wrappers/GeneralWrapper'
import { useRouter, useSearchParams } from 'next/navigation'
import { Suspense } from 'react'
diff --git a/apps/web/app/install/steps/account_creation.tsx b/apps/web/app/install/steps/account_creation.tsx
index 1a8d8531..492b492c 100644
--- a/apps/web/app/install/steps/account_creation.tsx
+++ b/apps/web/app/install/steps/account_creation.tsx
@@ -4,7 +4,7 @@ import FormLayout, {
FormField,
FormLabelAndMessage,
Input,
-} from '@components/StyledElements/Form/Form'
+} from '@components/Objects/StyledElements/Form/Form'
import * as Form from '@radix-ui/react-form'
import { getAPIUrl } from '@services/config/config'
import { createNewUserInstall, updateInstall } from '@services/install/install'
diff --git a/apps/web/app/install/steps/org_creation.tsx b/apps/web/app/install/steps/org_creation.tsx
index 5efd2c93..66e5fd6b 100644
--- a/apps/web/app/install/steps/org_creation.tsx
+++ b/apps/web/app/install/steps/org_creation.tsx
@@ -3,7 +3,7 @@ import FormLayout, {
FormField,
FormLabelAndMessage,
Input,
-} from '@components/StyledElements/Form/Form'
+} from '@components/Objects/StyledElements/Form/Form'
import * as Form from '@radix-ui/react-form'
import { useFormik } from 'formik'
import { BarLoader } from 'react-spinners'
diff --git a/apps/web/app/orgs/[orgslug]/(withmenu)/collection/[collectionid]/error.tsx b/apps/web/app/orgs/[orgslug]/(withmenu)/collection/[collectionid]/error.tsx
index d5320406..058142f5 100644
--- a/apps/web/app/orgs/[orgslug]/(withmenu)/collection/[collectionid]/error.tsx
+++ b/apps/web/app/orgs/[orgslug]/(withmenu)/collection/[collectionid]/error.tsx
@@ -1,6 +1,6 @@
'use client' // Error components must be Client Components
-import ErrorUI from '@components/StyledElements/Error/Error'
+import ErrorUI from '@components/Objects/StyledElements/Error/Error'
import { useEffect } from 'react'
export default function Error({
diff --git a/apps/web/app/orgs/[orgslug]/(withmenu)/collection/[collectionid]/page.tsx b/apps/web/app/orgs/[orgslug]/(withmenu)/collection/[collectionid]/page.tsx
index 523ca9bd..aebb4872 100644
--- a/apps/web/app/orgs/[orgslug]/(withmenu)/collection/[collectionid]/page.tsx
+++ b/apps/web/app/orgs/[orgslug]/(withmenu)/collection/[collectionid]/page.tsx
@@ -1,4 +1,4 @@
-import GeneralWrapperStyled from '@components/StyledElements/Wrappers/GeneralWrapper'
+import GeneralWrapperStyled from '@components/Objects/StyledElements/Wrappers/GeneralWrapper'
import { getUriWithOrg } from '@services/config/config'
import { getCollectionById } from '@services/courses/collections'
import { getCourseThumbnailMediaDirectory } from '@services/media/media'
diff --git a/apps/web/app/orgs/[orgslug]/(withmenu)/collections/page.tsx b/apps/web/app/orgs/[orgslug]/(withmenu)/collections/page.tsx
index 343bfacf..dfa4b147 100644
--- a/apps/web/app/orgs/[orgslug]/(withmenu)/collections/page.tsx
+++ b/apps/web/app/orgs/[orgslug]/(withmenu)/collections/page.tsx
@@ -1,17 +1,17 @@
import AuthenticatedClientElement from '@components/Security/AuthenticatedClientElement'
-import TypeOfContentTitle from '@components/StyledElements/Titles/TypeOfContentTitle'
-import GeneralWrapperStyled from '@components/StyledElements/Wrappers/GeneralWrapper'
+import TypeOfContentTitle from '@components/Objects/StyledElements/Titles/TypeOfContentTitle'
+import GeneralWrapperStyled from '@components/Objects/StyledElements/Wrappers/GeneralWrapper'
import { getUriWithOrg } from '@services/config/config'
import { getOrganizationContextInfo } from '@services/organizations/orgs'
import { Metadata } from 'next'
import Link from 'next/link'
import CollectionThumbnail from '@components/Objects/Thumbnails/CollectionThumbnail'
-import NewCollectionButton from '@components/StyledElements/Buttons/NewCollectionButton'
-import ContentPlaceHolderIfUserIsNotAdmin from '@components/ContentPlaceHolder'
+import NewCollectionButton from '@components/Objects/StyledElements/Buttons/NewCollectionButton'
import { nextAuthOptions } from 'app/auth/options'
import { getServerSession } from 'next-auth'
import { getOrgCollections } from '@services/courses/collections'
import { getOrgThumbnailMediaDirectory } from '@services/media/media'
+import ContentPlaceHolderIfUserIsNotAdmin from '@components/Objects/ContentPlaceHolder'
type MetadataProps = {
params: { orgslug: string; courseid: string }
diff --git a/apps/web/app/orgs/[orgslug]/(withmenu)/course/[courseuuid]/activity/[activityid]/activity.tsx b/apps/web/app/orgs/[orgslug]/(withmenu)/course/[courseuuid]/activity/[activityid]/activity.tsx
index 3dc1853b..20d03e54 100644
--- a/apps/web/app/orgs/[orgslug]/(withmenu)/course/[courseuuid]/activity/[activityid]/activity.tsx
+++ b/apps/web/app/orgs/[orgslug]/(withmenu)/course/[courseuuid]/activity/[activityid]/activity.tsx
@@ -7,7 +7,7 @@ import { BookOpenCheck, Check, CheckCircle, MoreVertical, UserRoundPen } from 'l
import { markActivityAsComplete } from '@services/courses/activity'
import DocumentPdfActivity from '@components/Objects/Activities/DocumentPdf/DocumentPdf'
import ActivityIndicators from '@components/Pages/Courses/ActivityIndicators'
-import GeneralWrapperStyled from '@components/StyledElements/Wrappers/GeneralWrapper'
+import GeneralWrapperStyled from '@components/Objects/StyledElements/Wrappers/GeneralWrapper'
import { usePathname, useRouter } from 'next/navigation'
import AuthenticatedClientElement from '@components/Security/AuthenticatedClientElement'
import { getCourseThumbnailMediaDirectory } from '@services/media/media'
@@ -24,8 +24,9 @@ import { AssignmentsTaskProvider } from '@components/Contexts/Assignments/Assign
import AssignmentSubmissionProvider, { useAssignmentSubmission } from '@components/Contexts/Assignments/AssignmentSubmissionContext'
import toast from 'react-hot-toast'
import { mutate } from 'swr'
-import ConfirmationModal from '@components/StyledElements/ConfirmationModal/ConfirmationModal'
+import ConfirmationModal from '@components/Objects/StyledElements/ConfirmationModal/ConfirmationModal'
import { useMediaQuery } from 'usehooks-ts'
+import PaidCourseActivityDisclaimer from '@components/Objects/Courses/CourseActions/PaidCourseActivityDisclaimer'
interface ActivityClientProps {
activityid: string
@@ -129,7 +130,7 @@ function ActivityClient(props: ActivityClientProps) {
- {activity && activity.published == true && (
+ {activity && activity.published == true && activity.content.paid_access != false && (
{activity.activity_type != 'TYPE_ASSIGNMENT' &&
<>
@@ -173,40 +174,44 @@ function ActivityClient(props: ActivityClientProps) {
)}
{activity && activity.published == true && (
-
-
- {activity.activity_type == 'TYPE_DYNAMIC' && (
-
- )}
- {/* todo : use apis & streams instead of this */}
- {activity.activity_type == 'TYPE_VIDEO' && (
-
- )}
- {activity.activity_type == 'TYPE_DOCUMENT' && (
-
- )}
- {activity.activity_type == 'TYPE_ASSIGNMENT' && (
+ <>
+ {activity.content.paid_access == false ? (
+
+ ) : (
+
+ {/* Activity Types */}
- {assignment ? (
-
-
-
-
-
-
-
- ) : (
-
+ {activity.activity_type == 'TYPE_DYNAMIC' && (
+
+ )}
+ {activity.activity_type == 'TYPE_VIDEO' && (
+
+ )}
+ {activity.activity_type == 'TYPE_DOCUMENT' && (
+
+ )}
+ {activity.activity_type == 'TYPE_ASSIGNMENT' && (
+
+ {assignment ? (
+
+
+
+
+
+
+
+ ) : (
+
+ )}
+
)}
- )}
-
-
+
+ )}
+ >
)}
{}
diff --git a/apps/web/app/orgs/[orgslug]/(withmenu)/course/[courseuuid]/activity/[activityid]/error.tsx b/apps/web/app/orgs/[orgslug]/(withmenu)/course/[courseuuid]/activity/[activityid]/error.tsx
index d5320406..058142f5 100644
--- a/apps/web/app/orgs/[orgslug]/(withmenu)/course/[courseuuid]/activity/[activityid]/error.tsx
+++ b/apps/web/app/orgs/[orgslug]/(withmenu)/course/[courseuuid]/activity/[activityid]/error.tsx
@@ -1,6 +1,6 @@
'use client' // Error components must be Client Components
-import ErrorUI from '@components/StyledElements/Error/Error'
+import ErrorUI from '@components/Objects/StyledElements/Error/Error'
import { useEffect } from 'react'
export default function Error({
diff --git a/apps/web/app/orgs/[orgslug]/(withmenu)/course/[courseuuid]/course.tsx b/apps/web/app/orgs/[orgslug]/(withmenu)/course/[courseuuid]/course.tsx
index b3311d8c..b98bb364 100644
--- a/apps/web/app/orgs/[orgslug]/(withmenu)/course/[courseuuid]/course.tsx
+++ b/apps/web/app/orgs/[orgslug]/(withmenu)/course/[courseuuid]/course.tsx
@@ -1,5 +1,4 @@
'use client'
-import { removeCourse, startCourse } from '@services/courses/activity'
import Link from 'next/link'
import React, { useEffect, useState } from 'react'
import { getUriWithOrg } from '@services/config/config'
@@ -7,7 +6,7 @@ import PageLoading from '@components/Objects/Loaders/PageLoading'
import { revalidateTags } from '@services/utils/ts/requests'
import ActivityIndicators from '@components/Pages/Courses/ActivityIndicators'
import { useRouter } from 'next/navigation'
-import GeneralWrapperStyled from '@components/StyledElements/Wrappers/GeneralWrapper'
+import GeneralWrapperStyled from '@components/Objects/StyledElements/Wrappers/GeneralWrapper'
import {
getCourseThumbnailMediaDirectory,
getUserAvatarMediaDirectory,
@@ -15,15 +14,13 @@ import {
import { ArrowRight, Backpack, Check, File, Sparkles, Video } from 'lucide-react'
import { useOrg } from '@components/Contexts/OrgContext'
import UserAvatar from '@components/Objects/UserAvatar'
-import CourseUpdates from '@components/Objects/CourseUpdates/CourseUpdates'
+import CourseUpdates from '@components/Objects/Courses/CourseUpdates/CourseUpdates'
import { CourseProvider } from '@components/Contexts/CourseContext'
-import { useLHSession } from '@components/Contexts/LHSessionContext'
import { useMediaQuery } from 'usehooks-ts'
+import CoursesActions from '@components/Objects/Courses/CourseActions/CoursesActions'
const CourseClient = (props: any) => {
- const [user, setUser] = useState({})
const [learnings, setLearnings] = useState([])
- const session = useLHSession() as any;
const courseuuid = props.courseuuid
const orgslug = props.orgslug
const course = props.course
@@ -37,33 +34,6 @@ const CourseClient = (props: any) => {
setLearnings(learnings)
}
- async function startCourseUI() {
- // Create activity
- await startCourse('course_' + courseuuid, orgslug, session.data?.tokens?.access_token)
- await revalidateTags(['courses'], orgslug)
- router.refresh()
-
- // refresh page (FIX for Next.js BUG)
- // window.location.reload();
- }
-
- function isCourseStarted() {
- const runs = course.trail?.runs
- if (!runs) return false
- return runs.some(
- (run: any) =>
- run.status === 'STATUS_IN_PROGRESS' && run.course_id === course.id
- )
- }
-
- async function quitCourse() {
- // Close activity
- let activity = await removeCourse('course_' + courseuuid, orgslug, session.data?.tokens?.access_token)
- // Mutate course
- await revalidateTags(['courses'], orgslug)
- router.refresh()
- }
-
useEffect(() => {
getLearningTags()
}, [org, course])
@@ -80,7 +50,7 @@ const CourseClient = (props: any) => {
{course.name}
- {!isMobile &&
+ {!isMobile &&
}
@@ -113,11 +83,11 @@ const CourseClient = (props: any) => {
course={course}
/>
-
-
-
Description
+
+
+
About
-
{course.description}
+
{course.about}
{learnings.length > 0 && learnings[0] !== 'null' && (
@@ -187,7 +157,7 @@ const CourseClient = (props: any) => {
/>
)}
- {activity.activity_type ===
+ {activity.activity_type ===
'TYPE_ASSIGNMENT' && (
{
>
)}
- {activity.activity_type ===
+ {activity.activity_type ===
'TYPE_ASSIGNMENT' && (
<>
{
})}
-
- {user && (
-
-
-
-
- Author
-
-
- {course.authors[0].first_name &&
- course.authors[0].last_name && (
-
-
- {course.authors[0].first_name +
- ' ' +
- course.authors[0].last_name}
-
-
- {' '}
- @{course.authors[0].username}
-
-
- )}
- {!course.authors[0].first_name &&
- !course.authors[0].last_name && (
-
-
@{course.authors[0].username}
-
- )}
-
-
-
- )}
-
- {isCourseStarted() ? (
-
- ) : (
-
- )}
+
+
diff --git a/apps/web/app/orgs/[orgslug]/(withmenu)/courses/courses.tsx b/apps/web/app/orgs/[orgslug]/(withmenu)/courses/courses.tsx
index a8a28136..bb17ecd6 100644
--- a/apps/web/app/orgs/[orgslug]/(withmenu)/courses/courses.tsx
+++ b/apps/web/app/orgs/[orgslug]/(withmenu)/courses/courses.tsx
@@ -1,13 +1,13 @@
'use client'
import CreateCourseModal from '@components/Objects/Modals/Course/Create/CreateCourse'
-import Modal from '@components/StyledElements/Modal/Modal'
+import Modal from '@components/Objects/StyledElements/Modal/Modal'
import React from 'react'
import { useSearchParams } from 'next/navigation'
-import GeneralWrapperStyled from '@components/StyledElements/Wrappers/GeneralWrapper'
-import TypeOfContentTitle from '@components/StyledElements/Titles/TypeOfContentTitle'
+import GeneralWrapperStyled from '@components/Objects/StyledElements/Wrappers/GeneralWrapper'
+import TypeOfContentTitle from '@components/Objects/StyledElements/Titles/TypeOfContentTitle'
import AuthenticatedClientElement from '@components/Security/AuthenticatedClientElement'
import CourseThumbnail from '@components/Objects/Thumbnails/CourseThumbnail'
-import NewCourseButton from '@components/StyledElements/Buttons/NewCourseButton'
+import NewCourseButton from '@components/Objects/StyledElements/Buttons/NewCourseButton'
import useAdminStatus from '@components/Hooks/useAdminStatus'
interface CourseProps {
diff --git a/apps/web/app/orgs/[orgslug]/(withmenu)/courses/error.tsx b/apps/web/app/orgs/[orgslug]/(withmenu)/courses/error.tsx
index d5320406..058142f5 100644
--- a/apps/web/app/orgs/[orgslug]/(withmenu)/courses/error.tsx
+++ b/apps/web/app/orgs/[orgslug]/(withmenu)/courses/error.tsx
@@ -1,6 +1,6 @@
'use client' // Error components must be Client Components
-import ErrorUI from '@components/StyledElements/Error/Error'
+import ErrorUI from '@components/Objects/StyledElements/Error/Error'
import { useEffect } from 'react'
export default function Error({
diff --git a/apps/web/app/orgs/[orgslug]/(withmenu)/error.tsx b/apps/web/app/orgs/[orgslug]/(withmenu)/error.tsx
index d5320406..058142f5 100644
--- a/apps/web/app/orgs/[orgslug]/(withmenu)/error.tsx
+++ b/apps/web/app/orgs/[orgslug]/(withmenu)/error.tsx
@@ -1,6 +1,6 @@
'use client' // Error components must be Client Components
-import ErrorUI from '@components/StyledElements/Error/Error'
+import ErrorUI from '@components/Objects/StyledElements/Error/Error'
import { useEffect } from 'react'
export default function Error({
diff --git a/apps/web/app/orgs/[orgslug]/(withmenu)/layout.tsx b/apps/web/app/orgs/[orgslug]/(withmenu)/layout.tsx
index 96e5c1aa..a825e3a5 100644
--- a/apps/web/app/orgs/[orgslug]/(withmenu)/layout.tsx
+++ b/apps/web/app/orgs/[orgslug]/(withmenu)/layout.tsx
@@ -1,8 +1,8 @@
'use client'
import '@styles/globals.css'
-import { Menu } from '@components/Objects/Menu/Menu'
import { SessionProvider } from 'next-auth/react'
-import Watermark from '@components/Watermark'
+import Watermark from '@components/Objects/Watermark'
+import { OrgMenu } from '@components/Objects/Menus/OrgMenu'
export default function RootLayout({
children,
@@ -14,7 +14,7 @@ export default function RootLayout({
return (
<>
-
+
{children}
diff --git a/apps/web/app/orgs/[orgslug]/(withmenu)/page.tsx b/apps/web/app/orgs/[orgslug]/(withmenu)/page.tsx
index 004ce80b..294614ce 100644
--- a/apps/web/app/orgs/[orgslug]/(withmenu)/page.tsx
+++ b/apps/web/app/orgs/[orgslug]/(withmenu)/page.tsx
@@ -4,14 +4,14 @@ import { getUriWithOrg } from '@services/config/config'
import { getOrgCourses } from '@services/courses/courses'
import Link from 'next/link'
import { getOrganizationContextInfo } from '@services/organizations/orgs'
-import GeneralWrapperStyled from '@components/StyledElements/Wrappers/GeneralWrapper'
-import TypeOfContentTitle from '@components/StyledElements/Titles/TypeOfContentTitle'
+import GeneralWrapperStyled from '@components/Objects/StyledElements/Wrappers/GeneralWrapper'
+import TypeOfContentTitle from '@components/Objects/StyledElements/Titles/TypeOfContentTitle'
import CourseThumbnail from '@components/Objects/Thumbnails/CourseThumbnail'
import CollectionThumbnail from '@components/Objects/Thumbnails/CollectionThumbnail'
import AuthenticatedClientElement from '@components/Security/AuthenticatedClientElement'
-import NewCourseButton from '@components/StyledElements/Buttons/NewCourseButton'
-import NewCollectionButton from '@components/StyledElements/Buttons/NewCollectionButton'
-import ContentPlaceHolderIfUserIsNotAdmin from '@components/ContentPlaceHolder'
+import NewCourseButton from '@components/Objects/StyledElements/Buttons/NewCourseButton'
+import NewCollectionButton from '@components/Objects/StyledElements/Buttons/NewCollectionButton'
+import ContentPlaceHolderIfUserIsNotAdmin from '@components/Objects/ContentPlaceHolder'
import { getOrgCollections } from '@services/courses/collections'
import { getServerSession } from 'next-auth'
import { nextAuthOptions } from 'app/auth/options'
diff --git a/apps/web/app/orgs/[orgslug]/(withmenu)/trail/trail.tsx b/apps/web/app/orgs/[orgslug]/(withmenu)/trail/trail.tsx
index 2d5a44c7..dbf3bb33 100644
--- a/apps/web/app/orgs/[orgslug]/(withmenu)/trail/trail.tsx
+++ b/apps/web/app/orgs/[orgslug]/(withmenu)/trail/trail.tsx
@@ -3,8 +3,8 @@ import { useLHSession } from '@components/Contexts/LHSessionContext'
import { useOrg } from '@components/Contexts/OrgContext'
import PageLoading from '@components/Objects/Loaders/PageLoading'
import TrailCourseElement from '@components/Pages/Trail/TrailCourseElement'
-import TypeOfContentTitle from '@components/StyledElements/Titles/TypeOfContentTitle'
-import GeneralWrapperStyled from '@components/StyledElements/Wrappers/GeneralWrapper'
+import TypeOfContentTitle from '@components/Objects/StyledElements/Titles/TypeOfContentTitle'
+import GeneralWrapperStyled from '@components/Objects/StyledElements/Wrappers/GeneralWrapper'
import { getAPIUrl } from '@services/config/config'
import { swrFetcher } from '@services/utils/ts/requests'
import React, { useEffect } from 'react'
diff --git a/apps/web/app/orgs/[orgslug]/dash/ClientAdminLayout.tsx b/apps/web/app/orgs/[orgslug]/dash/ClientAdminLayout.tsx
index 48370ca3..a65f6c02 100644
--- a/apps/web/app/orgs/[orgslug]/dash/ClientAdminLayout.tsx
+++ b/apps/web/app/orgs/[orgslug]/dash/ClientAdminLayout.tsx
@@ -1,9 +1,9 @@
'use client';
-import DashLeftMenu from '@components/Dashboard/UI/DashLeftMenu'
-import DashMobileMenu from '@components/Dashboard/UI/DashMobileMenu'
+import DashLeftMenu from '@components/Dashboard/Menus/DashLeftMenu';
+import DashMobileMenu from '@components/Dashboard/Menus/DashMobileMenu';
import AdminAuthorization from '@components/Security/AdminAuthorization'
import { SessionProvider } from 'next-auth/react'
-import React, { useState, useEffect } from 'react'
+import React from 'react'
import { useMediaQuery } from 'usehooks-ts';
function ClientAdminLayout({
diff --git a/apps/web/app/orgs/[orgslug]/dash/assignments/[assignmentuuid]/_components/TaskEditor/Subs/AssignmentTaskGeneralEdit.tsx b/apps/web/app/orgs/[orgslug]/dash/assignments/[assignmentuuid]/_components/TaskEditor/Subs/AssignmentTaskGeneralEdit.tsx
index f7bae0ef..f346e469 100644
--- a/apps/web/app/orgs/[orgslug]/dash/assignments/[assignmentuuid]/_components/TaskEditor/Subs/AssignmentTaskGeneralEdit.tsx
+++ b/apps/web/app/orgs/[orgslug]/dash/assignments/[assignmentuuid]/_components/TaskEditor/Subs/AssignmentTaskGeneralEdit.tsx
@@ -3,7 +3,7 @@ import { useAssignments } from '@components/Contexts/Assignments/AssignmentConte
import { useAssignmentsTask, useAssignmentsTaskDispatch } from '@components/Contexts/Assignments/AssignmentsTaskContext';
import { useLHSession } from '@components/Contexts/LHSessionContext';
import { useOrg } from '@components/Contexts/OrgContext';
-import FormLayout, { FormField, FormLabelAndMessage, Input, Textarea } from '@components/StyledElements/Form/Form';
+import FormLayout, { FormField, FormLabelAndMessage, Input, Textarea } from '@components/Objects/StyledElements/Form/Form';
import * as Form from '@radix-ui/react-form';
import { getActivityByID } from '@services/courses/activities';
import { updateAssignmentTask, updateReferenceFile } from '@services/courses/assignments';
diff --git a/apps/web/app/orgs/[orgslug]/dash/assignments/[assignmentuuid]/_components/Tasks.tsx b/apps/web/app/orgs/[orgslug]/dash/assignments/[assignmentuuid]/_components/Tasks.tsx
index 63997abe..5a816ab2 100644
--- a/apps/web/app/orgs/[orgslug]/dash/assignments/[assignmentuuid]/_components/Tasks.tsx
+++ b/apps/web/app/orgs/[orgslug]/dash/assignments/[assignmentuuid]/_components/Tasks.tsx
@@ -1,5 +1,5 @@
import { useAssignments } from '@components/Contexts/Assignments/AssignmentContext'
-import Modal from '@components/StyledElements/Modal/Modal';
+import Modal from '@components/Objects/StyledElements/Modal/Modal';
import { FileUp, ListTodo, PanelLeftOpen, Plus } from 'lucide-react';
import React, { useEffect } from 'react'
import NewTaskModal from './Modals/NewTaskModal';
diff --git a/apps/web/app/orgs/[orgslug]/dash/assignments/[assignmentuuid]/page.tsx b/apps/web/app/orgs/[orgslug]/dash/assignments/[assignmentuuid]/page.tsx
index 7c3144a5..fe6f4bdf 100644
--- a/apps/web/app/orgs/[orgslug]/dash/assignments/[assignmentuuid]/page.tsx
+++ b/apps/web/app/orgs/[orgslug]/dash/assignments/[assignmentuuid]/page.tsx
@@ -1,9 +1,9 @@
'use client';
-import BreadCrumbs from '@components/Dashboard/UI/BreadCrumbs'
+import BreadCrumbs from '@components/Dashboard/Misc/BreadCrumbs'
import { BookOpen, BookX, EllipsisVertical, Eye, Layers2, Monitor, UserRoundPen } from 'lucide-react'
import React, { useEffect } from 'react'
import { AssignmentProvider, useAssignments } from '@components/Contexts/Assignments/AssignmentContext';
-import ToolTip from '@components/StyledElements/Tooltip/Tooltip';
+import ToolTip from '@components/Objects/StyledElements/Tooltip/Tooltip';
import { updateAssignment } from '@services/courses/assignments';
import { useLHSession } from '@components/Contexts/LHSessionContext';
import { mutate } from 'swr';
diff --git a/apps/web/app/orgs/[orgslug]/dash/assignments/[assignmentuuid]/subpages/AssignmentSubmissionsSubPage.tsx b/apps/web/app/orgs/[orgslug]/dash/assignments/[assignmentuuid]/subpages/AssignmentSubmissionsSubPage.tsx
index 33a55715..4cc32f02 100644
--- a/apps/web/app/orgs/[orgslug]/dash/assignments/[assignmentuuid]/subpages/AssignmentSubmissionsSubPage.tsx
+++ b/apps/web/app/orgs/[orgslug]/dash/assignments/[assignmentuuid]/subpages/AssignmentSubmissionsSubPage.tsx
@@ -1,6 +1,6 @@
import { useLHSession } from '@components/Contexts/LHSessionContext';
import UserAvatar from '@components/Objects/UserAvatar';
-import Modal from '@components/StyledElements/Modal/Modal';
+import Modal from '@components/Objects/StyledElements/Modal/Modal';
import { getAPIUrl } from '@services/config/config';
import { getUserAvatarMediaDirectory } from '@services/media/media';
import { swrFetcher } from '@services/utils/ts/requests';
diff --git a/apps/web/app/orgs/[orgslug]/dash/assignments/page.tsx b/apps/web/app/orgs/[orgslug]/dash/assignments/page.tsx
index 8e11e60b..0ee94fd6 100644
--- a/apps/web/app/orgs/[orgslug]/dash/assignments/page.tsx
+++ b/apps/web/app/orgs/[orgslug]/dash/assignments/page.tsx
@@ -1,7 +1,7 @@
'use client';
import { useLHSession } from '@components/Contexts/LHSessionContext';
import { useOrg } from '@components/Contexts/OrgContext';
-import BreadCrumbs from '@components/Dashboard/UI/BreadCrumbs'
+import BreadCrumbs from '@components/Dashboard/Misc/BreadCrumbs'
import { getAPIUrl, getUriWithOrg } from '@services/config/config';
import { getAssignmentsFromACourse } from '@services/courses/assignments';
import { getCourseThumbnailMediaDirectory } from '@services/media/media';
diff --git a/apps/web/app/orgs/[orgslug]/dash/courses/client.tsx b/apps/web/app/orgs/[orgslug]/dash/courses/client.tsx
index 57f1ba2a..766c978b 100644
--- a/apps/web/app/orgs/[orgslug]/dash/courses/client.tsx
+++ b/apps/web/app/orgs/[orgslug]/dash/courses/client.tsx
@@ -1,10 +1,10 @@
'use client'
-import BreadCrumbs from '@components/Dashboard/UI/BreadCrumbs'
+import BreadCrumbs from '@components/Dashboard/Misc/BreadCrumbs'
import CreateCourseModal from '@components/Objects/Modals/Course/Create/CreateCourse'
import CourseThumbnail, { removeCoursePrefix } from '@components/Objects/Thumbnails/CourseThumbnail'
import AuthenticatedClientElement from '@components/Security/AuthenticatedClientElement'
-import NewCourseButton from '@components/StyledElements/Buttons/NewCourseButton'
-import Modal from '@components/StyledElements/Modal/Modal'
+import NewCourseButton from '@components/Objects/StyledElements/Buttons/NewCourseButton'
+import Modal from '@components/Objects/StyledElements/Modal/Modal'
import { useSearchParams } from 'next/navigation'
import React from 'react'
import useAdminStatus from '@components/Hooks/useAdminStatus'
diff --git a/apps/web/app/orgs/[orgslug]/dash/courses/course/[courseuuid]/[subpage]/page.tsx b/apps/web/app/orgs/[orgslug]/dash/courses/course/[courseuuid]/[subpage]/page.tsx
index 9b44a988..0c37a366 100644
--- a/apps/web/app/orgs/[orgslug]/dash/courses/course/[courseuuid]/[subpage]/page.tsx
+++ b/apps/web/app/orgs/[orgslug]/dash/courses/course/[courseuuid]/[subpage]/page.tsx
@@ -1,14 +1,14 @@
'use client'
-import EditCourseStructure from '../../../../../../../../components/Dashboard/Course/EditCourseStructure/EditCourseStructure'
import { getUriWithOrg } from '@services/config/config'
import React from 'react'
import { CourseProvider } from '../../../../../../../../components/Contexts/CourseContext'
import Link from 'next/link'
-import { CourseOverviewTop } from '@components/Dashboard/UI/CourseOverviewTop'
+import { CourseOverviewTop } from '@components/Dashboard/Misc/CourseOverviewTop'
import { motion } from 'framer-motion'
-import EditCourseGeneral from '@components/Dashboard/Course/EditCourseGeneral/EditCourseGeneral'
import { GalleryVerticalEnd, Info, UserRoundCog } from 'lucide-react'
-import EditCourseAccess from '@components/Dashboard/Course/EditCourseAccess/EditCourseAccess'
+import EditCourseStructure from '@components/Dashboard/Pages/Course/EditCourseStructure/EditCourseStructure'
+import EditCourseGeneral from '@components/Dashboard/Pages/Course/EditCourseGeneral/EditCourseGeneral'
+import EditCourseAccess from '@components/Dashboard/Pages/Course/EditCourseAccess/EditCourseAccess'
export type CourseOverviewParams = {
orgslug: string
diff --git a/apps/web/app/orgs/[orgslug]/dash/org/settings/[subpage]/page.tsx b/apps/web/app/orgs/[orgslug]/dash/org/settings/[subpage]/page.tsx
index d748809b..6054523b 100644
--- a/apps/web/app/orgs/[orgslug]/dash/org/settings/[subpage]/page.tsx
+++ b/apps/web/app/orgs/[orgslug]/dash/org/settings/[subpage]/page.tsx
@@ -1,11 +1,11 @@
'use client'
-import BreadCrumbs from '@components/Dashboard/UI/BreadCrumbs'
+import BreadCrumbs from '@components/Dashboard/Misc/BreadCrumbs'
import { getUriWithOrg } from '@services/config/config'
import { Info } from 'lucide-react'
import Link from 'next/link'
import React, { useEffect } from 'react'
import { motion } from 'framer-motion'
-import OrgEditGeneral from '@components/Dashboard/Org/OrgEditGeneral/OrgEditGeneral'
+import OrgEditGeneral from '@components/Dashboard/Pages/Org/OrgEditGeneral/OrgEditGeneral'
export type OrgParams = {
subpage: string
diff --git a/apps/web/app/orgs/[orgslug]/dash/payments/[subpage]/page.tsx b/apps/web/app/orgs/[orgslug]/dash/payments/[subpage]/page.tsx
new file mode 100644
index 00000000..173ececd
--- /dev/null
+++ b/apps/web/app/orgs/[orgslug]/dash/payments/[subpage]/page.tsx
@@ -0,0 +1,142 @@
+'use client'
+import React, { useState, useEffect } from 'react'
+import { motion } from 'framer-motion'
+import BreadCrumbs from '@components/Dashboard/Misc/BreadCrumbs'
+import Link from 'next/link'
+import { getUriWithOrg } from '@services/config/config'
+import { CreditCard, Settings, Repeat, BookOpen, Users, DollarSign, Gem } from 'lucide-react'
+import { useLHSession } from '@components/Contexts/LHSessionContext'
+import { useOrg } from '@components/Contexts/OrgContext'
+import PaymentsConfigurationPage from '@components/Dashboard/Pages/Payments/PaymentsConfigurationPage'
+import PaymentsProductPage from '@components/Dashboard/Pages/Payments/PaymentsProductPage'
+import PaymentsCustomersPage from '@components/Dashboard/Pages/Payments/PaymentsCustomersPage'
+import useFeatureFlag from '@components/Hooks/useFeatureFlag'
+
+export type PaymentsParams = {
+ subpage: string
+ orgslug: string
+}
+
+function PaymentsPage({ params }: { params: PaymentsParams }) {
+ const session = useLHSession() as any
+ const org = useOrg() as any
+ const [selectedSubPage, setSelectedSubPage] = useState(params.subpage || 'general')
+ const [H1Label, setH1Label] = useState('')
+ const [H2Label, setH2Label] = useState('')
+
+ const isPaymentsEnabled = useFeatureFlag({
+ path: ['features', 'payments', 'enabled'],
+ defaultValue: false
+ })
+
+ useEffect(() => {
+ handleLabels()
+ }, [selectedSubPage])
+
+ if (!isPaymentsEnabled) {
+ return (
+
+
+
Payments Not Available
+
The payments feature is not enabled for this organization.
+
Please contact your administrator to enable payments.
+
+
+ )
+ }
+
+ function handleLabels() {
+ if (selectedSubPage === 'general') {
+ setH1Label('Payments')
+ setH2Label('Overview of your payment settings and transactions')
+ }
+ if (selectedSubPage === 'configuration') {
+ setH1Label('Payment Configuration')
+ setH2Label('Set up and manage your payment gateway')
+ }
+ if (selectedSubPage === 'subscriptions') {
+ setH1Label('Subscriptions')
+ setH2Label('Manage your subscription plans')
+ }
+ if (selectedSubPage === 'paid-products') {
+ setH1Label('Paid Products')
+ setH2Label('Manage your paid products and pricing')
+ }
+ if (selectedSubPage === 'customers') {
+ setH1Label('Customers')
+ setH2Label('View and manage your customer information')
+ }
+ }
+
+ return (
+
+
+
+
+
+
+ {H1Label}
+
+
+ {H2Label}{' '}
+
+
+
+
+ }
+ label="Customers"
+ isActive={selectedSubPage === 'customers'}
+ onClick={() => setSelectedSubPage('customers')}
+ />
+ }
+ label="Products & Subscriptions"
+ isActive={selectedSubPage === 'paid-products'}
+ onClick={() => setSelectedSubPage('paid-products')}
+ />
+ }
+ label="Configuration"
+ isActive={selectedSubPage === 'configuration'}
+ onClick={() => setSelectedSubPage('configuration')}
+ />
+
+
+
+
+
+ {selectedSubPage === 'general' && General
}
+ {selectedSubPage === 'configuration' && }
+ {selectedSubPage === 'paid-products' && }
+ {selectedSubPage === 'customers' && }
+
+
+ )
+}
+
+const TabLink = ({ href, icon, label, isActive, onClick }: { href: string, icon: React.ReactNode, label: string, isActive: boolean, onClick: () => void }) => (
+
+
+
+)
+
+export default PaymentsPage
diff --git a/apps/web/app/orgs/[orgslug]/dash/user-account/owned/page.tsx b/apps/web/app/orgs/[orgslug]/dash/user-account/owned/page.tsx
new file mode 100644
index 00000000..767db3cf
--- /dev/null
+++ b/apps/web/app/orgs/[orgslug]/dash/user-account/owned/page.tsx
@@ -0,0 +1,64 @@
+'use client'
+
+import React from 'react'
+import { useOrg } from '@components/Contexts/OrgContext'
+import { useLHSession } from '@components/Contexts/LHSessionContext'
+import useSWR from 'swr'
+import { getOwnedCourses } from '@services/payments/payments'
+import CourseThumbnail from '@components/Objects/Thumbnails/CourseThumbnail'
+import PageLoading from '@components/Objects/Loaders/PageLoading'
+import { BookOpen, Package2 } from 'lucide-react'
+
+function OwnedCoursesPage() {
+ const org = useOrg() as any
+ const session = useLHSession() as any
+ const access_token = session?.data?.tokens?.access_token
+
+ const { data: ownedCourses, error, isLoading } = useSWR(
+ org ? [`/payments/${org.id}/courses/owned`, access_token] : null,
+ ([url, token]) => getOwnedCourses(org.id, token)
+ )
+
+ if (isLoading) return
+ if (error) return
Error loading owned courses
+
+ return (
+
+
+
+
+
+
My Courses
+ Courses you have purchased or subscribed to
+
+
+
+
+
+ {ownedCourses?.map((course: any) => (
+
+
+
+ ))}
+
+ {(!ownedCourses || ownedCourses.length === 0) && (
+
+
+
+
+
+
+ No purchased courses
+
+
+ You haven't purchased any courses yet
+
+
+
+ )}
+
+
+ )
+}
+
+export default OwnedCoursesPage
diff --git a/apps/web/app/orgs/[orgslug]/dash/user-account/settings/[subpage]/page.tsx b/apps/web/app/orgs/[orgslug]/dash/user-account/settings/[subpage]/page.tsx
index 6162f70e..357bbd39 100644
--- a/apps/web/app/orgs/[orgslug]/dash/user-account/settings/[subpage]/page.tsx
+++ b/apps/web/app/orgs/[orgslug]/dash/user-account/settings/[subpage]/page.tsx
@@ -1,12 +1,12 @@
'use client'
import React, { useEffect } from 'react'
import { motion } from 'framer-motion'
-import UserEditGeneral from '@components/Dashboard/UserAccount/UserEditGeneral/UserEditGeneral'
-import UserEditPassword from '@components/Dashboard/UserAccount/UserEditPassword/UserEditPassword'
+import UserEditGeneral from '@components/Dashboard/Pages/UserAccount/UserEditGeneral/UserEditGeneral'
+import UserEditPassword from '@components/Dashboard/Pages/UserAccount/UserEditPassword/UserEditPassword'
import Link from 'next/link'
import { getUriWithOrg } from '@services/config/config'
import { Info, Lock } from 'lucide-react'
-import BreadCrumbs from '@components/Dashboard/UI/BreadCrumbs'
+import BreadCrumbs from '@components/Dashboard/Misc/BreadCrumbs'
import { useLHSession } from '@components/Contexts/LHSessionContext'
export type SettingsParams = {
diff --git a/apps/web/app/orgs/[orgslug]/dash/users/settings/[subpage]/page.tsx b/apps/web/app/orgs/[orgslug]/dash/users/settings/[subpage]/page.tsx
index d6342e38..30e74c0a 100644
--- a/apps/web/app/orgs/[orgslug]/dash/users/settings/[subpage]/page.tsx
+++ b/apps/web/app/orgs/[orgslug]/dash/users/settings/[subpage]/page.tsx
@@ -5,13 +5,13 @@ import Link from 'next/link'
import { useMediaQuery } from 'usehooks-ts'
import { getUriWithOrg } from '@services/config/config'
import { Monitor, ScanEye, SquareUserRound, UserPlus, Users } from 'lucide-react'
-import BreadCrumbs from '@components/Dashboard/UI/BreadCrumbs'
+import BreadCrumbs from '@components/Dashboard/Misc/BreadCrumbs'
import { useLHSession } from '@components/Contexts/LHSessionContext'
import { useOrg } from '@components/Contexts/OrgContext'
-import OrgUsers from '@components/Dashboard/Users/OrgUsers/OrgUsers'
-import OrgAccess from '@components/Dashboard/Users/OrgAccess/OrgAccess'
-import OrgUsersAdd from '@components/Dashboard/Users/OrgUsersAdd/OrgUsersAdd'
-import OrgUserGroups from '@components/Dashboard/Users/OrgUserGroups/OrgUserGroups'
+import OrgUsers from '@components/Dashboard/Pages/Users/OrgUsers/OrgUsers'
+import OrgAccess from '@components/Dashboard/Pages/Users/OrgAccess/OrgAccess'
+import OrgUsersAdd from '@components/Dashboard/Pages/Users/OrgUsersAdd/OrgUsersAdd'
+import OrgUserGroups from '@components/Dashboard/Pages/Users/OrgUserGroups/OrgUserGroups'
export type SettingsParams = {
subpage: string
diff --git a/apps/web/app/orgs/[orgslug]/health/page.tsx b/apps/web/app/orgs/[orgslug]/health/page.tsx
deleted file mode 100644
index e9d89171..00000000
--- a/apps/web/app/orgs/[orgslug]/health/page.tsx
+++ /dev/null
@@ -1,9 +0,0 @@
-import React from 'react'
-
-function HealthPage() {
- return (
-
OK
- )
-}
-
-export default HealthPage
\ No newline at end of file
diff --git a/apps/web/app/orgs/[orgslug]/layout.tsx b/apps/web/app/orgs/[orgslug]/layout.tsx
index 56735360..42912f32 100644
--- a/apps/web/app/orgs/[orgslug]/layout.tsx
+++ b/apps/web/app/orgs/[orgslug]/layout.tsx
@@ -1,9 +1,9 @@
'use client'
import { OrgProvider } from '@components/Contexts/OrgContext'
import NextTopLoader from 'nextjs-toploader';
-import Toast from '@components/StyledElements/Toast/Toast'
+import Toast from '@components/Objects/StyledElements/Toast/Toast'
import '@styles/globals.css'
-import Onboarding from '@components/Onboarding/Onboarding';
+import Onboarding from '@components/Objects/Onboarding/Onboarding';
export default function RootLayout({
children,
diff --git a/apps/web/app/payments/stripe/connect/oauth/page.tsx b/apps/web/app/payments/stripe/connect/oauth/page.tsx
new file mode 100644
index 00000000..03b068a7
--- /dev/null
+++ b/apps/web/app/payments/stripe/connect/oauth/page.tsx
@@ -0,0 +1,124 @@
+'use client'
+import React, { useEffect, useState } from 'react'
+import { useRouter, useSearchParams } from 'next/navigation'
+import { useLHSession } from '@components/Contexts/LHSessionContext'
+import { useOrg } from '@components/Contexts/OrgContext'
+import { getUriWithOrg } from '@services/config/config'
+import { Check, Loader2, AlertTriangle } from 'lucide-react'
+import { motion } from 'framer-motion'
+import toast from 'react-hot-toast'
+import { verifyStripeConnection } from '@services/payments/payments'
+import Image from 'next/image'
+import learnhouseIcon from 'public/learnhouse_bigicon_1.png'
+
+function StripeConnectCallback() {
+ const router = useRouter()
+ const searchParams = useSearchParams()
+ const session = useLHSession() as any
+ const [status, setStatus] = useState<'processing' | 'success' | 'error'>('processing')
+ const [message, setMessage] = useState('')
+
+ useEffect(() => {
+ const verifyConnection = async () => {
+ try {
+ const code = searchParams.get('code')
+ const state = searchParams.get('state')
+ const orgId = state?.split('=')[1] // Extract org_id value after '='
+
+ if (!code || !orgId) {
+ throw new Error('Missing required parameters')
+ }
+
+ const response = await verifyStripeConnection(
+ parseInt(orgId),
+ code,
+ session?.data?.tokens?.access_token
+ )
+
+ // Wait for 1 second to show processing state
+ await new Promise(resolve => setTimeout(resolve, 1000))
+
+ setStatus('success')
+ setMessage('Successfully connected to Stripe!')
+
+ // Close the window after 2 seconds of showing success
+ setTimeout(() => {
+ window.close()
+ }, 2000)
+
+ } catch (error) {
+ console.error('Error verifying Stripe connection:', error)
+ setStatus('error')
+ setMessage('Failed to complete Stripe connection')
+ toast.error('Failed to connect to Stripe')
+ }
+ }
+
+ if (session) {
+ verifyConnection()
+ }
+ }, [session, router, searchParams])
+
+ return (
+
+
+
+
+
+
+
+
+ {status === 'processing' && (
+ <>
+
+
+ Completing Stripe Connection
+
+
+ Please wait while we finish setting up your Stripe integration...
+
+ >
+ )}
+
+ {status === 'success' && (
+ <>
+
+
+
+
{message}
+
+ You can now return to the dashboard to start using payments.
+
+ >
+ )}
+
+ {status === 'error' && (
+ <>
+
+
{message}
+
+ Please try again or contact support if the problem persists.
+
+ >
+ )}
+
+
+
+
+ )
+}
+
+export default StripeConnectCallback
\ No newline at end of file
diff --git a/apps/web/components/Contexts/LHSessionContext.tsx b/apps/web/components/Contexts/LHSessionContext.tsx
index 90acb9d8..b7a2965d 100644
--- a/apps/web/components/Contexts/LHSessionContext.tsx
+++ b/apps/web/components/Contexts/LHSessionContext.tsx
@@ -1,17 +1,13 @@
'use client'
import PageLoading from '@components/Objects/Loaders/PageLoading';
import { useSession } from 'next-auth/react';
-import React, { useContext, createContext, useEffect } from 'react'
+import React, { useContext, createContext } from 'react'
export const SessionContext = createContext({}) as any
function LHSessionProvider({ children }: { children: React.ReactNode }) {
const session = useSession();
- useEffect(() => {
- }, [])
-
-
if (session && session.status == 'loading') {
return
}
diff --git a/apps/web/components/Contexts/OrgContext.tsx b/apps/web/components/Contexts/OrgContext.tsx
index 8b50e39a..60a76144 100644
--- a/apps/web/components/Contexts/OrgContext.tsx
+++ b/apps/web/components/Contexts/OrgContext.tsx
@@ -4,8 +4,8 @@ import { swrFetcher } from '@services/utils/ts/requests'
import React, { createContext, useContext, useMemo } from 'react'
import useSWR from 'swr'
import { useLHSession } from '@components/Contexts/LHSessionContext'
-import ErrorUI from '@components/StyledElements/Error/Error'
-import InfoUI from '@components/StyledElements/Info/Info'
+import ErrorUI from '@components/Objects/StyledElements/Error/Error'
+import InfoUI from '@components/Objects/StyledElements/Info/Info'
import { usePathname } from 'next/navigation'
export const OrgContext = createContext(null)
diff --git a/apps/web/components/Dashboard/UI/DashLeftMenu.tsx b/apps/web/components/Dashboard/Menus/DashLeftMenu.tsx
similarity index 78%
rename from apps/web/components/Dashboard/UI/DashLeftMenu.tsx
rename to apps/web/components/Dashboard/Menus/DashLeftMenu.tsx
index b280d76b..4b0a3931 100644
--- a/apps/web/components/Dashboard/UI/DashLeftMenu.tsx
+++ b/apps/web/components/Dashboard/Menus/DashLeftMenu.tsx
@@ -1,9 +1,9 @@
'use client'
import { useOrg } from '@components/Contexts/OrgContext'
import { signOut } from 'next-auth/react'
-import ToolTip from '@components/StyledElements/Tooltip/Tooltip'
+import ToolTip from '@components/Objects/StyledElements/Tooltip/Tooltip'
import LearnHouseDashboardLogo from '@public/dashLogo.png'
-import { Backpack, BookCopy, Home, LogOut, School, Settings, Users } from 'lucide-react'
+import { Backpack, BadgeDollarSign, BookCopy, Home, LogOut, Package2, School, Settings, Users, Vault } from 'lucide-react'
import Image from 'next/image'
import Link from 'next/link'
import React, { useEffect } from 'react'
@@ -11,11 +11,13 @@ import UserAvatar from '../../Objects/UserAvatar'
import AdminAuthorization from '@components/Security/AdminAuthorization'
import { useLHSession } from '@components/Contexts/LHSessionContext'
import { getUriWithOrg, getUriWithoutOrg } from '@services/config/config'
+import useFeatureFlag from '@components/Hooks/useFeatureFlag'
function DashLeftMenu() {
const org = useOrg() as any
const session = useLHSession() as any
const [loading, setLoading] = React.useState(true)
+ const isPaymentsEnabled = useFeatureFlag({ path: ['features', 'payments', 'enabled'], defaultValue: false })
function waitForEverythingToLoad() {
if (org && session) {
@@ -112,6 +114,16 @@ function DashLeftMenu() {
+ {isPaymentsEnabled && (
+
+
+
+
+
+ )}
-
-
+
Assignments
+
+
+
+ Payments
+
+
diff --git a/apps/web/components/Dashboard/UI/BreadCrumbs.tsx b/apps/web/components/Dashboard/Misc/BreadCrumbs.tsx
similarity index 85%
rename from apps/web/components/Dashboard/UI/BreadCrumbs.tsx
rename to apps/web/components/Dashboard/Misc/BreadCrumbs.tsx
index f242f315..9e0d9dde 100644
--- a/apps/web/components/Dashboard/UI/BreadCrumbs.tsx
+++ b/apps/web/components/Dashboard/Misc/BreadCrumbs.tsx
@@ -1,11 +1,11 @@
'use client';
import { useOrg } from '@components/Contexts/OrgContext';
-import { Backpack, Book, ChevronRight, School, User, Users } from 'lucide-react'
+import { Backpack, Book, ChevronRight, CreditCard, School, User, Users } from 'lucide-react'
import Link from 'next/link'
import React from 'react'
type BreadCrumbsProps = {
- type: 'courses' | 'user' | 'users' | 'org' | 'orgusers' | 'assignments'
+ type: 'courses' | 'user' | 'users' | 'org' | 'orgusers' | 'assignments' | 'payments'
last_breadcrumb?: string
}
@@ -65,6 +65,15 @@ function BreadCrumbs(props: BreadCrumbsProps) {
) : (
''
)}
+ {props.type == 'payments' ? (
+
+ {' '}
+
+ Payments
+
+ ) : (
+ ''
+ )}
{props.last_breadcrumb ?
: ''}
diff --git a/apps/web/components/Dashboard/UI/CourseOverviewTop.tsx b/apps/web/components/Dashboard/Misc/CourseOverviewTop.tsx
similarity index 100%
rename from apps/web/components/Dashboard/UI/CourseOverviewTop.tsx
rename to apps/web/components/Dashboard/Misc/CourseOverviewTop.tsx
diff --git a/apps/web/components/Dashboard/UI/SaveState.tsx b/apps/web/components/Dashboard/Misc/SaveState.tsx
similarity index 100%
rename from apps/web/components/Dashboard/UI/SaveState.tsx
rename to apps/web/components/Dashboard/Misc/SaveState.tsx
diff --git a/apps/web/components/Dashboard/Course/EditCourseAccess/EditCourseAccess.tsx b/apps/web/components/Dashboard/Pages/Course/EditCourseAccess/EditCourseAccess.tsx
similarity index 98%
rename from apps/web/components/Dashboard/Course/EditCourseAccess/EditCourseAccess.tsx
rename to apps/web/components/Dashboard/Pages/Course/EditCourseAccess/EditCourseAccess.tsx
index a682bef5..1770b180 100644
--- a/apps/web/components/Dashboard/Course/EditCourseAccess/EditCourseAccess.tsx
+++ b/apps/web/components/Dashboard/Pages/Course/EditCourseAccess/EditCourseAccess.tsx
@@ -1,7 +1,7 @@
import { useCourse, useCourseDispatch } from '@components/Contexts/CourseContext'
import LinkToUserGroup from '@components/Objects/Modals/Dash/EditCourseAccess/LinkToUserGroup'
-import ConfirmationModal from '@components/StyledElements/ConfirmationModal/ConfirmationModal'
-import Modal from '@components/StyledElements/Modal/Modal'
+import ConfirmationModal from '@components/Objects/StyledElements/ConfirmationModal/ConfirmationModal'
+import Modal from '@components/Objects/StyledElements/Modal/Modal'
import { getAPIUrl } from '@services/config/config'
import { unLinkResourcesToUserGroup } from '@services/usergroups/usergroups'
import { swrFetcher } from '@services/utils/ts/requests'
diff --git a/apps/web/components/Dashboard/Course/EditCourseGeneral/EditCourseGeneral.tsx b/apps/web/components/Dashboard/Pages/Course/EditCourseGeneral/EditCourseGeneral.tsx
similarity index 97%
rename from apps/web/components/Dashboard/Course/EditCourseGeneral/EditCourseGeneral.tsx
rename to apps/web/components/Dashboard/Pages/Course/EditCourseGeneral/EditCourseGeneral.tsx
index 8535fd83..e8c8cbf4 100644
--- a/apps/web/components/Dashboard/Course/EditCourseGeneral/EditCourseGeneral.tsx
+++ b/apps/web/components/Dashboard/Pages/Course/EditCourseGeneral/EditCourseGeneral.tsx
@@ -3,13 +3,13 @@ import FormLayout, {
FormLabelAndMessage,
Input,
Textarea,
-} from '@components/StyledElements/Form/Form';
+} from '@components/Objects/StyledElements/Form/Form';
import { useFormik } from 'formik';
import { AlertTriangle } from 'lucide-react';
import * as Form from '@radix-ui/react-form';
import React, { useEffect, useState } from 'react';
-import { useCourse, useCourseDispatch } from '../../../Contexts/CourseContext';
import ThumbnailUpdate from './ThumbnailUpdate';
+import { useCourse, useCourseDispatch } from '@components/Contexts/CourseContext';
type EditCourseStructureProps = {
orgslug: string
diff --git a/apps/web/components/Dashboard/Course/EditCourseGeneral/ThumbnailUpdate.tsx b/apps/web/components/Dashboard/Pages/Course/EditCourseGeneral/ThumbnailUpdate.tsx
similarity index 100%
rename from apps/web/components/Dashboard/Course/EditCourseGeneral/ThumbnailUpdate.tsx
rename to apps/web/components/Dashboard/Pages/Course/EditCourseGeneral/ThumbnailUpdate.tsx
diff --git a/apps/web/components/Dashboard/Course/EditCourseGeneral/UnsplashImagePicker.tsx b/apps/web/components/Dashboard/Pages/Course/EditCourseGeneral/UnsplashImagePicker.tsx
similarity index 84%
rename from apps/web/components/Dashboard/Course/EditCourseGeneral/UnsplashImagePicker.tsx
rename to apps/web/components/Dashboard/Pages/Course/EditCourseGeneral/UnsplashImagePicker.tsx
index 662974af..68abb289 100644
--- a/apps/web/components/Dashboard/Course/EditCourseGeneral/UnsplashImagePicker.tsx
+++ b/apps/web/components/Dashboard/Pages/Course/EditCourseGeneral/UnsplashImagePicker.tsx
@@ -3,6 +3,7 @@ import { createApi } from 'unsplash-js';
import { Search, X, Cpu, Briefcase, GraduationCap, Heart, Palette, Plane, Utensils,
Dumbbell, Music, Shirt, Book, Building, Bike, Camera, Microscope, Coins, Coffee, Gamepad,
Flower} from 'lucide-react';
+import Modal from '@components/Objects/StyledElements/Modal/Modal';
const unsplash = createApi({
accessKey: process.env.NEXT_PUBLIC_UNSPLASH_ACCESS_KEY as string,
@@ -36,9 +37,10 @@ const predefinedLabels = [
interface UnsplashImagePickerProps {
onSelect: (imageUrl: string) => void;
onClose: () => void;
+ isOpen?: boolean;
}
-const UnsplashImagePicker: React.FC
= ({ onSelect, onClose }) => {
+const UnsplashImagePicker: React.FC = ({ onSelect, onClose, isOpen = true }) => {
const [query, setQuery] = useState('');
const [images, setImages] = useState([]);
const [page, setPage] = useState(1);
@@ -54,8 +56,6 @@ const UnsplashImagePicker: React.FC = ({ onSelect, onC
});
if (result && result.response) {
setImages(prevImages => pageNum === 1 ? result.response.results : [...prevImages, ...result.response.results]);
- } else {
- console.error('Unexpected response structure:', result);
}
} catch (error) {
console.error('Error fetching images:', error);
@@ -97,16 +97,10 @@ const UnsplashImagePicker: React.FC = ({ onSelect, onC
onClose();
};
- return (
-
-
-
-
Choose an image from Unsplash
-
-
-
+ const modalContent = (
+
+
+
= ({ onSelect, onC
/>
-
+
{predefinedLabels.map(label => (
))}
+
+
+
{images.map(image => (
@@ -135,7 +132,7 @@ const UnsplashImagePicker: React.FC = ({ onSelect, onC
src={image.urls.small}
alt={image.alt_description}
className="absolute inset-0 w-full h-full object-cover rounded-lg cursor-pointer hover:opacity-80 transition-opacity"
- onClick={() => handleImageSelect(image.urls.full)}
+ onClick={() => handleImageSelect(image.urls.regular)}
/>
))}
@@ -144,7 +141,7 @@ const UnsplashImagePicker: React.FC
= ({ onSelect, onC
{!loading && images.length > 0 && (
@@ -152,6 +149,18 @@ const UnsplashImagePicker: React.FC = ({ onSelect, onC
);
+
+ return (
+
+ );
};
// Custom debounce function
diff --git a/apps/web/components/Dashboard/Course/EditCourseStructure/Buttons/NewActivityButton.tsx b/apps/web/components/Dashboard/Pages/Course/EditCourseStructure/Buttons/NewActivityButton.tsx
similarity index 98%
rename from apps/web/components/Dashboard/Course/EditCourseStructure/Buttons/NewActivityButton.tsx
rename to apps/web/components/Dashboard/Pages/Course/EditCourseStructure/Buttons/NewActivityButton.tsx
index 896632ff..63e8f0f5 100644
--- a/apps/web/components/Dashboard/Course/EditCourseStructure/Buttons/NewActivityButton.tsx
+++ b/apps/web/components/Dashboard/Pages/Course/EditCourseStructure/Buttons/NewActivityButton.tsx
@@ -1,6 +1,6 @@
import { useCourse } from '@components/Contexts/CourseContext'
import NewActivityModal from '@components/Objects/Modals/Activities/Create/NewActivity'
-import Modal from '@components/StyledElements/Modal/Modal'
+import Modal from '@components/Objects/StyledElements/Modal/Modal'
import { getAPIUrl } from '@services/config/config'
import {
createActivity,
diff --git a/apps/web/components/Dashboard/Course/EditCourseStructure/DraggableElements/ActivityElement.tsx b/apps/web/components/Dashboard/Pages/Course/EditCourseStructure/DraggableElements/ActivityElement.tsx
similarity index 90%
rename from apps/web/components/Dashboard/Course/EditCourseStructure/DraggableElements/ActivityElement.tsx
rename to apps/web/components/Dashboard/Pages/Course/EditCourseStructure/DraggableElements/ActivityElement.tsx
index 642f9162..294fc2fb 100644
--- a/apps/web/components/Dashboard/Course/EditCourseStructure/DraggableElements/ActivityElement.tsx
+++ b/apps/web/components/Dashboard/Pages/Course/EditCourseStructure/DraggableElements/ActivityElement.tsx
@@ -1,4 +1,4 @@
-import ConfirmationModal from '@components/StyledElements/ConfirmationModal/ConfirmationModal'
+import ConfirmationModal from '@components/Objects/StyledElements/ConfirmationModal/ConfirmationModal'
import { getAPIUrl, getUriWithOrg } from '@services/config/config'
import { deleteActivity, updateActivity } from '@services/courses/activities'
import { revalidateTags } from '@services/utils/ts/requests'
@@ -7,6 +7,7 @@ import {
Eye,
File,
FilePenLine,
+ FileSymlink,
Globe,
Lock,
MoreVertical,
@@ -27,6 +28,7 @@ import { useOrg } from '@components/Contexts/OrgContext'
import { useCourse } from '@components/Contexts/CourseContext'
import toast from 'react-hot-toast'
import { useMediaQuery } from 'usehooks-ts'
+import ToolTip from '@components/Objects/StyledElements/Tooltip/Tooltip'
type ActivitiyElementProps = {
orgslug: string
@@ -176,24 +178,26 @@ function ActivityElement(props: ActivitiyElementProps) {
)}
{!props.activity.published ? 'Publish' : 'Unpublish'}
-
-
-
Preview
-
+
+
+
+
+
+
{/* Delete Button */}
- {!isMobile && Delete}
}
functionToExecute={() => deleteActivityUI()}
diff --git a/apps/web/components/Dashboard/Course/EditCourseStructure/DraggableElements/ChapterElement.tsx b/apps/web/components/Dashboard/Pages/Course/EditCourseStructure/DraggableElements/ChapterElement.tsx
similarity index 98%
rename from apps/web/components/Dashboard/Course/EditCourseStructure/DraggableElements/ChapterElement.tsx
rename to apps/web/components/Dashboard/Pages/Course/EditCourseStructure/DraggableElements/ChapterElement.tsx
index b3d27980..f035b937 100644
--- a/apps/web/components/Dashboard/Course/EditCourseStructure/DraggableElements/ChapterElement.tsx
+++ b/apps/web/components/Dashboard/Pages/Course/EditCourseStructure/DraggableElements/ChapterElement.tsx
@@ -1,4 +1,4 @@
-import ConfirmationModal from '@components/StyledElements/ConfirmationModal/ConfirmationModal'
+import ConfirmationModal from '@components/Objects/StyledElements/ConfirmationModal/ConfirmationModal'
import {
Hexagon,
MoreHorizontal,
diff --git a/apps/web/components/Dashboard/Course/EditCourseStructure/EditCourseStructure.tsx b/apps/web/components/Dashboard/Pages/Course/EditCourseStructure/EditCourseStructure.tsx
similarity index 98%
rename from apps/web/components/Dashboard/Course/EditCourseStructure/EditCourseStructure.tsx
rename to apps/web/components/Dashboard/Pages/Course/EditCourseStructure/EditCourseStructure.tsx
index f9b5b24a..834a0d1f 100644
--- a/apps/web/components/Dashboard/Course/EditCourseStructure/EditCourseStructure.tsx
+++ b/apps/web/components/Dashboard/Pages/Course/EditCourseStructure/EditCourseStructure.tsx
@@ -13,7 +13,7 @@ import {
useCourseDispatch,
} from '@components/Contexts/CourseContext'
import { Hexagon } from 'lucide-react'
-import Modal from '@components/StyledElements/Modal/Modal'
+import Modal from '@components/Objects/StyledElements/Modal/Modal'
import NewChapterModal from '@components/Objects/Modals/Chapters/NewChapter'
import { useLHSession } from '@components/Contexts/LHSessionContext'
diff --git a/apps/web/components/Dashboard/Org/OrgEditGeneral/OrgEditGeneral.tsx b/apps/web/components/Dashboard/Pages/Org/OrgEditGeneral/OrgEditGeneral.tsx
similarity index 99%
rename from apps/web/components/Dashboard/Org/OrgEditGeneral/OrgEditGeneral.tsx
rename to apps/web/components/Dashboard/Pages/Org/OrgEditGeneral/OrgEditGeneral.tsx
index 36c0fb20..032d4e69 100644
--- a/apps/web/components/Dashboard/Org/OrgEditGeneral/OrgEditGeneral.tsx
+++ b/apps/web/components/Dashboard/Pages/Org/OrgEditGeneral/OrgEditGeneral.tsx
@@ -12,7 +12,7 @@ import { useRouter } from 'next/navigation'
import { useOrg } from '@components/Contexts/OrgContext'
import { useLHSession } from '@components/Contexts/LHSessionContext'
import { getOrgLogoMediaDirectory, getOrgThumbnailMediaDirectory } from '@services/media/media'
-import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs"
+import { Tabs, TabsContent, TabsList, TabsTrigger } from "@components/ui/tabs"
import { Toaster, toast } from 'react-hot-toast';
import { constructAcceptValue } from '@/lib/constants';
diff --git a/apps/web/components/Dashboard/Pages/Payments/PaymentsConfigurationPage.tsx b/apps/web/components/Dashboard/Pages/Payments/PaymentsConfigurationPage.tsx
new file mode 100644
index 00000000..f2192d5e
--- /dev/null
+++ b/apps/web/components/Dashboard/Pages/Payments/PaymentsConfigurationPage.tsx
@@ -0,0 +1,290 @@
+'use client';
+import React, { useState, useEffect } from 'react';
+import { useOrg } from '@components/Contexts/OrgContext';
+import { SiStripe } from '@icons-pack/react-simple-icons'
+import { useLHSession } from '@components/Contexts/LHSessionContext';
+import { getPaymentConfigs, initializePaymentConfig, updatePaymentConfig, deletePaymentConfig, updateStripeAccountID, getStripeOnboardingLink } from '@services/payments/payments';
+import FormLayout, { ButtonBlack, Input, Textarea, FormField, FormLabelAndMessage, Flex } from '@components/Objects/StyledElements/Form/Form';
+import { AlertTriangle, BarChart2, Check, Coins, CreditCard, Edit, ExternalLink, Info, Loader2, RefreshCcw, Trash2, UnplugIcon } from 'lucide-react';
+import toast from 'react-hot-toast';
+import useSWR, { mutate } from 'swr';
+import Modal from '@components/Objects/StyledElements/Modal/Modal';
+import ConfirmationModal from '@components/Objects/StyledElements/ConfirmationModal/ConfirmationModal';
+import { Button } from '@components/ui/button';
+import { Alert, AlertDescription, AlertTitle } from '@components/ui/alert';
+import { useRouter } from 'next/navigation';
+import { getUriWithoutOrg } from '@services/config/config';
+
+const PaymentsConfigurationPage: React.FC = () => {
+ const org = useOrg() as any;
+ const session = useLHSession() as any;
+ const router = useRouter();
+ const access_token = session?.data?.tokens?.access_token;
+ const { data: paymentConfigs, error, isLoading } = useSWR(
+ () => (org && access_token ? [`/payments/${org.id}/config`, access_token] : null),
+ ([url, token]) => getPaymentConfigs(org.id, token)
+ );
+
+ const stripeConfig = paymentConfigs?.find((config: any) => config.provider === 'stripe');
+ const [isModalOpen, setIsModalOpen] = useState(false);
+ const [isOnboarding, setIsOnboarding] = useState(false);
+ const [isOnboardingLoading, setIsOnboardingLoading] = useState(false);
+
+ const enableStripe = async () => {
+ try {
+ setIsOnboarding(true);
+ const newConfig = { provider: 'stripe', enabled: true };
+ const config = await initializePaymentConfig(org.id, newConfig, 'stripe', access_token);
+ toast.success('Stripe enabled successfully');
+ mutate([`/payments/${org.id}/config`, access_token]);
+ } catch (error) {
+ console.error('Error enabling Stripe:', error);
+ toast.error('Failed to enable Stripe');
+ } finally {
+ setIsOnboarding(false);
+ }
+ };
+
+ const editConfig = async () => {
+ setIsModalOpen(true);
+ };
+
+ const deleteConfig = async () => {
+ try {
+ await deletePaymentConfig(org.id, stripeConfig.id, access_token);
+ toast.success('Stripe configuration deleted successfully');
+ mutate([`/payments/${org.id}/config`, access_token]);
+ } catch (error) {
+ console.error('Error deleting Stripe configuration:', error);
+ toast.error('Failed to delete Stripe configuration');
+ }
+ };
+
+ const handleStripeOnboarding = async () => {
+ try {
+ setIsOnboardingLoading(true);
+ const { connect_url } = await getStripeOnboardingLink(org.id, access_token, getUriWithoutOrg('/payments/stripe/connect/oauth'));
+ window.open(connect_url, '_blank');
+ } catch (error) {
+ console.error('Error getting onboarding link:', error);
+ toast.error('Failed to start Stripe onboarding');
+ } finally {
+ setIsOnboardingLoading(false);
+ }
+ };
+
+ if (isLoading) {
+ return Loading...
;
+ }
+
+ if (error) {
+ return Error loading payment configuration
;
+ }
+
+ return (
+
+
+
+
Payments Configuration
+ Manage your organization payments configuration
+
+
+
+
+ About the Stripe Integration
+
+
+
+ -
+
+ Accept payments for courses and subscriptions
+
+ -
+
+ Manage recurring billing and subscriptions
+
+ -
+
+ Handle multiple currencies and payment methods
+
+ -
+
+ Access detailed payment analytics
+
+
+
+
+ Learn more about Stripe
+
+
+
+
+
+
+ {stripeConfig ? (
+
+
+
+
+
+
Stripe
+ {stripeConfig.provider_specific_id && stripeConfig.active ? (
+
+ ) : (
+
+ )}
+
+
+ {stripeConfig.provider_specific_id ?
+ `Linked Account: ${stripeConfig.provider_specific_id}` :
+ 'Account ID not configured'}
+
+
+
+
+ {(!stripeConfig.provider_specific_id || !stripeConfig.active) && (
+
+ )}
+
+
+ Remove Connection
+
+ }
+ functionToExecute={deleteConfig}
+ status="warning"
+ />
+
+
+ ) : (
+
+ )}
+
+
+ {stripeConfig && (
+
setIsModalOpen(false)}
+ />
+ )}
+
+ );
+};
+
+interface EditStripeConfigModalProps {
+ orgId: number;
+ configId: string;
+ accessToken: string;
+ isOpen: boolean;
+ onClose: () => void;
+}
+
+const EditStripeConfigModal: React.FC = ({ orgId, configId, accessToken, isOpen, onClose }) => {
+ const [stripeAccountId, setStripeAccountId] = useState('');
+
+ useEffect(() => {
+ const fetchConfig = async () => {
+ try {
+ const config = await getPaymentConfigs(orgId, accessToken);
+ const stripeConfig = config.find((c: any) => c.id === configId);
+ if (stripeConfig && stripeConfig.provider_specific_id) {
+ setStripeAccountId(stripeConfig.provider_specific_id || '');
+ }
+ } catch (error) {
+ console.error('Error fetching Stripe configuration:', error);
+ toast.error('Failed to load existing configuration');
+ }
+ };
+
+ if (isOpen) {
+ fetchConfig();
+ }
+ }, [isOpen, orgId, configId, accessToken]);
+
+ const handleSubmit = async () => {
+ try {
+ const stripe_config = {
+ stripe_account_id: stripeAccountId,
+ };
+ await updateStripeAccountID(orgId, stripe_config, accessToken);
+ toast.success('Configuration updated successfully');
+ mutate([`/payments/${orgId}/config`, accessToken]);
+ onClose();
+ } catch (error) {
+ console.error('Error updating config:', error);
+ toast.error('Failed to update configuration');
+ }
+ };
+
+ return (
+
+
+
+ setStripeAccountId(e.target.value)}
+ placeholder="acct_..."
+ />
+
+
+
+ Save
+
+
+
+ }
+ />
+ );
+};
+
+export default PaymentsConfigurationPage;
diff --git a/apps/web/components/Dashboard/Pages/Payments/PaymentsCustomersPage.tsx b/apps/web/components/Dashboard/Pages/Payments/PaymentsCustomersPage.tsx
new file mode 100644
index 00000000..e62d7e39
--- /dev/null
+++ b/apps/web/components/Dashboard/Pages/Payments/PaymentsCustomersPage.tsx
@@ -0,0 +1,155 @@
+import React from 'react'
+import { useOrg } from '@components/Contexts/OrgContext'
+import { useLHSession } from '@components/Contexts/LHSessionContext'
+import useSWR from 'swr'
+import {
+ Table,
+ TableBody,
+ TableCell,
+ TableHead,
+ TableHeader,
+ TableRow,
+} from "@components/ui/table"
+import { getOrgCustomers } from '@services/payments/payments'
+import { Badge } from '@components/ui/badge'
+import PageLoading from '@components/Objects/Loaders/PageLoading'
+import { RefreshCcw, SquareCheck } from 'lucide-react'
+import { getUserAvatarMediaDirectory } from '@services/media/media'
+import UserAvatar from '@components/Objects/UserAvatar'
+import { usePaymentsEnabled } from '@hooks/usePaymentsEnabled'
+import UnconfiguredPaymentsDisclaimer from '@components/Pages/Payments/UnconfiguredPaymentsDisclaimer'
+
+interface PaymentUserData {
+ payment_user_id: number;
+ user: {
+ username: string;
+ first_name: string;
+ last_name: string;
+ email: string;
+ avatar_image: string;
+ user_uuid: string;
+ };
+ product: {
+ name: string;
+ description: string;
+ product_type: string;
+ amount: number;
+ currency: string;
+ };
+ status: string;
+ creation_date: string;
+}
+
+function PaymentsUsersTable({ data }: { data: PaymentUserData[] }) {
+ if (!data || data.length === 0) {
+ return (
+
+ No customers found
+
+ );
+ }
+
+ return (
+
+
+
+ User
+ Product
+ Type
+ Amount
+ Status
+ Purchase Date
+
+
+
+ {data.map((item) => (
+
+
+
+
+
+
+ {item.user.first_name || item.user.username}
+
+ {item.user.email}
+
+
+
+ {item.product.name}
+
+
+ {item.product.product_type === 'subscription' ? (
+
+
+ Subscription
+
+ ) : (
+
+
+ One-time
+
+ )}
+
+
+
+ {new Intl.NumberFormat('en-US', {
+ style: 'currency',
+ currency: item.product.currency
+ }).format(item.product.amount)}
+
+
+
+ {item.status}
+
+
+
+ {new Date(item.creation_date).toLocaleDateString()}
+
+
+ ))}
+
+
+ );
+}
+
+function PaymentsCustomersPage() {
+ const org = useOrg() as any
+ const session = useLHSession() as any
+ const access_token = session?.data?.tokens?.access_token
+ const { isEnabled, isLoading } = usePaymentsEnabled()
+
+ const { data: customers, error, isLoading: customersLoading } = useSWR(
+ org ? [`/payments/${org.id}/customers`, access_token] : null,
+ ([url, token]) => getOrgCustomers(org.id, token)
+ )
+
+ if (!isEnabled && !isLoading) {
+ return (
+
+ )
+ }
+
+ if (isLoading || customersLoading) return
+ if (error) return Error loading customers
+ if (!customers) return No customer data available
+
+ return (
+
+
+
Customers
+ View and manage your customer information
+
+
+
+
+ )
+}
+
+export default PaymentsCustomersPage
\ No newline at end of file
diff --git a/apps/web/components/Dashboard/Pages/Payments/PaymentsProductPage.tsx b/apps/web/components/Dashboard/Pages/Payments/PaymentsProductPage.tsx
new file mode 100644
index 00000000..7ccf8c97
--- /dev/null
+++ b/apps/web/components/Dashboard/Pages/Payments/PaymentsProductPage.tsx
@@ -0,0 +1,312 @@
+'use client';
+import React, { useState, useEffect } from 'react'
+import currencyCodes from 'currency-codes';
+import { useOrg } from '@components/Contexts/OrgContext';
+import { useLHSession } from '@components/Contexts/LHSessionContext';
+import useSWR, { mutate } from 'swr';
+import { getProducts, updateProduct, archiveProduct } from '@services/payments/products';
+import { Plus, Pencil, Info, RefreshCcw, SquareCheck, ChevronDown, ChevronUp, Archive } from 'lucide-react';
+import Modal from '@components/Objects/StyledElements/Modal/Modal';
+import ConfirmationModal from '@components/Objects/StyledElements/ConfirmationModal/ConfirmationModal';
+import toast from 'react-hot-toast';
+import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@components/ui/select"
+import { Button } from "@components/ui/button"
+import { Input } from "@components/ui/input"
+import { Textarea } from "@components/ui/textarea"
+import { Formik, Form, Field, ErrorMessage } from 'formik';
+import * as Yup from 'yup';
+import { Label } from '@components/ui/label';
+import { Badge } from '@components/ui/badge';
+import { getPaymentConfigs } from '@services/payments/payments';
+import ProductLinkedCourses from './SubComponents/ProductLinkedCourses';
+import { usePaymentsEnabled } from '@hooks/usePaymentsEnabled';
+import UnconfiguredPaymentsDisclaimer from '@components/Pages/Payments/UnconfiguredPaymentsDisclaimer';
+import CreateProductForm from './SubComponents/CreateProductForm';
+
+const validationSchema = Yup.object().shape({
+ name: Yup.string().required('Name is required'),
+ description: Yup.string().required('Description is required'),
+ amount: Yup.number().min(0, 'Amount must be positive').required('Amount is required'),
+ benefits: Yup.string(),
+ currency: Yup.string().required('Currency is required'),
+});
+
+function PaymentsProductPage() {
+ const org = useOrg() as any;
+ const session = useLHSession() as any;
+ const [isCreateModalOpen, setIsCreateModalOpen] = useState(false);
+ const [editingProductId, setEditingProductId] = useState(null);
+ const [expandedProducts, setExpandedProducts] = useState<{ [key: string]: boolean }>({});
+ const [isStripeEnabled, setIsStripeEnabled] = useState(false);
+ const { isEnabled, isLoading } = usePaymentsEnabled();
+
+ const { data: products, error } = useSWR(
+ () => org && session ? [`/payments/${org.id}/products`, session.data?.tokens?.access_token] : null,
+ ([url, token]) => getProducts(org.id, token)
+ );
+
+ const { data: paymentConfigs, error: paymentConfigError } = useSWR(
+ () => org && session ? [`/payments/${org.id}/config`, session.data?.tokens?.access_token] : null,
+ ([url, token]) => getPaymentConfigs(org.id, token)
+ );
+
+ useEffect(() => {
+ if (paymentConfigs) {
+ const stripeConfig = paymentConfigs.find((config: any) => config.provider === 'stripe');
+ setIsStripeEnabled(!!stripeConfig);
+ }
+ }, [paymentConfigs]);
+
+ const handleArchiveProduct = async (productId: string) => {
+ const res = await archiveProduct(org.id, productId, session.data?.tokens?.access_token);
+ mutate([`/payments/${org.id}/products`, session.data?.tokens?.access_token]);
+ if (res.status === 200) {
+ toast.success('Product archived successfully');
+ } else {
+ toast.error(res.data.detail);
+ }
+ }
+
+ const toggleProductExpansion = (productId: string) => {
+ setExpandedProducts(prev => ({
+ ...prev,
+ [productId]: !prev[productId]
+ }));
+ };
+
+ if (!isEnabled && !isLoading) {
+ return (
+
+ );
+ }
+
+ if (error) return Failed to load products
;
+ if (!products) return Loading...
;
+
+ return (
+
+
+
+
+
setIsCreateModalOpen(false)} />
+ }
+ />
+
+
+ {products.data.map((product: any) => (
+
+ {editingProductId === product.id ? (
+
setEditingProductId(null)}
+ onCancel={() => setEditingProductId(null)}
+ />
+ ) : (
+
+
+
+
+ {product.product_type === 'subscription' ? : }
+ {product.product_type === 'subscription' ? 'Subscription' : 'One-time payment'}
+
+
{product.name}
+
+
+
+
+
+
+ }
+ functionToExecute={() => handleArchiveProduct(product.id)}
+ status="warning"
+ />
+
+
+
+
+
+ {product.description}
+
+ {product.benefits && (
+
+
Benefits:
+
+ {product.benefits}
+
+
+ )}
+
+
+
+
+
+
+
+ Price:
+
+ {new Intl.NumberFormat('en-US', { style: 'currency', currency: product.currency }).format(product.amount)}
+
+
+
+ )}
+
+ ))}
+
+ {products.data.length === 0 && (
+
+
+
No products available. Create a new product to get started.
+
+ )}
+
+
+
+
+
+
+ )
+}
+
+const EditProductForm = ({ product, onSuccess, onCancel }: { product: any, onSuccess: () => void, onCancel: () => void }) => {
+ const org = useOrg() as any;
+ const session = useLHSession() as any;
+ const [currencies, setCurrencies] = useState<{ code: string; name: string }[]>([]);
+
+ useEffect(() => {
+ const allCurrencies = currencyCodes.data.map(currency => ({
+ code: currency.code,
+ name: `${currency.code} - ${currency.currency}`
+ }));
+ setCurrencies(allCurrencies);
+ }, []);
+
+ const initialValues = {
+ name: product.name,
+ description: product.description,
+ amount: product.amount,
+ benefits: product.benefits || '',
+ currency: product.currency || '',
+ product_type: product.product_type,
+ };
+
+ const handleSubmit = async (values: typeof initialValues, { setSubmitting }: { setSubmitting: (isSubmitting: boolean) => void }) => {
+ try {
+ await updateProduct(org.id, product.id, values, session.data?.tokens?.access_token);
+ mutate([`/payments/${org.id}/products`, session.data?.tokens?.access_token]);
+ onSuccess();
+ toast.success('Product updated successfully');
+ } catch (error) {
+ toast.error('Failed to update product');
+ } finally {
+ setSubmitting(false);
+ }
+ };
+
+ return (
+
+ {({ isSubmitting, values, setFieldValue }) => (
+
+ )}
+
+ );
+};
+
+export default PaymentsProductPage
diff --git a/apps/web/components/Dashboard/Pages/Payments/SubComponents/CreateProductForm.tsx b/apps/web/components/Dashboard/Pages/Payments/SubComponents/CreateProductForm.tsx
new file mode 100644
index 00000000..25efeb62
--- /dev/null
+++ b/apps/web/components/Dashboard/Pages/Payments/SubComponents/CreateProductForm.tsx
@@ -0,0 +1,184 @@
+import React, { useEffect, useState } from 'react';
+import { useOrg } from '@components/Contexts/OrgContext';
+import { useLHSession } from '@components/Contexts/LHSessionContext';
+import { createProduct } from '@services/payments/products';
+import { Formik, Form, Field, ErrorMessage } from 'formik';
+import * as Yup from 'yup';
+import toast from 'react-hot-toast';
+import { mutate } from 'swr';
+import { Button } from "@components/ui/button";
+import { Input } from "@components/ui/input";
+import { Textarea } from "@components/ui/textarea";
+import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@components/ui/select";
+import { Label } from "@components/ui/label";
+import currencyCodes from 'currency-codes';
+
+const validationSchema = Yup.object().shape({
+ name: Yup.string().required('Name is required'),
+ description: Yup.string().required('Description is required'),
+ amount: Yup.number()
+ .min(1, 'Amount must be greater than zero')
+ .required('Amount is required'),
+ benefits: Yup.string(),
+ currency: Yup.string().required('Currency is required'),
+ product_type: Yup.string().oneOf(['one_time', 'subscription']).required('Product type is required'),
+ price_type: Yup.string().oneOf(['fixed_price', 'customer_choice']).required('Price type is required'),
+});
+
+interface ProductFormValues {
+ name: string;
+ description: string;
+ product_type: 'one_time' | 'subscription';
+ price_type: 'fixed_price' | 'customer_choice';
+ benefits: string;
+ amount: number;
+ currency: string;
+}
+
+const CreateProductForm: React.FC<{ onSuccess: () => void }> = ({ onSuccess }) => {
+ const org = useOrg() as any;
+ const session = useLHSession() as any;
+ const [currencies, setCurrencies] = useState<{ code: string; name: string }[]>([]);
+
+ useEffect(() => {
+ const allCurrencies = currencyCodes.data.map(currency => ({
+ code: currency.code,
+ name: `${currency.code} - ${currency.currency}`
+ }));
+ setCurrencies(allCurrencies);
+ }, []);
+
+ const initialValues: ProductFormValues = {
+ name: '',
+ description: '',
+ product_type: 'one_time',
+ price_type: 'fixed_price',
+ benefits: '',
+ amount: 1,
+ currency: 'USD',
+ };
+
+ const handleSubmit = async (values: ProductFormValues, { setSubmitting, resetForm }: any) => {
+ try {
+ const res = await createProduct(org.id, values, session.data?.tokens?.access_token);
+ if (res.success) {
+ toast.success('Product created successfully');
+ mutate([`/payments/${org.id}/products`, session.data?.tokens?.access_token]);
+ resetForm();
+ onSuccess();
+ } else {
+ toast.error('Failed to create product');
+ }
+ } catch (error) {
+ console.error('Error creating product:', error);
+ toast.error('An error occurred while creating the product');
+ } finally {
+ setSubmitting(false);
+ }
+ };
+
+ return (
+
+ {({ isSubmitting, values, setFieldValue }) => (
+
+ )}
+
+ );
+};
+
+export default CreateProductForm;
diff --git a/apps/web/components/Dashboard/Pages/Payments/SubComponents/LinkCourseModal.tsx b/apps/web/components/Dashboard/Pages/Payments/SubComponents/LinkCourseModal.tsx
new file mode 100644
index 00000000..9052f239
--- /dev/null
+++ b/apps/web/components/Dashboard/Pages/Payments/SubComponents/LinkCourseModal.tsx
@@ -0,0 +1,143 @@
+import React, { useState } from 'react';
+import { useOrg } from '@components/Contexts/OrgContext';
+import { useLHSession } from '@components/Contexts/LHSessionContext';
+import { linkCourseToProduct } from '@services/payments/products';
+import { Button } from "@components/ui/button";
+import { Input } from "@components/ui/input";
+import { Search } from 'lucide-react';
+import toast from 'react-hot-toast';
+import { mutate } from 'swr';
+import useSWR from 'swr';
+import { getOrgCourses } from '@services/courses/courses';
+import { getCoursesLinkedToProduct } from '@services/payments/products';
+import Link from 'next/link';
+import { getCourseThumbnailMediaDirectory } from '@services/media/media';
+import { getUriWithOrg } from '@services/config/config';
+
+interface LinkCourseModalProps {
+ productId: string;
+ onSuccess: () => void;
+}
+
+interface CoursePreviewProps {
+ course: {
+ id: string;
+ name: string;
+ description: string;
+ thumbnail_image: string;
+ course_uuid: string;
+ };
+ orgslug: string;
+ onLink: (courseId: string) => void;
+ isLinked: boolean;
+}
+
+const CoursePreview = ({ course, orgslug, onLink, isLinked }: CoursePreviewProps) => {
+ const org = useOrg() as any;
+
+ const thumbnailImage = course.thumbnail_image
+ ? getCourseThumbnailMediaDirectory(org?.org_uuid, course.course_uuid, course.thumbnail_image)
+ : '../empty_thumbnail.png';
+
+ return (
+
+ {/* Thumbnail */}
+
+
+ {/* Content */}
+
+
+ {course.name}
+
+
+ {course.description}
+
+
+
+ {/* Action Button */}
+
+ {isLinked ? (
+
+ ) : (
+
+ )}
+
+
+ );
+};
+
+export default function LinkCourseModal({ productId, onSuccess }: LinkCourseModalProps) {
+ const [searchTerm, setSearchTerm] = useState('');
+ const org = useOrg() as any;
+ const session = useLHSession() as any;
+
+ const { data: courses } = useSWR(
+ () => org && session ? [org.slug, searchTerm, session.data?.tokens?.access_token] : null,
+ ([orgSlug, search, token]) => getOrgCourses(orgSlug, null, token)
+ );
+
+ const { data: linkedCourses } = useSWR(
+ () => org && session ? [`/payments/${org.id}/products/${productId}/courses`, session.data?.tokens?.access_token] : null,
+ ([_, token]) => getCoursesLinkedToProduct(org.id, productId, token)
+ );
+
+ const handleLinkCourse = async (courseId: string) => {
+ try {
+ const response = await linkCourseToProduct(org.id, productId, courseId, session.data?.tokens?.access_token);
+ if (response.success) {
+ mutate([`/payments/${org.id}/products`, session.data?.tokens?.access_token]);
+ toast.success('Course linked successfully');
+ onSuccess();
+ } else {
+ toast.error(response.data?.detail || 'Failed to link course');
+ }
+ } catch (error) {
+ toast.error('Failed to link course');
+ }
+ };
+
+ const isLinked = (courseId: string) => {
+ return linkedCourses?.data?.some((course: any) => course.id === courseId);
+ };
+
+ return (
+
+
+
+ {/* Course List */}
+
+ {courses?.map((course: any) => (
+
+ ))}
+
+ {/* Empty State */}
+ {(!courses || courses.length === 0) && (
+
+ No courses found
+
+ )}
+
+
+ );
+}
\ No newline at end of file
diff --git a/apps/web/components/Dashboard/Pages/Payments/SubComponents/ProductLinkedCourses.tsx b/apps/web/components/Dashboard/Pages/Payments/SubComponents/ProductLinkedCourses.tsx
new file mode 100644
index 00000000..8d07bcf0
--- /dev/null
+++ b/apps/web/components/Dashboard/Pages/Payments/SubComponents/ProductLinkedCourses.tsx
@@ -0,0 +1,106 @@
+import React, { useEffect, useState } from 'react';
+import { getCoursesLinkedToProduct, unlinkCourseFromProduct } from '@services/payments/products';
+import { useLHSession } from '@components/Contexts/LHSessionContext';
+import { useOrg } from '@components/Contexts/OrgContext';
+import { Trash2, Plus, BookOpen } from 'lucide-react';
+import { Button } from "@components/ui/button";
+import toast from 'react-hot-toast';
+import { mutate } from 'swr';
+import Modal from '@components/Objects/StyledElements/Modal/Modal';
+import LinkCourseModal from './LinkCourseModal';
+
+interface ProductLinkedCoursesProps {
+ productId: string;
+}
+
+export default function ProductLinkedCourses({ productId }: ProductLinkedCoursesProps) {
+ const [linkedCourses, setLinkedCourses] = useState([]);
+ const [isLinkModalOpen, setIsLinkModalOpen] = useState(false);
+ const session = useLHSession() as any;
+ const org = useOrg() as any;
+
+ const fetchLinkedCourses = async () => {
+ try {
+ const response = await getCoursesLinkedToProduct(org.id, productId, session.data?.tokens?.access_token);
+ setLinkedCourses(response.data || []);
+ } catch (error) {
+ toast.error('Failed to fetch linked courses');
+ }
+ };
+
+ const handleUnlinkCourse = async (courseId: string) => {
+ try {
+ const response = await unlinkCourseFromProduct(org.id, productId, courseId, session.data?.tokens?.access_token);
+ if (response.success) {
+ await fetchLinkedCourses();
+ mutate([`/payments/${org.id}/products`, session.data?.tokens?.access_token]);
+ toast.success('Course unlinked successfully');
+ } else {
+ toast.error(response.data?.detail || 'Failed to unlink course');
+ }
+ } catch (error) {
+ toast.error('Failed to unlink course');
+ }
+ };
+
+ useEffect(() => {
+ if (org && session && productId) {
+ fetchLinkedCourses();
+ }
+ }, [org, session, productId]);
+
+ return (
+
+
+
Linked Courses
+
{
+ setIsLinkModalOpen(false);
+ fetchLinkedCourses();
+ }}
+ />
+ }
+ dialogTrigger={
+
+ }
+ />
+
+
+
+ {linkedCourses.length === 0 ? (
+
+
+ No courses linked yet
+
+ ) : (
+ linkedCourses.map((course) => (
+
+ {course.name}
+
+
+ ))
+ )}
+
+
+ );
+}
\ No newline at end of file
diff --git a/apps/web/components/Dashboard/UserAccount/UserEditGeneral/UserEditGeneral.tsx b/apps/web/components/Dashboard/Pages/UserAccount/UserEditGeneral/UserEditGeneral.tsx
similarity index 100%
rename from apps/web/components/Dashboard/UserAccount/UserEditGeneral/UserEditGeneral.tsx
rename to apps/web/components/Dashboard/Pages/UserAccount/UserEditGeneral/UserEditGeneral.tsx
diff --git a/apps/web/components/Dashboard/UserAccount/UserEditPassword/UserEditPassword.tsx b/apps/web/components/Dashboard/Pages/UserAccount/UserEditPassword/UserEditPassword.tsx
similarity index 100%
rename from apps/web/components/Dashboard/UserAccount/UserEditPassword/UserEditPassword.tsx
rename to apps/web/components/Dashboard/Pages/UserAccount/UserEditPassword/UserEditPassword.tsx
diff --git a/apps/web/components/Dashboard/Users/OrgAccess/OrgAccess.tsx b/apps/web/components/Dashboard/Pages/Users/OrgAccess/OrgAccess.tsx
similarity index 98%
rename from apps/web/components/Dashboard/Users/OrgAccess/OrgAccess.tsx
rename to apps/web/components/Dashboard/Pages/Users/OrgAccess/OrgAccess.tsx
index 9c609c77..b5221e66 100644
--- a/apps/web/components/Dashboard/Users/OrgAccess/OrgAccess.tsx
+++ b/apps/web/components/Dashboard/Pages/Users/OrgAccess/OrgAccess.tsx
@@ -1,6 +1,6 @@
import { useOrg } from '@components/Contexts/OrgContext'
import PageLoading from '@components/Objects/Loaders/PageLoading'
-import ConfirmationModal from '@components/StyledElements/ConfirmationModal/ConfirmationModal'
+import ConfirmationModal from '@components/Objects/StyledElements/ConfirmationModal/ConfirmationModal'
import { getAPIUrl, getUriWithOrg } from '@services/config/config'
import { swrFetcher } from '@services/utils/ts/requests'
import { Globe, Ticket, UserSquare, Users, X } from 'lucide-react'
@@ -14,7 +14,7 @@ import {
} from '@services/organizations/invites'
import toast from 'react-hot-toast'
import { useRouter } from 'next/navigation'
-import Modal from '@components/StyledElements/Modal/Modal'
+import Modal from '@components/Objects/StyledElements/Modal/Modal'
import OrgInviteCodeGenerate from '@components/Objects/Modals/Dash/OrgAccess/OrgInviteCodeGenerate'
import { useLHSession } from '@components/Contexts/LHSessionContext'
diff --git a/apps/web/components/Dashboard/Users/OrgUserGroups/OrgUserGroups.tsx b/apps/web/components/Dashboard/Pages/Users/OrgUserGroups/OrgUserGroups.tsx
similarity index 98%
rename from apps/web/components/Dashboard/Users/OrgUserGroups/OrgUserGroups.tsx
rename to apps/web/components/Dashboard/Pages/Users/OrgUserGroups/OrgUserGroups.tsx
index d0d7b7c2..ba586ada 100644
--- a/apps/web/components/Dashboard/Users/OrgUserGroups/OrgUserGroups.tsx
+++ b/apps/web/components/Dashboard/Pages/Users/OrgUserGroups/OrgUserGroups.tsx
@@ -4,8 +4,8 @@ import { useOrg } from '@components/Contexts/OrgContext'
import AddUserGroup from '@components/Objects/Modals/Dash/OrgUserGroups/AddUserGroup'
import EditUserGroup from '@components/Objects/Modals/Dash/OrgUserGroups/EditUserGroup'
import ManageUsers from '@components/Objects/Modals/Dash/OrgUserGroups/ManageUsers'
-import ConfirmationModal from '@components/StyledElements/ConfirmationModal/ConfirmationModal'
-import Modal from '@components/StyledElements/Modal/Modal'
+import ConfirmationModal from '@components/Objects/StyledElements/ConfirmationModal/ConfirmationModal'
+import Modal from '@components/Objects/StyledElements/Modal/Modal'
import { getAPIUrl } from '@services/config/config'
import { deleteUserGroup } from '@services/usergroups/usergroups'
import { swrFetcher } from '@services/utils/ts/requests'
diff --git a/apps/web/components/Dashboard/Users/OrgUsers/OrgUsers.tsx b/apps/web/components/Dashboard/Pages/Users/OrgUsers/OrgUsers.tsx
similarity index 96%
rename from apps/web/components/Dashboard/Users/OrgUsers/OrgUsers.tsx
rename to apps/web/components/Dashboard/Pages/Users/OrgUsers/OrgUsers.tsx
index c25e5d84..7d5bee0b 100644
--- a/apps/web/components/Dashboard/Users/OrgUsers/OrgUsers.tsx
+++ b/apps/web/components/Dashboard/Pages/Users/OrgUsers/OrgUsers.tsx
@@ -2,9 +2,9 @@ import { useLHSession } from '@components/Contexts/LHSessionContext'
import { useOrg } from '@components/Contexts/OrgContext'
import PageLoading from '@components/Objects/Loaders/PageLoading'
import RolesUpdate from '@components/Objects/Modals/Dash/OrgUsers/RolesUpdate'
-import ConfirmationModal from '@components/StyledElements/ConfirmationModal/ConfirmationModal'
-import Modal from '@components/StyledElements/Modal/Modal'
-import Toast from '@components/StyledElements/Toast/Toast'
+import ConfirmationModal from '@components/Objects/StyledElements/ConfirmationModal/ConfirmationModal'
+import Modal from '@components/Objects/StyledElements/Modal/Modal'
+import Toast from '@components/Objects/StyledElements/Toast/Toast'
import { getAPIUrl } from '@services/config/config'
import { removeUserFromOrg } from '@services/organizations/orgs'
import { swrFetcher } from '@services/utils/ts/requests'
diff --git a/apps/web/components/Dashboard/Users/OrgUsersAdd/OrgUsersAdd.tsx b/apps/web/components/Dashboard/Pages/Users/OrgUsersAdd/OrgUsersAdd.tsx
similarity index 98%
rename from apps/web/components/Dashboard/Users/OrgUsersAdd/OrgUsersAdd.tsx
rename to apps/web/components/Dashboard/Pages/Users/OrgUsersAdd/OrgUsersAdd.tsx
index b724a350..496d25d9 100644
--- a/apps/web/components/Dashboard/Users/OrgUsersAdd/OrgUsersAdd.tsx
+++ b/apps/web/components/Dashboard/Pages/Users/OrgUsersAdd/OrgUsersAdd.tsx
@@ -1,8 +1,8 @@
import { useLHSession } from '@components/Contexts/LHSessionContext'
import { useOrg } from '@components/Contexts/OrgContext'
import PageLoading from '@components/Objects/Loaders/PageLoading'
-import Toast from '@components/StyledElements/Toast/Toast'
-import ToolTip from '@components/StyledElements/Tooltip/Tooltip'
+import Toast from '@components/Objects/StyledElements/Toast/Toast'
+import ToolTip from '@components/Objects/StyledElements/Tooltip/Tooltip'
import { getAPIUrl } from '@services/config/config'
import { inviteBatchUsers } from '@services/organizations/invites'
import { swrFetcher } from '@services/utils/ts/requests'
diff --git a/apps/web/components/Hooks/useFeatureFlag.tsx b/apps/web/components/Hooks/useFeatureFlag.tsx
new file mode 100644
index 00000000..97623043
--- /dev/null
+++ b/apps/web/components/Hooks/useFeatureFlag.tsx
@@ -0,0 +1,36 @@
+import { useOrg } from '@components/Contexts/OrgContext'
+import { useEffect, useState } from 'react'
+
+type FeatureType = {
+ path: string[]
+ defaultValue?: boolean
+}
+
+function useFeatureFlag(feature: FeatureType) {
+ const org = useOrg() as any
+ const [isEnabled, setIsEnabled] = useState(!!feature.defaultValue)
+
+ useEffect(() => {
+ if (org?.config?.config) {
+ let currentValue = org.config.config
+
+ // Traverse the path to get the feature flag value
+ for (const key of feature.path) {
+ if (currentValue && typeof currentValue === 'object') {
+ currentValue = currentValue[key]
+ } else {
+ currentValue = feature.defaultValue || false
+ break
+ }
+ }
+
+ setIsEnabled(!!currentValue)
+ } else {
+ setIsEnabled(!!feature.defaultValue)
+ }
+ }, [org, feature])
+
+ return isEnabled
+}
+
+export default useFeatureFlag
\ No newline at end of file
diff --git a/apps/web/components/AI/Hooks/useGetAIFeatures.tsx b/apps/web/components/Hooks/useGetAIFeatures.tsx
similarity index 100%
rename from apps/web/components/AI/Hooks/useGetAIFeatures.tsx
rename to apps/web/components/Hooks/useGetAIFeatures.tsx
diff --git a/apps/web/components/Hooks/usePaymentsEnabled.tsx b/apps/web/components/Hooks/usePaymentsEnabled.tsx
new file mode 100644
index 00000000..bc397928
--- /dev/null
+++ b/apps/web/components/Hooks/usePaymentsEnabled.tsx
@@ -0,0 +1,26 @@
+// hooks/usePaymentsEnabled.ts
+import { useOrg } from '@components/Contexts/OrgContext';
+import { useLHSession } from '@components/Contexts/LHSessionContext';
+import useSWR from 'swr';
+import { getPaymentConfigs } from '@services/payments/payments';
+
+export function usePaymentsEnabled() {
+ const org = useOrg() as any;
+ const session = useLHSession() as any;
+ const access_token = session?.data?.tokens?.access_token;
+
+ const { data: paymentConfigs, error, isLoading } = useSWR(
+ org && access_token ? [`/payments/${org.id}/config`, access_token] : null,
+ ([url, token]) => getPaymentConfigs(org.id, token)
+ );
+
+ const isStripeEnabled = paymentConfigs?.some(
+ (config: any) => config.provider === 'stripe' && config.active
+ );
+
+ return {
+ isEnabled: !!isStripeEnabled,
+ isLoading,
+ error
+ };
+}
\ No newline at end of file
diff --git a/apps/web/components/Objects/Activities/AI/AIActivityAsk.tsx b/apps/web/components/Objects/Activities/AI/AIActivityAsk.tsx
index e0b3c454..69a344b8 100644
--- a/apps/web/components/Objects/Activities/AI/AIActivityAsk.tsx
+++ b/apps/web/components/Objects/Activities/AI/AIActivityAsk.tsx
@@ -15,7 +15,7 @@ import {
useAIChatBot,
useAIChatBotDispatch,
} from '@components/Contexts/AI/AIChatBotContext'
-import useGetAIFeatures from '../../../AI/Hooks/useGetAIFeatures'
+import useGetAIFeatures from '../../../Hooks/useGetAIFeatures'
import UserAvatar from '@components/Objects/UserAvatar'
type AIActivityAskProps = {
diff --git a/apps/web/components/Objects/Activities/DynamicCanva/AI/AICanvaToolkit.tsx b/apps/web/components/Objects/Activities/DynamicCanva/AI/AICanvaToolkit.tsx
index 67692c0c..8a9bb0c9 100644
--- a/apps/web/components/Objects/Activities/DynamicCanva/AI/AICanvaToolkit.tsx
+++ b/apps/web/components/Objects/Activities/DynamicCanva/AI/AICanvaToolkit.tsx
@@ -4,7 +4,7 @@ import learnhouseAI_icon from 'public/learnhouse_ai_simple.png'
import Image from 'next/image'
import { BookOpen, FormInput, Languages, MoreVertical } from 'lucide-react'
import { BubbleMenu } from '@tiptap/react'
-import ToolTip from '@components/StyledElements/Tooltip/Tooltip'
+import ToolTip from '@components/Objects/StyledElements/Tooltip/Tooltip'
import {
AIChatBotStateTypes,
useAIChatBot,
@@ -14,7 +14,7 @@ import {
sendActivityAIChatMessage,
startActivityAIChatSession,
} from '@services/ai/ai'
-import useGetAIFeatures from '../../../../AI/Hooks/useGetAIFeatures'
+import useGetAIFeatures from '../../../../Hooks/useGetAIFeatures'
import { useLHSession } from '@components/Contexts/LHSessionContext'
type AICanvaToolkitProps = {
diff --git a/apps/web/components/ContentPlaceHolder.tsx b/apps/web/components/Objects/ContentPlaceHolder.tsx
similarity index 87%
rename from apps/web/components/ContentPlaceHolder.tsx
rename to apps/web/components/Objects/ContentPlaceHolder.tsx
index 68c5aa18..b9cb6547 100644
--- a/apps/web/components/ContentPlaceHolder.tsx
+++ b/apps/web/components/Objects/ContentPlaceHolder.tsx
@@ -1,6 +1,6 @@
'use client';
import React from 'react'
-import useAdminStatus from './Hooks/useAdminStatus'
+import useAdminStatus from '../Hooks/useAdminStatus'
// Terrible name and terible implementation, need to be refactored asap
diff --git a/apps/web/components/Objects/Courses/CourseActions/CoursePaidOptions.tsx b/apps/web/components/Objects/Courses/CourseActions/CoursePaidOptions.tsx
new file mode 100644
index 00000000..c2537497
--- /dev/null
+++ b/apps/web/components/Objects/Courses/CourseActions/CoursePaidOptions.tsx
@@ -0,0 +1,161 @@
+import React, { useState } from 'react'
+import { useOrg } from '@components/Contexts/OrgContext'
+import { useLHSession } from '@components/Contexts/LHSessionContext'
+import useSWR from 'swr'
+import { getProductsByCourse, getStripeProductCheckoutSession } from '@services/payments/products'
+import { RefreshCcw, SquareCheck, ChevronDown, ChevronUp } from 'lucide-react'
+import { Badge } from '@components/ui/badge'
+import { Button } from '@components/ui/button'
+import toast from 'react-hot-toast'
+import { useRouter } from 'next/navigation'
+import { getUriWithOrg } from '@services/config/config'
+
+interface CoursePaidOptionsProps {
+ course: {
+ id: string;
+ org_id: number;
+ }
+}
+
+function CoursePaidOptions({ course }: CoursePaidOptionsProps) {
+ const org = useOrg() as any
+ const session = useLHSession() as any
+ const [expandedProducts, setExpandedProducts] = useState<{ [key: string]: boolean }>({})
+ const [isProcessing, setIsProcessing] = useState<{ [key: string]: boolean }>({})
+ const router = useRouter()
+
+ const { data: linkedProducts, error } = useSWR(
+ () => org && session ? [`/payments/${course.org_id}/courses/${course.id}/products`, session.data?.tokens?.access_token] : null,
+ ([url, token]) => getProductsByCourse(course.org_id, course.id, token)
+ )
+
+ const handleCheckout = async (productId: number) => {
+ if (!session.data?.user) {
+ // Redirect to login if user is not authenticated
+ router.push(`/signup?orgslug=${org.slug}`)
+ return
+ }
+
+ try {
+ setIsProcessing(prev => ({ ...prev, [productId]: true }))
+ const redirect_uri = getUriWithOrg(org.slug, '/courses')
+ const response = await getStripeProductCheckoutSession(
+ course.org_id,
+ productId,
+ redirect_uri,
+ session.data?.tokens?.access_token
+ )
+
+ if (response.success) {
+ router.push(response.data.checkout_url)
+ } else {
+ toast.error('Failed to initiate checkout process')
+ }
+ } catch (error) {
+ toast.error('An error occurred while processing your request')
+ } finally {
+ setIsProcessing(prev => ({ ...prev, [productId]: false }))
+ }
+ }
+
+ const toggleProductExpansion = (productId: string) => {
+ setExpandedProducts(prev => ({
+ ...prev,
+ [productId]: !prev[productId]
+ }))
+ }
+
+ if (error) return Failed to load product options
+ if (!linkedProducts) return Loading...
+
+ return (
+
+ {linkedProducts.data.map((product: any) => (
+
+
+
+
+ {product.product_type === 'subscription' ? : }
+
+ {product.product_type === 'subscription' ? 'Subscription' : 'One-time payment'}
+ {product.product_type === 'subscription' && ' (per month)'}
+
+
+
{product.name}
+
+
+
+
+
+
+ {product.description}
+
+ {product.benefits && (
+
+
Benefits:
+
+ {product.benefits}
+
+
+ )}
+
+
+
+
+
+
+
+
+
+ {product.price_type === 'customer_choice' ? 'Minimum Price:' : 'Price:'}
+
+
+
+ {new Intl.NumberFormat('en-US', {
+ style: 'currency',
+ currency: product.currency
+ }).format(product.amount)}
+ {product.product_type === 'subscription' && /month}
+
+ {product.price_type === 'customer_choice' && (
+ Choose your price
+ )}
+
+
+
+
+
+ ))}
+
+ )
+}
+
+export default CoursePaidOptions
diff --git a/apps/web/components/Objects/Courses/CourseActions/CoursesActions.tsx b/apps/web/components/Objects/Courses/CourseActions/CoursesActions.tsx
new file mode 100644
index 00000000..bbcabe0d
--- /dev/null
+++ b/apps/web/components/Objects/Courses/CourseActions/CoursesActions.tsx
@@ -0,0 +1,257 @@
+import React, { useState, useEffect } from 'react'
+import UserAvatar from '../../UserAvatar'
+import { getUserAvatarMediaDirectory } from '@services/media/media'
+import { removeCourse, startCourse } from '@services/courses/activity'
+import { revalidateTags } from '@services/utils/ts/requests'
+import { useRouter } from 'next/navigation'
+import { useLHSession } from '@components/Contexts/LHSessionContext'
+import { useMediaQuery } from 'usehooks-ts'
+import { getUriWithOrg } from '@services/config/config'
+import { getProductsByCourse } from '@services/payments/products'
+import { LogIn, LogOut, ShoppingCart, AlertCircle } from 'lucide-react'
+import Modal from '@components/Objects/StyledElements/Modal/Modal'
+import CoursePaidOptions from './CoursePaidOptions'
+import { checkPaidAccess } from '@services/payments/payments'
+
+interface Author {
+ user_uuid: string
+ avatar_image: string
+ first_name: string
+ last_name: string
+ username: string
+}
+
+interface CourseRun {
+ status: string
+ course_id: string
+}
+
+interface Course {
+ id: string
+ authors: Author[]
+ trail?: {
+ runs: CourseRun[]
+ }
+}
+
+interface CourseActionsProps {
+ courseuuid: string
+ orgslug: string
+ course: Course & {
+ org_id: number
+ }
+}
+
+// Separate component for author display
+const AuthorInfo = ({ author, isMobile }: { author: Author, isMobile: boolean }) => (
+
+
+
+
Author
+
+ {(author.first_name && author.last_name) ? (
+
+
{`${author.first_name} ${author.last_name}`}
+
+ @{author.username}
+
+
+ ) : (
+
+ )}
+
+
+
+)
+
+const Actions = ({ courseuuid, orgslug, course }: CourseActionsProps) => {
+ const router = useRouter()
+ const session = useLHSession() as any
+ const [linkedProducts, setLinkedProducts] = useState([])
+ const [isLoading, setIsLoading] = useState(true)
+ const [isModalOpen, setIsModalOpen] = useState(false)
+ const [hasAccess, setHasAccess] = useState(null)
+
+ const isStarted = course.trail?.runs?.some(
+ (run) => run.status === 'STATUS_IN_PROGRESS' && run.course_id === course.id
+ ) ?? false
+
+ useEffect(() => {
+ const fetchLinkedProducts = async () => {
+ try {
+ const response = await getProductsByCourse(
+ course.org_id,
+ course.id,
+ session.data?.tokens?.access_token
+ )
+ setLinkedProducts(response.data || [])
+ } catch (error) {
+ console.error('Failed to fetch linked products')
+ } finally {
+ setIsLoading(false)
+ }
+ }
+
+ fetchLinkedProducts()
+ }, [course.id, course.org_id, session.data?.tokens?.access_token])
+
+ useEffect(() => {
+ const checkAccess = async () => {
+ if (!session.data?.user) return
+ try {
+ const response = await checkPaidAccess(
+ parseInt(course.id),
+ course.org_id,
+ session.data?.tokens?.access_token
+ )
+ setHasAccess(response.has_access)
+
+ } catch (error) {
+ console.error('Failed to check course access')
+ setHasAccess(false)
+ }
+ }
+
+ if (linkedProducts.length > 0) {
+ checkAccess()
+ }
+ }, [course.id, course.org_id, session.data?.tokens?.access_token, linkedProducts])
+
+ const handleCourseAction = async () => {
+ if (!session.data?.user) {
+ router.push(getUriWithOrg(orgslug, '/signup?orgslug=' + orgslug))
+ return
+ }
+ const action = isStarted ? removeCourse : startCourse
+ await action('course_' + courseuuid, orgslug, session.data?.tokens?.access_token)
+ await revalidateTags(['courses'], orgslug)
+ router.refresh()
+ }
+
+ if (isLoading) {
+ return
+ }
+
+ if (linkedProducts.length > 0) {
+ return (
+
+ {hasAccess ? (
+ <>
+
+
+
+
You Own This Course
+
+
+ You have purchased this course and have full access to all content.
+
+
+
+ >
+ ) : (
+
+
+
+ This course requires purchase to access its content.
+
+
+ )}
+
+ {!hasAccess && (
+ <>
+
}
+ dialogTitle="Purchase Course"
+ dialogDescription="Select a payment option to access this course"
+ minWidth="sm"
+ />
+
+ >
+ )}
+
+ )
+ }
+
+ return (
+
+ )
+}
+
+function CoursesActions({ courseuuid, orgslug, course }: CourseActionsProps) {
+ const router = useRouter()
+ const session = useLHSession() as any
+ const isMobile = useMediaQuery('(max-width: 768px)')
+
+
+ return (
+
+ )
+}
+
+export default CoursesActions
\ No newline at end of file
diff --git a/apps/web/components/Objects/Courses/CourseActions/PaidCourseActivityDisclaimer.tsx b/apps/web/components/Objects/Courses/CourseActions/PaidCourseActivityDisclaimer.tsx
new file mode 100644
index 00000000..bb576046
--- /dev/null
+++ b/apps/web/components/Objects/Courses/CourseActions/PaidCourseActivityDisclaimer.tsx
@@ -0,0 +1,26 @@
+import React from 'react'
+import { AlertCircle, ShoppingCart } from 'lucide-react'
+import CoursePaidOptions from './CoursePaidOptions'
+
+interface PaidCourseActivityProps {
+ course: any;
+}
+
+function PaidCourseActivityDisclaimer({ course }: PaidCourseActivityProps) {
+ return (
+
+
+
+
+ This content requires a course purchase to access.
+
+
+
+
+ )
+}
+
+export default PaidCourseActivityDisclaimer
\ No newline at end of file
diff --git a/apps/web/components/Objects/CourseUpdates/CourseUpdates.tsx b/apps/web/components/Objects/Courses/CourseUpdates/CourseUpdates.tsx
similarity index 98%
rename from apps/web/components/Objects/CourseUpdates/CourseUpdates.tsx
rename to apps/web/components/Objects/Courses/CourseUpdates/CourseUpdates.tsx
index a0709a6e..917f233b 100644
--- a/apps/web/components/Objects/CourseUpdates/CourseUpdates.tsx
+++ b/apps/web/components/Objects/Courses/CourseUpdates/CourseUpdates.tsx
@@ -8,7 +8,7 @@ import FormLayout, {
FormLabelAndMessage,
Input,
Textarea,
-} from '@components/StyledElements/Form/Form'
+} from '@components/Objects/StyledElements/Form/Form'
import { useCourse } from '@components/Contexts/CourseContext'
import useSWR, { mutate } from 'swr'
import { getAPIUrl } from '@services/config/config'
@@ -17,7 +17,7 @@ import useAdminStatus from '@components/Hooks/useAdminStatus'
import { useOrg } from '@components/Contexts/OrgContext'
import { createCourseUpdate, deleteCourseUpdate } from '@services/courses/updates'
import toast from 'react-hot-toast'
-import ConfirmationModal from '@components/StyledElements/ConfirmationModal/ConfirmationModal'
+import ConfirmationModal from '@components/Objects/StyledElements/ConfirmationModal/ConfirmationModal'
import dayjs from 'dayjs';
import relativeTime from 'dayjs/plugin/relativeTime';
import { useLHSession } from '@components/Contexts/LHSessionContext'
diff --git a/apps/web/components/Objects/Editor/AI/AIEditorToolkit.tsx b/apps/web/components/Objects/Editor/AI/AIEditorToolkit.tsx
index 4b12b60b..e8dd3953 100644
--- a/apps/web/components/Objects/Editor/AI/AIEditorToolkit.tsx
+++ b/apps/web/components/Objects/Editor/AI/AIEditorToolkit.tsx
@@ -23,7 +23,7 @@ import {
sendActivityAIChatMessage,
startActivityAIChatSession,
} from '@services/ai/ai'
-import useGetAIFeatures from '@components/AI/Hooks/useGetAIFeatures'
+import useGetAIFeatures from '@components/Hooks/useGetAIFeatures'
import { useLHSession } from '@components/Contexts/LHSessionContext'
type AIEditorToolkitProps = {
diff --git a/apps/web/components/Objects/Editor/Editor.tsx b/apps/web/components/Objects/Editor/Editor.tsx
index ddd40192..29d3fd8d 100644
--- a/apps/web/components/Objects/Editor/Editor.tsx
+++ b/apps/web/components/Objects/Editor/Editor.tsx
@@ -29,7 +29,7 @@ import Table from '@tiptap/extension-table'
import TableCell from '@tiptap/extension-table-cell'
import TableHeader from '@tiptap/extension-table-header'
import TableRow from '@tiptap/extension-table-row'
-import ToolTip from '@components/StyledElements/Tooltip/Tooltip'
+import ToolTip from '@components/Objects/StyledElements/Tooltip/Tooltip'
import Link from 'next/link'
import { getCourseThumbnailMediaDirectory } from '@services/media/media'
@@ -47,7 +47,7 @@ import java from 'highlight.js/lib/languages/java'
import { CourseProvider } from '@components/Contexts/CourseContext'
import { useLHSession } from '@components/Contexts/LHSessionContext'
import AIEditorToolkit from './AI/AIEditorToolkit'
-import useGetAIFeatures from '@components/AI/Hooks/useGetAIFeatures'
+import useGetAIFeatures from '@components/Hooks/useGetAIFeatures'
import Collaboration from '@tiptap/extension-collaboration'
import CollaborationCursor from '@tiptap/extension-collaboration-cursor'
import ActiveAvatars from './ActiveAvatars'
diff --git a/apps/web/components/Objects/Editor/EditorWrapper.tsx b/apps/web/components/Objects/Editor/EditorWrapper.tsx
index e51180a3..b9c3d7f1 100644
--- a/apps/web/components/Objects/Editor/EditorWrapper.tsx
+++ b/apps/web/components/Objects/Editor/EditorWrapper.tsx
@@ -3,7 +3,7 @@ import { default as React, useEffect, useRef, useState } from 'react'
import Editor from './Editor'
import { updateActivity } from '@services/courses/activities'
import { toast } from 'react-hot-toast'
-import Toast from '@components/StyledElements/Toast/Toast'
+import Toast from '@components/Objects/StyledElements/Toast/Toast'
import { OrgProvider } from '@components/Contexts/OrgContext'
import { useLHSession } from '@components/Contexts/LHSessionContext'
diff --git a/apps/web/components/Objects/Editor/Toolbar/ToolbarButtons.tsx b/apps/web/components/Objects/Editor/Toolbar/ToolbarButtons.tsx
index 5819bad6..0dc54119 100644
--- a/apps/web/components/Objects/Editor/Toolbar/ToolbarButtons.tsx
+++ b/apps/web/components/Objects/Editor/Toolbar/ToolbarButtons.tsx
@@ -29,7 +29,7 @@ import {
Video,
} from 'lucide-react'
import { SiYoutube } from '@icons-pack/react-simple-icons'
-import ToolTip from '@components/StyledElements/Tooltip/Tooltip'
+import ToolTip from '@components/Objects/StyledElements/Tooltip/Tooltip'
export const ToolbarButtons = ({ editor, props }: any) => {
if (!editor) {
diff --git a/apps/web/components/Objects/Menu/Menu.tsx b/apps/web/components/Objects/Menus/OrgMenu.tsx
similarity index 99%
rename from apps/web/components/Objects/Menu/Menu.tsx
rename to apps/web/components/Objects/Menus/OrgMenu.tsx
index ec33ddd8..22eef5c2 100644
--- a/apps/web/components/Objects/Menu/Menu.tsx
+++ b/apps/web/components/Objects/Menus/OrgMenu.tsx
@@ -3,12 +3,12 @@ import React from 'react'
import Link from 'next/link'
import { getUriWithOrg } from '@services/config/config'
import { HeaderProfileBox } from '@components/Security/HeaderProfileBox'
-import MenuLinks from './MenuLinks'
+import MenuLinks from './OrgMenuLinks'
import { getOrgLogoMediaDirectory } from '@services/media/media'
import { useLHSession } from '@components/Contexts/LHSessionContext'
import { useOrg } from '@components/Contexts/OrgContext'
-export const Menu = (props: any) => {
+export const OrgMenu = (props: any) => {
const orgslug = props.orgslug
const session = useLHSession() as any;
const access_token = session?.data?.tokens?.access_token;
diff --git a/apps/web/components/Objects/Menu/MenuLinks.tsx b/apps/web/components/Objects/Menus/OrgMenuLinks.tsx
similarity index 100%
rename from apps/web/components/Objects/Menu/MenuLinks.tsx
rename to apps/web/components/Objects/Menus/OrgMenuLinks.tsx
diff --git a/apps/web/components/Objects/Modals/Activities/Create/NewActivityModal/Assignment.tsx b/apps/web/components/Objects/Modals/Activities/Create/NewActivityModal/Assignment.tsx
index 17ea3fbe..f11a4323 100644
--- a/apps/web/components/Objects/Modals/Activities/Create/NewActivityModal/Assignment.tsx
+++ b/apps/web/components/Objects/Modals/Activities/Create/NewActivityModal/Assignment.tsx
@@ -7,7 +7,7 @@ import FormLayout, {
FormMessage,
Input,
-} from '@components/StyledElements/Form/Form'
+} from '@components/Objects/StyledElements/Form/Form'
import * as Form from '@radix-ui/react-form'
import { BarLoader } from 'react-spinners'
import { useOrg } from '@components/Contexts/OrgContext'
diff --git a/apps/web/components/Objects/Modals/Activities/Create/NewActivityModal/DocumentPdf.tsx b/apps/web/components/Objects/Modals/Activities/Create/NewActivityModal/DocumentPdf.tsx
index 06d14e55..8c978577 100644
--- a/apps/web/components/Objects/Modals/Activities/Create/NewActivityModal/DocumentPdf.tsx
+++ b/apps/web/components/Objects/Modals/Activities/Create/NewActivityModal/DocumentPdf.tsx
@@ -5,7 +5,7 @@ import FormLayout, {
FormLabel,
FormMessage,
Input,
-} from '@components/StyledElements/Form/Form'
+} from '@components/Objects/StyledElements/Form/Form'
import React, { useState } from 'react'
import * as Form from '@radix-ui/react-form'
import BarLoader from 'react-spinners/BarLoader'
diff --git a/apps/web/components/Objects/Modals/Activities/Create/NewActivityModal/DynamicCanva.tsx b/apps/web/components/Objects/Modals/Activities/Create/NewActivityModal/DynamicCanva.tsx
index 7d6082ab..492d125c 100644
--- a/apps/web/components/Objects/Modals/Activities/Create/NewActivityModal/DynamicCanva.tsx
+++ b/apps/web/components/Objects/Modals/Activities/Create/NewActivityModal/DynamicCanva.tsx
@@ -6,7 +6,7 @@ import FormLayout, {
FormMessage,
Input,
Textarea,
-} from '@components/StyledElements/Form/Form'
+} from '@components/Objects/StyledElements/Form/Form'
import React, { useState } from 'react'
import * as Form from '@radix-ui/react-form'
import BarLoader from 'react-spinners/BarLoader'
diff --git a/apps/web/components/Objects/Modals/Activities/Create/NewActivityModal/Video.tsx b/apps/web/components/Objects/Modals/Activities/Create/NewActivityModal/Video.tsx
index 0d9ce796..3cc61bd1 100644
--- a/apps/web/components/Objects/Modals/Activities/Create/NewActivityModal/Video.tsx
+++ b/apps/web/components/Objects/Modals/Activities/Create/NewActivityModal/Video.tsx
@@ -5,7 +5,7 @@ import FormLayout, {
FormLabel,
FormMessage,
Input,
-} from '@components/StyledElements/Form/Form'
+} from '@components/Objects/StyledElements/Form/Form'
import React, { useState } from 'react'
import * as Form from '@radix-ui/react-form'
import BarLoader from 'react-spinners/BarLoader'
diff --git a/apps/web/components/Objects/Modals/Chapters/NewChapter.tsx b/apps/web/components/Objects/Modals/Chapters/NewChapter.tsx
index 9976a3a8..dfa4dd86 100644
--- a/apps/web/components/Objects/Modals/Chapters/NewChapter.tsx
+++ b/apps/web/components/Objects/Modals/Chapters/NewChapter.tsx
@@ -5,7 +5,7 @@ import FormLayout, {
Textarea,
FormLabel,
ButtonBlack,
-} from '@components/StyledElements/Form/Form'
+} from '@components/Objects/StyledElements/Form/Form'
import { FormMessage } from '@radix-ui/react-form'
import * as Form from '@radix-ui/react-form'
import React, { useState } from 'react'
diff --git a/apps/web/components/Objects/Modals/Course/Create/CreateCourse.tsx b/apps/web/components/Objects/Modals/Course/Create/CreateCourse.tsx
index f3246ee4..f20f1490 100644
--- a/apps/web/components/Objects/Modals/Course/Create/CreateCourse.tsx
+++ b/apps/web/components/Objects/Modals/Course/Create/CreateCourse.tsx
@@ -1,189 +1,262 @@
'use client'
+import { Input } from "@components/ui/input"
+import { Textarea } from "@components/ui/textarea"
+import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@components/ui/select"
import FormLayout, {
- ButtonBlack,
- Flex,
FormField,
- FormLabel,
- Input,
- Textarea,
-} from '@components/StyledElements/Form/Form'
+ FormLabelAndMessage,
+} from '@components/Objects/StyledElements/Form/Form'
import * as Form from '@radix-ui/react-form'
-import { FormMessage } from '@radix-ui/react-form'
import { createNewCourse } from '@services/courses/courses'
import { getOrganizationContextInfoWithoutCredentials } from '@services/organizations/orgs'
-import React, { useState } from 'react'
+import React, { useEffect } from 'react'
import { BarLoader } from 'react-spinners'
import { revalidateTags } from '@services/utils/ts/requests'
import { useRouter } from 'next/navigation'
import { useLHSession } from '@components/Contexts/LHSessionContext'
import toast from 'react-hot-toast'
+import { useFormik } from 'formik'
+import * as Yup from 'yup'
+import { UploadCloud, Image as ImageIcon } from 'lucide-react'
+import UnsplashImagePicker from "@components/Dashboard/Pages/Course/EditCourseGeneral/UnsplashImagePicker"
+
+const validationSchema = Yup.object().shape({
+ name: Yup.string()
+ .required('Course name is required')
+ .max(100, 'Must be 100 characters or less'),
+ description: Yup.string()
+ .max(1000, 'Must be 1000 characters or less'),
+ learnings: Yup.string(),
+ tags: Yup.string(),
+ visibility: Yup.boolean(),
+ thumbnail: Yup.mixed().nullable()
+})
function CreateCourseModal({ closeModal, orgslug }: any) {
- const [isSubmitting, setIsSubmitting] = useState(false)
- const session = useLHSession() as any;
- const [name, setName] = React.useState('')
- const [description, setDescription] = React.useState('')
- const [learnings, setLearnings] = React.useState('')
- const [visibility, setVisibility] = React.useState(true)
- const [tags, setTags] = React.useState('')
- const [isLoading, setIsLoading] = React.useState(false)
- const [thumbnail, setThumbnail] = React.useState(null) as any
const router = useRouter()
-
+ const session = useLHSession() as any
const [orgId, setOrgId] = React.useState(null) as any
- const [org, setOrg] = React.useState(null) as any
+ const [showUnsplashPicker, setShowUnsplashPicker] = React.useState(false)
+ const [isUploading, setIsUploading] = React.useState(false)
+
+ const formik = useFormik({
+ initialValues: {
+ name: '',
+ description: '',
+ learnings: '',
+ visibility: true,
+ tags: '',
+ thumbnail: null
+ },
+ validationSchema,
+ onSubmit: async (values, { setSubmitting }) => {
+ const toast_loading = toast.loading('Creating course...')
+
+ try {
+ const res = await createNewCourse(
+ orgId,
+ {
+ name: values.name,
+ description: values.description,
+ tags: values.tags,
+ visibility: values.visibility
+ },
+ values.thumbnail,
+ session.data?.tokens?.access_token
+ )
+
+ if (res.success) {
+ await revalidateTags(['courses'], orgslug)
+ toast.dismiss(toast_loading)
+ toast.success('Course created successfully')
+
+ if (res.data.org_id === orgId) {
+ closeModal()
+ router.refresh()
+ await revalidateTags(['courses'], orgslug)
+ }
+ } else {
+ toast.error(res.data.detail)
+ }
+ } catch (error) {
+ toast.error('Failed to create course')
+ } finally {
+ setSubmitting(false)
+ }
+ }
+ })
const getOrgMetadata = async () => {
const org = await getOrganizationContextInfoWithoutCredentials(orgslug, {
revalidate: 360,
tags: ['organizations'],
})
-
setOrgId(org.id)
}
- const handleNameChange = (event: React.ChangeEvent) => {
- setName(event.target.value)
- }
-
- const handleDescriptionChange = (event: React.ChangeEvent) => {
- setDescription(event.target.value)
- }
-
- const handleLearningsChange = (event: React.ChangeEvent) => {
- setLearnings(event.target.value)
- }
-
- const handleVisibilityChange = (event: React.ChangeEvent) => {
- setVisibility(event.target.value)
- }
-
- const handleTagsChange = (event: React.ChangeEvent) => {
- setTags(event.target.value)
- }
-
- const handleThumbnailChange = (event: React.ChangeEvent) => {
- setThumbnail(event.target.files[0])
- }
-
- const handleSubmit = async (e: any) => {
- e.preventDefault()
- setIsSubmitting(true)
-
- let res = await createNewCourse(
- orgId,
- { name, description, tags, visibility },
- thumbnail,
- session.data?.tokens?.access_token
- )
- const toast_loading = toast.loading('Creating course...')
- if (res.success) {
- await revalidateTags(['courses'], orgslug)
- setIsSubmitting(false)
- toast.dismiss(toast_loading)
- toast.success('Course created successfully')
-
- if (res.data.org_id == orgId) {
- closeModal()
- router.refresh()
- await revalidateTags(['courses'], orgslug)
- }
-
- }
- else {
- setIsSubmitting(false)
- toast.error(res.data.detail)
- }
- }
-
- React.useEffect(() => {
+ useEffect(() => {
if (orgslug) {
getOrgMetadata()
}
- }, [isLoading, orgslug])
+ }, [orgslug])
+
+ const handleFileChange = async (event: React.ChangeEvent) => {
+ const file = event.target.files?.[0]
+ if (file) {
+ formik.setFieldValue('thumbnail', file)
+ }
+ }
+
+ const handleUnsplashSelect = async (imageUrl: string) => {
+ setIsUploading(true)
+ try {
+ const response = await fetch(imageUrl)
+ const blob = await response.blob()
+ const file = new File([blob], 'unsplash_image.jpg', { type: 'image/jpeg' })
+ formik.setFieldValue('thumbnail', file)
+ } catch (error) {
+ toast.error('Failed to load image from Unsplash')
+ }
+ setIsUploading(false)
+ }
return (
-
-
-
- Course name
-
- Please provide a course name
-
-
+
+
+
-
-
-
-
-
- Course description
-
- Please provide a course description
-
-
-
-
-
-
-
-
- Course thumbnail
-
- Please provide a thumbnail for your course
-
-
-
-
-
-
-
-
- Course Learnings (separated by comma)
-
- Please provide learning elements, separated by comma (,)
-
-
-
-
-
-
-
-
- Course Visibility
-
- Please choose course visibility
-
-
-
-
+ />
-
-
-
- {isSubmitting ? (
-
- ) : (
- 'Create Course'
- )}
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+ {formik.values.thumbnail ? (
+
})
+ ) : (
+

+ )}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {showUnsplashPicker && (
+ setShowUnsplashPicker(false)}
+ />
+ )}
)
}
diff --git a/apps/web/components/Objects/Modals/Dash/OrgUserGroups/AddUserGroup.tsx b/apps/web/components/Objects/Modals/Dash/OrgUserGroups/AddUserGroup.tsx
index 705a82bb..7389f50d 100644
--- a/apps/web/components/Objects/Modals/Dash/OrgUserGroups/AddUserGroup.tsx
+++ b/apps/web/components/Objects/Modals/Dash/OrgUserGroups/AddUserGroup.tsx
@@ -3,7 +3,7 @@ import FormLayout, {
FormField,
FormLabelAndMessage,
Input,
-} from '@components/StyledElements/Form/Form'
+} from '@components/Objects/StyledElements/Form/Form'
import * as Form from '@radix-ui/react-form'
import { useOrg } from '@components/Contexts/OrgContext'
import React from 'react'
diff --git a/apps/web/components/Objects/Modals/Dash/OrgUserGroups/EditUserGroup.tsx b/apps/web/components/Objects/Modals/Dash/OrgUserGroups/EditUserGroup.tsx
index b0d20fe0..c950415b 100644
--- a/apps/web/components/Objects/Modals/Dash/OrgUserGroups/EditUserGroup.tsx
+++ b/apps/web/components/Objects/Modals/Dash/OrgUserGroups/EditUserGroup.tsx
@@ -3,7 +3,7 @@ import FormLayout, {
FormField,
FormLabelAndMessage,
Input,
-} from '@components/StyledElements/Form/Form'
+} from '@components/Objects/StyledElements/Form/Form'
import * as Form from '@radix-ui/react-form'
import { useOrg } from '@components/Contexts/OrgContext'
import React from 'react'
diff --git a/apps/web/components/Objects/Modals/Dash/OrgUsers/RolesUpdate.tsx b/apps/web/components/Objects/Modals/Dash/OrgUsers/RolesUpdate.tsx
index 681f1d85..9168a7a2 100644
--- a/apps/web/components/Objects/Modals/Dash/OrgUsers/RolesUpdate.tsx
+++ b/apps/web/components/Objects/Modals/Dash/OrgUsers/RolesUpdate.tsx
@@ -6,7 +6,7 @@ import FormLayout, {
Flex,
FormField,
FormLabel,
-} from '@components/StyledElements/Form/Form'
+} from '@components/Objects/StyledElements/Form/Form'
import * as Form from '@radix-ui/react-form'
import { FormMessage } from '@radix-ui/react-form'
import { getAPIUrl } from '@services/config/config'
diff --git a/apps/web/components/Onboarding/Onboarding.tsx b/apps/web/components/Objects/Onboarding/Onboarding.tsx
similarity index 99%
rename from apps/web/components/Onboarding/Onboarding.tsx
rename to apps/web/components/Objects/Onboarding/Onboarding.tsx
index 9fd26726..43b1fd2e 100644
--- a/apps/web/components/Onboarding/Onboarding.tsx
+++ b/apps/web/components/Objects/Onboarding/Onboarding.tsx
@@ -1,4 +1,4 @@
-import Modal from '@components/StyledElements/Modal/Modal';
+import Modal from '@components/Objects/StyledElements/Modal/Modal';
import Image, { StaticImageData } from 'next/image';
import React, { useEffect, useState } from 'react';
import OnBoardWelcome from '@public/onboarding/OnBoardWelcome.png';
diff --git a/apps/web/components/StyledElements/Buttons/NewCollectionButton.tsx b/apps/web/components/Objects/StyledElements/Buttons/NewCollectionButton.tsx
similarity index 100%
rename from apps/web/components/StyledElements/Buttons/NewCollectionButton.tsx
rename to apps/web/components/Objects/StyledElements/Buttons/NewCollectionButton.tsx
diff --git a/apps/web/components/StyledElements/Buttons/NewCourseButton.tsx b/apps/web/components/Objects/StyledElements/Buttons/NewCourseButton.tsx
similarity index 100%
rename from apps/web/components/StyledElements/Buttons/NewCourseButton.tsx
rename to apps/web/components/Objects/StyledElements/Buttons/NewCourseButton.tsx
diff --git a/apps/web/components/StyledElements/ConfirmationModal/ConfirmationModal.tsx b/apps/web/components/Objects/StyledElements/ConfirmationModal/ConfirmationModal.tsx
similarity index 100%
rename from apps/web/components/StyledElements/ConfirmationModal/ConfirmationModal.tsx
rename to apps/web/components/Objects/StyledElements/ConfirmationModal/ConfirmationModal.tsx
diff --git a/apps/web/components/StyledElements/Error/Error.tsx b/apps/web/components/Objects/StyledElements/Error/Error.tsx
similarity index 100%
rename from apps/web/components/StyledElements/Error/Error.tsx
rename to apps/web/components/Objects/StyledElements/Error/Error.tsx
diff --git a/apps/web/components/StyledElements/Form/Form.tsx b/apps/web/components/Objects/StyledElements/Form/Form.tsx
similarity index 100%
rename from apps/web/components/StyledElements/Form/Form.tsx
rename to apps/web/components/Objects/StyledElements/Form/Form.tsx
diff --git a/apps/web/components/StyledElements/Info/Info.tsx b/apps/web/components/Objects/StyledElements/Info/Info.tsx
similarity index 100%
rename from apps/web/components/StyledElements/Info/Info.tsx
rename to apps/web/components/Objects/StyledElements/Info/Info.tsx
diff --git a/apps/web/components/Objects/StyledElements/Modal/Modal.tsx b/apps/web/components/Objects/StyledElements/Modal/Modal.tsx
new file mode 100644
index 00000000..dc0f859a
--- /dev/null
+++ b/apps/web/components/Objects/StyledElements/Modal/Modal.tsx
@@ -0,0 +1,80 @@
+'use client'
+
+import React from 'react'
+import { Dialog, DialogContent, DialogDescription, DialogHeader, DialogTitle, DialogTrigger, DialogFooter } from "@components/ui/dialog"
+import { ButtonBlack } from '../Form/Form'
+import { cn } from "@/lib/utils"
+
+type ModalParams = {
+ dialogTitle?: string
+ dialogDescription?: string
+ dialogContent: React.ReactNode
+ dialogClose?: React.ReactNode | null
+ dialogTrigger?: React.ReactNode
+ addDefCloseButton?: boolean
+ onOpenChange: (open: boolean) => void
+ isDialogOpen?: boolean
+ minHeight?: 'sm' | 'md' | 'lg' | 'xl' | 'no-min'
+ minWidth?: 'sm' | 'md' | 'lg' | 'xl' | 'no-min'
+ customHeight?: string
+ customWidth?: string
+}
+
+const Modal = (params: ModalParams) => {
+ const getMinHeight = () => {
+ switch (params.minHeight) {
+ case 'sm': return 'min-h-[300px]'
+ case 'md': return 'min-h-[500px]'
+ case 'lg': return 'min-h-[700px]'
+ case 'xl': return 'min-h-[900px]'
+ default: return ''
+ }
+ }
+
+ const getMinWidth = () => {
+ switch (params.minWidth) {
+ case 'sm': return 'min-w-[600px]'
+ case 'md': return 'min-w-[800px]'
+ case 'lg': return 'min-w-[1000px]'
+ case 'xl': return 'min-w-[1200px]'
+ default: return ''
+ }
+ }
+
+ return (
+
+ )
+}
+
+export default Modal
diff --git a/apps/web/components/StyledElements/Titles/TypeOfContentTitle.tsx b/apps/web/components/Objects/StyledElements/Titles/TypeOfContentTitle.tsx
similarity index 100%
rename from apps/web/components/StyledElements/Titles/TypeOfContentTitle.tsx
rename to apps/web/components/Objects/StyledElements/Titles/TypeOfContentTitle.tsx
diff --git a/apps/web/components/StyledElements/Toast/Toast.tsx b/apps/web/components/Objects/StyledElements/Toast/Toast.tsx
similarity index 100%
rename from apps/web/components/StyledElements/Toast/Toast.tsx
rename to apps/web/components/Objects/StyledElements/Toast/Toast.tsx
diff --git a/apps/web/components/StyledElements/Tooltip/Tooltip.tsx b/apps/web/components/Objects/StyledElements/Tooltip/Tooltip.tsx
similarity index 100%
rename from apps/web/components/StyledElements/Tooltip/Tooltip.tsx
rename to apps/web/components/Objects/StyledElements/Tooltip/Tooltip.tsx
diff --git a/apps/web/components/StyledElements/Wrappers/GeneralWrapper.tsx b/apps/web/components/Objects/StyledElements/Wrappers/GeneralWrapper.tsx
similarity index 100%
rename from apps/web/components/StyledElements/Wrappers/GeneralWrapper.tsx
rename to apps/web/components/Objects/StyledElements/Wrappers/GeneralWrapper.tsx
diff --git a/apps/web/components/Objects/Thumbnails/CollectionThumbnail.tsx b/apps/web/components/Objects/Thumbnails/CollectionThumbnail.tsx
index d1f92fe4..66847776 100644
--- a/apps/web/components/Objects/Thumbnails/CollectionThumbnail.tsx
+++ b/apps/web/components/Objects/Thumbnails/CollectionThumbnail.tsx
@@ -1,7 +1,7 @@
'use client'
import { useOrg } from '@components/Contexts/OrgContext'
import AuthenticatedClientElement from '@components/Security/AuthenticatedClientElement'
-import ConfirmationModal from '@components/StyledElements/ConfirmationModal/ConfirmationModal'
+import ConfirmationModal from '@components/Objects/StyledElements/ConfirmationModal/ConfirmationModal'
import { getUriWithOrg } from '@services/config/config'
import { deleteCollection } from '@services/courses/collections'
import { getCourseThumbnailMediaDirectory } from '@services/media/media'
diff --git a/apps/web/components/Objects/Thumbnails/CourseThumbnail.tsx b/apps/web/components/Objects/Thumbnails/CourseThumbnail.tsx
index dbb2a85d..42ebb5f0 100644
--- a/apps/web/components/Objects/Thumbnails/CourseThumbnail.tsx
+++ b/apps/web/components/Objects/Thumbnails/CourseThumbnail.tsx
@@ -1,7 +1,7 @@
'use client'
import { useOrg } from '@components/Contexts/OrgContext'
import AuthenticatedClientElement from '@components/Security/AuthenticatedClientElement'
-import ConfirmationModal from '@components/StyledElements/ConfirmationModal/ConfirmationModal'
+import ConfirmationModal from '@components/Objects/StyledElements/ConfirmationModal/ConfirmationModal'
import { getUriWithOrg } from '@services/config/config'
import { deleteCourseFromBackend } from '@services/courses/courses'
import { getCourseThumbnailMediaDirectory } from '@services/media/media'
@@ -17,7 +17,7 @@ import {
DropdownMenuContent,
DropdownMenuItem,
DropdownMenuTrigger,
-} from "@/components/ui/dropdown-menu"
+} from "@components/ui/dropdown-menu"
type Course = {
course_uuid: string
diff --git a/apps/web/components/Watermark.tsx b/apps/web/components/Objects/Watermark.tsx
similarity index 94%
rename from apps/web/components/Watermark.tsx
rename to apps/web/components/Objects/Watermark.tsx
index ff06fb4c..9d4998b0 100644
--- a/apps/web/components/Watermark.tsx
+++ b/apps/web/components/Objects/Watermark.tsx
@@ -2,7 +2,7 @@ import Image from 'next/image'
import Link from 'next/link'
import blacklogo from '@public/black_logo.png'
import React, { useEffect } from 'react'
-import { useOrg } from './Contexts/OrgContext'
+import { useOrg } from '../Contexts/OrgContext'
function Watermark() {
const org = useOrg() as any
diff --git a/apps/web/components/Pages/CourseEdit/Draggables/Activity.tsx b/apps/web/components/Pages/CourseEdit/Draggables/Activity.tsx
index e45c75e5..95eef6ad 100644
--- a/apps/web/components/Pages/CourseEdit/Draggables/Activity.tsx
+++ b/apps/web/components/Pages/CourseEdit/Draggables/Activity.tsx
@@ -15,7 +15,7 @@ import {
import { mutate } from 'swr'
import { revalidateTags } from '@services/utils/ts/requests'
import { useRouter } from 'next/navigation'
-import ConfirmationModal from '@components/StyledElements/ConfirmationModal/ConfirmationModal'
+import ConfirmationModal from '@components/Objects/StyledElements/ConfirmationModal/ConfirmationModal'
import { deleteActivity, updateActivity } from '@services/courses/activities'
import { useLHSession } from '@components/Contexts/LHSessionContext'
diff --git a/apps/web/components/Pages/CourseEdit/Draggables/Chapter.tsx b/apps/web/components/Pages/CourseEdit/Draggables/Chapter.tsx
index 2e449210..fa8b6cea 100644
--- a/apps/web/components/Pages/CourseEdit/Draggables/Chapter.tsx
+++ b/apps/web/components/Pages/CourseEdit/Draggables/Chapter.tsx
@@ -3,7 +3,7 @@ import styled from 'styled-components'
import { Droppable, Draggable } from 'react-beautiful-dnd'
import Activity from './Activity'
import { Hexagon, MoreVertical, Pencil, Save, Sparkles, X } from 'lucide-react'
-import ConfirmationModal from '@components/StyledElements/ConfirmationModal/ConfirmationModal'
+import ConfirmationModal from '@components/Objects/StyledElements/ConfirmationModal/ConfirmationModal'
import { useRouter } from 'next/navigation'
import { updateChapter } from '@services/courses/chapters'
import { mutate } from 'swr'
diff --git a/apps/web/components/Pages/Courses/ActivityIndicators.tsx b/apps/web/components/Pages/Courses/ActivityIndicators.tsx
index 43362651..a979c612 100644
--- a/apps/web/components/Pages/Courses/ActivityIndicators.tsx
+++ b/apps/web/components/Pages/Courses/ActivityIndicators.tsx
@@ -1,4 +1,4 @@
-import ToolTip from '@components/StyledElements/Tooltip/Tooltip'
+import ToolTip from '@components/Objects/StyledElements/Tooltip/Tooltip'
import { getUriWithOrg } from '@services/config/config'
import Link from 'next/link'
import React from 'react'
diff --git a/apps/web/components/Pages/Payments/UnconfiguredPaymentsDisclaimer.tsx b/apps/web/components/Pages/Payments/UnconfiguredPaymentsDisclaimer.tsx
new file mode 100644
index 00000000..1ed9bc08
--- /dev/null
+++ b/apps/web/components/Pages/Payments/UnconfiguredPaymentsDisclaimer.tsx
@@ -0,0 +1,48 @@
+import { Settings, ChevronRight, CreditCard } from 'lucide-react'
+import { Alert, AlertTitle, AlertDescription } from '@components/ui/alert'
+import { AlertTriangle, ShoppingCart, Users } from 'lucide-react'
+import React from 'react'
+import Link from 'next/link'
+
+function UnconfiguredPaymentsDisclaimer() {
+ return (
+
+
+
+
+
+ Payments not yet properly configured
+
+
+
+
+ -
+
+ Configure Stripe to start accepting payments
+
+ -
+
+ Create and manage products
+
+ -
+
+ Start selling to your customers
+
+
+
+
+
+ Go to Payment Configuration
+
+
+
+
+
+
+ )
+}
+
+export default UnconfiguredPaymentsDisclaimer
\ No newline at end of file
diff --git a/apps/web/components/Security/HeaderProfileBox.tsx b/apps/web/components/Security/HeaderProfileBox.tsx
index 7c1bb394..6fbddd2b 100644
--- a/apps/web/components/Security/HeaderProfileBox.tsx
+++ b/apps/web/components/Security/HeaderProfileBox.tsx
@@ -2,16 +2,17 @@
import React, { useEffect } from 'react'
import styled from 'styled-components'
import Link from 'next/link'
-import { Settings } from 'lucide-react'
+import { Package2, Settings } from 'lucide-react'
import UserAvatar from '@components/Objects/UserAvatar'
import useAdminStatus from '@components/Hooks/useAdminStatus'
import { useLHSession } from '@components/Contexts/LHSessionContext'
import { useOrg } from '@components/Contexts/OrgContext'
import { getUriWithoutOrg } from '@services/config/config'
+import Tooltip from '@components/Objects/StyledElements/Tooltip/Tooltip'
export const HeaderProfileBox = () => {
const session = useLHSession() as any
- const isUserAdmin = useAdminStatus()
+ const isUserAdmin = useAdminStatus()
const org = useOrg() as any
useEffect(() => { }
@@ -39,12 +40,30 @@ export const HeaderProfileBox = () => {
{session.data.user.username}
{isUserAdmin.isAdmin && ADMIN
}
+
+
-
-
-
)}
diff --git a/apps/web/components/StyledElements/Modal/Modal.tsx b/apps/web/components/StyledElements/Modal/Modal.tsx
deleted file mode 100644
index b78350ec..00000000
--- a/apps/web/components/StyledElements/Modal/Modal.tsx
+++ /dev/null
@@ -1,180 +0,0 @@
-'use client'
-import React from 'react'
-import * as Dialog from '@radix-ui/react-dialog'
-import { styled, keyframes } from '@stitches/react'
-import { blackA, mauve } from '@radix-ui/colors'
-import { ButtonBlack } from '../Form/Form'
-
-type ModalParams = {
- dialogTitle?: string
- dialogDescription?: string
- dialogContent: React.ReactNode
- dialogClose?: React.ReactNode | null
- dialogTrigger?: React.ReactNode
- addDefCloseButton?: boolean
- onOpenChange: any
- isDialogOpen?: boolean
- minHeight?: 'sm' | 'md' | 'lg' | 'xl' | 'no-min'
- minWidth?: 'sm' | 'md' | 'lg' | 'xl' | 'no-min'
- customHeight?: string
- customWidth?: string
-}
-
-const Modal = (params: ModalParams) => (
-
- {params.dialogTrigger ? (
- {params.dialogTrigger}
- ) : null}
-
-
-
-
- {params.dialogTitle && params.dialogDescription &&
-
- {params.dialogTitle}
- {params.dialogDescription}
-
- }
- {params.dialogContent}
- {params.dialogClose ? (
-
- {params.dialogClose}
-
- ) : null}
- {params.addDefCloseButton ? (
-
-
-
- Close
-
-
-
- ) : null}
-
-
-
-)
-
-const overlayShow = keyframes({
- '0%': { opacity: 0 },
- '100%': { opacity: 1 },
-})
-
-const overlayClose = keyframes({
- '0%': { opacity: 1 },
- '100%': { opacity: 0 },
-})
-
-const contentShow = keyframes({
- '0%': { opacity: 0, transform: 'translate(-50%, -50%) scale(.96)' },
- '100%': { opacity: 1, transform: 'translate(-50%, -50%) scale(1)' },
-})
-
-const contentClose = keyframes({
- '0%': { opacity: 1, transform: 'translate(-50%, -50%) scale(1)' },
- '100%': { opacity: 0, transform: 'translate(-50%, -52%) scale(.96)' },
-})
-
-const DialogOverlay = styled(Dialog.Overlay, {
- backgroundColor: blackA.blackA9,
- backdropFilter: 'blur(0.6px)',
- position: 'fixed',
- zIndex: 500,
- inset: 0,
- animation: `${overlayShow} 150ms cubic-bezier(0.16, 1, 0.3, 1)`,
- '&[data-state="closed"]': {
- animation: `${overlayClose} 150ms cubic-bezier(0.16, 1, 0.3, 1)`,
- },
-})
-
-const DialogContent = styled(Dialog.Content, {
- variants: {
- minHeight: {
- 'no-min': {
- minHeight: '0px',
- },
- sm: {
- minHeight: '300px',
- },
- md: {
- minHeight: '500px',
- },
- lg: {
- minHeight: '700px',
- },
- xl: {
- minHeight: '900px',
- },
- },
- minWidth: {
- 'no-min': {
- minWidth: '0px',
- },
- sm: {
- minWidth: '600px',
- },
- md: {
- minWidth: '800px',
- },
- lg: {
- minWidth: '1000px',
- },
- xl: {
- minWidth: '1200px',
- },
- },
- },
-
- backgroundColor: 'white',
- borderRadius: 18,
- zIndex: 501,
- boxShadow:
- 'hsl(206 22% 7% / 35%) 0px 10px 38px -10px, hsl(206 22% 7% / 20%) 0px 10px 20px -15px',
- position: 'fixed',
- top: '50%',
- left: '50%',
- transform: 'translate(-50%, -50%)',
- width: '90vw',
- maxHeight: '85vh',
- minHeight: '300px',
- maxWidth: '600px',
- padding: 11,
- animation: `${contentShow} 150ms cubic-bezier(0.16, 1, 0.3, 1)`,
- '&:focus': { outline: 'none' },
-
- '&[data-state="closed"]': {
- animation: `${contentClose} 150ms cubic-bezier(0.16, 1, 0.3, 1)`,
- },
- transition: 'max-height 0.3s ease-out',
-})
-
-const DialogTopBar = styled('div', {
- background: '#F7F7F7',
- padding: '8px 14px ',
- borderRadius: 14,
-})
-const DialogTitle = styled(Dialog.Title, {
- margin: 0,
- fontWeight: 700,
- letterSpacing: '-0.05em',
- padding: 0,
- color: mauve.mauve12,
- fontSize: 21,
-})
-
-const DialogDescription = styled(Dialog.Description, {
- color: mauve.mauve11,
- letterSpacing: '-0.03em',
- fontSize: 15,
- padding: 0,
- margin: 0,
-})
-
-const Flex = styled('div', { display: 'flex' })
-
-export default Modal
diff --git a/apps/web/components/ui/alert.tsx b/apps/web/components/ui/alert.tsx
new file mode 100644
index 00000000..5afd41d1
--- /dev/null
+++ b/apps/web/components/ui/alert.tsx
@@ -0,0 +1,59 @@
+import * as React from "react"
+import { cva, type VariantProps } from "class-variance-authority"
+
+import { cn } from "@/lib/utils"
+
+const alertVariants = cva(
+ "relative w-full rounded-lg border px-4 py-3 text-sm [&>svg+div]:translate-y-[-3px] [&>svg]:absolute [&>svg]:left-4 [&>svg]:top-4 [&>svg]:text-foreground [&>svg~*]:pl-7",
+ {
+ variants: {
+ variant: {
+ default: "bg-background text-foreground",
+ destructive:
+ "border-destructive/50 text-destructive dark:border-destructive [&>svg]:text-destructive",
+ },
+ },
+ defaultVariants: {
+ variant: "default",
+ },
+ }
+)
+
+const Alert = React.forwardRef<
+ HTMLDivElement,
+ React.HTMLAttributes
& VariantProps
+>(({ className, variant, ...props }, ref) => (
+
+))
+Alert.displayName = "Alert"
+
+const AlertTitle = React.forwardRef<
+ HTMLParagraphElement,
+ React.HTMLAttributes
+>(({ className, ...props }, ref) => (
+
+))
+AlertTitle.displayName = "AlertTitle"
+
+const AlertDescription = React.forwardRef<
+ HTMLParagraphElement,
+ React.HTMLAttributes
+>(({ className, ...props }, ref) => (
+
+))
+AlertDescription.displayName = "AlertDescription"
+
+export { Alert, AlertTitle, AlertDescription }
diff --git a/apps/web/components/ui/badge.tsx b/apps/web/components/ui/badge.tsx
new file mode 100644
index 00000000..e87d62bf
--- /dev/null
+++ b/apps/web/components/ui/badge.tsx
@@ -0,0 +1,36 @@
+import * as React from "react"
+import { cva, type VariantProps } from "class-variance-authority"
+
+import { cn } from "@/lib/utils"
+
+const badgeVariants = cva(
+ "inline-flex items-center rounded-md border px-2.5 py-0.5 text-xs font-semibold transition-colors focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2",
+ {
+ variants: {
+ variant: {
+ default:
+ "border-transparent bg-primary text-primary-foreground shadow hover:bg-primary/80",
+ secondary:
+ "border-transparent bg-secondary text-secondary-foreground hover:bg-secondary/80",
+ destructive:
+ "border-transparent bg-destructive text-destructive-foreground shadow hover:bg-destructive/80",
+ outline: "text-foreground",
+ },
+ },
+ defaultVariants: {
+ variant: "default",
+ },
+ }
+)
+
+export interface BadgeProps
+ extends React.HTMLAttributes,
+ VariantProps {}
+
+function Badge({ className, variant, ...props }: BadgeProps) {
+ return (
+
+ )
+}
+
+export { Badge, badgeVariants }
diff --git a/apps/web/components/ui/button.tsx b/apps/web/components/ui/button.tsx
new file mode 100644
index 00000000..ed0f6279
--- /dev/null
+++ b/apps/web/components/ui/button.tsx
@@ -0,0 +1,57 @@
+import * as React from "react"
+import { Slot } from "@radix-ui/react-slot"
+import { cva, type VariantProps } from "class-variance-authority"
+
+import { cn } from "@/lib/utils"
+
+const buttonVariants = cva(
+ "inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium ring-offset-background transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0",
+ {
+ variants: {
+ variant: {
+ default:
+ "bg-primary text-primary-foreground shadow hover:bg-primary/90",
+ destructive:
+ "bg-destructive text-destructive-foreground shadow-sm hover:bg-destructive/90",
+ outline:
+ "border border-input bg-background shadow-sm hover:bg-accent hover:text-accent-foreground",
+ secondary:
+ "bg-secondary text-secondary-foreground shadow-sm hover:bg-secondary/80",
+ ghost: "hover:bg-accent hover:text-accent-foreground",
+ link: "text-primary underline-offset-4 hover:underline",
+ },
+ size: {
+ default: "h-9 px-4 py-2",
+ sm: "h-8 rounded-md px-3 text-xs",
+ lg: "h-10 rounded-md px-8",
+ icon: "h-9 w-9",
+ },
+ },
+ defaultVariants: {
+ variant: "default",
+ size: "default",
+ },
+ }
+)
+
+export interface ButtonProps
+ extends React.ButtonHTMLAttributes,
+ VariantProps {
+ asChild?: boolean
+}
+
+const Button = React.forwardRef(
+ ({ className, variant, size, asChild = false, ...props }, ref) => {
+ const Comp = asChild ? Slot : "button"
+ return (
+
+ )
+ }
+)
+Button.displayName = "Button"
+
+export { Button, buttonVariants }
diff --git a/apps/web/components/ui/dialog.tsx b/apps/web/components/ui/dialog.tsx
new file mode 100644
index 00000000..fe42372d
--- /dev/null
+++ b/apps/web/components/ui/dialog.tsx
@@ -0,0 +1,122 @@
+"use client"
+
+import * as React from "react"
+import * as DialogPrimitive from "@radix-ui/react-dialog"
+import { Cross2Icon } from "@radix-ui/react-icons"
+
+import { cn } from "@/lib/utils"
+
+const Dialog = DialogPrimitive.Root
+
+const DialogTrigger = DialogPrimitive.Trigger
+
+const DialogPortal = DialogPrimitive.Portal
+
+const DialogClose = DialogPrimitive.Close
+
+const DialogOverlay = React.forwardRef<
+ React.ElementRef,
+ React.ComponentPropsWithoutRef
+>(({ className, ...props }, ref) => (
+
+))
+DialogOverlay.displayName = DialogPrimitive.Overlay.displayName
+
+const DialogContent = React.forwardRef<
+ React.ElementRef,
+ React.ComponentPropsWithoutRef
+>(({ className, children, ...props }, ref) => (
+
+
+
+ {children}
+
+
+ Close
+
+
+
+))
+DialogContent.displayName = DialogPrimitive.Content.displayName
+
+const DialogHeader = ({
+ className,
+ ...props
+}: React.HTMLAttributes) => (
+
+)
+DialogHeader.displayName = "DialogHeader"
+
+const DialogFooter = ({
+ className,
+ ...props
+}: React.HTMLAttributes) => (
+
+)
+DialogFooter.displayName = "DialogFooter"
+
+const DialogTitle = React.forwardRef<
+ React.ElementRef,
+ React.ComponentPropsWithoutRef
+>(({ className, ...props }, ref) => (
+
+))
+DialogTitle.displayName = DialogPrimitive.Title.displayName
+
+const DialogDescription = React.forwardRef<
+ React.ElementRef,
+ React.ComponentPropsWithoutRef
+>(({ className, ...props }, ref) => (
+
+))
+DialogDescription.displayName = DialogPrimitive.Description.displayName
+
+export {
+ Dialog,
+ DialogPortal,
+ DialogOverlay,
+ DialogTrigger,
+ DialogClose,
+ DialogContent,
+ DialogHeader,
+ DialogFooter,
+ DialogTitle,
+ DialogDescription,
+}
diff --git a/apps/web/components/ui/input.tsx b/apps/web/components/ui/input.tsx
new file mode 100644
index 00000000..5af26b2c
--- /dev/null
+++ b/apps/web/components/ui/input.tsx
@@ -0,0 +1,25 @@
+import * as React from "react"
+
+import { cn } from "@/lib/utils"
+
+export interface InputProps
+ extends React.InputHTMLAttributes {}
+
+const Input = React.forwardRef(
+ ({ className, type, ...props }, ref) => {
+ return (
+
+ )
+ }
+)
+Input.displayName = "Input"
+
+export { Input }
diff --git a/apps/web/components/ui/label.tsx b/apps/web/components/ui/label.tsx
new file mode 100644
index 00000000..53418217
--- /dev/null
+++ b/apps/web/components/ui/label.tsx
@@ -0,0 +1,26 @@
+"use client"
+
+import * as React from "react"
+import * as LabelPrimitive from "@radix-ui/react-label"
+import { cva, type VariantProps } from "class-variance-authority"
+
+import { cn } from "@/lib/utils"
+
+const labelVariants = cva(
+ "text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70"
+)
+
+const Label = React.forwardRef<
+ React.ElementRef,
+ React.ComponentPropsWithoutRef &
+ VariantProps
+>(({ className, ...props }, ref) => (
+
+))
+Label.displayName = LabelPrimitive.Root.displayName
+
+export { Label }
diff --git a/apps/web/components/ui/select.tsx b/apps/web/components/ui/select.tsx
new file mode 100644
index 00000000..ac2a8f2b
--- /dev/null
+++ b/apps/web/components/ui/select.tsx
@@ -0,0 +1,164 @@
+"use client"
+
+import * as React from "react"
+import {
+ CaretSortIcon,
+ CheckIcon,
+ ChevronDownIcon,
+ ChevronUpIcon,
+} from "@radix-ui/react-icons"
+import * as SelectPrimitive from "@radix-ui/react-select"
+
+import { cn } from "@/lib/utils"
+
+const Select = SelectPrimitive.Root
+
+const SelectGroup = SelectPrimitive.Group
+
+const SelectValue = SelectPrimitive.Value
+
+const SelectTrigger = React.forwardRef<
+ React.ElementRef,
+ React.ComponentPropsWithoutRef
+>(({ className, children, ...props }, ref) => (
+ span]:line-clamp-1",
+ className
+ )}
+ {...props}
+ >
+ {children}
+
+
+
+
+))
+SelectTrigger.displayName = SelectPrimitive.Trigger.displayName
+
+const SelectScrollUpButton = React.forwardRef<
+ React.ElementRef,
+ React.ComponentPropsWithoutRef
+>(({ className, ...props }, ref) => (
+
+
+
+))
+SelectScrollUpButton.displayName = SelectPrimitive.ScrollUpButton.displayName
+
+const SelectScrollDownButton = React.forwardRef<
+ React.ElementRef,
+ React.ComponentPropsWithoutRef
+>(({ className, ...props }, ref) => (
+
+
+
+))
+SelectScrollDownButton.displayName =
+ SelectPrimitive.ScrollDownButton.displayName
+
+const SelectContent = React.forwardRef<
+ React.ElementRef,
+ React.ComponentPropsWithoutRef
+>(({ className, children, position = "popper", ...props }, ref) => (
+
+
+
+
+ {children}
+
+
+
+
+))
+SelectContent.displayName = SelectPrimitive.Content.displayName
+
+const SelectLabel = React.forwardRef<
+ React.ElementRef,
+ React.ComponentPropsWithoutRef
+>(({ className, ...props }, ref) => (
+
+))
+SelectLabel.displayName = SelectPrimitive.Label.displayName
+
+const SelectItem = React.forwardRef<
+ React.ElementRef,
+ React.ComponentPropsWithoutRef
+>(({ className, children, ...props }, ref) => (
+
+
+
+
+
+
+ {children}
+
+))
+SelectItem.displayName = SelectPrimitive.Item.displayName
+
+const SelectSeparator = React.forwardRef<
+ React.ElementRef,
+ React.ComponentPropsWithoutRef
+>(({ className, ...props }, ref) => (
+
+))
+SelectSeparator.displayName = SelectPrimitive.Separator.displayName
+
+export {
+ Select,
+ SelectGroup,
+ SelectValue,
+ SelectTrigger,
+ SelectContent,
+ SelectLabel,
+ SelectItem,
+ SelectSeparator,
+ SelectScrollUpButton,
+ SelectScrollDownButton,
+}
diff --git a/apps/web/components/ui/table.tsx b/apps/web/components/ui/table.tsx
new file mode 100644
index 00000000..c0df655c
--- /dev/null
+++ b/apps/web/components/ui/table.tsx
@@ -0,0 +1,120 @@
+import * as React from "react"
+
+import { cn } from "@/lib/utils"
+
+const Table = React.forwardRef<
+ HTMLTableElement,
+ React.HTMLAttributes
+>(({ className, ...props }, ref) => (
+
+))
+Table.displayName = "Table"
+
+const TableHeader = React.forwardRef<
+ HTMLTableSectionElement,
+ React.HTMLAttributes
+>(({ className, ...props }, ref) => (
+
+))
+TableHeader.displayName = "TableHeader"
+
+const TableBody = React.forwardRef<
+ HTMLTableSectionElement,
+ React.HTMLAttributes
+>(({ className, ...props }, ref) => (
+
+))
+TableBody.displayName = "TableBody"
+
+const TableFooter = React.forwardRef<
+ HTMLTableSectionElement,
+ React.HTMLAttributes
+>(({ className, ...props }, ref) => (
+ tr]:last:border-b-0",
+ className
+ )}
+ {...props}
+ />
+))
+TableFooter.displayName = "TableFooter"
+
+const TableRow = React.forwardRef<
+ HTMLTableRowElement,
+ React.HTMLAttributes
+>(({ className, ...props }, ref) => (
+
+))
+TableRow.displayName = "TableRow"
+
+const TableHead = React.forwardRef<
+ HTMLTableCellElement,
+ React.ThHTMLAttributes
+>(({ className, ...props }, ref) => (
+ [role=checkbox]]:translate-y-[2px]",
+ className
+ )}
+ {...props}
+ />
+))
+TableHead.displayName = "TableHead"
+
+const TableCell = React.forwardRef<
+ HTMLTableCellElement,
+ React.TdHTMLAttributes
+>(({ className, ...props }, ref) => (
+ | [role=checkbox]]:translate-y-[2px]",
+ className
+ )}
+ {...props}
+ />
+))
+TableCell.displayName = "TableCell"
+
+const TableCaption = React.forwardRef<
+ HTMLTableCaptionElement,
+ React.HTMLAttributes
+>(({ className, ...props }, ref) => (
+
+))
+TableCaption.displayName = "TableCaption"
+
+export {
+ Table,
+ TableHeader,
+ TableBody,
+ TableFooter,
+ TableHead,
+ TableRow,
+ TableCell,
+ TableCaption,
+}
diff --git a/apps/web/components/ui/textarea.tsx b/apps/web/components/ui/textarea.tsx
new file mode 100644
index 00000000..d1258e47
--- /dev/null
+++ b/apps/web/components/ui/textarea.tsx
@@ -0,0 +1,24 @@
+import * as React from "react"
+
+import { cn } from "@/lib/utils"
+
+export interface TextareaProps
+ extends React.TextareaHTMLAttributes {}
+
+const Textarea = React.forwardRef(
+ ({ className, ...props }, ref) => {
+ return (
+
+ )
+ }
+)
+Textarea.displayName = "Textarea"
+
+export { Textarea }
diff --git a/apps/web/components/ui/toggle-group.tsx b/apps/web/components/ui/toggle-group.tsx
new file mode 100644
index 00000000..4df0e4fe
--- /dev/null
+++ b/apps/web/components/ui/toggle-group.tsx
@@ -0,0 +1,61 @@
+"use client"
+
+import * as React from "react"
+import * as ToggleGroupPrimitive from "@radix-ui/react-toggle-group"
+import { type VariantProps } from "class-variance-authority"
+
+import { cn } from "@/lib/utils"
+import { toggleVariants } from "@components/ui/toggle"
+
+const ToggleGroupContext = React.createContext<
+ VariantProps
+>({
+ size: "default",
+ variant: "default",
+})
+
+const ToggleGroup = React.forwardRef<
+ React.ElementRef,
+ React.ComponentPropsWithoutRef &
+ VariantProps
+>(({ className, variant, size, children, ...props }, ref) => (
+
+
+ {children}
+
+
+))
+
+ToggleGroup.displayName = ToggleGroupPrimitive.Root.displayName
+
+const ToggleGroupItem = React.forwardRef<
+ React.ElementRef,
+ React.ComponentPropsWithoutRef &
+ VariantProps
+>(({ className, children, variant, size, ...props }, ref) => {
+ const context = React.useContext(ToggleGroupContext)
+
+ return (
+
+ {children}
+
+ )
+})
+
+ToggleGroupItem.displayName = ToggleGroupPrimitive.Item.displayName
+
+export { ToggleGroup, ToggleGroupItem }
diff --git a/apps/web/components/ui/toggle.tsx b/apps/web/components/ui/toggle.tsx
new file mode 100644
index 00000000..c50c2733
--- /dev/null
+++ b/apps/web/components/ui/toggle.tsx
@@ -0,0 +1,45 @@
+"use client"
+
+import * as React from "react"
+import * as TogglePrimitive from "@radix-ui/react-toggle"
+import { cva, type VariantProps } from "class-variance-authority"
+
+import { cn } from "@/lib/utils"
+
+const toggleVariants = cva(
+ "inline-flex items-center justify-center rounded-md text-sm font-medium transition-colors hover:bg-muted hover:text-muted-foreground focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:pointer-events-none disabled:opacity-50 data-[state=on]:bg-accent data-[state=on]:text-accent-foreground",
+ {
+ variants: {
+ variant: {
+ default: "bg-transparent",
+ outline:
+ "border border-input bg-transparent shadow-sm hover:bg-accent hover:text-accent-foreground",
+ },
+ size: {
+ default: "h-9 px-3",
+ sm: "h-8 px-2",
+ lg: "h-10 px-3",
+ },
+ },
+ defaultVariants: {
+ variant: "default",
+ size: "default",
+ },
+ }
+)
+
+const Toggle = React.forwardRef<
+ React.ElementRef,
+ React.ComponentPropsWithoutRef &
+ VariantProps
+>(({ className, variant, size, ...props }, ref) => (
+
+))
+
+Toggle.displayName = TogglePrimitive.Root.displayName
+
+export { Toggle, toggleVariants }
diff --git a/apps/web/middleware.ts b/apps/web/middleware.ts
index 32ce38d6..423f6a5d 100644
--- a/apps/web/middleware.ts
+++ b/apps/web/middleware.ts
@@ -22,6 +22,7 @@ export const config = {
*/
'/((?!api|_next|fonts|umami|examples|[\\w-]+\\.\\w+).*)',
'/sitemap.xml',
+ '/payments/stripe/connect/oauth',
],
}
@@ -80,6 +81,32 @@ export default async function middleware(req: NextRequest) {
return NextResponse.rewrite(new URL(`/editor${pathname}`, req.url))
}
+ // Check if the request is for the Stripe callback URL
+ if (req.nextUrl.pathname.startsWith('/payments/stripe/connect/oauth')) {
+ const searchParams = req.nextUrl.searchParams
+ const orgslug = searchParams.get('state')?.split('_')[0] // Assuming state parameter contains orgslug_randomstring
+
+ // Construct the new URL with the required parameters
+ const redirectUrl = new URL('/payments/stripe/connect/oauth', req.url)
+
+ // Preserve all original search parameters
+ searchParams.forEach((value, key) => {
+ redirectUrl.searchParams.append(key, value)
+ })
+
+ // Add orgslug if available
+ if (orgslug) {
+ redirectUrl.searchParams.set('orgslug', orgslug)
+ }
+
+ return NextResponse.rewrite(redirectUrl)
+ }
+
+ // Health Check
+ if (pathname.startsWith('/health')) {
+ return NextResponse.rewrite(new URL(`/api/health`, req.url))
+ }
+
// Auth Redirects
if (pathname == '/redirect_from_auth') {
if (cookie_orgslug) {
diff --git a/apps/web/package.json b/apps/web/package.json
index 50e6da89..f6177871 100644
--- a/apps/web/package.json
+++ b/apps/web/package.json
@@ -13,37 +13,44 @@
"dependencies": {
"@emoji-mart/react": "^1.1.1",
"@hocuspocus/provider": "^2.13.7",
- "@icons-pack/react-simple-icons": "^10.0.0",
+ "@icons-pack/react-simple-icons": "^10.1.0",
"@radix-ui/colors": "^0.1.9",
"@radix-ui/react-aspect-ratio": "^1.1.0",
"@radix-ui/react-dialog": "^1.1.2",
"@radix-ui/react-dropdown-menu": "^2.1.2",
"@radix-ui/react-form": "^0.0.3",
- "@radix-ui/react-icons": "^1.3.0",
+ "@radix-ui/react-icons": "^1.3.1",
+ "@radix-ui/react-label": "^2.1.0",
+ "@radix-ui/react-select": "^2.1.2",
+ "@radix-ui/react-slot": "^1.1.0",
"@radix-ui/react-switch": "^1.1.1",
"@radix-ui/react-tabs": "^1.1.1",
+ "@radix-ui/react-toggle": "^1.1.0",
+ "@radix-ui/react-toggle-group": "^1.1.0",
"@radix-ui/react-tooltip": "^1.1.3",
- "@sentry/nextjs": "^8.34.0",
- "@sentry/utils": "^8.34.0",
+ "@sentry/nextjs": "^8.35.0",
+ "@sentry/utils": "^8.35.0",
"@stitches/react": "^1.2.8",
- "@tiptap/core": "^2.8.0",
- "@tiptap/extension-code-block-lowlight": "^2.8.0",
- "@tiptap/extension-collaboration": "^2.8.0",
- "@tiptap/extension-collaboration-cursor": "^2.8.0",
- "@tiptap/extension-table": "^2.8.0",
- "@tiptap/extension-table-cell": "^2.8.0",
- "@tiptap/extension-table-header": "^2.8.0",
- "@tiptap/extension-table-row": "^2.8.0",
- "@tiptap/extension-youtube": "^2.8.0",
- "@tiptap/html": "^2.8.0",
- "@tiptap/pm": "^2.8.0",
- "@tiptap/react": "^2.8.0",
- "@tiptap/starter-kit": "^2.8.0",
+ "@tanstack/react-table": "^8.20.5",
+ "@tiptap/core": "^2.9.1",
+ "@tiptap/extension-code-block-lowlight": "^2.9.1",
+ "@tiptap/extension-collaboration": "^2.9.1",
+ "@tiptap/extension-collaboration-cursor": "^2.9.1",
+ "@tiptap/extension-table": "^2.9.1",
+ "@tiptap/extension-table-cell": "^2.9.1",
+ "@tiptap/extension-table-header": "^2.9.1",
+ "@tiptap/extension-table-row": "^2.9.1",
+ "@tiptap/extension-youtube": "^2.9.1",
+ "@tiptap/html": "^2.9.1",
+ "@tiptap/pm": "^2.9.1",
+ "@tiptap/react": "^2.9.1",
+ "@tiptap/starter-kit": "^2.9.1",
"@types/dompurify": "^3.0.5",
"@types/randomcolor": "^0.5.9",
"avvvatars-react": "^0.4.2",
"class-variance-authority": "^0.7.0",
"clsx": "^2.1.1",
+ "currency-codes": "^2.2.0",
"dayjs": "^1.11.13",
"dompurify": "^3.1.7",
"formik": "^2.4.6",
@@ -52,9 +59,9 @@
"highlight.js": "^11.10.0",
"katex": "^0.16.11",
"lowlight": "^3.1.0",
- "lucide-react": "^0.424.0",
+ "lucide-react": "^0.453.0",
"next": "14.2.15",
- "next-auth": "^4.24.8",
+ "next-auth": "^4.24.10",
"nextjs-toploader": "^1.6.12",
"prosemirror-state": "^1.4.3",
"randomcolor": "^0.6.2",
@@ -70,7 +77,7 @@
"sharp": "^0.33.5",
"styled-components": "^6.1.13",
"swr": "^2.2.5",
- "tailwind-merge": "^2.5.3",
+ "tailwind-merge": "^2.5.4",
"tailwind-scrollbar": "^3.1.0",
"tailwindcss-animate": "^1.0.7",
"unsplash-js": "^7.0.19",
@@ -79,7 +86,8 @@
"y-indexeddb": "^9.0.12",
"y-prosemirror": "^1.2.12",
"y-webrtc": "^10.3.0",
- "yjs": "^13.6.19"
+ "yjs": "^13.6.20",
+ "yup": "^1.4.0"
},
"devDependencies": {
"@types/node": "20.12.2",
@@ -92,10 +100,10 @@
"@types/uuid": "^9.0.8",
"autoprefixer": "^10.4.20",
"eslint": "^8.57.1",
- "eslint-config-next": "^14.2.15",
+ "eslint-config-next": "^14.2.16",
"eslint-plugin-unused-imports": "^3.2.0",
"postcss": "^8.4.47",
- "tailwindcss": "^3.4.13",
+ "tailwindcss": "^3.4.14",
"typescript": "5.4.4"
}
-}
+}
\ No newline at end of file
diff --git a/apps/web/pnpm-lock.yaml b/apps/web/pnpm-lock.yaml
index b95dfb1d..dae8a39c 100644
--- a/apps/web/pnpm-lock.yaml
+++ b/apps/web/pnpm-lock.yaml
@@ -13,10 +13,10 @@ importers:
version: 1.1.1(emoji-mart@5.6.0)(react@18.3.1)
'@hocuspocus/provider':
specifier: ^2.13.7
- version: 2.13.7(y-protocols@1.0.6(yjs@13.6.19))(yjs@13.6.19)
+ version: 2.13.7(y-protocols@1.0.6(yjs@13.6.20))(yjs@13.6.20)
'@icons-pack/react-simple-icons':
- specifier: ^10.0.0
- version: 10.0.0(react@18.3.1)
+ specifier: ^10.1.0
+ version: 10.1.0(react@18.3.1)
'@radix-ui/colors':
specifier: ^0.1.9
version: 0.1.9
@@ -33,65 +33,83 @@ importers:
specifier: ^0.0.3
version: 0.0.3(@types/react-dom@18.2.23)(@types/react@18.2.74)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
'@radix-ui/react-icons':
- specifier: ^1.3.0
- version: 1.3.0(react@18.3.1)
+ specifier: ^1.3.1
+ version: 1.3.1(react@18.3.1)
+ '@radix-ui/react-label':
+ specifier: ^2.1.0
+ version: 2.1.0(@types/react-dom@18.2.23)(@types/react@18.2.74)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
+ '@radix-ui/react-select':
+ specifier: ^2.1.2
+ version: 2.1.2(@types/react-dom@18.2.23)(@types/react@18.2.74)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
+ '@radix-ui/react-slot':
+ specifier: ^1.1.0
+ version: 1.1.0(@types/react@18.2.74)(react@18.3.1)
'@radix-ui/react-switch':
specifier: ^1.1.1
version: 1.1.1(@types/react-dom@18.2.23)(@types/react@18.2.74)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
'@radix-ui/react-tabs':
specifier: ^1.1.1
version: 1.1.1(@types/react-dom@18.2.23)(@types/react@18.2.74)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
+ '@radix-ui/react-toggle':
+ specifier: ^1.1.0
+ version: 1.1.0(@types/react-dom@18.2.23)(@types/react@18.2.74)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
+ '@radix-ui/react-toggle-group':
+ specifier: ^1.1.0
+ version: 1.1.0(@types/react-dom@18.2.23)(@types/react@18.2.74)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
'@radix-ui/react-tooltip':
specifier: ^1.1.3
version: 1.1.3(@types/react-dom@18.2.23)(@types/react@18.2.74)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
'@sentry/nextjs':
- specifier: ^8.34.0
- version: 8.34.0(@opentelemetry/api@1.9.0)(@opentelemetry/core@1.26.0(@opentelemetry/api@1.9.0))(@opentelemetry/instrumentation@0.53.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@1.26.0(@opentelemetry/api@1.9.0))(next@14.2.15(@babel/core@7.25.8)(@opentelemetry/api@1.9.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react@18.3.1)(webpack@5.94.0)
+ specifier: ^8.35.0
+ version: 8.35.0(@opentelemetry/api@1.9.0)(@opentelemetry/core@1.27.0(@opentelemetry/api@1.9.0))(@opentelemetry/instrumentation@0.53.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@1.27.0(@opentelemetry/api@1.9.0))(next@14.2.15(@babel/core@7.26.0)(@opentelemetry/api@1.9.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react@18.3.1)(webpack@5.94.0)
'@sentry/utils':
- specifier: ^8.34.0
- version: 8.34.0
+ specifier: ^8.35.0
+ version: 8.35.0
'@stitches/react':
specifier: ^1.2.8
version: 1.2.8(react@18.3.1)
+ '@tanstack/react-table':
+ specifier: ^8.20.5
+ version: 8.20.5(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
'@tiptap/core':
- specifier: ^2.8.0
- version: 2.8.0(@tiptap/pm@2.8.0)
+ specifier: ^2.9.1
+ version: 2.9.1(@tiptap/pm@2.9.1)
'@tiptap/extension-code-block-lowlight':
- specifier: ^2.8.0
- version: 2.8.0(@tiptap/core@2.8.0(@tiptap/pm@2.8.0))(@tiptap/extension-code-block@2.8.0(@tiptap/core@2.8.0(@tiptap/pm@2.8.0))(@tiptap/pm@2.8.0))(@tiptap/pm@2.8.0)(highlight.js@11.10.0)(lowlight@3.1.0)
+ specifier: ^2.9.1
+ version: 2.9.1(@tiptap/core@2.9.1(@tiptap/pm@2.9.1))(@tiptap/extension-code-block@2.9.1(@tiptap/core@2.9.1(@tiptap/pm@2.9.1))(@tiptap/pm@2.9.1))(@tiptap/pm@2.9.1)(highlight.js@11.10.0)(lowlight@3.1.0)
'@tiptap/extension-collaboration':
- specifier: ^2.8.0
- version: 2.8.0(@tiptap/core@2.8.0(@tiptap/pm@2.8.0))(@tiptap/pm@2.8.0)(y-prosemirror@1.2.12(prosemirror-model@1.23.0)(prosemirror-state@1.4.3)(prosemirror-view@1.34.3)(y-protocols@1.0.6(yjs@13.6.19))(yjs@13.6.19))
+ specifier: ^2.9.1
+ version: 2.9.1(@tiptap/core@2.9.1(@tiptap/pm@2.9.1))(@tiptap/pm@2.9.1)(y-prosemirror@1.2.12(prosemirror-model@1.23.0)(prosemirror-state@1.4.3)(prosemirror-view@1.35.0)(y-protocols@1.0.6(yjs@13.6.20))(yjs@13.6.20))
'@tiptap/extension-collaboration-cursor':
- specifier: ^2.8.0
- version: 2.8.0(@tiptap/core@2.8.0(@tiptap/pm@2.8.0))(y-prosemirror@1.2.12(prosemirror-model@1.23.0)(prosemirror-state@1.4.3)(prosemirror-view@1.34.3)(y-protocols@1.0.6(yjs@13.6.19))(yjs@13.6.19))
+ specifier: ^2.9.1
+ version: 2.9.1(@tiptap/core@2.9.1(@tiptap/pm@2.9.1))(y-prosemirror@1.2.12(prosemirror-model@1.23.0)(prosemirror-state@1.4.3)(prosemirror-view@1.35.0)(y-protocols@1.0.6(yjs@13.6.20))(yjs@13.6.20))
'@tiptap/extension-table':
- specifier: ^2.8.0
- version: 2.8.0(@tiptap/core@2.8.0(@tiptap/pm@2.8.0))(@tiptap/pm@2.8.0)
+ specifier: ^2.9.1
+ version: 2.9.1(@tiptap/core@2.9.1(@tiptap/pm@2.9.1))(@tiptap/pm@2.9.1)
'@tiptap/extension-table-cell':
- specifier: ^2.8.0
- version: 2.8.0(@tiptap/core@2.8.0(@tiptap/pm@2.8.0))
+ specifier: ^2.9.1
+ version: 2.9.1(@tiptap/core@2.9.1(@tiptap/pm@2.9.1))
'@tiptap/extension-table-header':
- specifier: ^2.8.0
- version: 2.8.0(@tiptap/core@2.8.0(@tiptap/pm@2.8.0))
+ specifier: ^2.9.1
+ version: 2.9.1(@tiptap/core@2.9.1(@tiptap/pm@2.9.1))
'@tiptap/extension-table-row':
- specifier: ^2.8.0
- version: 2.8.0(@tiptap/core@2.8.0(@tiptap/pm@2.8.0))
+ specifier: ^2.9.1
+ version: 2.9.1(@tiptap/core@2.9.1(@tiptap/pm@2.9.1))
'@tiptap/extension-youtube':
- specifier: ^2.8.0
- version: 2.8.0(@tiptap/core@2.8.0(@tiptap/pm@2.8.0))
+ specifier: ^2.9.1
+ version: 2.9.1(@tiptap/core@2.9.1(@tiptap/pm@2.9.1))
'@tiptap/html':
- specifier: ^2.8.0
- version: 2.8.0(@tiptap/core@2.8.0(@tiptap/pm@2.8.0))(@tiptap/pm@2.8.0)
+ specifier: ^2.9.1
+ version: 2.9.1(@tiptap/core@2.9.1(@tiptap/pm@2.9.1))(@tiptap/pm@2.9.1)
'@tiptap/pm':
- specifier: ^2.8.0
- version: 2.8.0
+ specifier: ^2.9.1
+ version: 2.9.1
'@tiptap/react':
- specifier: ^2.8.0
- version: 2.8.0(@tiptap/core@2.8.0(@tiptap/pm@2.8.0))(@tiptap/pm@2.8.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
+ specifier: ^2.9.1
+ version: 2.9.1(@tiptap/core@2.9.1(@tiptap/pm@2.9.1))(@tiptap/pm@2.9.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
'@tiptap/starter-kit':
- specifier: ^2.8.0
- version: 2.8.0(@tiptap/extension-text-style@2.8.0(@tiptap/core@2.8.0(@tiptap/pm@2.8.0)))
+ specifier: ^2.9.1
+ version: 2.9.1
'@types/dompurify':
specifier: ^3.0.5
version: 3.0.5
@@ -107,6 +125,9 @@ importers:
clsx:
specifier: ^2.1.1
version: 2.1.1
+ currency-codes:
+ specifier: ^2.2.0
+ version: 2.2.0
dayjs:
specifier: ^1.11.13
version: 1.11.13
@@ -132,17 +153,17 @@ importers:
specifier: ^3.1.0
version: 3.1.0
lucide-react:
- specifier: ^0.424.0
- version: 0.424.0(react@18.3.1)
+ specifier: ^0.453.0
+ version: 0.453.0(react@18.3.1)
next:
specifier: 14.2.15
- version: 14.2.15(@babel/core@7.25.8)(@opentelemetry/api@1.9.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
+ version: 14.2.15(@babel/core@7.26.0)(@opentelemetry/api@1.9.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
next-auth:
- specifier: ^4.24.8
- version: 4.24.8(next@14.2.15(@babel/core@7.25.8)(@opentelemetry/api@1.9.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
+ specifier: ^4.24.10
+ version: 4.24.10(next@14.2.15(@babel/core@7.26.0)(@opentelemetry/api@1.9.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
nextjs-toploader:
specifier: ^1.6.12
- version: 1.6.12(next@14.2.15(@babel/core@7.25.8)(@opentelemetry/api@1.9.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
+ version: 1.6.12(next@14.2.15(@babel/core@7.26.0)(@opentelemetry/api@1.9.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
prosemirror-state:
specifier: ^1.4.3
version: 1.4.3
@@ -186,14 +207,14 @@ importers:
specifier: ^2.2.5
version: 2.2.5(react@18.3.1)
tailwind-merge:
- specifier: ^2.5.3
- version: 2.5.3
+ specifier: ^2.5.4
+ version: 2.5.4
tailwind-scrollbar:
specifier: ^3.1.0
- version: 3.1.0(tailwindcss@3.4.13)
+ version: 3.1.0(tailwindcss@3.4.14)
tailwindcss-animate:
specifier: ^1.0.7
- version: 1.0.7(tailwindcss@3.4.13)
+ version: 1.0.7(tailwindcss@3.4.14)
unsplash-js:
specifier: ^7.0.19
version: 7.0.19
@@ -205,16 +226,19 @@ importers:
version: 9.0.1
y-indexeddb:
specifier: ^9.0.12
- version: 9.0.12(yjs@13.6.19)
+ version: 9.0.12(yjs@13.6.20)
y-prosemirror:
specifier: ^1.2.12
- version: 1.2.12(prosemirror-model@1.23.0)(prosemirror-state@1.4.3)(prosemirror-view@1.34.3)(y-protocols@1.0.6(yjs@13.6.19))(yjs@13.6.19)
+ version: 1.2.12(prosemirror-model@1.23.0)(prosemirror-state@1.4.3)(prosemirror-view@1.35.0)(y-protocols@1.0.6(yjs@13.6.20))(yjs@13.6.20)
y-webrtc:
specifier: ^10.3.0
- version: 10.3.0(yjs@13.6.19)
+ version: 10.3.0(yjs@13.6.20)
yjs:
- specifier: ^13.6.19
- version: 13.6.19
+ specifier: ^13.6.20
+ version: 13.6.20
+ yup:
+ specifier: ^1.4.0
+ version: 1.4.0
devDependencies:
'@types/node':
specifier: 20.12.2
@@ -247,8 +271,8 @@ importers:
specifier: ^8.57.1
version: 8.57.1
eslint-config-next:
- specifier: ^14.2.15
- version: 14.2.15(eslint@8.57.1)(typescript@5.4.4)
+ specifier: ^14.2.16
+ version: 14.2.16(eslint@8.57.1)(typescript@5.4.4)
eslint-plugin-unused-imports:
specifier: ^3.2.0
version: 3.2.0(eslint@8.57.1)
@@ -256,8 +280,8 @@ importers:
specifier: ^8.4.47
version: 8.4.47
tailwindcss:
- specifier: ^3.4.13
- version: 3.4.13
+ specifier: ^3.4.14
+ version: 3.4.14
typescript:
specifier: 5.4.4
version: 5.4.4
@@ -272,83 +296,75 @@ packages:
resolution: {integrity: sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==}
engines: {node: '>=6.0.0'}
- '@babel/code-frame@7.25.7':
- resolution: {integrity: sha512-0xZJFNE5XMpENsgfHYTw8FbX4kv53mFLn2i3XPoq69LyhYSCBJtitaHx9QnsVTrsogI4Z3+HtEfZ2/GFPOtf5g==}
+ '@babel/code-frame@7.26.2':
+ resolution: {integrity: sha512-RJlIHRueQgwWitWgF8OdFYGZX328Ax5BCemNGlqHfplnRT9ESi8JkFlvaVYbS+UubVY6dpv87Fs2u5M29iNFVQ==}
engines: {node: '>=6.9.0'}
- '@babel/compat-data@7.25.8':
- resolution: {integrity: sha512-ZsysZyXY4Tlx+Q53XdnOFmqwfB9QDTHYxaZYajWRoBLuLEAwI2UIbtxOjWh/cFaa9IKUlcB+DDuoskLuKu56JA==}
+ '@babel/compat-data@7.26.2':
+ resolution: {integrity: sha512-Z0WgzSEa+aUcdiJuCIqgujCshpMWgUpgOxXotrYPSA53hA3qopNaqcJpyr0hVb1FeWdnqFA35/fUtXgBK8srQg==}
engines: {node: '>=6.9.0'}
- '@babel/core@7.25.8':
- resolution: {integrity: sha512-Oixnb+DzmRT30qu9d3tJSQkxuygWm32DFykT4bRoORPa9hZ/L4KhVB/XiRm6KG+roIEM7DBQlmg27kw2HZkdZg==}
+ '@babel/core@7.26.0':
+ resolution: {integrity: sha512-i1SLeK+DzNnQ3LL/CswPCa/E5u4lh1k6IAEphON8F+cXt0t9euTshDru0q7/IqMa1PMPz5RnHuHscF8/ZJsStg==}
engines: {node: '>=6.9.0'}
- '@babel/generator@7.25.7':
- resolution: {integrity: sha512-5Dqpl5fyV9pIAD62yK9P7fcA768uVPUyrQmqpqstHWgMma4feF1x/oFysBCVZLY5wJ2GkMUCdsNDnGZrPoR6rA==}
+ '@babel/generator@7.26.2':
+ resolution: {integrity: sha512-zevQbhbau95nkoxSq3f/DC/SC+EEOUZd3DYqfSkMhY2/wfSeaHV1Ew4vk8e+x8lja31IbyuUa2uQ3JONqKbysw==}
engines: {node: '>=6.9.0'}
- '@babel/helper-compilation-targets@7.25.7':
- resolution: {integrity: sha512-DniTEax0sv6isaw6qSQSfV4gVRNtw2rte8HHM45t9ZR0xILaufBRNkpMifCRiAPyvL4ACD6v0gfCwCmtOQaV4A==}
+ '@babel/helper-compilation-targets@7.25.9':
+ resolution: {integrity: sha512-j9Db8Suy6yV/VHa4qzrj9yZfZxhLWQdVnRlXxmKLYlhWUVB1sB2G5sxuWYXk/whHD9iW76PmNzxZ4UCnTQTVEQ==}
engines: {node: '>=6.9.0'}
- '@babel/helper-module-imports@7.25.7':
- resolution: {integrity: sha512-o0xCgpNmRohmnoWKQ0Ij8IdddjyBFE4T2kagL/x6M3+4zUgc+4qTOUBoNe4XxDskt1HPKO007ZPiMgLDq2s7Kw==}
+ '@babel/helper-module-imports@7.25.9':
+ resolution: {integrity: sha512-tnUA4RsrmflIM6W6RFTLFSXITtl0wKjgpnLgXyowocVPrbYrLUXSBXDgTs8BlbmIzIdlBySRQjINYs2BAkiLtw==}
engines: {node: '>=6.9.0'}
- '@babel/helper-module-transforms@7.25.7':
- resolution: {integrity: sha512-k/6f8dKG3yDz/qCwSM+RKovjMix563SLxQFo0UhRNo239SP6n9u5/eLtKD6EAjwta2JHJ49CsD8pms2HdNiMMQ==}
+ '@babel/helper-module-transforms@7.26.0':
+ resolution: {integrity: sha512-xO+xu6B5K2czEnQye6BHA7DolFFmS3LB7stHZFaOLb1pAwO1HWLS8fXA+eh0A2yIvltPVmx3eNNDBJA2SLHXFw==}
engines: {node: '>=6.9.0'}
peerDependencies:
'@babel/core': ^7.0.0
- '@babel/helper-simple-access@7.25.7':
- resolution: {integrity: sha512-FPGAkJmyoChQeM+ruBGIDyrT2tKfZJO8NcxdC+CWNJi7N8/rZpSxK7yvBJ5O/nF1gfu5KzN7VKG3YVSLFfRSxQ==}
+ '@babel/helper-string-parser@7.25.9':
+ resolution: {integrity: sha512-4A/SCr/2KLd5jrtOMFzaKjVtAei3+2r/NChoBNoZ3EyP/+GlhoaEGoWOZUmFmoITP7zOJyHIMm+DYRd8o3PvHA==}
engines: {node: '>=6.9.0'}
- '@babel/helper-string-parser@7.25.7':
- resolution: {integrity: sha512-CbkjYdsJNHFk8uqpEkpCvRs3YRp9tY6FmFY7wLMSYuGYkrdUi7r2lc4/wqsvlHoMznX3WJ9IP8giGPq68T/Y6g==}
+ '@babel/helper-validator-identifier@7.25.9':
+ resolution: {integrity: sha512-Ed61U6XJc3CVRfkERJWDz4dJwKe7iLmmJsbOGu9wSloNSFttHV0I8g6UAgb7qnK5ly5bGLPd4oXZlxCdANBOWQ==}
engines: {node: '>=6.9.0'}
- '@babel/helper-validator-identifier@7.25.7':
- resolution: {integrity: sha512-AM6TzwYqGChO45oiuPqwL2t20/HdMC1rTPAesnBCgPCSF1x3oN9MVUwQV2iyz4xqWrctwK5RNC8LV22kaQCNYg==}
+ '@babel/helper-validator-option@7.25.9':
+ resolution: {integrity: sha512-e/zv1co8pp55dNdEcCynfj9X7nyUKUXoUEwfXqaZt0omVOmDe9oOTdKStH4GmAw6zxMFs50ZayuMfHDKlO7Tfw==}
engines: {node: '>=6.9.0'}
- '@babel/helper-validator-option@7.25.7':
- resolution: {integrity: sha512-ytbPLsm+GjArDYXJ8Ydr1c/KJuutjF2besPNbIZnZ6MKUxi/uTA22t2ymmA4WFjZFpjiAMO0xuuJPqK2nvDVfQ==}
+ '@babel/helpers@7.26.0':
+ resolution: {integrity: sha512-tbhNuIxNcVb21pInl3ZSjksLCvgdZy9KwJ8brv993QtIVKJBBkYXz4q4ZbAv31GdnC+R90np23L5FbEBlthAEw==}
engines: {node: '>=6.9.0'}
- '@babel/helpers@7.25.7':
- resolution: {integrity: sha512-Sv6pASx7Esm38KQpF/U/OXLwPPrdGHNKoeblRxgZRLXnAtnkEe4ptJPDtAZM7fBLadbc1Q07kQpSiGQ0Jg6tRA==}
- engines: {node: '>=6.9.0'}
-
- '@babel/highlight@7.25.7':
- resolution: {integrity: sha512-iYyACpW3iW8Fw+ZybQK+drQre+ns/tKpXbNESfrhNnPLIklLbXr7MYJ6gPEd0iETGLOK+SxMjVvKb/ffmk+FEw==}
- engines: {node: '>=6.9.0'}
-
- '@babel/parser@7.25.8':
- resolution: {integrity: sha512-HcttkxzdPucv3nNFmfOOMfFf64KgdJVqm1KaCm25dPGMLElo9nsLvXeJECQg8UzPuBGLyTSA0ZzqCtDSzKTEoQ==}
+ '@babel/parser@7.26.2':
+ resolution: {integrity: sha512-DWMCZH9WA4Maitz2q21SRKHo9QXZxkDsbNZoVD62gusNtNBBqDg9i7uOhASfTfIGNzW+O+r7+jAlM8dwphcJKQ==}
engines: {node: '>=6.0.0'}
hasBin: true
- '@babel/runtime@7.25.7':
- resolution: {integrity: sha512-FjoyLe754PMiYsFaN5C94ttGiOmBNYTf6pLr4xXHAT5uctHb092PBszndLDR5XA/jghQvn4n7JMHl7dmTgbm9w==}
+ '@babel/runtime@7.26.0':
+ resolution: {integrity: sha512-FDSOghenHTiToteC/QRlv2q3DhPZ/oOXTBoirfWNx1Cx3TMVcGWQtMMmQcSvb/JjpNeGzx8Pq/b4fKEJuWm1sw==}
engines: {node: '>=6.9.0'}
- '@babel/template@7.25.7':
- resolution: {integrity: sha512-wRwtAgI3bAS+JGU2upWNL9lSlDcRCqD05BZ1n3X2ONLH1WilFP6O1otQjeMK/1g0pvYcXC7b/qVUB1keofjtZA==}
+ '@babel/template@7.25.9':
+ resolution: {integrity: sha512-9DGttpmPvIxBb/2uwpVo3dqJ+O6RooAFOS+lB+xDqoE2PVCE8nfoHMdZLpfCQRLwvohzXISPZcgxt80xLfsuwg==}
engines: {node: '>=6.9.0'}
- '@babel/traverse@7.25.7':
- resolution: {integrity: sha512-jatJPT1Zjqvh/1FyJs6qAHL+Dzb7sTb+xr7Q+gM1b+1oBsMsQQ4FkVKb6dFlJvLlVssqkRzV05Jzervt9yhnzg==}
+ '@babel/traverse@7.25.9':
+ resolution: {integrity: sha512-ZCuvfwOwlz/bawvAuvcj8rrithP2/N55Tzz342AkTvq4qaWbGfmCk/tKhNaV2cthijKrPAA8SRJV5WWe7IBMJw==}
engines: {node: '>=6.9.0'}
- '@babel/types@7.25.8':
- resolution: {integrity: sha512-JWtuCu8VQsMladxVz/P4HzHUGCAwpuqacmowgXFs5XjxIgKuNjnLokQzuVjlTvIzODaDmpjT3oxcC48vyk9EWg==}
+ '@babel/types@7.26.0':
+ resolution: {integrity: sha512-Z/yiTPj+lDVnF7lWeKCIJzaIkI0vYO87dMpZ4bg4TDrFe4XXLFWL1TbXU27gBP3QccxV9mZICCrnjnYlJjXHOA==}
engines: {node: '>=6.9.0'}
- '@emnapi/runtime@1.3.0':
- resolution: {integrity: sha512-XMBySMuNZs3DM96xcJmLW4EfGnf+uGmFNjzpehMjuX5PLB5j87ar2Zc4e3PVeZ3I5g3tYtAqskB28manlF69Zw==}
+ '@emnapi/runtime@1.3.1':
+ resolution: {integrity: sha512-kEBmG8KyqtxJZv+ygbEim+KCGtIq1fC22Ms3S4ziXmYKm8uyoLX0MHONVKwp+9opg390VaKRNt4a7A9NwmpNhw==}
'@emoji-mart/react@1.1.1':
resolution: {integrity: sha512-NMlFNeWgv1//uPsvLxvGQoIerPuVdXwK/EUek8OOkJ6wVOWPUizRBJU0hDqWZCOROVpfBgCemaC3m6jDOXi03g==}
@@ -371,14 +387,14 @@ packages:
'@emotion/unitless@0.8.1':
resolution: {integrity: sha512-KOEGMu6dmJZtpadb476IsZBclKvILjopjUii3V+7MnXIQCYh8W3NgNcgwo21n9LXZX6EDIKvqfjYxXebDwxKmQ==}
- '@eslint-community/eslint-utils@4.4.0':
- resolution: {integrity: sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==}
+ '@eslint-community/eslint-utils@4.4.1':
+ resolution: {integrity: sha512-s3O3waFUrMV8P/XaF/+ZTp1X9XBZW1a4B97ZnjQF2KYWaFD2A8KyFBsrsfSjEmjn3RGWAIuvlneuZm3CUK3jbA==}
engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
peerDependencies:
eslint: ^6.0.0 || ^7.0.0 || >=8.0.0
- '@eslint-community/regexpp@4.11.1':
- resolution: {integrity: sha512-m4DVN9ZqskZoLU5GlWZadwDnYo3vAEydiUayB9widCl9ffWx2IvPnp6n3on5rJmziJSw9Bv+Z3ChDVdMwXCY8Q==}
+ '@eslint-community/regexpp@4.12.1':
+ resolution: {integrity: sha512-CCZCDJuduB9OUkFkY2IgppNZMi2lBQgD2qzwXkEia16cge2pijY/aXi96CJMquDMn3nJdlPV1A5KrJEXwfLNzQ==}
engines: {node: ^12.0.0 || ^14.0.0 || >=16.0.0}
'@eslint/eslintrc@2.1.4':
@@ -392,8 +408,8 @@ packages:
'@floating-ui/core@1.6.8':
resolution: {integrity: sha512-7XJ9cPU+yI2QeLS+FCSlqNFZJq8arvswefkZrYI1yQBbftw6FyrZOxYSh+9S7z7TpeWlRt9zJ5IhM1WIL334jA==}
- '@floating-ui/dom@1.6.11':
- resolution: {integrity: sha512-qkMCxSR24v2vGkhYDo/UzxfJN3D4syqSjyuTFz6C7XcpU1pASPRieNI0Kj5VP3/503mOfYiGY891ugBX1GlABQ==}
+ '@floating-ui/dom@1.6.12':
+ resolution: {integrity: sha512-NP83c0HjokcGVEMeoStg317VD9W7eDlGK7457dMBANbKA6GJZdc7rjujdgqzTaz93jkGgc5P/jeWbaCHnMNc+w==}
'@floating-ui/react-dom@2.1.2':
resolution: {integrity: sha512-06okr5cgPzMNBy+Ycse2A6udMi4bqwW/zgBF/rwjcNqWkyr82Mcg8b0vjX8OJpZFy/FKjJmw6wV7t44kK6kW7A==}
@@ -426,8 +442,8 @@ packages:
resolution: {integrity: sha512-93zYdMES/c1D69yZiKDBj0V24vqNzB/koF26KPaagAfd3P/4gUlh3Dys5ogAK+Exi9QyzlD8x/08Zt7wIKcDcA==}
deprecated: Use @eslint/object-schema instead
- '@icons-pack/react-simple-icons@10.0.0':
- resolution: {integrity: sha512-oU0PVDx9sbNQjRxJN555dsHbRApYN+aBq/O9+wo3JgNkEfvBMgAEtsSGtXWWXQsLAxJcYiFOCzBWege/Xj/JFQ==}
+ '@icons-pack/react-simple-icons@10.1.0':
+ resolution: {integrity: sha512-sZ2oDkYaVAci7GuNL8okERJn4Ej0INbeCwtIDVuwWfO5zILW7j5frvKQbozTB+fLtZqEwAP9KkNp7oR8WeHaIg==}
peerDependencies:
react: ^16.13 || ^17 || ^18
@@ -567,8 +583,8 @@ packages:
'@next/env@14.2.15':
resolution: {integrity: sha512-S1qaj25Wru2dUpcIZMjxeMVSwkt8BK4dmWHHiBuRstcIyOsMapqT4A4jSB6onvqeygkSSmOkyny9VVx8JIGamQ==}
- '@next/eslint-plugin-next@14.2.15':
- resolution: {integrity: sha512-pKU0iqKRBlFB/ocOI1Ip2CkKePZpYpnw5bEItEkuZ/Nr9FQP1+p7VDWr4VfOdff4i9bFmrOaeaU1bFEyAcxiMQ==}
+ '@next/eslint-plugin-next@14.2.16':
+ resolution: {integrity: sha512-noORwKUMkKc96MWjTOwrsUCjky0oFegHbeJ1yEnQBGbMHAaTEIgLZIIfsYF0x3a06PiS+2TXppfifR+O6VWslg==}
'@next/swc-darwin-arm64@14.2.15':
resolution: {integrity: sha512-Rvh7KU9hOUBnZ9TJ28n2Oa7dD9cvDBKua9IKx7cfQQ0GoYUwg9ig31O2oMwH3wm+pE3IkAQ67ZobPfEgurPZIA==}
@@ -652,8 +668,8 @@ packages:
resolution: {integrity: sha512-3giAOQvZiH5F9bMlMiv8+GSPMeqg0dbaeo58/0SlA9sxSqZhnUtxzX9/2FzyhS9sWQf5S0GJE0AKBrFqjpeYcg==}
engines: {node: '>=8.0.0'}
- '@opentelemetry/context-async-hooks@1.26.0':
- resolution: {integrity: sha512-HedpXXYzzbaoutw6DFLWLDket2FwLkLpil4hGCZ1xYEIMTcivdfwEOISgdbLEWyG3HW52gTq2V9mOVJrONgiwg==}
+ '@opentelemetry/context-async-hooks@1.27.0':
+ resolution: {integrity: sha512-CdZ3qmHCwNhFAzjTgHqrDQ44Qxcpz43cVxZRhOs+Ns/79ug+Mr84Bkb626bkJLkA3+BLimA5YAEVRlJC6pFb7g==}
engines: {node: '>=14'}
peerDependencies:
'@opentelemetry/api': '>=1.0.0 <1.10.0'
@@ -664,6 +680,12 @@ packages:
peerDependencies:
'@opentelemetry/api': '>=1.0.0 <1.10.0'
+ '@opentelemetry/core@1.27.0':
+ resolution: {integrity: sha512-yQPKnK5e+76XuiqUH/gKyS8wv/7qITd5ln56QkBTf3uggr0VkXOXfcaAuG330UfdYu83wsyoBwqwxigpIG+Jkg==}
+ engines: {node: '>=14'}
+ peerDependencies:
+ '@opentelemetry/api': '>=1.0.0 <1.10.0'
+
'@opentelemetry/instrumentation-amqplib@0.42.0':
resolution: {integrity: sha512-fiuU6OKsqHJiydHWgTRQ7MnIrJ2lEqsdgFtNIH4LbAUJl/5XmrIeoDzDnox+hfkgWK65jsleFuQDtYb5hW1koQ==}
engines: {node: '>=14'}
@@ -682,14 +704,14 @@ packages:
peerDependencies:
'@opentelemetry/api': ^1.3.0
- '@opentelemetry/instrumentation-express@0.42.0':
- resolution: {integrity: sha512-YNcy7ZfGnLsVEqGXQPT+S0G1AE46N21ORY7i7yUQyfhGAL4RBjnZUqefMI0NwqIl6nGbr1IpF0rZGoN8Q7x12Q==}
+ '@opentelemetry/instrumentation-express@0.43.0':
+ resolution: {integrity: sha512-bxTIlzn9qPXJgrhz8/Do5Q3jIlqfpoJrSUtVGqH+90eM1v2PkPHc+SdE+zSqe4q9Y1UQJosmZ4N4bm7Zj/++MA==}
engines: {node: '>=14'}
peerDependencies:
'@opentelemetry/api': ^1.3.0
- '@opentelemetry/instrumentation-fastify@0.39.0':
- resolution: {integrity: sha512-SS9uSlKcsWZabhBp2szErkeuuBDgxOUlllwkS92dVaWRnMmwysPhcEgHKB8rUe3BHg/GnZC1eo1hbTZv4YhfoA==}
+ '@opentelemetry/instrumentation-fastify@0.40.0':
+ resolution: {integrity: sha512-74qj4nG3zPtU7g2x4sm2T4R3/pBMyrYstTsqSZwdlhQk1SD4l8OSY9sPRX1qkhfxOuW3U4KZQAV/Cymb3fB6hg==}
engines: {node: '>=14'}
peerDependencies:
'@opentelemetry/api': ^1.3.0
@@ -812,20 +834,20 @@ packages:
resolution: {integrity: sha512-faYX1N0gpLhej/6nyp6bgRjzAKXn5GOEMYY7YhciSfCoITAktLUtQ36d24QEWNA1/WA1y6qQunCe0OhHRkVl9g==}
engines: {node: '>=14'}
- '@opentelemetry/resources@1.26.0':
- resolution: {integrity: sha512-CPNYchBE7MBecCSVy0HKpUISEeJOniWqcHaAHpmasZ3j9o6V3AyBzhRc90jdmemq0HOxDr6ylhUbDhBqqPpeNw==}
+ '@opentelemetry/resources@1.27.0':
+ resolution: {integrity: sha512-jOwt2VJ/lUD5BLc+PMNymDrUCpm5PKi1E9oSVYAvz01U/VdndGmrtV3DU1pG4AwlYhJRHbHfOUIlpBeXCPw6QQ==}
engines: {node: '>=14'}
peerDependencies:
'@opentelemetry/api': '>=1.0.0 <1.10.0'
- '@opentelemetry/sdk-metrics@1.26.0':
- resolution: {integrity: sha512-0SvDXmou/JjzSDOjUmetAAvcKQW6ZrvosU0rkbDGpXvvZN+pQF6JbK/Kd4hNdK4q/22yeruqvukXEJyySTzyTQ==}
+ '@opentelemetry/sdk-metrics@1.27.0':
+ resolution: {integrity: sha512-JzWgzlutoXCydhHWIbLg+r76m+m3ncqvkCcsswXAQ4gqKS+LOHKhq+t6fx1zNytvLuaOUBur7EvWxECc4jPQKg==}
engines: {node: '>=14'}
peerDependencies:
'@opentelemetry/api': '>=1.3.0 <1.10.0'
- '@opentelemetry/sdk-trace-base@1.26.0':
- resolution: {integrity: sha512-olWQldtvbK4v22ymrKLbIcBi9L2SpMO84sCPY54IVsJhP9fRsxJT194C/AVaAuJzLE30EdhhM1VmvVYR7az+cw==}
+ '@opentelemetry/sdk-trace-base@1.27.0':
+ resolution: {integrity: sha512-btz6XTQzwsyJjombpeqCX6LhiMQYpzt2pIYNPnw0IPO/3AhT6yjnf8Mnv3ZC2A4eRYOjqrg+bfaXg9XHDRJDWQ==}
engines: {node: '>=14'}
peerDependencies:
'@opentelemetry/api': '>=1.0.0 <1.10.0'
@@ -856,6 +878,9 @@ packages:
'@radix-ui/colors@0.1.9':
resolution: {integrity: sha512-Vxq944ErPJsdVepjEUhOLO9ApUVOocA63knc+V2TkJ09D/AVOjiMIgkca/7VoYgODcla0qbSIBjje0SMfZMbAw==}
+ '@radix-ui/number@1.1.0':
+ resolution: {integrity: sha512-V3gRzhVNU1ldS5XhAPTom1fOIo4ccrjjJgmE+LI2h/WaFpHmx0MQApT+KZHnx8abG6Avtfcz4WoEciMnpFT3HQ==}
+
'@radix-ui/primitive@1.0.1':
resolution: {integrity: sha512-yQ8oGX2GVsEYMWGxcovu1uGWPCxV5BFfeeYxqPmuAzUyLT9qmaMXSAhXpb0WrspIeqYzdJpkh2vHModJPgRIaw==}
@@ -1029,10 +1054,10 @@ packages:
'@types/react-dom':
optional: true
- '@radix-ui/react-icons@1.3.0':
- resolution: {integrity: sha512-jQxj/0LKgp+j9BiTXz3O3sgs26RNet2iLWmsPyRz2SIcR4q/4SbazXfnYwbAr+vLYKSfc7qxzyGQA1HLlYiuNw==}
+ '@radix-ui/react-icons@1.3.1':
+ resolution: {integrity: sha512-QvYompk0X+8Yjlo/Fv4McrzxohDdM5GgLHyQcPpcsPvlOSXCGFjdbuyGL5dzRbg0GpknAjQJJZzdiRK7iWVuFQ==}
peerDependencies:
- react: ^16.x || ^17.x || ^18.x
+ react: ^16.x || ^17.x || ^18.x || ^19.x
'@radix-ui/react-id@1.0.1':
resolution: {integrity: sha512-tI7sT/kqYp8p96yGWY1OAnLHrqDgzHefRBKQ2YAkBS5ja7QLcZ9Z/uY7bEjPUatf8RomoXM8/1sMj1IJaE5UzQ==}
@@ -1065,6 +1090,19 @@ packages:
'@types/react-dom':
optional: true
+ '@radix-ui/react-label@2.1.0':
+ resolution: {integrity: sha512-peLblDlFw/ngk3UWq0VnYaOLy6agTZZ+MUO/WhVfm14vJGML+xH4FAl2XQGLqdefjNb7ApRg6Yn7U42ZhmYXdw==}
+ peerDependencies:
+ '@types/react': '*'
+ '@types/react-dom': '*'
+ react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
+ react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
+ peerDependenciesMeta:
+ '@types/react':
+ optional: true
+ '@types/react-dom':
+ optional: true
+
'@radix-ui/react-menu@2.1.2':
resolution: {integrity: sha512-lZ0R4qR2Al6fZ4yCCZzu/ReTFrylHFxIqy7OezIpWF4bL0o9biKo0pFIvkaew3TyZ9Fy5gYVrR5zCGZBVbO1zg==}
peerDependencies:
@@ -1156,6 +1194,19 @@ packages:
'@types/react-dom':
optional: true
+ '@radix-ui/react-select@2.1.2':
+ resolution: {integrity: sha512-rZJtWmorC7dFRi0owDmoijm6nSJH1tVw64QGiNIZ9PNLyBDtG+iAq+XGsya052At4BfarzY/Dhv9wrrUr6IMZA==}
+ peerDependencies:
+ '@types/react': '*'
+ '@types/react-dom': '*'
+ react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
+ react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
+ peerDependenciesMeta:
+ '@types/react':
+ optional: true
+ '@types/react-dom':
+ optional: true
+
'@radix-ui/react-slot@1.0.2':
resolution: {integrity: sha512-YeTpuq4deV+6DusvVUW4ivBgnkHwECUu0BiN43L5UCDFgdhsRUWAghhTF5MbvNTPzmiFOx90asDSUjWuCNapwg==}
peerDependencies:
@@ -1200,6 +1251,32 @@ packages:
'@types/react-dom':
optional: true
+ '@radix-ui/react-toggle-group@1.1.0':
+ resolution: {integrity: sha512-PpTJV68dZU2oqqgq75Uzto5o/XfOVgkrJ9rulVmfTKxWp3HfUjHE6CP/WLRR4AzPX9HWxw7vFow2me85Yu+Naw==}
+ peerDependencies:
+ '@types/react': '*'
+ '@types/react-dom': '*'
+ react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
+ react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
+ peerDependenciesMeta:
+ '@types/react':
+ optional: true
+ '@types/react-dom':
+ optional: true
+
+ '@radix-ui/react-toggle@1.1.0':
+ resolution: {integrity: sha512-gwoxaKZ0oJ4vIgzsfESBuSgJNdc0rv12VhHgcqN0TEJmmZixXG/2XpsLK8kzNWYcnaoRIEEQc0bEi3dIvdUpjw==}
+ peerDependencies:
+ '@types/react': '*'
+ '@types/react-dom': '*'
+ react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
+ react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
+ peerDependenciesMeta:
+ '@types/react':
+ optional: true
+ '@types/react-dom':
+ optional: true
+
'@radix-ui/react-tooltip@1.1.3':
resolution: {integrity: sha512-Z4w1FIS0BqVFI2c1jZvb/uDVJijJjJ2ZMuPV81oVgTZ7g3BZxobplnMVvXtFWgtozdvYJ+MFWtwkM5S2HnAong==}
peerDependencies:
@@ -1313,8 +1390,8 @@ packages:
rollup:
optional: true
- '@rollup/pluginutils@5.1.2':
- resolution: {integrity: sha512-/FIdS3PyZ39bjZlwqFnWqCOVnW7o963LtKMwQOD0NhQqw22gSr2YY1afu3FxRip4ZCZNsD5jq6Aaz6QV3D/Njw==}
+ '@rollup/pluginutils@5.1.3':
+ resolution: {integrity: sha512-Pnsb6f32CD2W3uCaLZIzDmeFyQ2b8UWMFI7xtwUezpcGBDVDW6y9XgAWIlARiGAo6eNF5FK5aQTr0LFyNyqq5A==}
engines: {node: '>=14.0.0'}
peerDependencies:
rollup: ^1.20.0||^2.0.0||^3.0.0||^4.0.0
@@ -1328,86 +1405,86 @@ packages:
'@rushstack/eslint-patch@1.10.4':
resolution: {integrity: sha512-WJgX9nzTqknM393q1QJDJmoW28kUfEnybeTfVNcNAPnIx210RXm2DiXiHzfNPJNIUUb1tJnz/l4QGtJ30PgWmA==}
- '@sentry-internal/browser-utils@8.34.0':
- resolution: {integrity: sha512-4AcYOzPzD1tL5eSRQ/GpKv5enquZf4dMVUez99/Bh3va8qiJrNP55AcM7UzZ7WZLTqKygIYruJTU5Zu2SpEAPQ==}
+ '@sentry-internal/browser-utils@8.35.0':
+ resolution: {integrity: sha512-uj9nwERm7HIS13f/Q52hF/NUS5Al8Ma6jkgpfYGeppYvU0uSjPkwMogtqoJQNbOoZg973tV8qUScbcWY616wNA==}
engines: {node: '>=14.18'}
- '@sentry-internal/feedback@8.34.0':
- resolution: {integrity: sha512-aYSM2KPUs0FLPxxbJCFSwCYG70VMzlT04xepD1Y/tTlPPOja/02tSv2tyOdZbv8Uw7xslZs3/8Lhj74oYcTBxw==}
+ '@sentry-internal/feedback@8.35.0':
+ resolution: {integrity: sha512-7bjSaUhL0bDArozre6EiIhhdWdT/1AWNWBC1Wc5w1IxEi5xF7nvF/FfvjQYrONQzZAI3HRxc45J2qhLUzHBmoQ==}
engines: {node: '>=14.18'}
- '@sentry-internal/replay-canvas@8.34.0':
- resolution: {integrity: sha512-x8KhZcCDpbKHqFOykYXiamX6x0LRxv6N1OJHoH+XCrMtiDBZr4Yo30d/MaS6rjmKGMtSRij30v+Uq+YWIgxUrg==}
+ '@sentry-internal/replay-canvas@8.35.0':
+ resolution: {integrity: sha512-TUrH6Piv19kvHIiRyIuapLdnuwxk/Un/l1WDCQfq7mK9p1Pac0FkQ7Uufjp6zY3lyhDDZQ8qvCS4ioCMibCwQg==}
engines: {node: '>=14.18'}
- '@sentry-internal/replay@8.34.0':
- resolution: {integrity: sha512-EoMh9NYljNewZK1quY23YILgtNdGgrkzJ9TPsj6jXUG0LZ0Q7N7eFWd0xOEDBvFxrmI3cSXF1i4d1sBb+eyKRw==}
+ '@sentry-internal/replay@8.35.0':
+ resolution: {integrity: sha512-3wkW03vXYMyWtTLxl9yrtkV+qxbnKFgfASdoGWhXzfLjycgT6o4/04eb3Gn71q9aXqRwH17ISVQbVswnRqMcmA==}
engines: {node: '>=14.18'}
'@sentry/babel-plugin-component-annotate@2.22.3':
resolution: {integrity: sha512-OlHA+i+vnQHRIdry4glpiS/xTOtgjmpXOt6IBOUqynx5Jd/iK1+fj+t8CckqOx9wRacO/hru2wfW/jFq0iViLg==}
engines: {node: '>= 14'}
- '@sentry/browser@8.34.0':
- resolution: {integrity: sha512-3HHG2NXxzHq1lVmDy2uRjYjGNf9NsJsTPlOC70vbQdOb+S49EdH/XMPy+J3ruIoyv6Cu0LwvA6bMOM6rHZOgNQ==}
+ '@sentry/browser@8.35.0':
+ resolution: {integrity: sha512-WHfI+NoZzpCsmIvtr6ChOe7yWPLQyMchPnVhY3Z4UeC70bkYNdKcoj/4XZbX3m0D8+71JAsm0mJ9s9OC3Ue6MQ==}
engines: {node: '>=14.18'}
'@sentry/bundler-plugin-core@2.22.3':
resolution: {integrity: sha512-DeoUl0WffcqZZRl5Wy9aHvX4WfZbbWt0QbJ7NJrcEViq+dRAI2FQTYECFLwdZi5Gtb3oyqZICO+P7k8wDnzsjQ==}
engines: {node: '>= 14'}
- '@sentry/cli-darwin@2.37.0':
- resolution: {integrity: sha512-CsusyMvO0eCPSN7H+sKHXS1pf637PWbS4rZak/7giz/z31/6qiXmeMlcL3f9lLZKtFPJmXVFO9uprn1wbBVF8A==}
+ '@sentry/cli-darwin@2.38.1':
+ resolution: {integrity: sha512-IHuxm072aSTAvwuHtLg065cF00Pxm2wprnrRr2lkyWp8nLOoO7DmumWZ4pjHvhB8yZXsAbM/PSxLRBoDIRDPzQ==}
engines: {node: '>=10'}
os: [darwin]
- '@sentry/cli-linux-arm64@2.37.0':
- resolution: {integrity: sha512-2vzUWHLZ3Ct5gpcIlfd/2Qsha+y9M8LXvbZE26VxzYrIkRoLAWcnClBv8m4XsHLMURYvz3J9QSZHMZHSO7kAzw==}
+ '@sentry/cli-linux-arm64@2.38.1':
+ resolution: {integrity: sha512-3bj5DS4wDusL0YHwG5qeI+O19kz4N4KDDmnWqIew56MmSSAEM5B0qKk5Hivu1vRU5vPKFwVn8BVjLnKXu9idjg==}
engines: {node: '>=10'}
cpu: [arm64]
os: [linux, freebsd]
- '@sentry/cli-linux-arm@2.37.0':
- resolution: {integrity: sha512-Dz0qH4Yt+gGUgoVsqVt72oDj4VQynRF1QB1/Sr8g76Vbi+WxWZmUh0iFwivYVwWxdQGu/OQrE0tx946HToCRyA==}
+ '@sentry/cli-linux-arm@2.38.1':
+ resolution: {integrity: sha512-xyf4f56O4/eeirol8t1tTQw0cwF34b3v69zn6wHtKfx2lW5IEBGO+agVNdOdosnCx6j3UadgdRXUJlSyM9kx/w==}
engines: {node: '>=10'}
cpu: [arm]
os: [linux, freebsd]
- '@sentry/cli-linux-i686@2.37.0':
- resolution: {integrity: sha512-MHRLGs4t/CQE1pG+mZBQixyWL6xDZfNalCjO8GMcTTbZFm44S3XRHfYJZNVCgdtnUP7b6OHGcu1v3SWE10LcwQ==}
+ '@sentry/cli-linux-i686@2.38.1':
+ resolution: {integrity: sha512-VygJO2oTc6GfiqqmPYNpO2bW1hzszuNyn37SSmeRuuhq1/kRwD+ZQj4OmXYEASjSLg+8mDPoWOurPjHEPKNtNw==}
engines: {node: '>=10'}
cpu: [x86, ia32]
os: [linux, freebsd]
- '@sentry/cli-linux-x64@2.37.0':
- resolution: {integrity: sha512-k76ClefKZaDNJZU/H3mGeR8uAzAGPzDRG/A7grzKfBeyhP3JW09L7Nz9IQcSjCK+xr399qLhM2HFCaPWQ6dlMw==}
+ '@sentry/cli-linux-x64@2.38.1':
+ resolution: {integrity: sha512-9SaPJK5yAGR7qGsDubTT9O7VpNQG9KIolCOov4xJU7scbmjGaFyYBm9c7ZIqbq6B+56YchPbtD0RewZC6CiF2w==}
engines: {node: '>=10'}
cpu: [x64]
os: [linux, freebsd]
- '@sentry/cli-win32-i686@2.37.0':
- resolution: {integrity: sha512-FFyi5RNYQQkEg4GkP2f3BJcgQn0F4fjFDMiWkjCkftNPXQG+HFUEtrGsWr6mnHPdFouwbYg3tEPUWNxAoypvTw==}
+ '@sentry/cli-win32-i686@2.38.1':
+ resolution: {integrity: sha512-BVUM5y+ZDBK/LqyVvt0C7oolmg8aq7PI/u04/Pp6FLRExySqwyQim0vNyL2FRjIeX1yhbk7x4Z79UjEKqJBltA==}
engines: {node: '>=10'}
cpu: [x86, ia32]
os: [win32]
- '@sentry/cli-win32-x64@2.37.0':
- resolution: {integrity: sha512-nSMj4OcfQmyL+Tu/jWCJwhKCXFsCZW1MUk6wjjQlRt9SDLfgeapaMlK1ZvT1eZv5ZH6bj3qJfefwj4U8160uOA==}
+ '@sentry/cli-win32-x64@2.38.1':
+ resolution: {integrity: sha512-+HgsdM3LFSzUNlDpicPRdTKfr5u+nJ2C5p4aDYPb2G+qoYW+66FI4NxgWSyzJsj3nVQ8lW5/6AoMP6U5z/e/0A==}
engines: {node: '>=10'}
cpu: [x64]
os: [win32]
- '@sentry/cli@2.37.0':
- resolution: {integrity: sha512-fM3V4gZRJR/s8lafc3O07hhOYRnvkySdPkvL/0e0XW0r+xRwqIAgQ5ECbsZO16A5weUiXVSf03ztDL1FcmbJCQ==}
+ '@sentry/cli@2.38.1':
+ resolution: {integrity: sha512-XFO04nP7cn0tboMQ4ALR81QRF/6xoWAFzNld7Io6jHbaFzihqewjxAqy7pSvVPaieepUjqe7m/Ippt00kKOACg==}
engines: {node: '>= 10'}
hasBin: true
- '@sentry/core@8.34.0':
- resolution: {integrity: sha512-adrXCTK/zsg5pJ67lgtZqdqHvyx6etMjQW3P82NgWdj83c8fb+zH+K79Z47pD4zQjX0ou2Ws5nwwi4wJbz4bfA==}
+ '@sentry/core@8.35.0':
+ resolution: {integrity: sha512-Ci0Nmtw5ETWLqQJGY4dyF+iWh7PWKy6k303fCEoEmqj2czDrKJCp7yHBNV0XYbo00prj2ZTbCr6I7albYiyONA==}
engines: {node: '>=14.18'}
- '@sentry/nextjs@8.34.0':
- resolution: {integrity: sha512-REHE3E21Mnm92B3BfJz3GTMsaZM8vaDJAe7RlAMDltESRECv+ELJ5qVRLgAp8Bd6w4mG8IRNINmK2UwHrAIi9g==}
+ '@sentry/nextjs@8.35.0':
+ resolution: {integrity: sha512-7V6Yd0llWvarebVhtK2UyIqkfw/BzKn/hQxJAob/FQ6V9wKFjF5W0EFtE2n/T0RCetL2JPF8iHu3/b4/TVREmg==}
engines: {node: '>=14.18'}
peerDependencies:
next: ^13.2.0 || ^14.0 || ^15.0.0-rc.0
@@ -1416,12 +1493,12 @@ packages:
webpack:
optional: true
- '@sentry/node@8.34.0':
- resolution: {integrity: sha512-Q7BPp7Y8yCcwD620xoziWSOuPi/PCIdttkczvB0BGzBRYh2s702h+qNusRijRpVNZmzmYOo9m1x7Y1O/b8/v2A==}
+ '@sentry/node@8.35.0':
+ resolution: {integrity: sha512-B0FLOcZEfYe3CJ2t0l1N0HJcHXcIrLlGENQ2kf5HqR2zcOcOzRxyITJTSV5brCnmzVNgkz9PG8VWo3w0HXZQpA==}
engines: {node: '>=14.18'}
- '@sentry/opentelemetry@8.34.0':
- resolution: {integrity: sha512-WS91L+HVKGVIzOgt0szGp+24iKOs86BZsAHGt0HWnMR4kqWP6Ak+TLvqWDCxnuzniZMxdewDGA8p5hrBAPsmsA==}
+ '@sentry/opentelemetry@8.35.0':
+ resolution: {integrity: sha512-2mWMpEiIFop/omia9BqTJa+0Khe+tSsiZSUrxbnSpxM0zgw8DFIzJMHbiqw/I7Qaluz9pnO2HZXqgUTwNPsU8A==}
engines: {node: '>=14.18'}
peerDependencies:
'@opentelemetry/api': ^1.9.0
@@ -1430,22 +1507,22 @@ packages:
'@opentelemetry/sdk-trace-base': ^1.26.0
'@opentelemetry/semantic-conventions': ^1.27.0
- '@sentry/react@8.34.0':
- resolution: {integrity: sha512-gIgzhj7h67C+Sdq2ul4fOSK142Gf0uV99bqHRdtIiUlXw9yjzZQY5TKTtzbOaevn7qBJ0xrRKtIRUbOBMl0clw==}
+ '@sentry/react@8.35.0':
+ resolution: {integrity: sha512-8Y+s4pE9hvT2TwSo5JS/Enw2cNFlwiLcJDNGCj/Hho+FePFYA59hbN06ouTHWARnO+swANHKZQj24Wp57p1/tg==}
engines: {node: '>=14.18'}
peerDependencies:
react: ^16.14.0 || 17.x || 18.x || 19.x
- '@sentry/types@8.34.0':
- resolution: {integrity: sha512-zLRc60CzohGCo6zNsNeQ9JF3SiEeRE4aDCP9fDDdIVCOKovS+mn1rtSip0qd0Vp2fidOu0+2yY0ALCz1A3PJSQ==}
+ '@sentry/types@8.35.0':
+ resolution: {integrity: sha512-AVEZjb16MlYPifiDDvJ19dPQyDn0jlrtC1PHs6ZKO+Rzyz+2EX2BRdszvanqArldexPoU1p5Bn2w81XZNXThBA==}
engines: {node: '>=14.18'}
- '@sentry/utils@8.34.0':
- resolution: {integrity: sha512-W1KoRlFUjprlh3t86DZPFxLfM6mzjRzshVfMY7vRlJFymBelJsnJ3A1lPeBZM9nCraOSiw6GtOWu6k5BAkiGIg==}
+ '@sentry/utils@8.35.0':
+ resolution: {integrity: sha512-MdMb6+uXjqND7qIPWhulubpSeHzia6HtxeJa8jYI09OCvIcmNGPydv/Gx/LZBwosfMHrLdTWcFH7Y7aCxrq7cg==}
engines: {node: '>=14.18'}
- '@sentry/vercel-edge@8.34.0':
- resolution: {integrity: sha512-yF6043FcVO9GqPawCJZp0psEL8iF9+5bOlAdQydCyaj2BtDgFvAeBVI19qlDeAHhqsXNfTD0JsIox2aJPNupwg==}
+ '@sentry/vercel-edge@8.35.0':
+ resolution: {integrity: sha512-Wp5HCkBb6hA1oE4gETzi4laMsPsc7UBqKCMY4H/UOkuD6HzgpyWuHZeS6nrs2A3MJWcoNoFZ2sJD1hdo4apzGQ==}
engines: {node: '>=14.18'}
'@sentry/webpack-plugin@2.22.3':
@@ -1465,36 +1542,45 @@ packages:
'@swc/helpers@0.5.5':
resolution: {integrity: sha512-KGYxvIOXcceOAbEk4bi/dVLEK9z8sZ0uBB3Il5b1rhfClSpcX0yfRO0KmTkqR2cnQDymwLB+25ZyMzICg/cm/A==}
- '@tiptap/core@2.8.0':
- resolution: {integrity: sha512-xsqDI4BNzYRWRtBq7+/38ThhqEr7uG9Njip1x+9/wgR3vWPBFnBkYJTz6jSxS35NRE6BSnERm4/B/vrLuY1Hdw==}
+ '@tanstack/react-table@8.20.5':
+ resolution: {integrity: sha512-WEHopKw3znbUZ61s9i0+i9g8drmDo6asTWbrQh8Us63DAk/M0FkmIqERew6P71HI75ksZ2Pxyuf4vvKh9rAkiA==}
+ engines: {node: '>=12'}
+ peerDependencies:
+ react: '>=16.8'
+ react-dom: '>=16.8'
+
+ '@tanstack/table-core@8.20.5':
+ resolution: {integrity: sha512-P9dF7XbibHph2PFRz8gfBKEXEY/HJPOhym8CHmjF8y3q5mWpKx9xtZapXQUWCgkqvsK0R46Azuz+VaxD4Xl+Tg==}
+ engines: {node: '>=12'}
+
+ '@tiptap/core@2.9.1':
+ resolution: {integrity: sha512-tifnLL/ARzQ6/FGEJjVwj9UT3v+pENdWHdk9x6F3X0mB1y0SeCjV21wpFLYESzwNdBPAj8NMp8Behv7dBnhIfw==}
peerDependencies:
'@tiptap/pm': ^2.7.0
- '@tiptap/extension-blockquote@2.8.0':
- resolution: {integrity: sha512-m3CKrOIvV7fY1Ak2gYf5LkKiz6AHxHpg6wxfVaJvdBqXgLyVtHo552N+A4oSHOSRbB4AG9EBQ2NeBM8cdEQ4MA==}
+ '@tiptap/extension-blockquote@2.9.1':
+ resolution: {integrity: sha512-Y0jZxc/pdkvcsftmEZFyG+73um8xrx6/DMfgUcNg3JAM63CISedNcr+OEI11L0oFk1KFT7/aQ9996GM6Kubdqg==}
peerDependencies:
'@tiptap/core': ^2.7.0
- '@tiptap/extension-bold@2.8.0':
- resolution: {integrity: sha512-U1YkZBxDkSLNvPNiqxB5g42IeJHr27C7zDb/yGQN2xL4UBeg4O9xVhCFfe32f6tLwivSL0dar4ScElpaCJuqow==}
+ '@tiptap/extension-bold@2.9.1':
+ resolution: {integrity: sha512-e2P1zGpnnt4+TyxTC5pX/lPxPasZcuHCYXY0iwQ3bf8qRQQEjDfj3X7EI+cXqILtnhOiviEOcYmeu5op2WhQDg==}
peerDependencies:
'@tiptap/core': ^2.7.0
- '@tiptap/extension-bubble-menu@2.8.0':
- resolution: {integrity: sha512-swg+myJPN60LduQvLMF4hVBqP5LOIN01INZBzBI8egz8QufqtSyRCgXl7Xcma0RT5xIXnZSG9XOqNFf2rtkjKA==}
+ '@tiptap/extension-bubble-menu@2.9.1':
+ resolution: {integrity: sha512-DWUF6NG08/bZDWw0jCeotSTvpkyqZTi4meJPomG9Wzs/Ol7mEwlNCsCViD999g0+IjyXFatBk4DfUq1YDDu++Q==}
peerDependencies:
'@tiptap/core': ^2.7.0
'@tiptap/pm': ^2.7.0
- '@tiptap/extension-bullet-list@2.8.0':
- resolution: {integrity: sha512-H4O2X0ozbc/ce9/XF1H98sqWVUdtt7jzy7hMBunwmY8ZxI4dHtcRkeg81CZbpKTqOqRrMCLWjE3M2tgiDXrDkA==}
+ '@tiptap/extension-bullet-list@2.9.1':
+ resolution: {integrity: sha512-0hizL/0j9PragJObjAWUVSuGhN1jKjCFnhLQVRxtx4HutcvS/lhoWMvFg6ZF8xqWgIa06n6A7MaknQkqhTdhKA==}
peerDependencies:
'@tiptap/core': ^2.7.0
- '@tiptap/extension-list-item': ^2.7.0
- '@tiptap/extension-text-style': ^2.7.0
- '@tiptap/extension-code-block-lowlight@2.8.0':
- resolution: {integrity: sha512-6RRGtzmRXlUrxJXz6cuTIpGTeMe+0Mz3XBQaZ0t7Y7PgbBDFj9tw9+LeO4eHLS/SraX4Y+P73HzKe03AdfQ95A==}
+ '@tiptap/extension-code-block-lowlight@2.9.1':
+ resolution: {integrity: sha512-SJtOE4b0VKgfdb+DQaXUTPT2xFa3nN37ejjZyOMsn/IX1LayVDciWM+3OD5Mq7KiNIEC6v++LDG88XSaPpM04Q==}
peerDependencies:
'@tiptap/core': ^2.7.0
'@tiptap/extension-code-block': ^2.7.0
@@ -1502,157 +1588,155 @@ packages:
highlight.js: ^11
lowlight: ^2 || ^3
- '@tiptap/extension-code-block@2.8.0':
- resolution: {integrity: sha512-POuA5Igx+Dto0DTazoBFAQTj/M/FCdkqRVD9Uhsxhv49swPyANTJRr05vgbgtHB+NDDsZfCawVh7pI0IAD/O0w==}
+ '@tiptap/extension-code-block@2.9.1':
+ resolution: {integrity: sha512-A/50wPWDqEUUUPhrwRKILP5gXMO5UlQ0F6uBRGYB9CEVOREam9yIgvONOnZVJtszHqOayjIVMXbH/JMBeq11/g==}
peerDependencies:
'@tiptap/core': ^2.7.0
'@tiptap/pm': ^2.7.0
- '@tiptap/extension-code@2.8.0':
- resolution: {integrity: sha512-VSFn3sFF6qPpOGkXFhik8oYRH5iByVJpFEFd/duIEftmS0MdPzkbSItOpN3mc9xsJ5dCX80LYaResSj5hr5zkA==}
+ '@tiptap/extension-code@2.9.1':
+ resolution: {integrity: sha512-WQqcVGe7i/E+yO3wz5XQteU1ETNZ00euUEl4ylVVmH2NM4Dh0KDjEhbhHlCM0iCfLUo7jhjC7dmS+hMdPUb+Tg==}
peerDependencies:
'@tiptap/core': ^2.7.0
- '@tiptap/extension-collaboration-cursor@2.8.0':
- resolution: {integrity: sha512-48UtdoF5ZXNPEkEKYOh+NQEdw+/6jrZp/VOrvEs/pdL0XqPgHsW0cpEV7D69l1sLheiMhMeOGC0iX+0Du6g1Yg==}
+ '@tiptap/extension-collaboration-cursor@2.9.1':
+ resolution: {integrity: sha512-mbNYEdlp22UpKY0SULVWRvOm3U19V80DokC+iDIDZXGXI2nCevB4vMQVkXmiQtiz3qT9I9mIN0vSgy3w75A7WA==}
peerDependencies:
'@tiptap/core': ^2.7.0
y-prosemirror: ^1.2.11
- '@tiptap/extension-collaboration@2.8.0':
- resolution: {integrity: sha512-Ae5NZWj2aq8ZElsGxQiq3cAxxbb0cR7VHvmIG1mPA6USvrQL6/xtBVutersBqINFELmIuxh/jm8qVffBm2qXyg==}
+ '@tiptap/extension-collaboration@2.9.1':
+ resolution: {integrity: sha512-AaS66O4X0rx1WLnCC3LW5fLk7ZULr0t7/gj/7dNgbBtyg4NhoYc3PhSBiimLbgD+R8wFrfm1lsHb02deaqQB6w==}
peerDependencies:
'@tiptap/core': ^2.7.0
'@tiptap/pm': ^2.7.0
y-prosemirror: ^1.2.11
- '@tiptap/extension-document@2.8.0':
- resolution: {integrity: sha512-mp7Isx1sVc/ifeW4uW/PexGQ9exN3NRUOebSpnLfqXeWYk4y1RS1PA/3+IHkOPVetbnapgPjFx/DswlCP3XLjA==}
+ '@tiptap/extension-document@2.9.1':
+ resolution: {integrity: sha512-1a+HCoDPnBttjqExfYLwfABq8MYdiowhy/wp8eCxVb6KGFEENO53KapstISvPzqH7eOi+qRjBB1KtVYb/ZXicg==}
peerDependencies:
'@tiptap/core': ^2.7.0
- '@tiptap/extension-dropcursor@2.8.0':
- resolution: {integrity: sha512-rAFvx44YuT6dtS1c+ALw0ROAGI16l5L1HxquL4hR1gtxDcTieST5xhw5bkshXlmrlfotZXPrhokzqA7qjhZtJw==}
+ '@tiptap/extension-dropcursor@2.9.1':
+ resolution: {integrity: sha512-wJZspSmJRkDBtPkzFz1g7gvZOEOayk8s93UHsgbJxcV4VWHYleZ5XhT74sZunSjefNDm3qC6v2BSgLp3vNHVKQ==}
peerDependencies:
'@tiptap/core': ^2.7.0
'@tiptap/pm': ^2.7.0
- '@tiptap/extension-floating-menu@2.8.0':
- resolution: {integrity: sha512-H4QT61CrkLqisnGGC7zgiYmsl2jXPHl89yQCbdlkQN7aw11H7PltcJS2PJguL0OrRVJS/Mv/VTTUiMslmsEV5g==}
+ '@tiptap/extension-floating-menu@2.9.1':
+ resolution: {integrity: sha512-MxZ7acNNsoNaKpetxfwi3Z11Bgrh0T2EJlCV77v9N1vWK38+st3H1WJanmLbPNtc2ocvhHJrz+DjDz3CWxQ9rQ==}
peerDependencies:
'@tiptap/core': ^2.7.0
'@tiptap/pm': ^2.7.0
- '@tiptap/extension-gapcursor@2.8.0':
- resolution: {integrity: sha512-Be1LWCmvteQInOnNVN+HTqc1XWsj1bCl+Q7et8qqNjtGtTaCbdCp8ppcH1SKJxNTM/RLUtPyJ8FDgOTj51ixCA==}
+ '@tiptap/extension-gapcursor@2.9.1':
+ resolution: {integrity: sha512-jsRBmX01vr+5H02GljiHMo0n5H1vzoMLmFarxe0Yq2d2l9G/WV2VWX2XnGliqZAYWd1bI0phs7uLQIN3mxGQTw==}
peerDependencies:
'@tiptap/core': ^2.7.0
'@tiptap/pm': ^2.7.0
- '@tiptap/extension-hard-break@2.8.0':
- resolution: {integrity: sha512-vqiIfviNiCmy/pJTHuDSCAGL2O4QDEdDmAvGJu8oRmElUrnlg8DbJUfKvn6DWQHNSQwRb+LDrwWlzAYj1K9u6A==}
+ '@tiptap/extension-hard-break@2.9.1':
+ resolution: {integrity: sha512-fCuaOD/b7nDjm47PZ58oanq7y4ccS2wjPh42Qm0B0yipu/1fmC8eS1SmaXmk28F89BLtuL6uOCtR1spe+lZtlQ==}
peerDependencies:
'@tiptap/core': ^2.7.0
- '@tiptap/extension-heading@2.8.0':
- resolution: {integrity: sha512-4inWgrTPiqlivPmEHFOM5ck2UsmOsbKKPtqga6bALvWPmCv24S6/EBwFp8Jz4YABabXDnkviihmGu0LpP9D69w==}
+ '@tiptap/extension-heading@2.9.1':
+ resolution: {integrity: sha512-SjZowzLixOFaCrV2cMaWi1mp8REK0zK1b3OcVx7bCZfVSmsOETJyrAIUpCKA8o60NwF7pwhBg0MN8oXlNKMeFw==}
peerDependencies:
'@tiptap/core': ^2.7.0
- '@tiptap/extension-history@2.8.0':
- resolution: {integrity: sha512-u5YS0J5Egsxt8TUWMMAC3QhPZaak+IzQeyHch4gtqxftx96tprItY7AD/A3pGDF2uCSnN+SZrk6yVexm6EncDw==}
+ '@tiptap/extension-history@2.9.1':
+ resolution: {integrity: sha512-wp9qR1NM+LpvyLZFmdNaAkDq0d4jDJ7z7Fz7icFQPu31NVxfQYO3IXNmvJDCNu8hFAbImpA5aG8MBuwzRo0H9w==}
peerDependencies:
'@tiptap/core': ^2.7.0
'@tiptap/pm': ^2.7.0
- '@tiptap/extension-horizontal-rule@2.8.0':
- resolution: {integrity: sha512-Sn/MI8WVFBoIYSIHA9NJryJIyCEzZdRysau8pC5TFnfifre0QV1ksPz2bgF+DyCD69ozQiRdBBHDEwKe47ZbfQ==}
+ '@tiptap/extension-horizontal-rule@2.9.1':
+ resolution: {integrity: sha512-ydUhABeaBI1CoJp+/BBqPhXINfesp1qMNL/jiDcMsB66fsD4nOyphpAJT7FaRFZFtQVF06+nttBtFZVkITQVqg==}
peerDependencies:
'@tiptap/core': ^2.7.0
'@tiptap/pm': ^2.7.0
- '@tiptap/extension-italic@2.8.0':
- resolution: {integrity: sha512-PwwSE2LTYiHI47NJnsfhBmPiLE8IXZYqaSoNPU6flPrk1KxEzqvRI1joKZBmD9wuqzmHJ93VFIeZcC+kfwi8ZA==}
+ '@tiptap/extension-italic@2.9.1':
+ resolution: {integrity: sha512-VkNA6Vz96+/+7uBlsgM7bDXXx4b62T1fDam/3UKifA72aD/fZckeWrbT7KrtdUbzuIniJSbA0lpTs5FY29+86Q==}
peerDependencies:
'@tiptap/core': ^2.7.0
- '@tiptap/extension-list-item@2.8.0':
- resolution: {integrity: sha512-o7OGymGxB0B9x3x2prp3KBDYFuBYGc5sW69O672jk8G52DqhzzndgPnkk0qUn8nXAUKuDGbJmpmHVA2kagqnRg==}
+ '@tiptap/extension-list-item@2.9.1':
+ resolution: {integrity: sha512-6O4NtYNR5N2Txi4AC0/4xMRJq9xd4+7ShxCZCDVL0WDVX37IhaqMO7LGQtA6MVlYyNaX4W1swfdJaqrJJ5HIUw==}
peerDependencies:
'@tiptap/core': ^2.7.0
- '@tiptap/extension-ordered-list@2.8.0':
- resolution: {integrity: sha512-sCvNbcTS1+5QTTXwUPFa10vf5I1pr8sGcOTIh0G+a5ZkS5+6FxT12k7VLzPt39QyNbOi+77U2o4Xr4XyaEkfSg==}
- peerDependencies:
- '@tiptap/core': ^2.7.0
- '@tiptap/extension-list-item': ^2.7.0
- '@tiptap/extension-text-style': ^2.7.0
-
- '@tiptap/extension-paragraph@2.8.0':
- resolution: {integrity: sha512-XgxxNNbuBF48rAGwv7/s6as92/xjm/lTZIGTq9aG13ClUKFtgdel7C33SpUCcxg3cO2WkEyllXVyKUiauFZw/A==}
+ '@tiptap/extension-ordered-list@2.9.1':
+ resolution: {integrity: sha512-6J9jtv1XP8dW7/JNSH/K4yiOABc92tBJtgCsgP8Ep4+fjfjdj4HbjS1oSPWpgItucF2Fp/VF8qg55HXhjxHjTw==}
peerDependencies:
'@tiptap/core': ^2.7.0
- '@tiptap/extension-strike@2.8.0':
- resolution: {integrity: sha512-ezkDiXxQ3ME/dDMMM7tAMkKRi6UWw7tIu+Mx7Os0z8HCGpVBk1gFhLlhEd8I5rJaPZr4tK1wtSehMA9bscFGQw==}
+ '@tiptap/extension-paragraph@2.9.1':
+ resolution: {integrity: sha512-JOmT0xd4gd3lIhLwrsjw8lV+ZFROKZdIxLi0Ia05XSu4RLrrvWj0zdKMSB+V87xOWfSB3Epo95zAvnPox5Q16A==}
peerDependencies:
'@tiptap/core': ^2.7.0
- '@tiptap/extension-table-cell@2.8.0':
- resolution: {integrity: sha512-IZpxONWyOd474L8+k4bHrFNRhbsl9eRwbNs5O877JkVFItc2WUz1DIhbJzjmBRsqExtWQJuOsiqWFab1kpiwGQ==}
+ '@tiptap/extension-strike@2.9.1':
+ resolution: {integrity: sha512-V5aEXdML+YojlPhastcu7w4biDPwmzy/fWq0T2qjfu5Te/THcqDmGYVBKESBm5x6nBy5OLkanw2O+KHu2quDdg==}
peerDependencies:
'@tiptap/core': ^2.7.0
- '@tiptap/extension-table-header@2.8.0':
- resolution: {integrity: sha512-B67A96yMQlG96IFzZBc7D5dnn7O29hcjuDLtjyZkKvU5D/RlFKPMmC9nVphCV3CnbkvEOZUdK9pNaOpen64naw==}
+ '@tiptap/extension-table-cell@2.9.1':
+ resolution: {integrity: sha512-/wrcniLdhMhs5M2NDetFcfq510N5to7YKK+52KOXNotBI8K/GjMmGmtwWEKPITD0/RgYrXzpMcta/O+/0OCOPQ==}
peerDependencies:
'@tiptap/core': ^2.7.0
- '@tiptap/extension-table-row@2.8.0':
- resolution: {integrity: sha512-Iezej6l7X+WqKzGLmCgAwmpL+QsfjFv1g8yVH5d0/3Pkcj3G9nDn+GSm4bZnbfYFyqInHG94PZ5PMReiALrJtA==}
+ '@tiptap/extension-table-header@2.9.1':
+ resolution: {integrity: sha512-KtI01636Du1IB/I3pe9ZJWKkOc6INqAaIw+RFirRCnd8Xnik7tJfAwdhXzoPRcer6ViZmlzSrM2dkwaZCF7gcw==}
peerDependencies:
'@tiptap/core': ^2.7.0
- '@tiptap/extension-table@2.8.0':
- resolution: {integrity: sha512-dm9CitjacXyJuE5SZfV2lUc3uOiP2sxo6fygIzMz7iuxHqQueyONWG+TBkK7HjqzXOiMPsvOf/25NazzIG8HMg==}
+ '@tiptap/extension-table-row@2.9.1':
+ resolution: {integrity: sha512-Wq7QlI/S5iX4UCAdX+ok/szegVMbvrM3H8o6jwO+G4p8JJt6iv7ZmEnJ19xIINhmiKsrdanqH9FFK4tQ3yvQ0A==}
+ peerDependencies:
+ '@tiptap/core': ^2.7.0
+
+ '@tiptap/extension-table@2.9.1':
+ resolution: {integrity: sha512-OmWZFZOSZwSSEvoVUkDsRFyCXTYei/pV396Xjv9pfFzXQkVbfq/CjTp61zvb/9mmEz3rcfvfG7G39eRlZTvBNg==}
peerDependencies:
'@tiptap/core': ^2.7.0
'@tiptap/pm': ^2.7.0
- '@tiptap/extension-text-style@2.8.0':
- resolution: {integrity: sha512-jJp0vcZ2Ty7RvIL0VU6dm1y+fTfXq1lN2GwtYzYM0ueFuESa+Qo8ticYOImyWZ3wGJGVrjn7OV9r0ReW0/NYkQ==}
+ '@tiptap/extension-text-style@2.9.1':
+ resolution: {integrity: sha512-LAxc0SeeiPiAVBwksczeA7BJSZb6WtVpYhy5Esvy9K0mK5kttB4KxtnXWeQzMIJZQbza65yftGKfQlexf/Y7yg==}
peerDependencies:
'@tiptap/core': ^2.7.0
- '@tiptap/extension-text@2.8.0':
- resolution: {integrity: sha512-EDAdFFzWOvQfVy7j3qkKhBpOeE5thkJaBemSWfXI93/gMVc0ZCdLi24mDvNNgUHlT+RjlIoQq908jZaaxLKN2A==}
+ '@tiptap/extension-text@2.9.1':
+ resolution: {integrity: sha512-3wo9uCrkLVLQFgbw2eFU37QAa1jq1/7oExa+FF/DVxdtHRS9E2rnUZ8s2hat/IWzvPUHXMwo3Zg2XfhoamQpCA==}
peerDependencies:
'@tiptap/core': ^2.7.0
- '@tiptap/extension-youtube@2.8.0':
- resolution: {integrity: sha512-mE80+XOkvbUvWYOZnDTtftIUfZhpBJdNKRY73Fyg20GApspdpLejo1x/lhJbAFk09jyRMvnBw292Spy2JRcF0g==}
+ '@tiptap/extension-youtube@2.9.1':
+ resolution: {integrity: sha512-wGclckWIXCzzGMb2yN2LsfCEiJu2pMZaR6eRhHCiZWuewZeXNvlYIo73xNnR7JvwyLynwEA/VEb0jw4HMhOnrQ==}
peerDependencies:
'@tiptap/core': ^2.7.0
- '@tiptap/html@2.8.0':
- resolution: {integrity: sha512-eOmceeQmJoYk0T6TvmaoPhVLV3yseyb8O/MewoviP1lImcdrYNw/HOifYLG1Ni/g1TzeOvbVyzF1hTJTRzyXYQ==}
+ '@tiptap/html@2.9.1':
+ resolution: {integrity: sha512-0wX+f735rqWxPQG7spKxPiP8BElwPIByXE35DFKw5Prc8D4G9BmkbKA8yRltriDyuSdTEstsE1u4qughHP5zdw==}
peerDependencies:
'@tiptap/core': ^2.7.0
'@tiptap/pm': ^2.7.0
- '@tiptap/pm@2.8.0':
- resolution: {integrity: sha512-eMGpRooUMvKz/vOpnKKppApMSoNM325HxTdAJvTlVAmuHp5bOY5kyY1kfUlePRiVx1t1UlFcXs3kecFwkkBD3Q==}
+ '@tiptap/pm@2.9.1':
+ resolution: {integrity: sha512-mvV86fr7kEuDYEApQ2uMPCKL2uagUE0BsXiyyz3KOkY1zifyVm1fzdkscb24Qy1GmLzWAIIihA+3UHNRgYdOlQ==}
- '@tiptap/react@2.8.0':
- resolution: {integrity: sha512-o/aSCjO5Nu4MsNpTF+N1SzYzVQvvBiclmTOZX2E6usZ8jre5zmKfXHDSZnjGSRTK6z6kw5KW8wpjRQha03f9mg==}
+ '@tiptap/react@2.9.1':
+ resolution: {integrity: sha512-LQJ34ZPfXtJF36SZdcn4Fiwsl2WxZ9YRJI87OLnsjJ45O+gV/PfBzz/4ap+LF8LOS0AbbGhTTjBOelPoNm+aYA==}
peerDependencies:
'@tiptap/core': ^2.7.0
'@tiptap/pm': ^2.7.0
react: ^17.0.0 || ^18.0.0
react-dom: ^17.0.0 || ^18.0.0
- '@tiptap/starter-kit@2.8.0':
- resolution: {integrity: sha512-r7UwaTrECkQoheWVZKFDqtL5tBx07x7IFT+prfgnsVlYFutGWskVVqzCDvD3BDmrg5PzeCWYZrQGlPaLib7tjg==}
+ '@tiptap/starter-kit@2.9.1':
+ resolution: {integrity: sha512-nsw6UF/7wDpPfHRhtGOwkj1ipIEiWZS1VGw+c14K61vM1CNj0uQ4jogbHwHZqN1dlL5Hh+FCqUHDPxG6ECbijg==}
'@types/connect@3.4.36':
resolution: {integrity: sha512-P63Zd/JUGq+PdrM1lv0Wv5SBYeA2+CORvbrXbngriYY0jzLUWfQMQQxOhjONEz/wlHOAxOdY7CY65rgQdTjq2w==}
@@ -1741,8 +1825,8 @@ packages:
'@types/uuid@9.0.8':
resolution: {integrity: sha512-jg+97EGIcY9AGHJJRaaPVgetKDsrTgbRjQ5Msgjh/DQKEFl0DtyRr/VCOyD1T2R1MNeWPK/u7JoGhlDZnKBAfA==}
- '@typescript-eslint/eslint-plugin@8.8.1':
- resolution: {integrity: sha512-xfvdgA8AP/vxHgtgU310+WBnLB4uJQ9XdyP17RebG26rLtDrQJV3ZYrcopX91GrHmMoH8bdSwMRh2a//TiJ1jQ==}
+ '@typescript-eslint/eslint-plugin@8.12.2':
+ resolution: {integrity: sha512-gQxbxM8mcxBwaEmWdtLCIGLfixBMHhQjBqR8sVWNTPpcj45WlYL2IObS/DNMLH1DBP0n8qz+aiiLTGfopPEebw==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
peerDependencies:
'@typescript-eslint/parser': ^8.0.0 || ^8.0.0-alpha.0
@@ -1752,8 +1836,8 @@ packages:
typescript:
optional: true
- '@typescript-eslint/parser@8.8.1':
- resolution: {integrity: sha512-hQUVn2Lij2NAxVFEdvIGxT9gP1tq2yM83m+by3whWFsWC+1y8pxxxHUFE1UqDu2VsGi2i6RLcv4QvouM84U+ow==}
+ '@typescript-eslint/parser@8.12.2':
+ resolution: {integrity: sha512-MrvlXNfGPLH3Z+r7Tk+Z5moZAc0dzdVjTgUgwsdGweH7lydysQsnSww3nAmsq8blFuRD5VRlAr9YdEFw3e6PBw==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
peerDependencies:
eslint: ^8.57.0 || ^9.0.0
@@ -1762,12 +1846,12 @@ packages:
typescript:
optional: true
- '@typescript-eslint/scope-manager@8.8.1':
- resolution: {integrity: sha512-X4JdU+66Mazev/J0gfXlcC/dV6JI37h+93W9BRYXrSn0hrE64IoWgVkO9MSJgEzoWkxONgaQpICWg8vAN74wlA==}
+ '@typescript-eslint/scope-manager@8.12.2':
+ resolution: {integrity: sha512-gPLpLtrj9aMHOvxJkSbDBmbRuYdtiEbnvO25bCMza3DhMjTQw0u7Y1M+YR5JPbMsXXnSPuCf5hfq0nEkQDL/JQ==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
- '@typescript-eslint/type-utils@8.8.1':
- resolution: {integrity: sha512-qSVnpcbLP8CALORf0za+vjLYj1Wp8HSoiI8zYU5tHxRVj30702Z1Yw4cLwfNKhTPWp5+P+k1pjmD5Zd1nhxiZA==}
+ '@typescript-eslint/type-utils@8.12.2':
+ resolution: {integrity: sha512-bwuU4TAogPI+1q/IJSKuD4shBLc/d2vGcRT588q+jzayQyjVK2X6v/fbR4InY2U2sgf8MEvVCqEWUzYzgBNcGQ==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
peerDependencies:
typescript: '*'
@@ -1775,12 +1859,12 @@ packages:
typescript:
optional: true
- '@typescript-eslint/types@8.8.1':
- resolution: {integrity: sha512-WCcTP4SDXzMd23N27u66zTKMuEevH4uzU8C9jf0RO4E04yVHgQgW+r+TeVTNnO1KIfrL8ebgVVYYMMO3+jC55Q==}
+ '@typescript-eslint/types@8.12.2':
+ resolution: {integrity: sha512-VwDwMF1SZ7wPBUZwmMdnDJ6sIFk4K4s+ALKLP6aIQsISkPv8jhiw65sAK6SuWODN/ix+m+HgbYDkH+zLjrzvOA==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
- '@typescript-eslint/typescript-estree@8.8.1':
- resolution: {integrity: sha512-A5d1R9p+X+1js4JogdNilDuuq+EHZdsH9MjTVxXOdVFfTJXunKJR/v+fNNyO4TnoOn5HqobzfRlc70NC6HTcdg==}
+ '@typescript-eslint/typescript-estree@8.12.2':
+ resolution: {integrity: sha512-mME5MDwGe30Pq9zKPvyduyU86PH7aixwqYR2grTglAdB+AN8xXQ1vFGpYaUSJ5o5P/5znsSBeNcs5g5/2aQwow==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
peerDependencies:
typescript: '*'
@@ -1788,14 +1872,14 @@ packages:
typescript:
optional: true
- '@typescript-eslint/utils@8.8.1':
- resolution: {integrity: sha512-/QkNJDbV0bdL7H7d0/y0qBbV2HTtf0TIyjSDTvvmQEzeVx8jEImEbLuOA4EsvE8gIgqMitns0ifb5uQhMj8d9w==}
+ '@typescript-eslint/utils@8.12.2':
+ resolution: {integrity: sha512-UTTuDIX3fkfAz6iSVa5rTuSfWIYZ6ATtEocQ/umkRSyC9O919lbZ8dcH7mysshrCdrAM03skJOEYaBugxN+M6A==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
peerDependencies:
eslint: ^8.57.0 || ^9.0.0
- '@typescript-eslint/visitor-keys@8.8.1':
- resolution: {integrity: sha512-0/TdC3aeRAsW7MDvYRwEc1Uwm0TIBfzjPFgg60UU2Haj5qsCs9cc3zNgY71edqE3LbWfF/WoZQd3lJoDXFQpag==}
+ '@typescript-eslint/visitor-keys@8.12.2':
+ resolution: {integrity: sha512-PChz8UaKQAVNHghsHcPyx1OMHoFRUEA7rJSK/mDhdq85bk+PLsUHUBqTQTFt18VJZbmxBovM65fezlheQRsSDA==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
'@ungap/structured-clone@1.2.0':
@@ -1862,8 +1946,8 @@ packages:
peerDependencies:
acorn: ^6.0.0 || ^7.0.0 || ^8.0.0
- acorn@8.12.1:
- resolution: {integrity: sha512-tcpGyI9zbizT9JbV6oYE477V6mTlXvvi0T0G3SNIYE2apm/G5huBa1+K89VGeovbg+jycCrfhl3ADxErOuO6Jg==}
+ acorn@8.14.0:
+ resolution: {integrity: sha512-cl669nCJTZBsL97OF4kUQm5g5hC2uihk0NxY3WENAC0TYdILVkAyHymAntgxGkl7K+t0cXIrH5siy5S4XkFycA==}
engines: {node: '>=0.4.0'}
hasBin: true
@@ -1887,10 +1971,6 @@ packages:
resolution: {integrity: sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA==}
engines: {node: '>=12'}
- ansi-styles@3.2.1:
- resolution: {integrity: sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==}
- engines: {node: '>=4'}
-
ansi-styles@4.3.0:
resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==}
engines: {node: '>=8'}
@@ -1916,8 +1996,9 @@ packages:
resolution: {integrity: sha512-y+CcFFwelSXpLZk/7fMB2mUbGtX9lKycf1MWJ7CaTIERyitVlyQx6C+sxcROU2BAJ24OiZyK+8wj2i8AlBoS3A==}
engines: {node: '>=10'}
- aria-query@5.1.3:
- resolution: {integrity: sha512-R5iJ5lkuHybztUfuOAznmboyjWq8O6sqNqtK7CLOqdydi54VNbORp49mb14KbWgG1QD3JFO9hJdZ+y4KutfdOQ==}
+ aria-query@5.3.2:
+ resolution: {integrity: sha512-COROpnaoap1E2F000S62r6A60uHZnmlvomhfyT2DlTcrY1OrBKn2UhH7qn5wTC9zMvD0AY7csdPSNwKP+7WiQw==}
+ engines: {node: '>= 0.4'}
array-buffer-byte-length@1.0.1:
resolution: {integrity: sha512-ahC5W1xgou+KTXix4sAO8Ki12Q+jf4i0+tmk3sC+zgcynshkHxzpXdImBehiUYKKKDwvfFiJl1tZt6ewscS1Mg==}
@@ -1971,8 +2052,8 @@ packages:
react: '>= 16'
react-dom: '>= 16'
- axe-core@4.10.0:
- resolution: {integrity: sha512-Mr2ZakwQ7XUAjp7pAwQWRhhK8mQQ6JAaNWSjmjxil0R8BPioMtQsTLOolGYkji1rcL++3dCqZA3zWqpT+9Ew6g==}
+ axe-core@4.10.2:
+ resolution: {integrity: sha512-RE3mdQ7P3FRSe7eqCWoeQ/Z9QXrtniSjp1wUjt5nRC3WIpz5rSCve6o3fsZ2aCpJtrZjSZgjwXAoTO5k4tEI0w==}
engines: {node: '>=4'}
axobject-query@4.1.0:
@@ -1999,8 +2080,8 @@ packages:
resolution: {integrity: sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==}
engines: {node: '>=8'}
- browserslist@4.24.0:
- resolution: {integrity: sha512-Rmb62sR1Zpjql25eSanFGEhAxcFwfA1K0GuQcLoaJBAcENegrQut3hYdhXFF1obQfiDyqIW/cLM5HSJ/9k884A==}
+ browserslist@4.24.2:
+ resolution: {integrity: sha512-ZIc+Q62revdMcqC6aChtW4jz3My3klmCO1fEmINZY/8J3EpBg5/A/D0AKmBveUh6pgoeycoMkVMko84tuYS+Gg==}
engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7}
hasBin: true
@@ -2029,12 +2110,8 @@ packages:
camelize@1.0.1:
resolution: {integrity: sha512-dU+Tx2fsypxTgtLoE36npi3UqcjSSMNYfkqgmoEhtZrraP5VWq0K7FkWVTYa8eMPtnU/G2txVsfdCJTn9uzpuQ==}
- caniuse-lite@1.0.30001667:
- resolution: {integrity: sha512-7LTwJjcRkzKFmtqGsibMeuXmvFDfZq/nzIjnmgCGzKKRVzjD72selLDK1oPF/Oxzmt4fNcPvTDvGqSDG4tCALw==}
-
- chalk@2.4.2:
- resolution: {integrity: sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==}
- engines: {node: '>=4'}
+ caniuse-lite@1.0.30001675:
+ resolution: {integrity: sha512-/wV1bQwPrkLiQMjaJF5yUMVM/VdRPOCU8QZ+PmG6uW6DvYSrNY1bpwHI/3mOcUosLaJCzYDi5o91IQB51ft6cg==}
chalk@3.0.0:
resolution: {integrity: sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg==}
@@ -2069,16 +2146,10 @@ packages:
resolution: {integrity: sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==}
engines: {node: '>=6'}
- color-convert@1.9.3:
- resolution: {integrity: sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==}
-
color-convert@2.0.1:
resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==}
engines: {node: '>=7.0.0'}
- color-name@1.1.3:
- resolution: {integrity: sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==}
-
color-name@1.1.4:
resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==}
@@ -2109,8 +2180,8 @@ packages:
convert-source-map@2.0.0:
resolution: {integrity: sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==}
- cookie@0.5.0:
- resolution: {integrity: sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw==}
+ cookie@0.7.2:
+ resolution: {integrity: sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==}
engines: {node: '>= 0.6'}
crelt@1.0.6:
@@ -2142,6 +2213,9 @@ packages:
csstype@3.1.3:
resolution: {integrity: sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==}
+ currency-codes@2.2.0:
+ resolution: {integrity: sha512-vpbQc5sEYHGdTVAYUhHnKv0DWiYLRvzl/KKyqeHzBh7HD/j3UlWoScpZ9tN/jG6w2feddWoObsBbaNVu5yDapg==}
+
damerau-levenshtein@1.0.8:
resolution: {integrity: sha512-sdQSFB7+llfUcQHUQO3+B8ERRj0Oa4w9POWMI/puGtuf7gFywGmkaLCElnudfTiKZV+NvHqL0ifzdrI8Ro7ESA==}
@@ -2185,10 +2259,6 @@ packages:
supports-color:
optional: true
- deep-equal@2.2.3:
- resolution: {integrity: sha512-ZIwpnevOurS8bpT4192sqAowWM76JDKSHYzMLty3BZGSswgq6pBaH3DhCSW5xVAZICZyKdOBPjwww5wfgT/6PA==}
- engines: {node: '>= 0.4'}
-
deep-is@0.1.4:
resolution: {integrity: sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==}
@@ -2242,8 +2312,8 @@ packages:
eastasianwidth@0.2.0:
resolution: {integrity: sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==}
- electron-to-chromium@1.5.36:
- resolution: {integrity: sha512-HYTX8tKge/VNp6FGO+f/uVDmUkq+cEfcxYhKf15Akc4M5yxt5YmorwlAitKWjWhWQnKcDRBAQKXkhqqXMqcrjw==}
+ electron-to-chromium@1.5.49:
+ resolution: {integrity: sha512-ZXfs1Of8fDb6z7WEYZjXpgIRF6MEu8JdeGA0A40aZq6OQbS+eJpnnV49epZRna2DU/YsEjSQuGtQPPtvt6J65A==}
emoji-mart@5.6.0:
resolution: {integrity: sha512-eJp3QRe79pjwa+duv+n7+5YsNhRcMl812EcFVwrnRvYKoNPoQb5qxU8DG6Bgwji0akHdp6D4Ln6tYLG58MFSow==}
@@ -2281,9 +2351,6 @@ packages:
resolution: {integrity: sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==}
engines: {node: '>= 0.4'}
- es-get-iterator@1.1.3:
- resolution: {integrity: sha512-sPZmqHBe6JIiTfN5q2pEi//TwxmAFHwj/XEuYjTuse78i8KxaqMTTzxPoFKuzRpDpTJ+0NAbpfenkmH2rePtuw==}
-
es-iterator-helpers@1.1.0:
resolution: {integrity: sha512-/SurEfycdyssORP/E+bj4sEu1CWw4EmLDsHynHwSXQ7utgbrMRWW195pTrCjFgFCddf/UkYm3oqKPRq5i8bJbw==}
engines: {node: '>= 0.4'}
@@ -2310,16 +2377,12 @@ packages:
resolution: {integrity: sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==}
engines: {node: '>=6'}
- escape-string-regexp@1.0.5:
- resolution: {integrity: sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==}
- engines: {node: '>=0.8.0'}
-
escape-string-regexp@4.0.0:
resolution: {integrity: sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==}
engines: {node: '>=10'}
- eslint-config-next@14.2.15:
- resolution: {integrity: sha512-mKg+NC/8a4JKLZRIOBplxXNdStgxy7lzWuedUaCc8tev+Al9mwDUTujQH6W6qXDH9kycWiVo28tADWGvpBsZcQ==}
+ eslint-config-next@14.2.16:
+ resolution: {integrity: sha512-HOcnCJsyLXR7B8wmjaCgkTSpz+ijgOyAkP8OlvANvciP8PspBYFEBTmakNMxOf71fY0aKOm/blFIiKnrM4K03Q==}
peerDependencies:
eslint: ^7.23.0 || ^8.0.0
typescript: '>=3.3.1'
@@ -2374,20 +2437,20 @@ packages:
'@typescript-eslint/parser':
optional: true
- eslint-plugin-jsx-a11y@6.10.0:
- resolution: {integrity: sha512-ySOHvXX8eSN6zz8Bywacm7CvGNhUtdjvqfQDVe6020TUK34Cywkw7m0KsCCk1Qtm9G1FayfTN1/7mMYnYO2Bhg==}
+ eslint-plugin-jsx-a11y@6.10.2:
+ resolution: {integrity: sha512-scB3nz4WmG75pV8+3eRUQOHZlNSUhFNq37xnpgRkCCELU3XMvXAxLk1eqWWyE22Ki4Q01Fnsw9BA3cJHDPgn2Q==}
engines: {node: '>=4.0'}
peerDependencies:
eslint: ^3 || ^4 || ^5 || ^6 || ^7 || ^8 || ^9
- eslint-plugin-react-hooks@4.6.2:
- resolution: {integrity: sha512-QzliNJq4GinDBcD8gPB5v0wh6g8q3SUi6EFF0x8N/BL9PoVs0atuGc47ozMRyOWAKdwaZ5OnbOEa3WR+dSGKuQ==}
+ eslint-plugin-react-hooks@5.0.0-canary-7118f5dd7-20230705:
+ resolution: {integrity: sha512-AZYbMo/NW9chdL7vk6HQzQhT+PvTAEVqWk9ziruUoW2kAOcN5qNyelv70e0F1VNQAbvutOC9oc+xfWycI9FxDw==}
engines: {node: '>=10'}
peerDependencies:
eslint: ^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0
- eslint-plugin-react@7.37.1:
- resolution: {integrity: sha512-xwTnwDqzbDRA8uJ7BMxPs/EXRB3i8ZfnOIp8BsxEQkT0nHPp+WWceqGgo6rKb9ctNi8GJLDT4Go5HAWELa/WMg==}
+ eslint-plugin-react@7.37.2:
+ resolution: {integrity: sha512-EsTAnj9fLVr/GZleBLFbj/sSuXeWmp1eXIN60ceYnZveqEaUCyW4X+Vh4WTdUhCkW4xutXYqTXCUSyqD4rB75w==}
engines: {node: '>=4'}
peerDependencies:
eslint: ^3 || ^4 || ^5 || ^6 || ^7 || ^8 || ^9.7
@@ -2483,6 +2546,9 @@ packages:
resolution: {integrity: sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==}
engines: {node: '>=10'}
+ first-match@0.0.1:
+ resolution: {integrity: sha512-VvKbnaxrC0polTFDC+teKPTdl2mn6B/KUW+WB3C9RzKDeNwbzfLdnUz3FxC+tnjvus6bI0jWrWicQyVIPdS37A==}
+
flat-cache@3.2.0:
resolution: {integrity: sha512-CYcENa+FtcUKLmhhqyctpclsq7QF38pKjZHsGNiSQF5r4FtoKDWabFDl3hzaEQMvT1LHEysw5twgLvpYYb4vbw==}
engines: {node: ^10.12.0 || >=12.0.0}
@@ -2599,8 +2665,8 @@ packages:
resolution: {integrity: sha512-DpLKbNU4WylpxJykQujfCcwYWiV/Jhm50Goo0wrVILAv5jOr9d+H+UR3PhSCD2rCCEIg0uc+G+muBTwD54JhDQ==}
engines: {node: '>= 0.4'}
- goober@2.1.15:
- resolution: {integrity: sha512-LP0xChUqgLlr5ORa1m4LobVy++/dhP4Kta2gVla9i2pc30XvtpEFrye4JtcD265g1tEFLOjYIQEiTa+9bGGQ/g==}
+ goober@2.1.16:
+ resolution: {integrity: sha512-erjk19y1U33+XAMe1VTvIONHYoSqE4iS7BYUZfHaqeohLmnC0FdxEh7rQU+6MZ4OajItzjZFSRtVANrQwNq6/g==}
peerDependencies:
csstype: ^3.0.10
@@ -2616,10 +2682,6 @@ packages:
has-bigints@1.0.2:
resolution: {integrity: sha512-tSvCKtBr9lkF0Ex0aQiP9N+OpV4zi2r/Nee5VkRDbaqv35RLYMzbwQfFSZZH0kR+Rd6302UJZ2p/bJCEoR3VoQ==}
- has-flag@3.0.0:
- resolution: {integrity: sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==}
- engines: {node: '>=4'}
-
has-flag@4.0.0:
resolution: {integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==}
engines: {node: '>=8'}
@@ -2690,10 +2752,6 @@ packages:
invariant@2.2.4:
resolution: {integrity: sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA==}
- is-arguments@1.1.1:
- resolution: {integrity: sha512-8Q7EARjzEnKpt/PCD7e1cgUS0a6X8u5tdSiMqXhojOdoV9TsMsiO+9VLC5vAmO8N7/GmXn7yjR8qnA6bVAEzfA==}
- engines: {node: '>= 0.4'}
-
is-array-buffer@3.0.4:
resolution: {integrity: sha512-wcjaerHw0ydZwfhiKbXJWLDY8A7yV7KhjQOpb83hGgGfId/aQa4TOvwyzn2PuswW2gPCYEL/nEAiSVpdOj1lXw==}
engines: {node: '>= 0.4'}
@@ -2957,13 +3015,13 @@ packages:
resolution: {integrity: sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==}
engines: {node: '>=10'}
- lucide-react@0.424.0:
- resolution: {integrity: sha512-x2Nj2aytk1iOyHqt4hKenfVlySq0rYxNeEf8hE0o+Yh0iE36Rqz0rkngVdv2uQtjZ70LAE73eeplhhptYt9x4Q==}
+ lucide-react@0.453.0:
+ resolution: {integrity: sha512-kL+RGZCcJi9BvJtzg2kshO192Ddy9hv3ij+cPrVPWSRzgCWCVazoQJxOjAwgK53NomL07HB7GPHW120FimjNhQ==}
peerDependencies:
react: ^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0-rc
- magic-string@0.30.11:
- resolution: {integrity: sha512-+Wri9p0QHMy+545hKww7YAu5NyzF8iomPL/RQazugQ9+Ez4Ic3mERMd8ZTX5rfK944j+560ZJi8iAwgak1Ac7A==}
+ magic-string@0.30.12:
+ resolution: {integrity: sha512-Ea8I3sQMVXr8JhN4z+H/d8zwo+tYDgHE9+5G4Wnrwhs0gaK9fXTKx0Tw5Xwsd/bCPTTZNRAdpyzvoeORe9LYpw==}
magic-string@0.30.8:
resolution: {integrity: sha512-ISQTe55T2ao7XtlAStud6qwYPZjE4GK1S/BeVPus4jrq6JuOnQ00YKQC581RWhR122W7msZV263KzVeLoqidyQ==}
@@ -3043,11 +3101,11 @@ packages:
neo-async@2.6.2:
resolution: {integrity: sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==}
- next-auth@4.24.8:
- resolution: {integrity: sha512-SLt3+8UCtklsotnz2p+nB4aN3IHNmpsQFAZ24VLxGotWGzSxkBh192zxNhm/J5wgkcrDWVp0bwqvW0HksK/Lcw==}
+ next-auth@4.24.10:
+ resolution: {integrity: sha512-8NGqiRO1GXBcVfV8tbbGcUgQkAGsX4GRzzXXea4lDikAsJtD5KiEY34bfhUOjHLvr6rT6afpcxw2H8EZqOV6aQ==}
peerDependencies:
'@auth/core': 0.34.2
- next: ^12.2.5 || ^13 || ^14
+ next: ^12.2.5 || ^13 || ^14 || ^15
nodemailer: ^6.6.5
react: ^17.0.2 || ^18
react-dom: ^17.0.2 || ^18
@@ -3105,6 +3163,9 @@ packages:
nprogress@0.2.0:
resolution: {integrity: sha512-I19aIingLgR1fmhftnbWWO3dXc0hSxqHQHQb3H8m+K3TnEn/iSeTZZOyvKXWqQESMwuUVnatlCnZdLBZZt2VSA==}
+ nub@0.0.0:
+ resolution: {integrity: sha512-dK0Ss9C34R/vV0FfYJXuqDAqHlaW9fvWVufq9MmGF2umCuDbd5GRfRD9fpi/LiM0l4ZXf8IBB+RYmZExqCrf0w==}
+
oauth@0.9.15:
resolution: {integrity: sha512-a5ERWK1kh38ExDEfoO6qUHJb32rd7aYmPHuyCu3Fta/cnICvYmgd2uhuKXvPD+PXB+gCEYYEaQdIRAjCOwAKNA==}
@@ -3124,10 +3185,6 @@ packages:
resolution: {integrity: sha512-IRZSRuzJiynemAXPYtPe5BoI/RESNYR7TYm50MC5Mqbd3Jmw5y790sErYw3V6SryFJD64b74qQQs9wn5Bg/k3g==}
engines: {node: '>= 0.4'}
- object-is@1.1.6:
- resolution: {integrity: sha512-F8cZ+KfGlSGi09lJT7/Nd6KJZ9ygtvYC0/UYYLI9nmQKLMnydpB9yvbv9K1uSkEu7FU9vYPmVwLg328tX+ot3Q==}
- engines: {node: '>= 0.4'}
-
object-keys@1.1.1:
resolution: {integrity: sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==}
engines: {node: '>= 0.4'}
@@ -3214,13 +3271,17 @@ packages:
resolution: {integrity: sha512-qTAAlrEsl8s4OiEQY69wDvcMIdQN6wdz5ojQiOy6YRMuynxenON0O5oCpJI6lshc6scgAY8qvJ2On/p+CXY0GA==}
engines: {node: '>=4'}
- picocolors@1.1.0:
- resolution: {integrity: sha512-TQ92mBOW0l3LeMeyLV6mzy/kWr8lkd/hp3mTg7wYK7zJhuBStmGMBG0BdeDZS/dZx1IukaX6Bk11zcln25o1Aw==}
+ picocolors@1.1.1:
+ resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==}
picomatch@2.3.1:
resolution: {integrity: sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==}
engines: {node: '>=8.6'}
+ picomatch@4.0.2:
+ resolution: {integrity: sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==}
+ engines: {node: '>=12'}
+
pify@2.3.0:
resolution: {integrity: sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==}
engines: {node: '>=0.10.0'}
@@ -3303,8 +3364,8 @@ packages:
peerDependencies:
preact: '>=10'
- preact@10.24.2:
- resolution: {integrity: sha512-1cSoF0aCC8uaARATfrlz4VCBqE8LwZwRfLgkxJOQwAlQt6ayTmi0D9OF7nXid1POI5SZidFuG9CnlXbDfLqY/Q==}
+ preact@10.24.3:
+ resolution: {integrity: sha512-Z2dPnBnMUfyQfSQ+GBdsGa16hz35YmLmtTLhM169uW944hYL6xzTYkJjC07j+Wosz733pMWx0fgON3JNw1jJQA==}
prelude-ls@1.2.1:
resolution: {integrity: sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==}
@@ -3320,14 +3381,17 @@ packages:
prop-types@15.8.1:
resolution: {integrity: sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==}
+ property-expr@2.0.6:
+ resolution: {integrity: sha512-SVtmxhRE/CGkn3eZY1T6pC8Nln6Fr/lu1mKSgRud0eC73whjGfoAogbn78LkD8aFL0zz3bAFerKSnOl7NlErBA==}
+
prosemirror-changeset@2.2.1:
resolution: {integrity: sha512-J7msc6wbxB4ekDFj+n9gTW/jav/p53kdlivvuppHsrZXCaQdVgRghoZbSS3kwrRyAstRVQ4/+u5k7YfLgkkQvQ==}
prosemirror-collab@1.3.1:
resolution: {integrity: sha512-4SnynYR9TTYaQVXd/ieUvsVV4PDMBzrq2xPUWutHivDuOshZXqQ5rGbZM84HEaXKbLdItse7weMGOUdDVcLKEQ==}
- prosemirror-commands@1.6.0:
- resolution: {integrity: sha512-xn1U/g36OqXn2tn5nGmvnnimAj/g1pUx2ypJJIe8WkVX83WyJVC5LTARaxZa2AtQRwntu9Jc5zXs9gL9svp/mg==}
+ prosemirror-commands@1.6.2:
+ resolution: {integrity: sha512-0nDHH++qcf/BuPLYvmqZTUUsPJUCPBUXt0J1ErTcDIS369CTp773itzLGIgIXG4LJXOlwYCr44+Mh4ii6MP1QA==}
prosemirror-dropcursor@1.8.1:
resolution: {integrity: sha512-M30WJdJZLyXHi3N8vxN6Zh5O8ZBbQCz0gURTfPmTIBNQ5pxrdU7A58QkNqfa98YEjSAL1HUyyU34f6Pm5xBSGw==}
@@ -3362,8 +3426,8 @@ packages:
prosemirror-state@1.4.3:
resolution: {integrity: sha512-goFKORVbvPuAQaXhpbemJFRKJ2aixr+AZMGiquiqKxaucC6hlpHNZHWgz5R7dS4roHiwq9vDctE//CZ++o0W1Q==}
- prosemirror-tables@1.5.0:
- resolution: {integrity: sha512-VMx4zlYWm7aBlZ5xtfJHpqa3Xgu3b7srV54fXYnXgsAcIGRqKSrhiK3f89omzzgaAgAtDOV4ImXnLKhVfheVNQ==}
+ prosemirror-tables@1.6.1:
+ resolution: {integrity: sha512-p8WRJNA96jaNQjhJolmbxTzd6M4huRE5xQ8OxjvMhQUP0Nzpo4zz6TztEiwk6aoqGBhz9lxRWR1yRZLlpQN98w==}
prosemirror-trailing-node@3.0.0:
resolution: {integrity: sha512-xiun5/3q0w5eRnGYfNlW1uU9W6x5MoFKWwq/0TIRgt09lv7Hcser2QYV8t4muXbEr+Fwo0geYn79Xs4GKywrRQ==}
@@ -3372,11 +3436,11 @@ packages:
prosemirror-state: ^1.4.2
prosemirror-view: ^1.33.8
- prosemirror-transform@1.10.1:
- resolution: {integrity: sha512-jANxn50pNsBT7042mM4pxKpGyzgJWen/GrfCEQOPtHa5gLrQ2+0OgDtuQu2noG1X0+2cQ5uPsWqCRy7w7J+obw==}
+ prosemirror-transform@1.10.2:
+ resolution: {integrity: sha512-2iUq0wv2iRoJO/zj5mv8uDUriOHWzXRnOTVgCzSXnktS/2iQRa3UUQwVlkBlYZFtygw6Nh1+X4mGqoYBINn5KQ==}
- prosemirror-view@1.34.3:
- resolution: {integrity: sha512-mKZ54PrX19sSaQye+sef+YjBbNu2voNwLS1ivb6aD2IRmxRGW64HU9B644+7OfJStGLyxvOreKqEgfvXa91WIA==}
+ prosemirror-view@1.35.0:
+ resolution: {integrity: sha512-Umtbh22fmUlpZpRTiOVXA0PpdRZeYEeXQsLp51VfnMhjkJrqJ0n8APinIZrRAD5Jr3UxH8FnOaUqRylSuMsqHA==}
proxy-from-env@1.1.0:
resolution: {integrity: sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==}
@@ -3409,6 +3473,7 @@ packages:
react-beautiful-dnd@13.1.1:
resolution: {integrity: sha512-0Lvs4tq2VcrEjEgDXHjT98r+63drkKEgqyxdA7qD3mvKwga6a5SscbdLPO2IExotU1jW8L0Ksdl0Cj2AF67nPQ==}
+ deprecated: 'react-beautiful-dnd is now deprecated. Context and options: https://github.com/atlassian/react-beautiful-dnd/issues/2672'
peerDependencies:
react: ^16.8.5 || ^17.0.0 || ^18.0.0
react-dom: ^16.8.5 || ^17.0.0 || ^18.0.0
@@ -3656,10 +3721,6 @@ packages:
resolution: {integrity: sha512-KJP1OCML99+8fhOHxwwzyWrlUuVX5GQ0ZpJTd1DFXhdkrvg1szxfHhawXUZ3g9TkXORQd4/WG68jMlQZ2p8wlg==}
engines: {node: '>=6'}
- stop-iteration-iterator@1.0.0:
- resolution: {integrity: sha512-iCGQj+0l0HOdZ2AEeBADlsRC+vsnDsZsbdSiH1yNSjcfKM7fdpCMfqAL/dwF5BLiw/XhRft/Wax6zQbhq2BcjQ==}
- engines: {node: '>= 0.4'}
-
streamsearch@1.1.0:
resolution: {integrity: sha512-Mcc5wHehp9aXz1ax6bZUyY5afg9u2rv5cqQI3mRrYkGC8rW2hM02jWuwjtL++LS5qinSyhj2QfLyNsuc+VsExg==}
engines: {node: '>=10.0.0'}
@@ -3672,8 +3733,9 @@ packages:
resolution: {integrity: sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==}
engines: {node: '>=12'}
- string.prototype.includes@2.0.0:
- resolution: {integrity: sha512-E34CkBgyeqNDcrbU76cDjL5JLcVrtSdYq0MEh/B10r17pRP4ciHLwTgnuLV8Ay6cgEMLkcBkFCKyFZ43YldYzg==}
+ string.prototype.includes@2.0.1:
+ resolution: {integrity: sha512-o7+c9bW6zpAdJHTtujeePODAhkuicdAryFsfVKwA+wGw89wJ4GTY484WTucM9hLtDEOpOvI+aHnzqnC5lHp4Rg==}
+ engines: {node: '>= 0.4'}
string.prototype.matchall@4.0.11:
resolution: {integrity: sha512-NUdh0aDavY2og7IbBPenWqR9exH+E26Sv8e0/eTe1tltDGZL+GtBkDAnnyBtmekfK6/Dq3MkcGtzXFEd1LQrtg==}
@@ -3740,10 +3802,6 @@ packages:
engines: {node: '>=16 || 14 >=14.17'}
hasBin: true
- supports-color@5.5.0:
- resolution: {integrity: sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==}
- engines: {node: '>=4'}
-
supports-color@7.2.0:
resolution: {integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==}
engines: {node: '>=8'}
@@ -3761,8 +3819,8 @@ packages:
peerDependencies:
react: ^16.11.0 || ^17.0.0 || ^18.0.0
- tailwind-merge@2.5.3:
- resolution: {integrity: sha512-d9ZolCAIzom1nf/5p4LdD5zvjmgSxY0BGgdSvmXIoMYAiPdAW/dSpP7joCDYFY7r/HkEa2qmPtkgsu0xjQeQtw==}
+ tailwind-merge@2.5.4:
+ resolution: {integrity: sha512-0q8cfZHMu9nuYP/b5Shb7Y7Sh1B7Nnl5GqNr1U+n2p6+mybvRtayrQ+0042Z5byvTA8ihjlP8Odo8/VnHbZu4Q==}
tailwind-scrollbar@3.1.0:
resolution: {integrity: sha512-pmrtDIZeHyu2idTejfV59SbaJyvp1VRjYxAjZBH0jnyrPRo6HL1kD5Glz8VPagasqr6oAx6M05+Tuw429Z8jxg==}
@@ -3775,8 +3833,8 @@ packages:
peerDependencies:
tailwindcss: '>=3.0.0 || insiders'
- tailwindcss@3.4.13:
- resolution: {integrity: sha512-KqjHOJKogOUt5Bs752ykCeiwvi0fKVkr5oqsFNt/8px/tA8scFPIlkygsf6jXrfCqGHz7VflA6+yytWuM+XhFw==}
+ tailwindcss@3.4.14:
+ resolution: {integrity: sha512-IcSvOcTRcUtQQ7ILQL5quRDg7Xs93PdJEk1ZLbhhvJc7uj/OAhYOnruEiwnGgBvUtaUAJ8/mhSw1o8L2jCiENA==}
engines: {node: '>=14.0.0'}
hasBin: true
@@ -3800,8 +3858,8 @@ packages:
uglify-js:
optional: true
- terser@5.34.1:
- resolution: {integrity: sha512-FsJZ7iZLd/BXkz+4xrRTGJ26o/6VTjQytUk8b8OxkwcD2I+79VPJlz7qss1+zE7h8GNIScFqXcDyJ/KqBYZFVA==}
+ terser@5.36.0:
+ resolution: {integrity: sha512-IYV9eNMuFAV4THUspIRXkLakHnV6XO7FEdtKjf/mDyrnqUg9LnlOn6/RwRvM9SZjR4GUq8Nk8zj67FzVARr74w==}
engines: {node: '>=10'}
hasBin: true
@@ -3815,6 +3873,9 @@ packages:
thenify@3.3.1:
resolution: {integrity: sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw==}
+ tiny-case@1.0.3:
+ resolution: {integrity: sha512-Eet/eeMhkO6TX8mnUteS9zgPbUMQa4I6Kkp5ORiBD5476/m+PIRiumP5tmh5ioJpH7k51Kehawy2UDfsnxxY8Q==}
+
tiny-invariant@1.3.3:
resolution: {integrity: sha512-+FbBPE1o9QAYvviau/qC5SE3caw21q3xkvWKBtja5vgqOWIHHJ3ioaq1VPfn/Szqctz2bU/oYeKd9/z5BL+PVg==}
@@ -3824,19 +3885,18 @@ packages:
tippy.js@6.3.7:
resolution: {integrity: sha512-E1d3oP2emgJ9dRQZdf3Kkn0qJgI6ZLpyS5z6ZkY1DF3kaQaBsGZsndEpHwx+eC+tYM41HaSNvNtLx8tU57FzTQ==}
- to-fast-properties@2.0.0:
- resolution: {integrity: sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==}
- engines: {node: '>=4'}
-
to-regex-range@5.0.1:
resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==}
engines: {node: '>=8.0'}
+ toposort@2.0.2:
+ resolution: {integrity: sha512-0a5EOkAUp8D4moMi2W8ZF8jcga7BgZd91O/yabJCFY8az+XSzeGyTKs0Aoo897iV1Nj6guFq8orWDS96z91oGg==}
+
tr46@0.0.3:
resolution: {integrity: sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==}
- ts-api-utils@1.3.0:
- resolution: {integrity: sha512-UQMIo7pb8WRomKR1/+MFVLTroIvDVtMX3K6OUir8ynLyzB8Jeriont2bTAtmNPa1ekAgN7YPDyf6V+ygrdU+eQ==}
+ ts-api-utils@1.4.0:
+ resolution: {integrity: sha512-032cPxaEKwM+GT3vA5JXNzIaizx388rhsSW79vGRNGXfRRAdEAn2mvk36PvK5HnOchyWZ7afLEXqYCvPCrzuzQ==}
engines: {node: '>=16'}
peerDependencies:
typescript: '>=4.2.0'
@@ -3850,8 +3910,8 @@ packages:
tslib@2.6.2:
resolution: {integrity: sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==}
- tslib@2.7.0:
- resolution: {integrity: sha512-gLXCKdN1/j47AiHiOkJN69hJmcbGTHI0ImLmbYLHykhgeN0jVGola9yVjFgzCUklsZQMW55o+dW7IXv3RCXDzA==}
+ tslib@2.8.0:
+ resolution: {integrity: sha512-jWVzBLplnCmoaTr13V9dYbiQ99wvZRd0vNWaDRg+aVYRcjDF3nDksxFDE/+fkXnKhpnUUkmx5pK/v8mCtLVqZA==}
tween-functions@1.2.0:
resolution: {integrity: sha512-PZBtLYcCLtEcjL14Fzb1gSxPBeL7nWvGhO5ZFPGqziCcr8uvHp0NDmdjBchp6KHL+tExcg0m3NISmKxhU394dA==}
@@ -3868,6 +3928,10 @@ packages:
resolution: {integrity: sha512-Ne2YiiGN8bmrmJJEuTWTLJR32nh/JdL1+PSicowtNb0WFpn59GK8/lfD61bVtzguz7b3PBt74nxpv/Pw5po5Rg==}
engines: {node: '>=8'}
+ type-fest@2.19.0:
+ resolution: {integrity: sha512-RAH822pAdBgcNMAfWnCBU3CFZcfZ/i1eZjwFU/dsLKumyuuP3niueg2UAukXYF0E2AAoc82ZSSf9J0WQBinzHA==}
+ engines: {node: '>=12.20'}
+
typed-array-buffer@1.0.2:
resolution: {integrity: sha512-gEymJYKZtKXzzBzM4jqa9w6Q1Jjm7x2d+sh19AdsD4wqnMPDYyvwpsIc2Q/835kHuo3BEQ7CjelGhfTsoBb2MQ==}
engines: {node: '>= 0.4'}
@@ -4077,13 +4141,13 @@ packages:
yallist@4.0.0:
resolution: {integrity: sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==}
- yaml@2.5.1:
- resolution: {integrity: sha512-bLQOjaX/ADgQ20isPJRvF0iRUHIxVhYvr53Of7wGcWlO2jvtUlH5m87DsmulFVxRpNLOnI4tB6p/oh8D7kpn9Q==}
+ yaml@2.6.0:
+ resolution: {integrity: sha512-a6ae//JvKDEra2kdi1qzCyrJW/WZCgFi8ydDV+eXExl95t+5R+ijnqHJbz9tmMh8FUjx3iv2fCQ4dclAQlO2UQ==}
engines: {node: '>= 14'}
hasBin: true
- yjs@13.6.19:
- resolution: {integrity: sha512-GNKw4mEUn5yWU2QPHRx8jppxmCm9KzbBhB4qJLUJFiiYD0g/tDVgXQ7aPkyh01YO28kbs2J/BEbWBagjuWyejw==}
+ yjs@13.6.20:
+ resolution: {integrity: sha512-Z2YZI+SYqK7XdWlloI3lhMiKnCdFCVC4PchpdO+mCYwtiTwncjUbnRK9R1JmkNfdmHyDXuWN3ibJAt0wsqTbLQ==}
engines: {node: '>=16.0.0', npm: '>=8.0.0'}
yocto-queue@0.1.0:
@@ -4093,6 +4157,9 @@ packages:
youtube-player@5.5.2:
resolution: {integrity: sha512-ZGtsemSpXnDky2AUYWgxjaopgB+shFHgXVpiJFeNB5nWEugpW1KWYDaHKuLqh2b67r24GtP6HoSW5swvf0fFIQ==}
+ yup@1.4.0:
+ resolution: {integrity: sha512-wPbgkJRCqIf+OHyiTBQoJiP5PFuAXaWiJK6AmYkzQAh5/c2K9hzSApBZG5wV9KoKSePF7sAxmNSvh/13YHkFDg==}
+
zeed-dom@0.15.1:
resolution: {integrity: sha512-dtZ0aQSFyZmoJS0m06/xBN1SazUBPL5HpzlAcs/KcRW0rzadYw12deQBjeMhGKMMeGEp7bA9vmikMLaO4exBcg==}
engines: {node: '>=14.13.1'}
@@ -4106,25 +4173,26 @@ snapshots:
'@jridgewell/gen-mapping': 0.3.5
'@jridgewell/trace-mapping': 0.3.25
- '@babel/code-frame@7.25.7':
+ '@babel/code-frame@7.26.2':
dependencies:
- '@babel/highlight': 7.25.7
- picocolors: 1.1.0
+ '@babel/helper-validator-identifier': 7.25.9
+ js-tokens: 4.0.0
+ picocolors: 1.1.1
- '@babel/compat-data@7.25.8': {}
+ '@babel/compat-data@7.26.2': {}
- '@babel/core@7.25.8':
+ '@babel/core@7.26.0':
dependencies:
'@ampproject/remapping': 2.3.0
- '@babel/code-frame': 7.25.7
- '@babel/generator': 7.25.7
- '@babel/helper-compilation-targets': 7.25.7
- '@babel/helper-module-transforms': 7.25.7(@babel/core@7.25.8)
- '@babel/helpers': 7.25.7
- '@babel/parser': 7.25.8
- '@babel/template': 7.25.7
- '@babel/traverse': 7.25.7
- '@babel/types': 7.25.8
+ '@babel/code-frame': 7.26.2
+ '@babel/generator': 7.26.2
+ '@babel/helper-compilation-targets': 7.25.9
+ '@babel/helper-module-transforms': 7.26.0(@babel/core@7.26.0)
+ '@babel/helpers': 7.26.0
+ '@babel/parser': 7.26.2
+ '@babel/template': 7.25.9
+ '@babel/traverse': 7.25.9
+ '@babel/types': 7.26.0
convert-source-map: 2.0.0
debug: 4.3.7
gensync: 1.0.0-beta.2
@@ -4133,98 +4201,83 @@ snapshots:
transitivePeerDependencies:
- supports-color
- '@babel/generator@7.25.7':
+ '@babel/generator@7.26.2':
dependencies:
- '@babel/types': 7.25.8
+ '@babel/parser': 7.26.2
+ '@babel/types': 7.26.0
'@jridgewell/gen-mapping': 0.3.5
'@jridgewell/trace-mapping': 0.3.25
jsesc: 3.0.2
- '@babel/helper-compilation-targets@7.25.7':
+ '@babel/helper-compilation-targets@7.25.9':
dependencies:
- '@babel/compat-data': 7.25.8
- '@babel/helper-validator-option': 7.25.7
- browserslist: 4.24.0
+ '@babel/compat-data': 7.26.2
+ '@babel/helper-validator-option': 7.25.9
+ browserslist: 4.24.2
lru-cache: 5.1.1
semver: 6.3.1
- '@babel/helper-module-imports@7.25.7':
+ '@babel/helper-module-imports@7.25.9':
dependencies:
- '@babel/traverse': 7.25.7
- '@babel/types': 7.25.8
+ '@babel/traverse': 7.25.9
+ '@babel/types': 7.26.0
transitivePeerDependencies:
- supports-color
- '@babel/helper-module-transforms@7.25.7(@babel/core@7.25.8)':
+ '@babel/helper-module-transforms@7.26.0(@babel/core@7.26.0)':
dependencies:
- '@babel/core': 7.25.8
- '@babel/helper-module-imports': 7.25.7
- '@babel/helper-simple-access': 7.25.7
- '@babel/helper-validator-identifier': 7.25.7
- '@babel/traverse': 7.25.7
+ '@babel/core': 7.26.0
+ '@babel/helper-module-imports': 7.25.9
+ '@babel/helper-validator-identifier': 7.25.9
+ '@babel/traverse': 7.25.9
transitivePeerDependencies:
- supports-color
- '@babel/helper-simple-access@7.25.7':
+ '@babel/helper-string-parser@7.25.9': {}
+
+ '@babel/helper-validator-identifier@7.25.9': {}
+
+ '@babel/helper-validator-option@7.25.9': {}
+
+ '@babel/helpers@7.26.0':
dependencies:
- '@babel/traverse': 7.25.7
- '@babel/types': 7.25.8
- transitivePeerDependencies:
- - supports-color
+ '@babel/template': 7.25.9
+ '@babel/types': 7.26.0
- '@babel/helper-string-parser@7.25.7': {}
-
- '@babel/helper-validator-identifier@7.25.7': {}
-
- '@babel/helper-validator-option@7.25.7': {}
-
- '@babel/helpers@7.25.7':
+ '@babel/parser@7.26.2':
dependencies:
- '@babel/template': 7.25.7
- '@babel/types': 7.25.8
+ '@babel/types': 7.26.0
- '@babel/highlight@7.25.7':
- dependencies:
- '@babel/helper-validator-identifier': 7.25.7
- chalk: 2.4.2
- js-tokens: 4.0.0
- picocolors: 1.1.0
-
- '@babel/parser@7.25.8':
- dependencies:
- '@babel/types': 7.25.8
-
- '@babel/runtime@7.25.7':
+ '@babel/runtime@7.26.0':
dependencies:
regenerator-runtime: 0.14.1
- '@babel/template@7.25.7':
+ '@babel/template@7.25.9':
dependencies:
- '@babel/code-frame': 7.25.7
- '@babel/parser': 7.25.8
- '@babel/types': 7.25.8
+ '@babel/code-frame': 7.26.2
+ '@babel/parser': 7.26.2
+ '@babel/types': 7.26.0
- '@babel/traverse@7.25.7':
+ '@babel/traverse@7.25.9':
dependencies:
- '@babel/code-frame': 7.25.7
- '@babel/generator': 7.25.7
- '@babel/parser': 7.25.8
- '@babel/template': 7.25.7
- '@babel/types': 7.25.8
+ '@babel/code-frame': 7.26.2
+ '@babel/generator': 7.26.2
+ '@babel/parser': 7.26.2
+ '@babel/template': 7.25.9
+ '@babel/types': 7.26.0
debug: 4.3.7
globals: 11.12.0
transitivePeerDependencies:
- supports-color
- '@babel/types@7.25.8':
+ '@babel/types@7.26.0':
dependencies:
- '@babel/helper-string-parser': 7.25.7
- '@babel/helper-validator-identifier': 7.25.7
- to-fast-properties: 2.0.0
+ '@babel/helper-string-parser': 7.25.9
+ '@babel/helper-validator-identifier': 7.25.9
- '@emnapi/runtime@1.3.0':
+ '@emnapi/runtime@1.3.1':
dependencies:
- tslib: 2.7.0
+ tslib: 2.8.0
optional: true
'@emoji-mart/react@1.1.1(emoji-mart@5.6.0)(react@18.3.1)':
@@ -4248,12 +4301,12 @@ snapshots:
'@emotion/unitless@0.8.1': {}
- '@eslint-community/eslint-utils@4.4.0(eslint@8.57.1)':
+ '@eslint-community/eslint-utils@4.4.1(eslint@8.57.1)':
dependencies:
eslint: 8.57.1
eslint-visitor-keys: 3.4.3
- '@eslint-community/regexpp@4.11.1': {}
+ '@eslint-community/regexpp@4.12.1': {}
'@eslint/eslintrc@2.1.4':
dependencies:
@@ -4275,14 +4328,14 @@ snapshots:
dependencies:
'@floating-ui/utils': 0.2.8
- '@floating-ui/dom@1.6.11':
+ '@floating-ui/dom@1.6.12':
dependencies:
'@floating-ui/core': 1.6.8
'@floating-ui/utils': 0.2.8
'@floating-ui/react-dom@2.1.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1)':
dependencies:
- '@floating-ui/dom': 1.6.11
+ '@floating-ui/dom': 1.6.12
react: 18.3.1
react-dom: 18.3.1(react@18.3.1)
@@ -4292,14 +4345,14 @@ snapshots:
dependencies:
lib0: 0.2.98
- '@hocuspocus/provider@2.13.7(y-protocols@1.0.6(yjs@13.6.19))(yjs@13.6.19)':
+ '@hocuspocus/provider@2.13.7(y-protocols@1.0.6(yjs@13.6.20))(yjs@13.6.20)':
dependencies:
'@hocuspocus/common': 2.13.7
'@lifeomic/attempt': 3.1.0
lib0: 0.2.98
ws: 8.18.0
- y-protocols: 1.0.6(yjs@13.6.19)
- yjs: 13.6.19
+ y-protocols: 1.0.6(yjs@13.6.20)
+ yjs: 13.6.20
transitivePeerDependencies:
- bufferutil
- utf-8-validate
@@ -4316,7 +4369,7 @@ snapshots:
'@humanwhocodes/object-schema@2.0.3': {}
- '@icons-pack/react-simple-icons@10.0.0(react@18.3.1)':
+ '@icons-pack/react-simple-icons@10.1.0(react@18.3.1)':
dependencies:
react: 18.3.1
@@ -4386,7 +4439,7 @@ snapshots:
'@img/sharp-wasm32@0.33.5':
dependencies:
- '@emnapi/runtime': 1.3.0
+ '@emnapi/runtime': 1.3.1
optional: true
'@img/sharp-win32-ia32@0.33.5':
@@ -4430,7 +4483,7 @@ snapshots:
'@next/env@14.2.15': {}
- '@next/eslint-plugin-next@14.2.15':
+ '@next/eslint-plugin-next@14.2.16':
dependencies:
glob: 10.3.10
@@ -4485,7 +4538,7 @@ snapshots:
'@opentelemetry/api@1.9.0': {}
- '@opentelemetry/context-async-hooks@1.26.0(@opentelemetry/api@1.9.0)':
+ '@opentelemetry/context-async-hooks@1.27.0(@opentelemetry/api@1.9.0)':
dependencies:
'@opentelemetry/api': 1.9.0
@@ -4494,10 +4547,15 @@ snapshots:
'@opentelemetry/api': 1.9.0
'@opentelemetry/semantic-conventions': 1.27.0
+ '@opentelemetry/core@1.27.0(@opentelemetry/api@1.9.0)':
+ dependencies:
+ '@opentelemetry/api': 1.9.0
+ '@opentelemetry/semantic-conventions': 1.27.0
+
'@opentelemetry/instrumentation-amqplib@0.42.0(@opentelemetry/api@1.9.0)':
dependencies:
'@opentelemetry/api': 1.9.0
- '@opentelemetry/core': 1.26.0(@opentelemetry/api@1.9.0)
+ '@opentelemetry/core': 1.27.0(@opentelemetry/api@1.9.0)
'@opentelemetry/instrumentation': 0.53.0(@opentelemetry/api@1.9.0)
'@opentelemetry/semantic-conventions': 1.27.0
transitivePeerDependencies:
@@ -4506,7 +4564,7 @@ snapshots:
'@opentelemetry/instrumentation-connect@0.39.0(@opentelemetry/api@1.9.0)':
dependencies:
'@opentelemetry/api': 1.9.0
- '@opentelemetry/core': 1.26.0(@opentelemetry/api@1.9.0)
+ '@opentelemetry/core': 1.27.0(@opentelemetry/api@1.9.0)
'@opentelemetry/instrumentation': 0.53.0(@opentelemetry/api@1.9.0)
'@opentelemetry/semantic-conventions': 1.27.0
'@types/connect': 3.4.36
@@ -4520,19 +4578,19 @@ snapshots:
transitivePeerDependencies:
- supports-color
- '@opentelemetry/instrumentation-express@0.42.0(@opentelemetry/api@1.9.0)':
+ '@opentelemetry/instrumentation-express@0.43.0(@opentelemetry/api@1.9.0)':
dependencies:
'@opentelemetry/api': 1.9.0
- '@opentelemetry/core': 1.26.0(@opentelemetry/api@1.9.0)
+ '@opentelemetry/core': 1.27.0(@opentelemetry/api@1.9.0)
'@opentelemetry/instrumentation': 0.53.0(@opentelemetry/api@1.9.0)
'@opentelemetry/semantic-conventions': 1.27.0
transitivePeerDependencies:
- supports-color
- '@opentelemetry/instrumentation-fastify@0.39.0(@opentelemetry/api@1.9.0)':
+ '@opentelemetry/instrumentation-fastify@0.40.0(@opentelemetry/api@1.9.0)':
dependencies:
'@opentelemetry/api': 1.9.0
- '@opentelemetry/core': 1.26.0(@opentelemetry/api@1.9.0)
+ '@opentelemetry/core': 1.27.0(@opentelemetry/api@1.9.0)
'@opentelemetry/instrumentation': 0.53.0(@opentelemetry/api@1.9.0)
'@opentelemetry/semantic-conventions': 1.27.0
transitivePeerDependencies:
@@ -4541,7 +4599,7 @@ snapshots:
'@opentelemetry/instrumentation-fs@0.15.0(@opentelemetry/api@1.9.0)':
dependencies:
'@opentelemetry/api': 1.9.0
- '@opentelemetry/core': 1.26.0(@opentelemetry/api@1.9.0)
+ '@opentelemetry/core': 1.27.0(@opentelemetry/api@1.9.0)
'@opentelemetry/instrumentation': 0.53.0(@opentelemetry/api@1.9.0)
transitivePeerDependencies:
- supports-color
@@ -4563,7 +4621,7 @@ snapshots:
'@opentelemetry/instrumentation-hapi@0.41.0(@opentelemetry/api@1.9.0)':
dependencies:
'@opentelemetry/api': 1.9.0
- '@opentelemetry/core': 1.26.0(@opentelemetry/api@1.9.0)
+ '@opentelemetry/core': 1.27.0(@opentelemetry/api@1.9.0)
'@opentelemetry/instrumentation': 0.53.0(@opentelemetry/api@1.9.0)
'@opentelemetry/semantic-conventions': 1.27.0
transitivePeerDependencies:
@@ -4599,7 +4657,7 @@ snapshots:
'@opentelemetry/instrumentation-koa@0.43.0(@opentelemetry/api@1.9.0)':
dependencies:
'@opentelemetry/api': 1.9.0
- '@opentelemetry/core': 1.26.0(@opentelemetry/api@1.9.0)
+ '@opentelemetry/core': 1.27.0(@opentelemetry/api@1.9.0)
'@opentelemetry/instrumentation': 0.53.0(@opentelemetry/api@1.9.0)
'@opentelemetry/semantic-conventions': 1.27.0
transitivePeerDependencies:
@@ -4616,7 +4674,7 @@ snapshots:
dependencies:
'@opentelemetry/api': 1.9.0
'@opentelemetry/instrumentation': 0.53.0(@opentelemetry/api@1.9.0)
- '@opentelemetry/sdk-metrics': 1.26.0(@opentelemetry/api@1.9.0)
+ '@opentelemetry/sdk-metrics': 1.27.0(@opentelemetry/api@1.9.0)
'@opentelemetry/semantic-conventions': 1.27.0
transitivePeerDependencies:
- supports-color
@@ -4624,7 +4682,7 @@ snapshots:
'@opentelemetry/instrumentation-mongoose@0.42.0(@opentelemetry/api@1.9.0)':
dependencies:
'@opentelemetry/api': 1.9.0
- '@opentelemetry/core': 1.26.0(@opentelemetry/api@1.9.0)
+ '@opentelemetry/core': 1.27.0(@opentelemetry/api@1.9.0)
'@opentelemetry/instrumentation': 0.53.0(@opentelemetry/api@1.9.0)
'@opentelemetry/semantic-conventions': 1.27.0
transitivePeerDependencies:
@@ -4679,7 +4737,7 @@ snapshots:
'@opentelemetry/instrumentation-undici@0.6.0(@opentelemetry/api@1.9.0)':
dependencies:
'@opentelemetry/api': 1.9.0
- '@opentelemetry/core': 1.26.0(@opentelemetry/api@1.9.0)
+ '@opentelemetry/core': 1.27.0(@opentelemetry/api@1.9.0)
'@opentelemetry/instrumentation': 0.53.0(@opentelemetry/api@1.9.0)
transitivePeerDependencies:
- supports-color
@@ -4710,23 +4768,23 @@ snapshots:
'@opentelemetry/redis-common@0.36.2': {}
- '@opentelemetry/resources@1.26.0(@opentelemetry/api@1.9.0)':
+ '@opentelemetry/resources@1.27.0(@opentelemetry/api@1.9.0)':
dependencies:
'@opentelemetry/api': 1.9.0
- '@opentelemetry/core': 1.26.0(@opentelemetry/api@1.9.0)
+ '@opentelemetry/core': 1.27.0(@opentelemetry/api@1.9.0)
'@opentelemetry/semantic-conventions': 1.27.0
- '@opentelemetry/sdk-metrics@1.26.0(@opentelemetry/api@1.9.0)':
+ '@opentelemetry/sdk-metrics@1.27.0(@opentelemetry/api@1.9.0)':
dependencies:
'@opentelemetry/api': 1.9.0
- '@opentelemetry/core': 1.26.0(@opentelemetry/api@1.9.0)
- '@opentelemetry/resources': 1.26.0(@opentelemetry/api@1.9.0)
+ '@opentelemetry/core': 1.27.0(@opentelemetry/api@1.9.0)
+ '@opentelemetry/resources': 1.27.0(@opentelemetry/api@1.9.0)
- '@opentelemetry/sdk-trace-base@1.26.0(@opentelemetry/api@1.9.0)':
+ '@opentelemetry/sdk-trace-base@1.27.0(@opentelemetry/api@1.9.0)':
dependencies:
'@opentelemetry/api': 1.9.0
- '@opentelemetry/core': 1.26.0(@opentelemetry/api@1.9.0)
- '@opentelemetry/resources': 1.26.0(@opentelemetry/api@1.9.0)
+ '@opentelemetry/core': 1.27.0(@opentelemetry/api@1.9.0)
+ '@opentelemetry/resources': 1.27.0(@opentelemetry/api@1.9.0)
'@opentelemetry/semantic-conventions': 1.27.0
'@opentelemetry/semantic-conventions@1.27.0': {}
@@ -4734,7 +4792,7 @@ snapshots:
'@opentelemetry/sql-common@0.40.1(@opentelemetry/api@1.9.0)':
dependencies:
'@opentelemetry/api': 1.9.0
- '@opentelemetry/core': 1.26.0(@opentelemetry/api@1.9.0)
+ '@opentelemetry/core': 1.27.0(@opentelemetry/api@1.9.0)
'@panva/hkdf@1.2.1': {}
@@ -4747,15 +4805,17 @@ snapshots:
dependencies:
'@opentelemetry/api': 1.9.0
'@opentelemetry/instrumentation': 0.52.1(@opentelemetry/api@1.9.0)
- '@opentelemetry/sdk-trace-base': 1.26.0(@opentelemetry/api@1.9.0)
+ '@opentelemetry/sdk-trace-base': 1.27.0(@opentelemetry/api@1.9.0)
transitivePeerDependencies:
- supports-color
'@radix-ui/colors@0.1.9': {}
+ '@radix-ui/number@1.1.0': {}
+
'@radix-ui/primitive@1.0.1':
dependencies:
- '@babel/runtime': 7.25.7
+ '@babel/runtime': 7.26.0
'@radix-ui/primitive@1.1.0': {}
@@ -4791,7 +4851,7 @@ snapshots:
'@radix-ui/react-compose-refs@1.0.1(@types/react@18.2.74)(react@18.3.1)':
dependencies:
- '@babel/runtime': 7.25.7
+ '@babel/runtime': 7.26.0
react: 18.3.1
optionalDependencies:
'@types/react': 18.2.74
@@ -4804,7 +4864,7 @@ snapshots:
'@radix-ui/react-context@1.0.1(@types/react@18.2.74)(react@18.3.1)':
dependencies:
- '@babel/runtime': 7.25.7
+ '@babel/runtime': 7.26.0
react: 18.3.1
optionalDependencies:
'@types/react': 18.2.74
@@ -4896,7 +4956,7 @@ snapshots:
'@radix-ui/react-form@0.0.3(@types/react-dom@18.2.23)(@types/react@18.2.74)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)':
dependencies:
- '@babel/runtime': 7.25.7
+ '@babel/runtime': 7.26.0
'@radix-ui/primitive': 1.0.1
'@radix-ui/react-compose-refs': 1.0.1(@types/react@18.2.74)(react@18.3.1)
'@radix-ui/react-context': 1.0.1(@types/react@18.2.74)(react@18.3.1)
@@ -4909,13 +4969,13 @@ snapshots:
'@types/react': 18.2.74
'@types/react-dom': 18.2.23
- '@radix-ui/react-icons@1.3.0(react@18.3.1)':
+ '@radix-ui/react-icons@1.3.1(react@18.3.1)':
dependencies:
react: 18.3.1
'@radix-ui/react-id@1.0.1(@types/react@18.2.74)(react@18.3.1)':
dependencies:
- '@babel/runtime': 7.25.7
+ '@babel/runtime': 7.26.0
'@radix-ui/react-use-layout-effect': 1.0.1(@types/react@18.2.74)(react@18.3.1)
react: 18.3.1
optionalDependencies:
@@ -4930,7 +4990,7 @@ snapshots:
'@radix-ui/react-label@2.0.2(@types/react-dom@18.2.23)(@types/react@18.2.74)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)':
dependencies:
- '@babel/runtime': 7.25.7
+ '@babel/runtime': 7.26.0
'@radix-ui/react-primitive': 1.0.3(@types/react-dom@18.2.23)(@types/react@18.2.74)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
react: 18.3.1
react-dom: 18.3.1(react@18.3.1)
@@ -4938,6 +4998,15 @@ snapshots:
'@types/react': 18.2.74
'@types/react-dom': 18.2.23
+ '@radix-ui/react-label@2.1.0(@types/react-dom@18.2.23)(@types/react@18.2.74)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)':
+ dependencies:
+ '@radix-ui/react-primitive': 2.0.0(@types/react-dom@18.2.23)(@types/react@18.2.74)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
+ react: 18.3.1
+ react-dom: 18.3.1(react@18.3.1)
+ optionalDependencies:
+ '@types/react': 18.2.74
+ '@types/react-dom': 18.2.23
+
'@radix-ui/react-menu@2.1.2(@types/react-dom@18.2.23)(@types/react@18.2.74)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)':
dependencies:
'@radix-ui/primitive': 1.1.0
@@ -5004,7 +5073,7 @@ snapshots:
'@radix-ui/react-primitive@1.0.3(@types/react-dom@18.2.23)(@types/react@18.2.74)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)':
dependencies:
- '@babel/runtime': 7.25.7
+ '@babel/runtime': 7.26.0
'@radix-ui/react-slot': 1.0.2(@types/react@18.2.74)(react@18.3.1)
react: 18.3.1
react-dom: 18.3.1(react@18.3.1)
@@ -5038,9 +5107,38 @@ snapshots:
'@types/react': 18.2.74
'@types/react-dom': 18.2.23
+ '@radix-ui/react-select@2.1.2(@types/react-dom@18.2.23)(@types/react@18.2.74)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)':
+ dependencies:
+ '@radix-ui/number': 1.1.0
+ '@radix-ui/primitive': 1.1.0
+ '@radix-ui/react-collection': 1.1.0(@types/react-dom@18.2.23)(@types/react@18.2.74)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
+ '@radix-ui/react-compose-refs': 1.1.0(@types/react@18.2.74)(react@18.3.1)
+ '@radix-ui/react-context': 1.1.1(@types/react@18.2.74)(react@18.3.1)
+ '@radix-ui/react-direction': 1.1.0(@types/react@18.2.74)(react@18.3.1)
+ '@radix-ui/react-dismissable-layer': 1.1.1(@types/react-dom@18.2.23)(@types/react@18.2.74)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
+ '@radix-ui/react-focus-guards': 1.1.1(@types/react@18.2.74)(react@18.3.1)
+ '@radix-ui/react-focus-scope': 1.1.0(@types/react-dom@18.2.23)(@types/react@18.2.74)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
+ '@radix-ui/react-id': 1.1.0(@types/react@18.2.74)(react@18.3.1)
+ '@radix-ui/react-popper': 1.2.0(@types/react-dom@18.2.23)(@types/react@18.2.74)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
+ '@radix-ui/react-portal': 1.1.2(@types/react-dom@18.2.23)(@types/react@18.2.74)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
+ '@radix-ui/react-primitive': 2.0.0(@types/react-dom@18.2.23)(@types/react@18.2.74)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
+ '@radix-ui/react-slot': 1.1.0(@types/react@18.2.74)(react@18.3.1)
+ '@radix-ui/react-use-callback-ref': 1.1.0(@types/react@18.2.74)(react@18.3.1)
+ '@radix-ui/react-use-controllable-state': 1.1.0(@types/react@18.2.74)(react@18.3.1)
+ '@radix-ui/react-use-layout-effect': 1.1.0(@types/react@18.2.74)(react@18.3.1)
+ '@radix-ui/react-use-previous': 1.1.0(@types/react@18.2.74)(react@18.3.1)
+ '@radix-ui/react-visually-hidden': 1.1.0(@types/react-dom@18.2.23)(@types/react@18.2.74)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
+ aria-hidden: 1.2.4
+ react: 18.3.1
+ react-dom: 18.3.1(react@18.3.1)
+ react-remove-scroll: 2.6.0(@types/react@18.2.74)(react@18.3.1)
+ optionalDependencies:
+ '@types/react': 18.2.74
+ '@types/react-dom': 18.2.23
+
'@radix-ui/react-slot@1.0.2(@types/react@18.2.74)(react@18.3.1)':
dependencies:
- '@babel/runtime': 7.25.7
+ '@babel/runtime': 7.26.0
'@radix-ui/react-compose-refs': 1.0.1(@types/react@18.2.74)(react@18.3.1)
react: 18.3.1
optionalDependencies:
@@ -5084,6 +5182,32 @@ snapshots:
'@types/react': 18.2.74
'@types/react-dom': 18.2.23
+ '@radix-ui/react-toggle-group@1.1.0(@types/react-dom@18.2.23)(@types/react@18.2.74)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)':
+ dependencies:
+ '@radix-ui/primitive': 1.1.0
+ '@radix-ui/react-context': 1.1.0(@types/react@18.2.74)(react@18.3.1)
+ '@radix-ui/react-direction': 1.1.0(@types/react@18.2.74)(react@18.3.1)
+ '@radix-ui/react-primitive': 2.0.0(@types/react-dom@18.2.23)(@types/react@18.2.74)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
+ '@radix-ui/react-roving-focus': 1.1.0(@types/react-dom@18.2.23)(@types/react@18.2.74)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
+ '@radix-ui/react-toggle': 1.1.0(@types/react-dom@18.2.23)(@types/react@18.2.74)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
+ '@radix-ui/react-use-controllable-state': 1.1.0(@types/react@18.2.74)(react@18.3.1)
+ react: 18.3.1
+ react-dom: 18.3.1(react@18.3.1)
+ optionalDependencies:
+ '@types/react': 18.2.74
+ '@types/react-dom': 18.2.23
+
+ '@radix-ui/react-toggle@1.1.0(@types/react-dom@18.2.23)(@types/react@18.2.74)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)':
+ dependencies:
+ '@radix-ui/primitive': 1.1.0
+ '@radix-ui/react-primitive': 2.0.0(@types/react-dom@18.2.23)(@types/react@18.2.74)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
+ '@radix-ui/react-use-controllable-state': 1.1.0(@types/react@18.2.74)(react@18.3.1)
+ react: 18.3.1
+ react-dom: 18.3.1(react@18.3.1)
+ optionalDependencies:
+ '@types/react': 18.2.74
+ '@types/react-dom': 18.2.23
+
'@radix-ui/react-tooltip@1.1.3(@types/react-dom@18.2.23)(@types/react@18.2.74)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)':
dependencies:
'@radix-ui/primitive': 1.1.0
@@ -5126,7 +5250,7 @@ snapshots:
'@radix-ui/react-use-layout-effect@1.0.1(@types/react@18.2.74)(react@18.3.1)':
dependencies:
- '@babel/runtime': 7.25.7
+ '@babel/runtime': 7.26.0
react: 18.3.1
optionalDependencies:
'@types/react': 18.2.74
@@ -5172,20 +5296,20 @@ snapshots:
'@rollup/plugin-commonjs@26.0.1(rollup@3.29.5)':
dependencies:
- '@rollup/pluginutils': 5.1.2(rollup@3.29.5)
+ '@rollup/pluginutils': 5.1.3(rollup@3.29.5)
commondir: 1.0.1
estree-walker: 2.0.2
glob: 10.4.5
is-reference: 1.2.1
- magic-string: 0.30.11
+ magic-string: 0.30.12
optionalDependencies:
rollup: 3.29.5
- '@rollup/pluginutils@5.1.2(rollup@3.29.5)':
+ '@rollup/pluginutils@5.1.3(rollup@3.29.5)':
dependencies:
'@types/estree': 1.0.6
estree-walker: 2.0.2
- picomatch: 2.3.1
+ picomatch: 4.0.2
optionalDependencies:
rollup: 3.29.5
@@ -5193,49 +5317,49 @@ snapshots:
'@rushstack/eslint-patch@1.10.4': {}
- '@sentry-internal/browser-utils@8.34.0':
+ '@sentry-internal/browser-utils@8.35.0':
dependencies:
- '@sentry/core': 8.34.0
- '@sentry/types': 8.34.0
- '@sentry/utils': 8.34.0
+ '@sentry/core': 8.35.0
+ '@sentry/types': 8.35.0
+ '@sentry/utils': 8.35.0
- '@sentry-internal/feedback@8.34.0':
+ '@sentry-internal/feedback@8.35.0':
dependencies:
- '@sentry/core': 8.34.0
- '@sentry/types': 8.34.0
- '@sentry/utils': 8.34.0
+ '@sentry/core': 8.35.0
+ '@sentry/types': 8.35.0
+ '@sentry/utils': 8.35.0
- '@sentry-internal/replay-canvas@8.34.0':
+ '@sentry-internal/replay-canvas@8.35.0':
dependencies:
- '@sentry-internal/replay': 8.34.0
- '@sentry/core': 8.34.0
- '@sentry/types': 8.34.0
- '@sentry/utils': 8.34.0
+ '@sentry-internal/replay': 8.35.0
+ '@sentry/core': 8.35.0
+ '@sentry/types': 8.35.0
+ '@sentry/utils': 8.35.0
- '@sentry-internal/replay@8.34.0':
+ '@sentry-internal/replay@8.35.0':
dependencies:
- '@sentry-internal/browser-utils': 8.34.0
- '@sentry/core': 8.34.0
- '@sentry/types': 8.34.0
- '@sentry/utils': 8.34.0
+ '@sentry-internal/browser-utils': 8.35.0
+ '@sentry/core': 8.35.0
+ '@sentry/types': 8.35.0
+ '@sentry/utils': 8.35.0
'@sentry/babel-plugin-component-annotate@2.22.3': {}
- '@sentry/browser@8.34.0':
+ '@sentry/browser@8.35.0':
dependencies:
- '@sentry-internal/browser-utils': 8.34.0
- '@sentry-internal/feedback': 8.34.0
- '@sentry-internal/replay': 8.34.0
- '@sentry-internal/replay-canvas': 8.34.0
- '@sentry/core': 8.34.0
- '@sentry/types': 8.34.0
- '@sentry/utils': 8.34.0
+ '@sentry-internal/browser-utils': 8.35.0
+ '@sentry-internal/feedback': 8.35.0
+ '@sentry-internal/replay': 8.35.0
+ '@sentry-internal/replay-canvas': 8.35.0
+ '@sentry/core': 8.35.0
+ '@sentry/types': 8.35.0
+ '@sentry/utils': 8.35.0
'@sentry/bundler-plugin-core@2.22.3':
dependencies:
- '@babel/core': 7.25.8
+ '@babel/core': 7.26.0
'@sentry/babel-plugin-component-annotate': 2.22.3
- '@sentry/cli': 2.37.0
+ '@sentry/cli': 2.38.1
dotenv: 16.4.5
find-up: 5.0.0
glob: 9.3.5
@@ -5245,28 +5369,28 @@ snapshots:
- encoding
- supports-color
- '@sentry/cli-darwin@2.37.0':
+ '@sentry/cli-darwin@2.38.1':
optional: true
- '@sentry/cli-linux-arm64@2.37.0':
+ '@sentry/cli-linux-arm64@2.38.1':
optional: true
- '@sentry/cli-linux-arm@2.37.0':
+ '@sentry/cli-linux-arm@2.38.1':
optional: true
- '@sentry/cli-linux-i686@2.37.0':
+ '@sentry/cli-linux-i686@2.38.1':
optional: true
- '@sentry/cli-linux-x64@2.37.0':
+ '@sentry/cli-linux-x64@2.38.1':
optional: true
- '@sentry/cli-win32-i686@2.37.0':
+ '@sentry/cli-win32-i686@2.38.1':
optional: true
- '@sentry/cli-win32-x64@2.37.0':
+ '@sentry/cli-win32-x64@2.38.1':
optional: true
- '@sentry/cli@2.37.0':
+ '@sentry/cli@2.38.1':
dependencies:
https-proxy-agent: 5.0.1
node-fetch: 2.7.0
@@ -5274,38 +5398,38 @@ snapshots:
proxy-from-env: 1.1.0
which: 2.0.2
optionalDependencies:
- '@sentry/cli-darwin': 2.37.0
- '@sentry/cli-linux-arm': 2.37.0
- '@sentry/cli-linux-arm64': 2.37.0
- '@sentry/cli-linux-i686': 2.37.0
- '@sentry/cli-linux-x64': 2.37.0
- '@sentry/cli-win32-i686': 2.37.0
- '@sentry/cli-win32-x64': 2.37.0
+ '@sentry/cli-darwin': 2.38.1
+ '@sentry/cli-linux-arm': 2.38.1
+ '@sentry/cli-linux-arm64': 2.38.1
+ '@sentry/cli-linux-i686': 2.38.1
+ '@sentry/cli-linux-x64': 2.38.1
+ '@sentry/cli-win32-i686': 2.38.1
+ '@sentry/cli-win32-x64': 2.38.1
transitivePeerDependencies:
- encoding
- supports-color
- '@sentry/core@8.34.0':
+ '@sentry/core@8.35.0':
dependencies:
- '@sentry/types': 8.34.0
- '@sentry/utils': 8.34.0
+ '@sentry/types': 8.35.0
+ '@sentry/utils': 8.35.0
- '@sentry/nextjs@8.34.0(@opentelemetry/api@1.9.0)(@opentelemetry/core@1.26.0(@opentelemetry/api@1.9.0))(@opentelemetry/instrumentation@0.53.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@1.26.0(@opentelemetry/api@1.9.0))(next@14.2.15(@babel/core@7.25.8)(@opentelemetry/api@1.9.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react@18.3.1)(webpack@5.94.0)':
+ '@sentry/nextjs@8.35.0(@opentelemetry/api@1.9.0)(@opentelemetry/core@1.27.0(@opentelemetry/api@1.9.0))(@opentelemetry/instrumentation@0.53.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@1.27.0(@opentelemetry/api@1.9.0))(next@14.2.15(@babel/core@7.26.0)(@opentelemetry/api@1.9.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react@18.3.1)(webpack@5.94.0)':
dependencies:
'@opentelemetry/instrumentation-http': 0.53.0(@opentelemetry/api@1.9.0)
'@opentelemetry/semantic-conventions': 1.27.0
'@rollup/plugin-commonjs': 26.0.1(rollup@3.29.5)
- '@sentry-internal/browser-utils': 8.34.0
- '@sentry/core': 8.34.0
- '@sentry/node': 8.34.0
- '@sentry/opentelemetry': 8.34.0(@opentelemetry/api@1.9.0)(@opentelemetry/core@1.26.0(@opentelemetry/api@1.9.0))(@opentelemetry/instrumentation@0.53.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@1.26.0(@opentelemetry/api@1.9.0))(@opentelemetry/semantic-conventions@1.27.0)
- '@sentry/react': 8.34.0(react@18.3.1)
- '@sentry/types': 8.34.0
- '@sentry/utils': 8.34.0
- '@sentry/vercel-edge': 8.34.0
+ '@sentry-internal/browser-utils': 8.35.0
+ '@sentry/core': 8.35.0
+ '@sentry/node': 8.35.0
+ '@sentry/opentelemetry': 8.35.0(@opentelemetry/api@1.9.0)(@opentelemetry/core@1.27.0(@opentelemetry/api@1.9.0))(@opentelemetry/instrumentation@0.53.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@1.27.0(@opentelemetry/api@1.9.0))(@opentelemetry/semantic-conventions@1.27.0)
+ '@sentry/react': 8.35.0(react@18.3.1)
+ '@sentry/types': 8.35.0
+ '@sentry/utils': 8.35.0
+ '@sentry/vercel-edge': 8.35.0
'@sentry/webpack-plugin': 2.22.3(webpack@5.94.0)
chalk: 3.0.0
- next: 14.2.15(@babel/core@7.25.8)(@opentelemetry/api@1.9.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
+ next: 14.2.15(@babel/core@7.26.0)(@opentelemetry/api@1.9.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
resolve: 1.22.8
rollup: 3.29.5
stacktrace-parser: 0.1.10
@@ -5320,17 +5444,17 @@ snapshots:
- react
- supports-color
- '@sentry/node@8.34.0':
+ '@sentry/node@8.35.0':
dependencies:
'@opentelemetry/api': 1.9.0
- '@opentelemetry/context-async-hooks': 1.26.0(@opentelemetry/api@1.9.0)
- '@opentelemetry/core': 1.26.0(@opentelemetry/api@1.9.0)
+ '@opentelemetry/context-async-hooks': 1.27.0(@opentelemetry/api@1.9.0)
+ '@opentelemetry/core': 1.27.0(@opentelemetry/api@1.9.0)
'@opentelemetry/instrumentation': 0.53.0(@opentelemetry/api@1.9.0)
'@opentelemetry/instrumentation-amqplib': 0.42.0(@opentelemetry/api@1.9.0)
'@opentelemetry/instrumentation-connect': 0.39.0(@opentelemetry/api@1.9.0)
'@opentelemetry/instrumentation-dataloader': 0.12.0(@opentelemetry/api@1.9.0)
- '@opentelemetry/instrumentation-express': 0.42.0(@opentelemetry/api@1.9.0)
- '@opentelemetry/instrumentation-fastify': 0.39.0(@opentelemetry/api@1.9.0)
+ '@opentelemetry/instrumentation-express': 0.43.0(@opentelemetry/api@1.9.0)
+ '@opentelemetry/instrumentation-fastify': 0.40.0(@opentelemetry/api@1.9.0)
'@opentelemetry/instrumentation-fs': 0.15.0(@opentelemetry/api@1.9.0)
'@opentelemetry/instrumentation-generic-pool': 0.39.0(@opentelemetry/api@1.9.0)
'@opentelemetry/instrumentation-graphql': 0.43.0(@opentelemetry/api@1.9.0)
@@ -5348,49 +5472,49 @@ snapshots:
'@opentelemetry/instrumentation-pg': 0.44.0(@opentelemetry/api@1.9.0)
'@opentelemetry/instrumentation-redis-4': 0.42.0(@opentelemetry/api@1.9.0)
'@opentelemetry/instrumentation-undici': 0.6.0(@opentelemetry/api@1.9.0)
- '@opentelemetry/resources': 1.26.0(@opentelemetry/api@1.9.0)
- '@opentelemetry/sdk-trace-base': 1.26.0(@opentelemetry/api@1.9.0)
+ '@opentelemetry/resources': 1.27.0(@opentelemetry/api@1.9.0)
+ '@opentelemetry/sdk-trace-base': 1.27.0(@opentelemetry/api@1.9.0)
'@opentelemetry/semantic-conventions': 1.27.0
'@prisma/instrumentation': 5.19.1
- '@sentry/core': 8.34.0
- '@sentry/opentelemetry': 8.34.0(@opentelemetry/api@1.9.0)(@opentelemetry/core@1.26.0(@opentelemetry/api@1.9.0))(@opentelemetry/instrumentation@0.53.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@1.26.0(@opentelemetry/api@1.9.0))(@opentelemetry/semantic-conventions@1.27.0)
- '@sentry/types': 8.34.0
- '@sentry/utils': 8.34.0
+ '@sentry/core': 8.35.0
+ '@sentry/opentelemetry': 8.35.0(@opentelemetry/api@1.9.0)(@opentelemetry/core@1.27.0(@opentelemetry/api@1.9.0))(@opentelemetry/instrumentation@0.53.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@1.27.0(@opentelemetry/api@1.9.0))(@opentelemetry/semantic-conventions@1.27.0)
+ '@sentry/types': 8.35.0
+ '@sentry/utils': 8.35.0
import-in-the-middle: 1.11.2
transitivePeerDependencies:
- supports-color
- '@sentry/opentelemetry@8.34.0(@opentelemetry/api@1.9.0)(@opentelemetry/core@1.26.0(@opentelemetry/api@1.9.0))(@opentelemetry/instrumentation@0.53.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@1.26.0(@opentelemetry/api@1.9.0))(@opentelemetry/semantic-conventions@1.27.0)':
+ '@sentry/opentelemetry@8.35.0(@opentelemetry/api@1.9.0)(@opentelemetry/core@1.27.0(@opentelemetry/api@1.9.0))(@opentelemetry/instrumentation@0.53.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@1.27.0(@opentelemetry/api@1.9.0))(@opentelemetry/semantic-conventions@1.27.0)':
dependencies:
'@opentelemetry/api': 1.9.0
- '@opentelemetry/core': 1.26.0(@opentelemetry/api@1.9.0)
+ '@opentelemetry/core': 1.27.0(@opentelemetry/api@1.9.0)
'@opentelemetry/instrumentation': 0.53.0(@opentelemetry/api@1.9.0)
- '@opentelemetry/sdk-trace-base': 1.26.0(@opentelemetry/api@1.9.0)
+ '@opentelemetry/sdk-trace-base': 1.27.0(@opentelemetry/api@1.9.0)
'@opentelemetry/semantic-conventions': 1.27.0
- '@sentry/core': 8.34.0
- '@sentry/types': 8.34.0
- '@sentry/utils': 8.34.0
+ '@sentry/core': 8.35.0
+ '@sentry/types': 8.35.0
+ '@sentry/utils': 8.35.0
- '@sentry/react@8.34.0(react@18.3.1)':
+ '@sentry/react@8.35.0(react@18.3.1)':
dependencies:
- '@sentry/browser': 8.34.0
- '@sentry/core': 8.34.0
- '@sentry/types': 8.34.0
- '@sentry/utils': 8.34.0
+ '@sentry/browser': 8.35.0
+ '@sentry/core': 8.35.0
+ '@sentry/types': 8.35.0
+ '@sentry/utils': 8.35.0
hoist-non-react-statics: 3.3.2
react: 18.3.1
- '@sentry/types@8.34.0': {}
+ '@sentry/types@8.35.0': {}
- '@sentry/utils@8.34.0':
+ '@sentry/utils@8.35.0':
dependencies:
- '@sentry/types': 8.34.0
+ '@sentry/types': 8.35.0
- '@sentry/vercel-edge@8.34.0':
+ '@sentry/vercel-edge@8.35.0':
dependencies:
- '@sentry/core': 8.34.0
- '@sentry/types': 8.34.0
- '@sentry/utils': 8.34.0
+ '@sentry/core': 8.35.0
+ '@sentry/types': 8.35.0
+ '@sentry/utils': 8.35.0
'@sentry/webpack-plugin@2.22.3(webpack@5.94.0)':
dependencies:
@@ -5411,160 +5535,164 @@ snapshots:
'@swc/helpers@0.5.5':
dependencies:
'@swc/counter': 0.1.3
- tslib: 2.7.0
+ tslib: 2.8.0
- '@tiptap/core@2.8.0(@tiptap/pm@2.8.0)':
+ '@tanstack/react-table@8.20.5(react-dom@18.3.1(react@18.3.1))(react@18.3.1)':
dependencies:
- '@tiptap/pm': 2.8.0
+ '@tanstack/table-core': 8.20.5
+ react: 18.3.1
+ react-dom: 18.3.1(react@18.3.1)
- '@tiptap/extension-blockquote@2.8.0(@tiptap/core@2.8.0(@tiptap/pm@2.8.0))':
- dependencies:
- '@tiptap/core': 2.8.0(@tiptap/pm@2.8.0)
+ '@tanstack/table-core@8.20.5': {}
- '@tiptap/extension-bold@2.8.0(@tiptap/core@2.8.0(@tiptap/pm@2.8.0))':
+ '@tiptap/core@2.9.1(@tiptap/pm@2.9.1)':
dependencies:
- '@tiptap/core': 2.8.0(@tiptap/pm@2.8.0)
+ '@tiptap/pm': 2.9.1
- '@tiptap/extension-bubble-menu@2.8.0(@tiptap/core@2.8.0(@tiptap/pm@2.8.0))(@tiptap/pm@2.8.0)':
+ '@tiptap/extension-blockquote@2.9.1(@tiptap/core@2.9.1(@tiptap/pm@2.9.1))':
dependencies:
- '@tiptap/core': 2.8.0(@tiptap/pm@2.8.0)
- '@tiptap/pm': 2.8.0
+ '@tiptap/core': 2.9.1(@tiptap/pm@2.9.1)
+
+ '@tiptap/extension-bold@2.9.1(@tiptap/core@2.9.1(@tiptap/pm@2.9.1))':
+ dependencies:
+ '@tiptap/core': 2.9.1(@tiptap/pm@2.9.1)
+
+ '@tiptap/extension-bubble-menu@2.9.1(@tiptap/core@2.9.1(@tiptap/pm@2.9.1))(@tiptap/pm@2.9.1)':
+ dependencies:
+ '@tiptap/core': 2.9.1(@tiptap/pm@2.9.1)
+ '@tiptap/pm': 2.9.1
tippy.js: 6.3.7
- '@tiptap/extension-bullet-list@2.8.0(@tiptap/core@2.8.0(@tiptap/pm@2.8.0))(@tiptap/extension-list-item@2.8.0(@tiptap/core@2.8.0(@tiptap/pm@2.8.0)))(@tiptap/extension-text-style@2.8.0(@tiptap/core@2.8.0(@tiptap/pm@2.8.0)))':
+ '@tiptap/extension-bullet-list@2.9.1(@tiptap/core@2.9.1(@tiptap/pm@2.9.1))':
dependencies:
- '@tiptap/core': 2.8.0(@tiptap/pm@2.8.0)
- '@tiptap/extension-list-item': 2.8.0(@tiptap/core@2.8.0(@tiptap/pm@2.8.0))
- '@tiptap/extension-text-style': 2.8.0(@tiptap/core@2.8.0(@tiptap/pm@2.8.0))
+ '@tiptap/core': 2.9.1(@tiptap/pm@2.9.1)
- '@tiptap/extension-code-block-lowlight@2.8.0(@tiptap/core@2.8.0(@tiptap/pm@2.8.0))(@tiptap/extension-code-block@2.8.0(@tiptap/core@2.8.0(@tiptap/pm@2.8.0))(@tiptap/pm@2.8.0))(@tiptap/pm@2.8.0)(highlight.js@11.10.0)(lowlight@3.1.0)':
+ '@tiptap/extension-code-block-lowlight@2.9.1(@tiptap/core@2.9.1(@tiptap/pm@2.9.1))(@tiptap/extension-code-block@2.9.1(@tiptap/core@2.9.1(@tiptap/pm@2.9.1))(@tiptap/pm@2.9.1))(@tiptap/pm@2.9.1)(highlight.js@11.10.0)(lowlight@3.1.0)':
dependencies:
- '@tiptap/core': 2.8.0(@tiptap/pm@2.8.0)
- '@tiptap/extension-code-block': 2.8.0(@tiptap/core@2.8.0(@tiptap/pm@2.8.0))(@tiptap/pm@2.8.0)
- '@tiptap/pm': 2.8.0
+ '@tiptap/core': 2.9.1(@tiptap/pm@2.9.1)
+ '@tiptap/extension-code-block': 2.9.1(@tiptap/core@2.9.1(@tiptap/pm@2.9.1))(@tiptap/pm@2.9.1)
+ '@tiptap/pm': 2.9.1
highlight.js: 11.10.0
lowlight: 3.1.0
- '@tiptap/extension-code-block@2.8.0(@tiptap/core@2.8.0(@tiptap/pm@2.8.0))(@tiptap/pm@2.8.0)':
+ '@tiptap/extension-code-block@2.9.1(@tiptap/core@2.9.1(@tiptap/pm@2.9.1))(@tiptap/pm@2.9.1)':
dependencies:
- '@tiptap/core': 2.8.0(@tiptap/pm@2.8.0)
- '@tiptap/pm': 2.8.0
+ '@tiptap/core': 2.9.1(@tiptap/pm@2.9.1)
+ '@tiptap/pm': 2.9.1
- '@tiptap/extension-code@2.8.0(@tiptap/core@2.8.0(@tiptap/pm@2.8.0))':
+ '@tiptap/extension-code@2.9.1(@tiptap/core@2.9.1(@tiptap/pm@2.9.1))':
dependencies:
- '@tiptap/core': 2.8.0(@tiptap/pm@2.8.0)
+ '@tiptap/core': 2.9.1(@tiptap/pm@2.9.1)
- '@tiptap/extension-collaboration-cursor@2.8.0(@tiptap/core@2.8.0(@tiptap/pm@2.8.0))(y-prosemirror@1.2.12(prosemirror-model@1.23.0)(prosemirror-state@1.4.3)(prosemirror-view@1.34.3)(y-protocols@1.0.6(yjs@13.6.19))(yjs@13.6.19))':
+ '@tiptap/extension-collaboration-cursor@2.9.1(@tiptap/core@2.9.1(@tiptap/pm@2.9.1))(y-prosemirror@1.2.12(prosemirror-model@1.23.0)(prosemirror-state@1.4.3)(prosemirror-view@1.35.0)(y-protocols@1.0.6(yjs@13.6.20))(yjs@13.6.20))':
dependencies:
- '@tiptap/core': 2.8.0(@tiptap/pm@2.8.0)
- y-prosemirror: 1.2.12(prosemirror-model@1.23.0)(prosemirror-state@1.4.3)(prosemirror-view@1.34.3)(y-protocols@1.0.6(yjs@13.6.19))(yjs@13.6.19)
+ '@tiptap/core': 2.9.1(@tiptap/pm@2.9.1)
+ y-prosemirror: 1.2.12(prosemirror-model@1.23.0)(prosemirror-state@1.4.3)(prosemirror-view@1.35.0)(y-protocols@1.0.6(yjs@13.6.20))(yjs@13.6.20)
- '@tiptap/extension-collaboration@2.8.0(@tiptap/core@2.8.0(@tiptap/pm@2.8.0))(@tiptap/pm@2.8.0)(y-prosemirror@1.2.12(prosemirror-model@1.23.0)(prosemirror-state@1.4.3)(prosemirror-view@1.34.3)(y-protocols@1.0.6(yjs@13.6.19))(yjs@13.6.19))':
+ '@tiptap/extension-collaboration@2.9.1(@tiptap/core@2.9.1(@tiptap/pm@2.9.1))(@tiptap/pm@2.9.1)(y-prosemirror@1.2.12(prosemirror-model@1.23.0)(prosemirror-state@1.4.3)(prosemirror-view@1.35.0)(y-protocols@1.0.6(yjs@13.6.20))(yjs@13.6.20))':
dependencies:
- '@tiptap/core': 2.8.0(@tiptap/pm@2.8.0)
- '@tiptap/pm': 2.8.0
- y-prosemirror: 1.2.12(prosemirror-model@1.23.0)(prosemirror-state@1.4.3)(prosemirror-view@1.34.3)(y-protocols@1.0.6(yjs@13.6.19))(yjs@13.6.19)
+ '@tiptap/core': 2.9.1(@tiptap/pm@2.9.1)
+ '@tiptap/pm': 2.9.1
+ y-prosemirror: 1.2.12(prosemirror-model@1.23.0)(prosemirror-state@1.4.3)(prosemirror-view@1.35.0)(y-protocols@1.0.6(yjs@13.6.20))(yjs@13.6.20)
- '@tiptap/extension-document@2.8.0(@tiptap/core@2.8.0(@tiptap/pm@2.8.0))':
+ '@tiptap/extension-document@2.9.1(@tiptap/core@2.9.1(@tiptap/pm@2.9.1))':
dependencies:
- '@tiptap/core': 2.8.0(@tiptap/pm@2.8.0)
+ '@tiptap/core': 2.9.1(@tiptap/pm@2.9.1)
- '@tiptap/extension-dropcursor@2.8.0(@tiptap/core@2.8.0(@tiptap/pm@2.8.0))(@tiptap/pm@2.8.0)':
+ '@tiptap/extension-dropcursor@2.9.1(@tiptap/core@2.9.1(@tiptap/pm@2.9.1))(@tiptap/pm@2.9.1)':
dependencies:
- '@tiptap/core': 2.8.0(@tiptap/pm@2.8.0)
- '@tiptap/pm': 2.8.0
+ '@tiptap/core': 2.9.1(@tiptap/pm@2.9.1)
+ '@tiptap/pm': 2.9.1
- '@tiptap/extension-floating-menu@2.8.0(@tiptap/core@2.8.0(@tiptap/pm@2.8.0))(@tiptap/pm@2.8.0)':
+ '@tiptap/extension-floating-menu@2.9.1(@tiptap/core@2.9.1(@tiptap/pm@2.9.1))(@tiptap/pm@2.9.1)':
dependencies:
- '@tiptap/core': 2.8.0(@tiptap/pm@2.8.0)
- '@tiptap/pm': 2.8.0
+ '@tiptap/core': 2.9.1(@tiptap/pm@2.9.1)
+ '@tiptap/pm': 2.9.1
tippy.js: 6.3.7
- '@tiptap/extension-gapcursor@2.8.0(@tiptap/core@2.8.0(@tiptap/pm@2.8.0))(@tiptap/pm@2.8.0)':
+ '@tiptap/extension-gapcursor@2.9.1(@tiptap/core@2.9.1(@tiptap/pm@2.9.1))(@tiptap/pm@2.9.1)':
dependencies:
- '@tiptap/core': 2.8.0(@tiptap/pm@2.8.0)
- '@tiptap/pm': 2.8.0
+ '@tiptap/core': 2.9.1(@tiptap/pm@2.9.1)
+ '@tiptap/pm': 2.9.1
- '@tiptap/extension-hard-break@2.8.0(@tiptap/core@2.8.0(@tiptap/pm@2.8.0))':
+ '@tiptap/extension-hard-break@2.9.1(@tiptap/core@2.9.1(@tiptap/pm@2.9.1))':
dependencies:
- '@tiptap/core': 2.8.0(@tiptap/pm@2.8.0)
+ '@tiptap/core': 2.9.1(@tiptap/pm@2.9.1)
- '@tiptap/extension-heading@2.8.0(@tiptap/core@2.8.0(@tiptap/pm@2.8.0))':
+ '@tiptap/extension-heading@2.9.1(@tiptap/core@2.9.1(@tiptap/pm@2.9.1))':
dependencies:
- '@tiptap/core': 2.8.0(@tiptap/pm@2.8.0)
+ '@tiptap/core': 2.9.1(@tiptap/pm@2.9.1)
- '@tiptap/extension-history@2.8.0(@tiptap/core@2.8.0(@tiptap/pm@2.8.0))(@tiptap/pm@2.8.0)':
+ '@tiptap/extension-history@2.9.1(@tiptap/core@2.9.1(@tiptap/pm@2.9.1))(@tiptap/pm@2.9.1)':
dependencies:
- '@tiptap/core': 2.8.0(@tiptap/pm@2.8.0)
- '@tiptap/pm': 2.8.0
+ '@tiptap/core': 2.9.1(@tiptap/pm@2.9.1)
+ '@tiptap/pm': 2.9.1
- '@tiptap/extension-horizontal-rule@2.8.0(@tiptap/core@2.8.0(@tiptap/pm@2.8.0))(@tiptap/pm@2.8.0)':
+ '@tiptap/extension-horizontal-rule@2.9.1(@tiptap/core@2.9.1(@tiptap/pm@2.9.1))(@tiptap/pm@2.9.1)':
dependencies:
- '@tiptap/core': 2.8.0(@tiptap/pm@2.8.0)
- '@tiptap/pm': 2.8.0
+ '@tiptap/core': 2.9.1(@tiptap/pm@2.9.1)
+ '@tiptap/pm': 2.9.1
- '@tiptap/extension-italic@2.8.0(@tiptap/core@2.8.0(@tiptap/pm@2.8.0))':
+ '@tiptap/extension-italic@2.9.1(@tiptap/core@2.9.1(@tiptap/pm@2.9.1))':
dependencies:
- '@tiptap/core': 2.8.0(@tiptap/pm@2.8.0)
+ '@tiptap/core': 2.9.1(@tiptap/pm@2.9.1)
- '@tiptap/extension-list-item@2.8.0(@tiptap/core@2.8.0(@tiptap/pm@2.8.0))':
+ '@tiptap/extension-list-item@2.9.1(@tiptap/core@2.9.1(@tiptap/pm@2.9.1))':
dependencies:
- '@tiptap/core': 2.8.0(@tiptap/pm@2.8.0)
+ '@tiptap/core': 2.9.1(@tiptap/pm@2.9.1)
- '@tiptap/extension-ordered-list@2.8.0(@tiptap/core@2.8.0(@tiptap/pm@2.8.0))(@tiptap/extension-list-item@2.8.0(@tiptap/core@2.8.0(@tiptap/pm@2.8.0)))(@tiptap/extension-text-style@2.8.0(@tiptap/core@2.8.0(@tiptap/pm@2.8.0)))':
+ '@tiptap/extension-ordered-list@2.9.1(@tiptap/core@2.9.1(@tiptap/pm@2.9.1))':
dependencies:
- '@tiptap/core': 2.8.0(@tiptap/pm@2.8.0)
- '@tiptap/extension-list-item': 2.8.0(@tiptap/core@2.8.0(@tiptap/pm@2.8.0))
- '@tiptap/extension-text-style': 2.8.0(@tiptap/core@2.8.0(@tiptap/pm@2.8.0))
+ '@tiptap/core': 2.9.1(@tiptap/pm@2.9.1)
- '@tiptap/extension-paragraph@2.8.0(@tiptap/core@2.8.0(@tiptap/pm@2.8.0))':
+ '@tiptap/extension-paragraph@2.9.1(@tiptap/core@2.9.1(@tiptap/pm@2.9.1))':
dependencies:
- '@tiptap/core': 2.8.0(@tiptap/pm@2.8.0)
+ '@tiptap/core': 2.9.1(@tiptap/pm@2.9.1)
- '@tiptap/extension-strike@2.8.0(@tiptap/core@2.8.0(@tiptap/pm@2.8.0))':
+ '@tiptap/extension-strike@2.9.1(@tiptap/core@2.9.1(@tiptap/pm@2.9.1))':
dependencies:
- '@tiptap/core': 2.8.0(@tiptap/pm@2.8.0)
+ '@tiptap/core': 2.9.1(@tiptap/pm@2.9.1)
- '@tiptap/extension-table-cell@2.8.0(@tiptap/core@2.8.0(@tiptap/pm@2.8.0))':
+ '@tiptap/extension-table-cell@2.9.1(@tiptap/core@2.9.1(@tiptap/pm@2.9.1))':
dependencies:
- '@tiptap/core': 2.8.0(@tiptap/pm@2.8.0)
+ '@tiptap/core': 2.9.1(@tiptap/pm@2.9.1)
- '@tiptap/extension-table-header@2.8.0(@tiptap/core@2.8.0(@tiptap/pm@2.8.0))':
+ '@tiptap/extension-table-header@2.9.1(@tiptap/core@2.9.1(@tiptap/pm@2.9.1))':
dependencies:
- '@tiptap/core': 2.8.0(@tiptap/pm@2.8.0)
+ '@tiptap/core': 2.9.1(@tiptap/pm@2.9.1)
- '@tiptap/extension-table-row@2.8.0(@tiptap/core@2.8.0(@tiptap/pm@2.8.0))':
+ '@tiptap/extension-table-row@2.9.1(@tiptap/core@2.9.1(@tiptap/pm@2.9.1))':
dependencies:
- '@tiptap/core': 2.8.0(@tiptap/pm@2.8.0)
+ '@tiptap/core': 2.9.1(@tiptap/pm@2.9.1)
- '@tiptap/extension-table@2.8.0(@tiptap/core@2.8.0(@tiptap/pm@2.8.0))(@tiptap/pm@2.8.0)':
+ '@tiptap/extension-table@2.9.1(@tiptap/core@2.9.1(@tiptap/pm@2.9.1))(@tiptap/pm@2.9.1)':
dependencies:
- '@tiptap/core': 2.8.0(@tiptap/pm@2.8.0)
- '@tiptap/pm': 2.8.0
+ '@tiptap/core': 2.9.1(@tiptap/pm@2.9.1)
+ '@tiptap/pm': 2.9.1
- '@tiptap/extension-text-style@2.8.0(@tiptap/core@2.8.0(@tiptap/pm@2.8.0))':
+ '@tiptap/extension-text-style@2.9.1(@tiptap/core@2.9.1(@tiptap/pm@2.9.1))':
dependencies:
- '@tiptap/core': 2.8.0(@tiptap/pm@2.8.0)
+ '@tiptap/core': 2.9.1(@tiptap/pm@2.9.1)
- '@tiptap/extension-text@2.8.0(@tiptap/core@2.8.0(@tiptap/pm@2.8.0))':
+ '@tiptap/extension-text@2.9.1(@tiptap/core@2.9.1(@tiptap/pm@2.9.1))':
dependencies:
- '@tiptap/core': 2.8.0(@tiptap/pm@2.8.0)
+ '@tiptap/core': 2.9.1(@tiptap/pm@2.9.1)
- '@tiptap/extension-youtube@2.8.0(@tiptap/core@2.8.0(@tiptap/pm@2.8.0))':
+ '@tiptap/extension-youtube@2.9.1(@tiptap/core@2.9.1(@tiptap/pm@2.9.1))':
dependencies:
- '@tiptap/core': 2.8.0(@tiptap/pm@2.8.0)
+ '@tiptap/core': 2.9.1(@tiptap/pm@2.9.1)
- '@tiptap/html@2.8.0(@tiptap/core@2.8.0(@tiptap/pm@2.8.0))(@tiptap/pm@2.8.0)':
+ '@tiptap/html@2.9.1(@tiptap/core@2.9.1(@tiptap/pm@2.9.1))(@tiptap/pm@2.9.1)':
dependencies:
- '@tiptap/core': 2.8.0(@tiptap/pm@2.8.0)
- '@tiptap/pm': 2.8.0
+ '@tiptap/core': 2.9.1(@tiptap/pm@2.9.1)
+ '@tiptap/pm': 2.9.1
zeed-dom: 0.15.1
- '@tiptap/pm@2.8.0':
+ '@tiptap/pm@2.9.1':
dependencies:
prosemirror-changeset: 2.2.1
prosemirror-collab: 1.3.1
- prosemirror-commands: 1.6.0
+ prosemirror-commands: 1.6.2
prosemirror-dropcursor: 1.8.1
prosemirror-gapcursor: 1.3.2
prosemirror-history: 1.4.1
@@ -5576,47 +5704,46 @@ snapshots:
prosemirror-schema-basic: 1.2.3
prosemirror-schema-list: 1.4.1
prosemirror-state: 1.4.3
- prosemirror-tables: 1.5.0
- prosemirror-trailing-node: 3.0.0(prosemirror-model@1.23.0)(prosemirror-state@1.4.3)(prosemirror-view@1.34.3)
- prosemirror-transform: 1.10.1
- prosemirror-view: 1.34.3
+ prosemirror-tables: 1.6.1
+ prosemirror-trailing-node: 3.0.0(prosemirror-model@1.23.0)(prosemirror-state@1.4.3)(prosemirror-view@1.35.0)
+ prosemirror-transform: 1.10.2
+ prosemirror-view: 1.35.0
- '@tiptap/react@2.8.0(@tiptap/core@2.8.0(@tiptap/pm@2.8.0))(@tiptap/pm@2.8.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)':
+ '@tiptap/react@2.9.1(@tiptap/core@2.9.1(@tiptap/pm@2.9.1))(@tiptap/pm@2.9.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)':
dependencies:
- '@tiptap/core': 2.8.0(@tiptap/pm@2.8.0)
- '@tiptap/extension-bubble-menu': 2.8.0(@tiptap/core@2.8.0(@tiptap/pm@2.8.0))(@tiptap/pm@2.8.0)
- '@tiptap/extension-floating-menu': 2.8.0(@tiptap/core@2.8.0(@tiptap/pm@2.8.0))(@tiptap/pm@2.8.0)
- '@tiptap/pm': 2.8.0
+ '@tiptap/core': 2.9.1(@tiptap/pm@2.9.1)
+ '@tiptap/extension-bubble-menu': 2.9.1(@tiptap/core@2.9.1(@tiptap/pm@2.9.1))(@tiptap/pm@2.9.1)
+ '@tiptap/extension-floating-menu': 2.9.1(@tiptap/core@2.9.1(@tiptap/pm@2.9.1))(@tiptap/pm@2.9.1)
+ '@tiptap/pm': 2.9.1
'@types/use-sync-external-store': 0.0.6
fast-deep-equal: 3.1.3
react: 18.3.1
react-dom: 18.3.1(react@18.3.1)
use-sync-external-store: 1.2.2(react@18.3.1)
- '@tiptap/starter-kit@2.8.0(@tiptap/extension-text-style@2.8.0(@tiptap/core@2.8.0(@tiptap/pm@2.8.0)))':
+ '@tiptap/starter-kit@2.9.1':
dependencies:
- '@tiptap/core': 2.8.0(@tiptap/pm@2.8.0)
- '@tiptap/extension-blockquote': 2.8.0(@tiptap/core@2.8.0(@tiptap/pm@2.8.0))
- '@tiptap/extension-bold': 2.8.0(@tiptap/core@2.8.0(@tiptap/pm@2.8.0))
- '@tiptap/extension-bullet-list': 2.8.0(@tiptap/core@2.8.0(@tiptap/pm@2.8.0))(@tiptap/extension-list-item@2.8.0(@tiptap/core@2.8.0(@tiptap/pm@2.8.0)))(@tiptap/extension-text-style@2.8.0(@tiptap/core@2.8.0(@tiptap/pm@2.8.0)))
- '@tiptap/extension-code': 2.8.0(@tiptap/core@2.8.0(@tiptap/pm@2.8.0))
- '@tiptap/extension-code-block': 2.8.0(@tiptap/core@2.8.0(@tiptap/pm@2.8.0))(@tiptap/pm@2.8.0)
- '@tiptap/extension-document': 2.8.0(@tiptap/core@2.8.0(@tiptap/pm@2.8.0))
- '@tiptap/extension-dropcursor': 2.8.0(@tiptap/core@2.8.0(@tiptap/pm@2.8.0))(@tiptap/pm@2.8.0)
- '@tiptap/extension-gapcursor': 2.8.0(@tiptap/core@2.8.0(@tiptap/pm@2.8.0))(@tiptap/pm@2.8.0)
- '@tiptap/extension-hard-break': 2.8.0(@tiptap/core@2.8.0(@tiptap/pm@2.8.0))
- '@tiptap/extension-heading': 2.8.0(@tiptap/core@2.8.0(@tiptap/pm@2.8.0))
- '@tiptap/extension-history': 2.8.0(@tiptap/core@2.8.0(@tiptap/pm@2.8.0))(@tiptap/pm@2.8.0)
- '@tiptap/extension-horizontal-rule': 2.8.0(@tiptap/core@2.8.0(@tiptap/pm@2.8.0))(@tiptap/pm@2.8.0)
- '@tiptap/extension-italic': 2.8.0(@tiptap/core@2.8.0(@tiptap/pm@2.8.0))
- '@tiptap/extension-list-item': 2.8.0(@tiptap/core@2.8.0(@tiptap/pm@2.8.0))
- '@tiptap/extension-ordered-list': 2.8.0(@tiptap/core@2.8.0(@tiptap/pm@2.8.0))(@tiptap/extension-list-item@2.8.0(@tiptap/core@2.8.0(@tiptap/pm@2.8.0)))(@tiptap/extension-text-style@2.8.0(@tiptap/core@2.8.0(@tiptap/pm@2.8.0)))
- '@tiptap/extension-paragraph': 2.8.0(@tiptap/core@2.8.0(@tiptap/pm@2.8.0))
- '@tiptap/extension-strike': 2.8.0(@tiptap/core@2.8.0(@tiptap/pm@2.8.0))
- '@tiptap/extension-text': 2.8.0(@tiptap/core@2.8.0(@tiptap/pm@2.8.0))
- '@tiptap/pm': 2.8.0
- transitivePeerDependencies:
- - '@tiptap/extension-text-style'
+ '@tiptap/core': 2.9.1(@tiptap/pm@2.9.1)
+ '@tiptap/extension-blockquote': 2.9.1(@tiptap/core@2.9.1(@tiptap/pm@2.9.1))
+ '@tiptap/extension-bold': 2.9.1(@tiptap/core@2.9.1(@tiptap/pm@2.9.1))
+ '@tiptap/extension-bullet-list': 2.9.1(@tiptap/core@2.9.1(@tiptap/pm@2.9.1))
+ '@tiptap/extension-code': 2.9.1(@tiptap/core@2.9.1(@tiptap/pm@2.9.1))
+ '@tiptap/extension-code-block': 2.9.1(@tiptap/core@2.9.1(@tiptap/pm@2.9.1))(@tiptap/pm@2.9.1)
+ '@tiptap/extension-document': 2.9.1(@tiptap/core@2.9.1(@tiptap/pm@2.9.1))
+ '@tiptap/extension-dropcursor': 2.9.1(@tiptap/core@2.9.1(@tiptap/pm@2.9.1))(@tiptap/pm@2.9.1)
+ '@tiptap/extension-gapcursor': 2.9.1(@tiptap/core@2.9.1(@tiptap/pm@2.9.1))(@tiptap/pm@2.9.1)
+ '@tiptap/extension-hard-break': 2.9.1(@tiptap/core@2.9.1(@tiptap/pm@2.9.1))
+ '@tiptap/extension-heading': 2.9.1(@tiptap/core@2.9.1(@tiptap/pm@2.9.1))
+ '@tiptap/extension-history': 2.9.1(@tiptap/core@2.9.1(@tiptap/pm@2.9.1))(@tiptap/pm@2.9.1)
+ '@tiptap/extension-horizontal-rule': 2.9.1(@tiptap/core@2.9.1(@tiptap/pm@2.9.1))(@tiptap/pm@2.9.1)
+ '@tiptap/extension-italic': 2.9.1(@tiptap/core@2.9.1(@tiptap/pm@2.9.1))
+ '@tiptap/extension-list-item': 2.9.1(@tiptap/core@2.9.1(@tiptap/pm@2.9.1))
+ '@tiptap/extension-ordered-list': 2.9.1(@tiptap/core@2.9.1(@tiptap/pm@2.9.1))
+ '@tiptap/extension-paragraph': 2.9.1(@tiptap/core@2.9.1(@tiptap/pm@2.9.1))
+ '@tiptap/extension-strike': 2.9.1(@tiptap/core@2.9.1(@tiptap/pm@2.9.1))
+ '@tiptap/extension-text': 2.9.1(@tiptap/core@2.9.1(@tiptap/pm@2.9.1))
+ '@tiptap/extension-text-style': 2.9.1(@tiptap/core@2.9.1(@tiptap/pm@2.9.1))
+ '@tiptap/pm': 2.9.1
'@types/connect@3.4.36':
dependencies:
@@ -5718,30 +5845,30 @@ snapshots:
'@types/uuid@9.0.8': {}
- '@typescript-eslint/eslint-plugin@8.8.1(@typescript-eslint/parser@8.8.1(eslint@8.57.1)(typescript@5.4.4))(eslint@8.57.1)(typescript@5.4.4)':
+ '@typescript-eslint/eslint-plugin@8.12.2(@typescript-eslint/parser@8.12.2(eslint@8.57.1)(typescript@5.4.4))(eslint@8.57.1)(typescript@5.4.4)':
dependencies:
- '@eslint-community/regexpp': 4.11.1
- '@typescript-eslint/parser': 8.8.1(eslint@8.57.1)(typescript@5.4.4)
- '@typescript-eslint/scope-manager': 8.8.1
- '@typescript-eslint/type-utils': 8.8.1(eslint@8.57.1)(typescript@5.4.4)
- '@typescript-eslint/utils': 8.8.1(eslint@8.57.1)(typescript@5.4.4)
- '@typescript-eslint/visitor-keys': 8.8.1
+ '@eslint-community/regexpp': 4.12.1
+ '@typescript-eslint/parser': 8.12.2(eslint@8.57.1)(typescript@5.4.4)
+ '@typescript-eslint/scope-manager': 8.12.2
+ '@typescript-eslint/type-utils': 8.12.2(eslint@8.57.1)(typescript@5.4.4)
+ '@typescript-eslint/utils': 8.12.2(eslint@8.57.1)(typescript@5.4.4)
+ '@typescript-eslint/visitor-keys': 8.12.2
eslint: 8.57.1
graphemer: 1.4.0
ignore: 5.3.2
natural-compare: 1.4.0
- ts-api-utils: 1.3.0(typescript@5.4.4)
+ ts-api-utils: 1.4.0(typescript@5.4.4)
optionalDependencies:
typescript: 5.4.4
transitivePeerDependencies:
- supports-color
- '@typescript-eslint/parser@8.8.1(eslint@8.57.1)(typescript@5.4.4)':
+ '@typescript-eslint/parser@8.12.2(eslint@8.57.1)(typescript@5.4.4)':
dependencies:
- '@typescript-eslint/scope-manager': 8.8.1
- '@typescript-eslint/types': 8.8.1
- '@typescript-eslint/typescript-estree': 8.8.1(typescript@5.4.4)
- '@typescript-eslint/visitor-keys': 8.8.1
+ '@typescript-eslint/scope-manager': 8.12.2
+ '@typescript-eslint/types': 8.12.2
+ '@typescript-eslint/typescript-estree': 8.12.2(typescript@5.4.4)
+ '@typescript-eslint/visitor-keys': 8.12.2
debug: 4.3.7
eslint: 8.57.1
optionalDependencies:
@@ -5749,54 +5876,54 @@ snapshots:
transitivePeerDependencies:
- supports-color
- '@typescript-eslint/scope-manager@8.8.1':
+ '@typescript-eslint/scope-manager@8.12.2':
dependencies:
- '@typescript-eslint/types': 8.8.1
- '@typescript-eslint/visitor-keys': 8.8.1
+ '@typescript-eslint/types': 8.12.2
+ '@typescript-eslint/visitor-keys': 8.12.2
- '@typescript-eslint/type-utils@8.8.1(eslint@8.57.1)(typescript@5.4.4)':
+ '@typescript-eslint/type-utils@8.12.2(eslint@8.57.1)(typescript@5.4.4)':
dependencies:
- '@typescript-eslint/typescript-estree': 8.8.1(typescript@5.4.4)
- '@typescript-eslint/utils': 8.8.1(eslint@8.57.1)(typescript@5.4.4)
+ '@typescript-eslint/typescript-estree': 8.12.2(typescript@5.4.4)
+ '@typescript-eslint/utils': 8.12.2(eslint@8.57.1)(typescript@5.4.4)
debug: 4.3.7
- ts-api-utils: 1.3.0(typescript@5.4.4)
+ ts-api-utils: 1.4.0(typescript@5.4.4)
optionalDependencies:
typescript: 5.4.4
transitivePeerDependencies:
- eslint
- supports-color
- '@typescript-eslint/types@8.8.1': {}
+ '@typescript-eslint/types@8.12.2': {}
- '@typescript-eslint/typescript-estree@8.8.1(typescript@5.4.4)':
+ '@typescript-eslint/typescript-estree@8.12.2(typescript@5.4.4)':
dependencies:
- '@typescript-eslint/types': 8.8.1
- '@typescript-eslint/visitor-keys': 8.8.1
+ '@typescript-eslint/types': 8.12.2
+ '@typescript-eslint/visitor-keys': 8.12.2
debug: 4.3.7
fast-glob: 3.3.2
is-glob: 4.0.3
minimatch: 9.0.5
semver: 7.6.3
- ts-api-utils: 1.3.0(typescript@5.4.4)
+ ts-api-utils: 1.4.0(typescript@5.4.4)
optionalDependencies:
typescript: 5.4.4
transitivePeerDependencies:
- supports-color
- '@typescript-eslint/utils@8.8.1(eslint@8.57.1)(typescript@5.4.4)':
+ '@typescript-eslint/utils@8.12.2(eslint@8.57.1)(typescript@5.4.4)':
dependencies:
- '@eslint-community/eslint-utils': 4.4.0(eslint@8.57.1)
- '@typescript-eslint/scope-manager': 8.8.1
- '@typescript-eslint/types': 8.8.1
- '@typescript-eslint/typescript-estree': 8.8.1(typescript@5.4.4)
+ '@eslint-community/eslint-utils': 4.4.1(eslint@8.57.1)
+ '@typescript-eslint/scope-manager': 8.12.2
+ '@typescript-eslint/types': 8.12.2
+ '@typescript-eslint/typescript-estree': 8.12.2(typescript@5.4.4)
eslint: 8.57.1
transitivePeerDependencies:
- supports-color
- typescript
- '@typescript-eslint/visitor-keys@8.8.1':
+ '@typescript-eslint/visitor-keys@8.12.2':
dependencies:
- '@typescript-eslint/types': 8.8.1
+ '@typescript-eslint/types': 8.12.2
eslint-visitor-keys: 3.4.3
'@ungap/structured-clone@1.2.0': {}
@@ -5881,15 +6008,15 @@ snapshots:
'@xtuc/long@4.2.2': {}
- acorn-import-attributes@1.9.5(acorn@8.12.1):
+ acorn-import-attributes@1.9.5(acorn@8.14.0):
dependencies:
- acorn: 8.12.1
+ acorn: 8.14.0
- acorn-jsx@5.3.2(acorn@8.12.1):
+ acorn-jsx@5.3.2(acorn@8.14.0):
dependencies:
- acorn: 8.12.1
+ acorn: 8.14.0
- acorn@8.12.1: {}
+ acorn@8.14.0: {}
agent-base@6.0.2:
dependencies:
@@ -5912,10 +6039,6 @@ snapshots:
ansi-regex@6.1.0: {}
- ansi-styles@3.2.1:
- dependencies:
- color-convert: 1.9.3
-
ansi-styles@4.3.0:
dependencies:
color-convert: 2.0.1
@@ -5935,11 +6058,9 @@ snapshots:
aria-hidden@1.2.4:
dependencies:
- tslib: 2.7.0
+ tslib: 2.8.0
- aria-query@5.1.3:
- dependencies:
- deep-equal: 2.2.3
+ aria-query@5.3.2: {}
array-buffer-byte-length@1.0.1:
dependencies:
@@ -6010,11 +6131,11 @@ snapshots:
autoprefixer@10.4.20(postcss@8.4.47):
dependencies:
- browserslist: 4.24.0
- caniuse-lite: 1.0.30001667
+ browserslist: 4.24.2
+ caniuse-lite: 1.0.30001675
fraction.js: 4.3.7
normalize-range: 0.1.2
- picocolors: 1.1.0
+ picocolors: 1.1.1
postcss: 8.4.47
postcss-value-parser: 4.2.0
@@ -6024,13 +6145,13 @@ snapshots:
avvvatars-react@0.4.2(csstype@3.1.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1):
dependencies:
- goober: 2.1.15(csstype@3.1.3)
+ goober: 2.1.16(csstype@3.1.3)
react: 18.3.1
react-dom: 18.3.1(react@18.3.1)
transitivePeerDependencies:
- csstype
- axe-core@4.10.0: {}
+ axe-core@4.10.2: {}
axobject-query@4.1.0: {}
@@ -6053,12 +6174,12 @@ snapshots:
dependencies:
fill-range: 7.1.1
- browserslist@4.24.0:
+ browserslist@4.24.2:
dependencies:
- caniuse-lite: 1.0.30001667
- electron-to-chromium: 1.5.36
+ caniuse-lite: 1.0.30001675
+ electron-to-chromium: 1.5.49
node-releases: 2.0.18
- update-browserslist-db: 1.1.1(browserslist@4.24.0)
+ update-browserslist-db: 1.1.1(browserslist@4.24.2)
buffer-from@1.1.2: {}
@@ -6085,13 +6206,7 @@ snapshots:
camelize@1.0.1: {}
- caniuse-lite@1.0.30001667: {}
-
- chalk@2.4.2:
- dependencies:
- ansi-styles: 3.2.1
- escape-string-regexp: 1.0.5
- supports-color: 5.5.0
+ caniuse-lite@1.0.30001675: {}
chalk@3.0.0:
dependencies:
@@ -6129,16 +6244,10 @@ snapshots:
clsx@2.1.1: {}
- color-convert@1.9.3:
- dependencies:
- color-name: 1.1.3
-
color-convert@2.0.1:
dependencies:
color-name: 1.1.4
- color-name@1.1.3: {}
-
color-name@1.1.4: {}
color-string@1.9.1:
@@ -6163,7 +6272,7 @@ snapshots:
convert-source-map@2.0.0: {}
- cookie@0.5.0: {}
+ cookie@0.7.2: {}
crelt@1.0.6: {}
@@ -6191,6 +6300,11 @@ snapshots:
csstype@3.1.3: {}
+ currency-codes@2.2.0:
+ dependencies:
+ first-match: 0.0.1
+ nub: 0.0.0
+
damerau-levenshtein@1.0.8: {}
data-view-buffer@1.0.1:
@@ -6225,27 +6339,6 @@ snapshots:
dependencies:
ms: 2.1.3
- deep-equal@2.2.3:
- dependencies:
- array-buffer-byte-length: 1.0.1
- call-bind: 1.0.7
- es-get-iterator: 1.1.3
- get-intrinsic: 1.2.4
- is-arguments: 1.1.1
- is-array-buffer: 3.0.4
- is-date-object: 1.0.5
- is-regex: 1.1.4
- is-shared-array-buffer: 1.0.3
- isarray: 2.0.5
- object-is: 1.1.6
- object-keys: 1.1.1
- object.assign: 4.1.5
- regexp.prototype.flags: 1.5.3
- side-channel: 1.0.6
- which-boxed-primitive: 1.0.2
- which-collection: 1.0.2
- which-typed-array: 1.1.15
-
deep-is@0.1.4: {}
deepmerge@2.2.1: {}
@@ -6290,7 +6383,7 @@ snapshots:
eastasianwidth@0.2.0: {}
- electron-to-chromium@1.5.36: {}
+ electron-to-chromium@1.5.49: {}
emoji-mart@5.6.0: {}
@@ -6364,18 +6457,6 @@ snapshots:
es-errors@1.3.0: {}
- es-get-iterator@1.1.3:
- dependencies:
- call-bind: 1.0.7
- get-intrinsic: 1.2.4
- has-symbols: 1.0.3
- is-arguments: 1.1.1
- is-map: 2.0.3
- is-set: 2.0.3
- is-string: 1.0.7
- isarray: 2.0.5
- stop-iteration-iterator: 1.0.0
-
es-iterator-helpers@1.1.0:
dependencies:
call-bind: 1.0.7
@@ -6417,23 +6498,21 @@ snapshots:
escalade@3.2.0: {}
- escape-string-regexp@1.0.5: {}
-
escape-string-regexp@4.0.0: {}
- eslint-config-next@14.2.15(eslint@8.57.1)(typescript@5.4.4):
+ eslint-config-next@14.2.16(eslint@8.57.1)(typescript@5.4.4):
dependencies:
- '@next/eslint-plugin-next': 14.2.15
+ '@next/eslint-plugin-next': 14.2.16
'@rushstack/eslint-patch': 1.10.4
- '@typescript-eslint/eslint-plugin': 8.8.1(@typescript-eslint/parser@8.8.1(eslint@8.57.1)(typescript@5.4.4))(eslint@8.57.1)(typescript@5.4.4)
- '@typescript-eslint/parser': 8.8.1(eslint@8.57.1)(typescript@5.4.4)
+ '@typescript-eslint/eslint-plugin': 8.12.2(@typescript-eslint/parser@8.12.2(eslint@8.57.1)(typescript@5.4.4))(eslint@8.57.1)(typescript@5.4.4)
+ '@typescript-eslint/parser': 8.12.2(eslint@8.57.1)(typescript@5.4.4)
eslint: 8.57.1
eslint-import-resolver-node: 0.3.9
- eslint-import-resolver-typescript: 3.6.3(@typescript-eslint/parser@8.8.1(eslint@8.57.1)(typescript@5.4.4))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.31.0)(eslint@8.57.1)
- eslint-plugin-import: 2.31.0(@typescript-eslint/parser@8.8.1(eslint@8.57.1)(typescript@5.4.4))(eslint-import-resolver-typescript@3.6.3)(eslint@8.57.1)
- eslint-plugin-jsx-a11y: 6.10.0(eslint@8.57.1)
- eslint-plugin-react: 7.37.1(eslint@8.57.1)
- eslint-plugin-react-hooks: 4.6.2(eslint@8.57.1)
+ eslint-import-resolver-typescript: 3.6.3(@typescript-eslint/parser@8.12.2(eslint@8.57.1)(typescript@5.4.4))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.31.0)(eslint@8.57.1)
+ eslint-plugin-import: 2.31.0(@typescript-eslint/parser@8.12.2(eslint@8.57.1)(typescript@5.4.4))(eslint-import-resolver-typescript@3.6.3)(eslint@8.57.1)
+ eslint-plugin-jsx-a11y: 6.10.2(eslint@8.57.1)
+ eslint-plugin-react: 7.37.2(eslint@8.57.1)
+ eslint-plugin-react-hooks: 5.0.0-canary-7118f5dd7-20230705(eslint@8.57.1)
optionalDependencies:
typescript: 5.4.4
transitivePeerDependencies:
@@ -6449,37 +6528,37 @@ snapshots:
transitivePeerDependencies:
- supports-color
- eslint-import-resolver-typescript@3.6.3(@typescript-eslint/parser@8.8.1(eslint@8.57.1)(typescript@5.4.4))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.31.0)(eslint@8.57.1):
+ eslint-import-resolver-typescript@3.6.3(@typescript-eslint/parser@8.12.2(eslint@8.57.1)(typescript@5.4.4))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.31.0)(eslint@8.57.1):
dependencies:
'@nolyfill/is-core-module': 1.0.39
debug: 4.3.7
enhanced-resolve: 5.17.1
eslint: 8.57.1
- eslint-module-utils: 2.12.0(@typescript-eslint/parser@8.8.1(eslint@8.57.1)(typescript@5.4.4))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.3(@typescript-eslint/parser@8.8.1(eslint@8.57.1)(typescript@5.4.4))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.31.0)(eslint@8.57.1))(eslint@8.57.1)
+ eslint-module-utils: 2.12.0(@typescript-eslint/parser@8.12.2(eslint@8.57.1)(typescript@5.4.4))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.3(@typescript-eslint/parser@8.12.2(eslint@8.57.1)(typescript@5.4.4))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.31.0)(eslint@8.57.1))(eslint@8.57.1)
fast-glob: 3.3.2
get-tsconfig: 4.8.1
is-bun-module: 1.2.1
is-glob: 4.0.3
optionalDependencies:
- eslint-plugin-import: 2.31.0(@typescript-eslint/parser@8.8.1(eslint@8.57.1)(typescript@5.4.4))(eslint-import-resolver-typescript@3.6.3)(eslint@8.57.1)
+ eslint-plugin-import: 2.31.0(@typescript-eslint/parser@8.12.2(eslint@8.57.1)(typescript@5.4.4))(eslint-import-resolver-typescript@3.6.3)(eslint@8.57.1)
transitivePeerDependencies:
- '@typescript-eslint/parser'
- eslint-import-resolver-node
- eslint-import-resolver-webpack
- supports-color
- eslint-module-utils@2.12.0(@typescript-eslint/parser@8.8.1(eslint@8.57.1)(typescript@5.4.4))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.3(@typescript-eslint/parser@8.8.1(eslint@8.57.1)(typescript@5.4.4))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.31.0)(eslint@8.57.1))(eslint@8.57.1):
+ eslint-module-utils@2.12.0(@typescript-eslint/parser@8.12.2(eslint@8.57.1)(typescript@5.4.4))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.3(@typescript-eslint/parser@8.12.2(eslint@8.57.1)(typescript@5.4.4))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.31.0)(eslint@8.57.1))(eslint@8.57.1):
dependencies:
debug: 3.2.7
optionalDependencies:
- '@typescript-eslint/parser': 8.8.1(eslint@8.57.1)(typescript@5.4.4)
+ '@typescript-eslint/parser': 8.12.2(eslint@8.57.1)(typescript@5.4.4)
eslint: 8.57.1
eslint-import-resolver-node: 0.3.9
- eslint-import-resolver-typescript: 3.6.3(@typescript-eslint/parser@8.8.1(eslint@8.57.1)(typescript@5.4.4))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.31.0)(eslint@8.57.1)
+ eslint-import-resolver-typescript: 3.6.3(@typescript-eslint/parser@8.12.2(eslint@8.57.1)(typescript@5.4.4))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.31.0)(eslint@8.57.1)
transitivePeerDependencies:
- supports-color
- eslint-plugin-import@2.31.0(@typescript-eslint/parser@8.8.1(eslint@8.57.1)(typescript@5.4.4))(eslint-import-resolver-typescript@3.6.3)(eslint@8.57.1):
+ eslint-plugin-import@2.31.0(@typescript-eslint/parser@8.12.2(eslint@8.57.1)(typescript@5.4.4))(eslint-import-resolver-typescript@3.6.3)(eslint@8.57.1):
dependencies:
'@rtsao/scc': 1.1.0
array-includes: 3.1.8
@@ -6490,7 +6569,7 @@ snapshots:
doctrine: 2.1.0
eslint: 8.57.1
eslint-import-resolver-node: 0.3.9
- eslint-module-utils: 2.12.0(@typescript-eslint/parser@8.8.1(eslint@8.57.1)(typescript@5.4.4))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.3(@typescript-eslint/parser@8.8.1(eslint@8.57.1)(typescript@5.4.4))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.31.0)(eslint@8.57.1))(eslint@8.57.1)
+ eslint-module-utils: 2.12.0(@typescript-eslint/parser@8.12.2(eslint@8.57.1)(typescript@5.4.4))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.3(@typescript-eslint/parser@8.12.2(eslint@8.57.1)(typescript@5.4.4))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.31.0)(eslint@8.57.1))(eslint@8.57.1)
hasown: 2.0.2
is-core-module: 2.15.1
is-glob: 4.0.3
@@ -6502,23 +6581,22 @@ snapshots:
string.prototype.trimend: 1.0.8
tsconfig-paths: 3.15.0
optionalDependencies:
- '@typescript-eslint/parser': 8.8.1(eslint@8.57.1)(typescript@5.4.4)
+ '@typescript-eslint/parser': 8.12.2(eslint@8.57.1)(typescript@5.4.4)
transitivePeerDependencies:
- eslint-import-resolver-typescript
- eslint-import-resolver-webpack
- supports-color
- eslint-plugin-jsx-a11y@6.10.0(eslint@8.57.1):
+ eslint-plugin-jsx-a11y@6.10.2(eslint@8.57.1):
dependencies:
- aria-query: 5.1.3
+ aria-query: 5.3.2
array-includes: 3.1.8
array.prototype.flatmap: 1.3.2
ast-types-flow: 0.0.8
- axe-core: 4.10.0
+ axe-core: 4.10.2
axobject-query: 4.1.0
damerau-levenshtein: 1.0.8
emoji-regex: 9.2.2
- es-iterator-helpers: 1.1.0
eslint: 8.57.1
hasown: 2.0.2
jsx-ast-utils: 3.3.5
@@ -6526,13 +6604,13 @@ snapshots:
minimatch: 3.1.2
object.fromentries: 2.0.8
safe-regex-test: 1.0.3
- string.prototype.includes: 2.0.0
+ string.prototype.includes: 2.0.1
- eslint-plugin-react-hooks@4.6.2(eslint@8.57.1):
+ eslint-plugin-react-hooks@5.0.0-canary-7118f5dd7-20230705(eslint@8.57.1):
dependencies:
eslint: 8.57.1
- eslint-plugin-react@7.37.1(eslint@8.57.1):
+ eslint-plugin-react@7.37.2(eslint@8.57.1):
dependencies:
array-includes: 3.1.8
array.prototype.findlast: 1.2.5
@@ -6575,8 +6653,8 @@ snapshots:
eslint@8.57.1:
dependencies:
- '@eslint-community/eslint-utils': 4.4.0(eslint@8.57.1)
- '@eslint-community/regexpp': 4.11.1
+ '@eslint-community/eslint-utils': 4.4.1(eslint@8.57.1)
+ '@eslint-community/regexpp': 4.12.1
'@eslint/eslintrc': 2.1.4
'@eslint/js': 8.57.1
'@humanwhocodes/config-array': 0.13.0
@@ -6618,8 +6696,8 @@ snapshots:
espree@9.6.1:
dependencies:
- acorn: 8.12.1
- acorn-jsx: 5.3.2(acorn@8.12.1)
+ acorn: 8.14.0
+ acorn-jsx: 5.3.2(acorn@8.14.0)
eslint-visitor-keys: 3.4.3
esquery@1.6.0:
@@ -6671,6 +6749,8 @@ snapshots:
locate-path: 6.0.0
path-exists: 4.0.0
+ first-match@0.0.1: {}
+
flat-cache@3.2.0:
dependencies:
flatted: 3.3.1
@@ -6698,13 +6778,13 @@ snapshots:
react: 18.3.1
react-fast-compare: 2.0.4
tiny-warning: 1.0.3
- tslib: 2.7.0
+ tslib: 2.8.0
fraction.js@4.3.7: {}
framer-motion@10.18.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1):
dependencies:
- tslib: 2.7.0
+ tslib: 2.8.0
optionalDependencies:
'@emotion/is-prop-valid': 0.8.8
react: 18.3.1
@@ -6806,7 +6886,7 @@ snapshots:
define-properties: 1.2.1
gopd: 1.0.1
- goober@2.1.15(csstype@3.1.3):
+ goober@2.1.16(csstype@3.1.3):
dependencies:
csstype: 3.1.3
@@ -6820,8 +6900,6 @@ snapshots:
has-bigints@1.0.2: {}
- has-flag@3.0.0: {}
-
has-flag@4.0.0: {}
has-property-descriptors@1.0.2:
@@ -6866,8 +6944,8 @@ snapshots:
import-in-the-middle@1.11.2:
dependencies:
- acorn: 8.12.1
- acorn-import-attributes: 1.9.5(acorn@8.12.1)
+ acorn: 8.14.0
+ acorn-import-attributes: 1.9.5(acorn@8.14.0)
cjs-module-lexer: 1.4.1
module-details-from-path: 1.0.3
@@ -6890,11 +6968,6 @@ snapshots:
dependencies:
loose-envify: 1.4.0
- is-arguments@1.1.1:
- dependencies:
- call-bind: 1.0.7
- has-tostringtag: 1.0.2
-
is-array-buffer@3.0.4:
dependencies:
call-bind: 1.0.7
@@ -7137,11 +7210,11 @@ snapshots:
dependencies:
yallist: 4.0.0
- lucide-react@0.424.0(react@18.3.1):
+ lucide-react@0.453.0(react@18.3.1):
dependencies:
react: 18.3.1
- magic-string@0.30.11:
+ magic-string@0.30.12:
dependencies:
'@jridgewell/sourcemap-codec': 1.5.0
@@ -7213,32 +7286,32 @@ snapshots:
neo-async@2.6.2: {}
- next-auth@4.24.8(next@14.2.15(@babel/core@7.25.8)(@opentelemetry/api@1.9.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react-dom@18.3.1(react@18.3.1))(react@18.3.1):
+ next-auth@4.24.10(next@14.2.15(@babel/core@7.26.0)(@opentelemetry/api@1.9.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react-dom@18.3.1(react@18.3.1))(react@18.3.1):
dependencies:
- '@babel/runtime': 7.25.7
+ '@babel/runtime': 7.26.0
'@panva/hkdf': 1.2.1
- cookie: 0.5.0
+ cookie: 0.7.2
jose: 4.15.9
- next: 14.2.15(@babel/core@7.25.8)(@opentelemetry/api@1.9.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
+ next: 14.2.15(@babel/core@7.26.0)(@opentelemetry/api@1.9.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
oauth: 0.9.15
openid-client: 5.7.0
- preact: 10.24.2
- preact-render-to-string: 5.2.6(preact@10.24.2)
+ preact: 10.24.3
+ preact-render-to-string: 5.2.6(preact@10.24.3)
react: 18.3.1
react-dom: 18.3.1(react@18.3.1)
uuid: 8.3.2
- next@14.2.15(@babel/core@7.25.8)(@opentelemetry/api@1.9.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1):
+ next@14.2.15(@babel/core@7.26.0)(@opentelemetry/api@1.9.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1):
dependencies:
'@next/env': 14.2.15
'@swc/helpers': 0.5.5
busboy: 1.6.0
- caniuse-lite: 1.0.30001667
+ caniuse-lite: 1.0.30001675
graceful-fs: 4.2.11
postcss: 8.4.31
react: 18.3.1
react-dom: 18.3.1(react@18.3.1)
- styled-jsx: 5.1.1(@babel/core@7.25.8)(react@18.3.1)
+ styled-jsx: 5.1.1(@babel/core@7.26.0)(react@18.3.1)
optionalDependencies:
'@next/swc-darwin-arm64': 14.2.15
'@next/swc-darwin-x64': 14.2.15
@@ -7254,9 +7327,9 @@ snapshots:
- '@babel/core'
- babel-plugin-macros
- nextjs-toploader@1.6.12(next@14.2.15(@babel/core@7.25.8)(@opentelemetry/api@1.9.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react-dom@18.3.1(react@18.3.1))(react@18.3.1):
+ nextjs-toploader@1.6.12(next@14.2.15(@babel/core@7.26.0)(@opentelemetry/api@1.9.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react-dom@18.3.1(react@18.3.1))(react@18.3.1):
dependencies:
- next: 14.2.15(@babel/core@7.25.8)(@opentelemetry/api@1.9.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
+ next: 14.2.15(@babel/core@7.26.0)(@opentelemetry/api@1.9.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
nprogress: 0.2.0
prop-types: 15.8.1
react: 18.3.1
@@ -7274,6 +7347,8 @@ snapshots:
nprogress@0.2.0: {}
+ nub@0.0.0: {}
+
oauth@0.9.15: {}
object-assign@4.1.1: {}
@@ -7284,11 +7359,6 @@ snapshots:
object-inspect@1.13.2: {}
- object-is@1.1.6:
- dependencies:
- call-bind: 1.0.7
- define-properties: 1.2.1
-
object-keys@1.1.1: {}
object.assign@4.1.5:
@@ -7386,10 +7456,12 @@ snapshots:
postgres-date: 1.0.7
postgres-interval: 1.2.0
- picocolors@1.1.0: {}
+ picocolors@1.1.1: {}
picomatch@2.3.1: {}
+ picomatch@4.0.2: {}
+
pify@2.3.0: {}
pirates@4.0.6: {}
@@ -7411,7 +7483,7 @@ snapshots:
postcss-load-config@4.0.2(postcss@8.4.47):
dependencies:
lilconfig: 3.1.2
- yaml: 2.5.1
+ yaml: 2.6.0
optionalDependencies:
postcss: 8.4.47
@@ -7430,19 +7502,19 @@ snapshots:
postcss@8.4.31:
dependencies:
nanoid: 3.3.7
- picocolors: 1.1.0
+ picocolors: 1.1.1
source-map-js: 1.2.1
postcss@8.4.38:
dependencies:
nanoid: 3.3.7
- picocolors: 1.1.0
+ picocolors: 1.1.1
source-map-js: 1.2.1
postcss@8.4.47:
dependencies:
nanoid: 3.3.7
- picocolors: 1.1.0
+ picocolors: 1.1.1
source-map-js: 1.2.1
postgres-array@2.0.0: {}
@@ -7455,12 +7527,12 @@ snapshots:
dependencies:
xtend: 4.0.2
- preact-render-to-string@5.2.6(preact@10.24.2):
+ preact-render-to-string@5.2.6(preact@10.24.3):
dependencies:
- preact: 10.24.2
+ preact: 10.24.3
pretty-format: 3.8.0
- preact@10.24.2: {}
+ preact@10.24.3: {}
prelude-ls@1.2.1: {}
@@ -7474,44 +7546,46 @@ snapshots:
object-assign: 4.1.1
react-is: 16.13.1
+ property-expr@2.0.6: {}
+
prosemirror-changeset@2.2.1:
dependencies:
- prosemirror-transform: 1.10.1
+ prosemirror-transform: 1.10.2
prosemirror-collab@1.3.1:
dependencies:
prosemirror-state: 1.4.3
- prosemirror-commands@1.6.0:
+ prosemirror-commands@1.6.2:
dependencies:
prosemirror-model: 1.23.0
prosemirror-state: 1.4.3
- prosemirror-transform: 1.10.1
+ prosemirror-transform: 1.10.2
prosemirror-dropcursor@1.8.1:
dependencies:
prosemirror-state: 1.4.3
- prosemirror-transform: 1.10.1
- prosemirror-view: 1.34.3
+ prosemirror-transform: 1.10.2
+ prosemirror-view: 1.35.0
prosemirror-gapcursor@1.3.2:
dependencies:
prosemirror-keymap: 1.2.2
prosemirror-model: 1.23.0
prosemirror-state: 1.4.3
- prosemirror-view: 1.34.3
+ prosemirror-view: 1.35.0
prosemirror-history@1.4.1:
dependencies:
prosemirror-state: 1.4.3
- prosemirror-transform: 1.10.1
- prosemirror-view: 1.34.3
+ prosemirror-transform: 1.10.2
+ prosemirror-view: 1.35.0
rope-sequence: 1.3.4
prosemirror-inputrules@1.4.0:
dependencies:
prosemirror-state: 1.4.3
- prosemirror-transform: 1.10.1
+ prosemirror-transform: 1.10.2
prosemirror-keymap@1.2.2:
dependencies:
@@ -7527,7 +7601,7 @@ snapshots:
prosemirror-menu@1.2.4:
dependencies:
crelt: 1.0.6
- prosemirror-commands: 1.6.0
+ prosemirror-commands: 1.6.2
prosemirror-history: 1.4.1
prosemirror-state: 1.4.3
@@ -7543,39 +7617,39 @@ snapshots:
dependencies:
prosemirror-model: 1.23.0
prosemirror-state: 1.4.3
- prosemirror-transform: 1.10.1
+ prosemirror-transform: 1.10.2
prosemirror-state@1.4.3:
dependencies:
prosemirror-model: 1.23.0
- prosemirror-transform: 1.10.1
- prosemirror-view: 1.34.3
+ prosemirror-transform: 1.10.2
+ prosemirror-view: 1.35.0
- prosemirror-tables@1.5.0:
+ prosemirror-tables@1.6.1:
dependencies:
prosemirror-keymap: 1.2.2
prosemirror-model: 1.23.0
prosemirror-state: 1.4.3
- prosemirror-transform: 1.10.1
- prosemirror-view: 1.34.3
+ prosemirror-transform: 1.10.2
+ prosemirror-view: 1.35.0
- prosemirror-trailing-node@3.0.0(prosemirror-model@1.23.0)(prosemirror-state@1.4.3)(prosemirror-view@1.34.3):
+ prosemirror-trailing-node@3.0.0(prosemirror-model@1.23.0)(prosemirror-state@1.4.3)(prosemirror-view@1.35.0):
dependencies:
'@remirror/core-constants': 3.0.0
escape-string-regexp: 4.0.0
prosemirror-model: 1.23.0
prosemirror-state: 1.4.3
- prosemirror-view: 1.34.3
+ prosemirror-view: 1.35.0
- prosemirror-transform@1.10.1:
+ prosemirror-transform@1.10.2:
dependencies:
prosemirror-model: 1.23.0
- prosemirror-view@1.34.3:
+ prosemirror-view@1.35.0:
dependencies:
prosemirror-model: 1.23.0
prosemirror-state: 1.4.3
- prosemirror-transform: 1.10.1
+ prosemirror-transform: 1.10.2
proxy-from-env@1.1.0: {}
@@ -7600,7 +7674,7 @@ snapshots:
react-beautiful-dnd@13.1.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1):
dependencies:
- '@babel/runtime': 7.25.7
+ '@babel/runtime': 7.26.0
css-box-model: 1.2.1
memoize-one: 5.2.1
raf-schd: 4.0.3
@@ -7627,7 +7701,7 @@ snapshots:
react-hot-toast@2.4.1(csstype@3.1.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1):
dependencies:
- goober: 2.1.15(csstype@3.1.3)
+ goober: 2.1.16(csstype@3.1.3)
react: 18.3.1
react-dom: 18.3.1(react@18.3.1)
transitivePeerDependencies:
@@ -7645,7 +7719,7 @@ snapshots:
react-redux@7.2.9(react-dom@18.3.1(react@18.3.1))(react@18.3.1):
dependencies:
- '@babel/runtime': 7.25.7
+ '@babel/runtime': 7.26.0
'@types/react-redux': 7.1.34
hoist-non-react-statics: 3.3.2
loose-envify: 1.4.0
@@ -7659,7 +7733,7 @@ snapshots:
dependencies:
react: 18.3.1
react-style-singleton: 2.2.1(@types/react@18.2.74)(react@18.3.1)
- tslib: 2.7.0
+ tslib: 2.8.0
optionalDependencies:
'@types/react': 18.2.74
@@ -7668,7 +7742,7 @@ snapshots:
react: 18.3.1
react-remove-scroll-bar: 2.3.6(@types/react@18.2.74)(react@18.3.1)
react-style-singleton: 2.2.1(@types/react@18.2.74)(react@18.3.1)
- tslib: 2.7.0
+ tslib: 2.8.0
use-callback-ref: 1.3.2(@types/react@18.2.74)(react@18.3.1)
use-sidecar: 1.1.2(@types/react@18.2.74)(react@18.3.1)
optionalDependencies:
@@ -7684,7 +7758,7 @@ snapshots:
get-nonce: 1.0.1
invariant: 2.2.4
react: 18.3.1
- tslib: 2.7.0
+ tslib: 2.8.0
optionalDependencies:
'@types/react': 18.2.74
@@ -7717,7 +7791,7 @@ snapshots:
redux@4.2.1:
dependencies:
- '@babel/runtime': 7.25.7
+ '@babel/runtime': 7.26.0
reflect.getprototypeof@1.0.6:
dependencies:
@@ -7903,10 +7977,6 @@ snapshots:
dependencies:
type-fest: 0.7.1
- stop-iteration-iterator@1.0.0:
- dependencies:
- internal-slot: 1.0.7
-
streamsearch@1.1.0: {}
string-width@4.2.3:
@@ -7921,8 +7991,9 @@ snapshots:
emoji-regex: 9.2.2
strip-ansi: 7.1.0
- string.prototype.includes@2.0.0:
+ string.prototype.includes@2.0.1:
dependencies:
+ call-bind: 1.0.7
define-properties: 1.2.1
es-abstract: 1.23.3
@@ -7995,12 +8066,12 @@ snapshots:
stylis: 4.3.2
tslib: 2.6.2
- styled-jsx@5.1.1(@babel/core@7.25.8)(react@18.3.1):
+ styled-jsx@5.1.1(@babel/core@7.26.0)(react@18.3.1):
dependencies:
client-only: 0.0.1
react: 18.3.1
optionalDependencies:
- '@babel/core': 7.25.8
+ '@babel/core': 7.26.0
stylis@4.3.2: {}
@@ -8014,10 +8085,6 @@ snapshots:
pirates: 4.0.6
ts-interface-checker: 0.1.13
- supports-color@5.5.0:
- dependencies:
- has-flag: 3.0.0
-
supports-color@7.2.0:
dependencies:
has-flag: 4.0.0
@@ -8034,17 +8101,17 @@ snapshots:
react: 18.3.1
use-sync-external-store: 1.2.2(react@18.3.1)
- tailwind-merge@2.5.3: {}
+ tailwind-merge@2.5.4: {}
- tailwind-scrollbar@3.1.0(tailwindcss@3.4.13):
+ tailwind-scrollbar@3.1.0(tailwindcss@3.4.14):
dependencies:
- tailwindcss: 3.4.13
+ tailwindcss: 3.4.14
- tailwindcss-animate@1.0.7(tailwindcss@3.4.13):
+ tailwindcss-animate@1.0.7(tailwindcss@3.4.14):
dependencies:
- tailwindcss: 3.4.13
+ tailwindcss: 3.4.14
- tailwindcss@3.4.13:
+ tailwindcss@3.4.14:
dependencies:
'@alloc/quick-lru': 5.2.0
arg: 5.0.2
@@ -8059,7 +8126,7 @@ snapshots:
micromatch: 4.0.8
normalize-path: 3.0.0
object-hash: 3.0.0
- picocolors: 1.1.0
+ picocolors: 1.1.1
postcss: 8.4.47
postcss-import: 15.1.0(postcss@8.4.47)
postcss-js: 4.0.1(postcss@8.4.47)
@@ -8079,13 +8146,13 @@ snapshots:
jest-worker: 27.5.1
schema-utils: 3.3.0
serialize-javascript: 6.0.2
- terser: 5.34.1
+ terser: 5.36.0
webpack: 5.94.0
- terser@5.34.1:
+ terser@5.36.0:
dependencies:
'@jridgewell/source-map': 0.3.6
- acorn: 8.12.1
+ acorn: 8.14.0
commander: 2.20.3
source-map-support: 0.5.21
@@ -8099,6 +8166,8 @@ snapshots:
dependencies:
any-promise: 1.3.0
+ tiny-case@1.0.3: {}
+
tiny-invariant@1.3.3: {}
tiny-warning@1.0.3: {}
@@ -8107,15 +8176,15 @@ snapshots:
dependencies:
'@popperjs/core': 2.11.8
- to-fast-properties@2.0.0: {}
-
to-regex-range@5.0.1:
dependencies:
is-number: 7.0.0
+ toposort@2.0.2: {}
+
tr46@0.0.3: {}
- ts-api-utils@1.3.0(typescript@5.4.4):
+ ts-api-utils@1.4.0(typescript@5.4.4):
dependencies:
typescript: 5.4.4
@@ -8130,7 +8199,7 @@ snapshots:
tslib@2.6.2: {}
- tslib@2.7.0: {}
+ tslib@2.8.0: {}
tween-functions@1.2.0: {}
@@ -8142,6 +8211,8 @@ snapshots:
type-fest@0.7.1: {}
+ type-fest@2.19.0: {}
+
typed-array-buffer@1.0.2:
dependencies:
call-bind: 1.0.7
@@ -8189,18 +8260,18 @@ snapshots:
unplugin@1.0.1:
dependencies:
- acorn: 8.12.1
+ acorn: 8.14.0
chokidar: 3.6.0
webpack-sources: 3.2.3
webpack-virtual-modules: 0.5.0
unsplash-js@7.0.19: {}
- update-browserslist-db@1.1.1(browserslist@4.24.0):
+ update-browserslist-db@1.1.1(browserslist@4.24.2):
dependencies:
- browserslist: 4.24.0
+ browserslist: 4.24.2
escalade: 3.2.0
- picocolors: 1.1.0
+ picocolors: 1.1.1
uri-js@4.4.1:
dependencies:
@@ -8209,7 +8280,7 @@ snapshots:
use-callback-ref@1.3.2(@types/react@18.2.74)(react@18.3.1):
dependencies:
react: 18.3.1
- tslib: 2.7.0
+ tslib: 2.8.0
optionalDependencies:
'@types/react': 18.2.74
@@ -8221,7 +8292,7 @@ snapshots:
dependencies:
detect-node-es: 1.1.0
react: 18.3.1
- tslib: 2.7.0
+ tslib: 2.8.0
optionalDependencies:
'@types/react': 18.2.74
@@ -8259,9 +8330,9 @@ snapshots:
'@webassemblyjs/ast': 1.12.1
'@webassemblyjs/wasm-edit': 1.12.1
'@webassemblyjs/wasm-parser': 1.12.1
- acorn: 8.12.1
- acorn-import-attributes: 1.9.5(acorn@8.12.1)
- browserslist: 4.24.0
+ acorn: 8.14.0
+ acorn-import-attributes: 1.9.5(acorn@8.14.0)
+ browserslist: 4.24.2
chrome-trace-event: 1.0.4
enhanced-resolve: 5.17.1
es-module-lexer: 1.5.4
@@ -8350,31 +8421,31 @@ snapshots:
xtend@4.0.2: {}
- y-indexeddb@9.0.12(yjs@13.6.19):
+ y-indexeddb@9.0.12(yjs@13.6.20):
dependencies:
lib0: 0.2.98
- yjs: 13.6.19
+ yjs: 13.6.20
- y-prosemirror@1.2.12(prosemirror-model@1.23.0)(prosemirror-state@1.4.3)(prosemirror-view@1.34.3)(y-protocols@1.0.6(yjs@13.6.19))(yjs@13.6.19):
+ y-prosemirror@1.2.12(prosemirror-model@1.23.0)(prosemirror-state@1.4.3)(prosemirror-view@1.35.0)(y-protocols@1.0.6(yjs@13.6.20))(yjs@13.6.20):
dependencies:
lib0: 0.2.98
prosemirror-model: 1.23.0
prosemirror-state: 1.4.3
- prosemirror-view: 1.34.3
- y-protocols: 1.0.6(yjs@13.6.19)
- yjs: 13.6.19
+ prosemirror-view: 1.35.0
+ y-protocols: 1.0.6(yjs@13.6.20)
+ yjs: 13.6.20
- y-protocols@1.0.6(yjs@13.6.19):
+ y-protocols@1.0.6(yjs@13.6.20):
dependencies:
lib0: 0.2.98
- yjs: 13.6.19
+ yjs: 13.6.20
- y-webrtc@10.3.0(yjs@13.6.19):
+ y-webrtc@10.3.0(yjs@13.6.20):
dependencies:
lib0: 0.2.98
simple-peer: 9.11.1
- y-protocols: 1.0.6(yjs@13.6.19)
- yjs: 13.6.19
+ y-protocols: 1.0.6(yjs@13.6.20)
+ yjs: 13.6.20
optionalDependencies:
ws: 8.18.0
transitivePeerDependencies:
@@ -8386,9 +8457,9 @@ snapshots:
yallist@4.0.0: {}
- yaml@2.5.1: {}
+ yaml@2.6.0: {}
- yjs@13.6.19:
+ yjs@13.6.20:
dependencies:
lib0: 0.2.98
@@ -8402,6 +8473,13 @@ snapshots:
transitivePeerDependencies:
- supports-color
+ yup@1.4.0:
+ dependencies:
+ property-expr: 2.0.6
+ tiny-case: 1.0.3
+ toposort: 2.0.2
+ type-fest: 2.19.0
+
zeed-dom@0.15.1:
dependencies:
css-what: 6.1.0
diff --git a/apps/web/services/courses/chapters.ts b/apps/web/services/courses/chapters.ts
index f04c5b37..6b924904 100644
--- a/apps/web/services/courses/chapters.ts
+++ b/apps/web/services/courses/chapters.ts
@@ -1,4 +1,4 @@
-import { OrderPayload } from '@components/Dashboard/Course/EditCourseStructure/EditCourseStructure'
+import { OrderPayload } from '@components/Dashboard/Pages/Course/EditCourseStructure/EditCourseStructure'
import { getAPIUrl } from '@services/config/config'
import {
RequestBodyWithAuthHeader,
diff --git a/apps/web/services/payments/payments.ts b/apps/web/services/payments/payments.ts
new file mode 100644
index 00000000..37dd7388
--- /dev/null
+++ b/apps/web/services/payments/payments.ts
@@ -0,0 +1,93 @@
+'use server';
+import { getAPIUrl } from '@services/config/config';
+import { RequestBodyWithAuthHeader, errorHandling } from '@services/utils/ts/requests';
+
+export async function getPaymentConfigs(orgId: number, access_token: string) {
+ const result = await fetch(
+ `${getAPIUrl()}payments/${orgId}/config`,
+ RequestBodyWithAuthHeader('GET', null, null, access_token)
+ );
+ const res = await errorHandling(result);
+ return res;
+}
+
+export async function checkPaidAccess(courseId: number, orgId: number, access_token: string) {
+ const result = await fetch(
+ `${getAPIUrl()}payments/${orgId}/courses/${courseId}/access`,
+ RequestBodyWithAuthHeader('GET', null, null, access_token)
+ );
+ const res = await errorHandling(result);
+ return res;
+}
+
+export async function initializePaymentConfig(orgId: number, data: any, provider: string, access_token: string) {
+ const result = await fetch(
+ `${getAPIUrl()}payments/${orgId}/config?provider=${provider}`,
+ RequestBodyWithAuthHeader('POST', data, null, access_token)
+ );
+ const res = await errorHandling(result);
+ return res;
+}
+
+export async function updatePaymentConfig(orgId: number, id: string, data: any, access_token: string) {
+ const result = await fetch(
+ `${getAPIUrl()}payments/${orgId}/config?id=${id}`,
+ RequestBodyWithAuthHeader('PUT', data, null, access_token)
+ );
+ const res = await errorHandling(result);
+ return res;
+}
+
+export async function updateStripeAccountID(orgId: number, data: any, access_token: string) {
+ const result = await fetch(
+ `${getAPIUrl()}payments/${orgId}/stripe/account?stripe_account_id=${data.stripe_account_id}`,
+ RequestBodyWithAuthHeader('PUT', data, null, access_token)
+ );
+ const res = await errorHandling(result);
+ return res;
+}
+
+export async function getStripeOnboardingLink(orgId: number, access_token: string, redirect_uri: string) {
+ const result = await fetch(
+ `${getAPIUrl()}payments/${orgId}/stripe/connect/link?redirect_uri=${redirect_uri}`,
+ RequestBodyWithAuthHeader('POST', null, null, access_token)
+ );
+ const res = await errorHandling(result);
+ return res;
+}
+
+export async function verifyStripeConnection(orgId: number, code: string, access_token: string) {
+ const result = await fetch(
+ `${getAPIUrl()}payments/stripe/oauth/callback?code=${code}&org_id=${orgId}`,
+ RequestBodyWithAuthHeader('GET', null, null, access_token)
+ );
+ const res = await errorHandling(result);
+ return res;
+}
+
+export async function deletePaymentConfig(orgId: number, id: string, access_token: string) {
+ const result = await fetch(
+ `${getAPIUrl()}payments/${orgId}/config?id=${id}`,
+ RequestBodyWithAuthHeader('DELETE', null, null, access_token)
+ );
+ const res = await errorHandling(result);
+ return res;
+}
+
+export async function getOrgCustomers(orgId: number, access_token: string) {
+ const result = await fetch(
+ `${getAPIUrl()}payments/${orgId}/customers`,
+ RequestBodyWithAuthHeader('GET', null, null, access_token)
+ );
+ const res = await errorHandling(result);
+ return res;
+}
+
+export async function getOwnedCourses(orgId: number, access_token: string) {
+ const result = await fetch(
+ `${getAPIUrl()}payments/${orgId}/courses/owned`,
+ RequestBodyWithAuthHeader('GET', null, null, access_token)
+ );
+ const res = await errorHandling(result);
+ return res;
+}
\ No newline at end of file
diff --git a/apps/web/services/payments/products.ts b/apps/web/services/payments/products.ts
new file mode 100644
index 00000000..c1f1ae80
--- /dev/null
+++ b/apps/web/services/payments/products.ts
@@ -0,0 +1,94 @@
+'use server';
+import { getAPIUrl } from '@services/config/config';
+import { RequestBodyWithAuthHeader, getResponseMetadata } from '@services/utils/ts/requests';
+
+export async function getProducts(orgId: number, access_token: string) {
+ const result = await fetch(
+ `${getAPIUrl()}payments/${orgId}/products`,
+ RequestBodyWithAuthHeader('GET', null, null, access_token)
+ );
+ const res = await getResponseMetadata(result);
+ return res;
+}
+
+export async function createProduct(orgId: number, data: any, access_token: string) {
+ const result = await fetch(
+ `${getAPIUrl()}payments/${orgId}/products`,
+ RequestBodyWithAuthHeader('POST', data, null, access_token)
+ );
+ const res = await getResponseMetadata(result);
+ return res;
+}
+
+export async function updateProduct(orgId: number, productId: string, data: any, access_token: string) {
+ const result = await fetch(
+ `${getAPIUrl()}payments/${orgId}/products/${productId}`,
+ RequestBodyWithAuthHeader('PUT', data, null, access_token)
+ );
+ const res = await getResponseMetadata(result);
+ return res;
+}
+
+export async function archiveProduct(orgId: number, productId: string, access_token: string) {
+ const result = await fetch(
+ `${getAPIUrl()}payments/${orgId}/products/${productId}`,
+ RequestBodyWithAuthHeader('DELETE', null, null, access_token)
+ );
+ const res = await getResponseMetadata(result);
+ return res;
+}
+
+export async function getProductDetails(orgId: number, productId: string, access_token: string) {
+ const result = await fetch(
+ `${getAPIUrl()}payments/${orgId}/products/${productId}`,
+ RequestBodyWithAuthHeader('GET', null, null, access_token)
+ );
+ const res = await getResponseMetadata(result);
+ return res;
+}
+
+export async function linkCourseToProduct(orgId: number, productId: string, courseId: string, access_token: string) {
+ const result = await fetch(
+ `${getAPIUrl()}payments/${orgId}/products/${productId}/courses/${courseId}`,
+ RequestBodyWithAuthHeader('POST', null, null, access_token)
+ );
+ const res = await getResponseMetadata(result);
+ return res;
+}
+
+export async function unlinkCourseFromProduct(orgId: number, productId: string, courseId: string, access_token: string) {
+ const result = await fetch(
+ `${getAPIUrl()}payments/${orgId}/products/${productId}/courses/${courseId}`,
+ RequestBodyWithAuthHeader('DELETE', null, null, access_token)
+ );
+ const res = await getResponseMetadata(result);
+ return res;
+}
+
+export async function getCoursesLinkedToProduct(orgId: number, productId: string, access_token: string) {
+ const result = await fetch(
+ `${getAPIUrl()}payments/${orgId}/products/${productId}/courses`,
+ RequestBodyWithAuthHeader('GET', null, null, access_token)
+ );
+ const res = await getResponseMetadata(result);
+ return res;
+}
+
+export async function getProductsByCourse(orgId: number, courseId: string, access_token: string) {
+ const result = await fetch(
+ `${getAPIUrl()}payments/${orgId}/courses/${courseId}/products`,
+ RequestBodyWithAuthHeader('GET', null, null, access_token)
+ );
+ const res = await getResponseMetadata(result);
+ return res;
+}
+
+export async function getStripeProductCheckoutSession(orgId: number, productId: number, redirect_uri: string, access_token: string) {
+ const result = await fetch(
+ `${getAPIUrl()}payments/${orgId}/stripe/checkout/product/${productId}?redirect_uri=${redirect_uri}`,
+ RequestBodyWithAuthHeader('POST', null, null, access_token)
+ );
+ const res = await getResponseMetadata(result);
+ return res;
+}
+
diff --git a/apps/web/services/utils/health.ts b/apps/web/services/utils/health.ts
new file mode 100644
index 00000000..78cd8b63
--- /dev/null
+++ b/apps/web/services/utils/health.ts
@@ -0,0 +1,33 @@
+import { getAPIUrl } from '@services/config/config'
+import {
+ RequestBody,
+ getResponseMetadata,
+} from '@services/utils/ts/requests'
+
+export async function checkHealth() {
+ try {
+ const result = await fetch(
+ `${getAPIUrl()}health`,
+ RequestBody('GET', null, null)
+ )
+
+ if (!result.ok) {
+ return {
+ success: false,
+ status: result.status,
+ HTTPmessage: result.statusText,
+ data: null
+ }
+ }
+
+ const res = await getResponseMetadata(result)
+ return res
+ } catch (error) {
+ return {
+ success: false,
+ status: 503,
+ HTTPmessage: 'Service unavailable',
+ data: null
+ }
+ }
+}
diff --git a/apps/web/tsconfig.json b/apps/web/tsconfig.json
index aa2bce06..1eac0bcb 100644
--- a/apps/web/tsconfig.json
+++ b/apps/web/tsconfig.json
@@ -26,6 +26,7 @@
"@styles/*": ["styles/*"],
"@services/*": ["services/*"],
"@editor/*": ["components/Objects/Editor/*"],
+ "@hooks/*": ["components/Hooks/*"],
"@/*": ["./*"]
}
},
diff --git a/docker-compose.yml b/docker-compose.yml
index 09fd07c5..80ffe7e6 100644
--- a/docker-compose.yml
+++ b/docker-compose.yml
@@ -42,6 +42,6 @@ services:
timeout: 4s
retries: 5
chromadb:
- image: chromadb/chroma:latest
+ image: chromadb/chroma:0.5.16
ports:
- "8000:8000"
| |